]> git.dujemihanovic.xyz Git - nameless-os.git/commitdiff
Load ELF kernel instead of flat binary 0.1.1
authorDuje Mihanović <duje.mihanovic@skole.hr>
Tue, 21 Jun 2022 18:40:37 +0000 (20:40 +0200)
committerDuje Mihanović <duje.mihanovic@skole.hr>
Wed, 22 Jun 2022 16:03:19 +0000 (18:03 +0200)
Also moves most of the protected mode portion of the bootloader to a C
environment rather than assembly.

12 files changed:
.gitignore
Makefile
boot/x86/Makefile
boot/x86/stage3/elf.c [new file with mode: 0644]
boot/x86/stage3/elf.h [new file with mode: 0644]
boot/x86/stage3/loader.s
boot/x86/stage3/paging.c
boot/x86/stage3/paging.h [new file with mode: 0644]
boot/x86/stage3/prekernel.c [new file with mode: 0644]
kernel/entry.s
kernel/linker.ld
nameless2disk

index 9fc5bb791de04c0037818d3ffd4aa4f55db8b732..505290e343ff63ed7a90f33cc3aed7a0d612bd91 100644 (file)
@@ -1,6 +1,3 @@
-toolchain/
-boot.img
-boot/x86/boot
 **/*.o
 **/*.bin
 **/*.elf
index 714528a346d610ace66c17c2933e0ea8e6b7752b..f254d0c8ed42952a44a7d1cd42ad6c699b727b63 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -25,34 +25,31 @@ debug: all
 
 boot/x86/mbr: boot/x86/mbr.s
 boot/x86/vbr-fat32: boot/x86/vbr-fat32.s boot/x86/fat32/*.s 
-boot/x86/stage3/LOADER.BIN: boot/x86/stage3/*.s boot/x86/stage3/*.c boot/x86/fat32/*.s
-boot/x86/stage3/loader.elf: boot/x86/stage3/*.s boot/x86/stage3/*.c boot/x86/fat32/*.s
+boot/x86/stage3/LOADER.BIN: boot/x86/stage3/*.s boot/x86/stage3/*.c boot/x86/fat32/*.s boot/x86/stage3/stage3.ld
+boot/x86/stage3/loader.elf: boot/x86/stage3/*.s boot/x86/stage3/*.c boot/x86/fat32/*.s boot/x86/stage3/stage3.ld
 $(BOOTLOADER_OBJ):
        $(MAKE) -C boot/x86
 
-boot/x86/disk.img: boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN boot/x86/disk.dump kernel/kernel.bin
+boot/x86/disk.img: boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN boot/x86/disk.dump kernel/kernel.elf
        truncate -s1G boot/x86/disk.img
        sfdisk boot/x86/disk.img < boot/x86/disk.dump
        mkfs.fat -F 32 --offset 2048 boot/x86/disk.img
        dd if=boot/x86/mbr of=boot/x86/disk.img bs=440 count=1 conv=notrunc
        dd if=boot/x86/vbr-fat32 of=boot/x86/disk.img bs=1 skip=90 seek=1048666 conv=notrunc
        mcopy -i boot/x86/disk.img@@1M boot/x86/stage3/LOADER.BIN ::.
-       mcopy -i boot/x86/disk.img@@1M kernel/kernel.bin ::./KERNEL.BIN
-
-kernel/kernel.bin: ${KERNEL_OBJ} kernel/linker.ld
-       $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ}
+       mcopy -i boot/x86/disk.img@@1M kernel/kernel.elf ::./KERNEL.ELF
 
 kernel/arch/x86/irq/sample_handler.o: kernel/arch/x86/irq/sample_handler.c
        $(CC) $(CFLAGS) -mgeneral-regs-only -c $< -o $@
 
-kernel/kernel.elf: kernel/kernel.bin
-       $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ} -Wl,--oformat=elf32-i386
+kernel/kernel.elf: $(KERNEL_OBJ)
+       $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ} -lgcc
        i686-elf-objcopy --only-keep-debug kernel/kernel.elf kernel/kernel.dbg
        i686-elf-objcopy --add-gnu-debuglink=kernel/kernel.dbg kernel/kernel.elf
        i686-elf-strip --strip-unneeded kernel/kernel.elf
 
 clean:
-       -rm kernel/kernel.{bin,dbg,elf} ${KERNEL_OBJ}
+       -rm kernel/kernel.{dbg,elf} ${KERNEL_OBJ}
        cd boot/x86 && $(MAKE) clean
 
 .PHONY: default all clean run debug bootloader
index a5bad92491c5763fbdc74b3cd95b48f66d55d446..e75e8dc23573c664b7cbeb8de2ab66ae69720b39 100644 (file)
@@ -1,10 +1,10 @@
 default: mbr vbr-fat32 stage3/LOADER.BIN stage3/loader.elf
 
-CFLAGS=-g -ffreestanding
+CFLAGS=-g -ffreestanding -Istage3
 LD=i686-elf-gcc
-LDFLAGS=-T stage3/stage3.ld -nostdlib -g
+LDFLAGS=-T stage3/stage3.ld -ffreestanding -nostdlib -g
 
-STAGE3_OBJ=stage3/loader.o stage3/paging.o
+STAGE3_OBJ=stage3/loader.o stage3/paging.o stage3/elf.o stage3/prekernel.o
 
 mbr: mbr.s
        $(AS) $(ASFLAGS) -w-zeroing -o $@ $<
@@ -13,10 +13,10 @@ vbr-fat32: vbr-fat32.s fat32/*.s
        $(AS) $(ASFLAGS) -o $@ $<
 
 stage3/loader.elf: $(STAGE3_OBJ)
-       $(CC) $(LDFLAGS) -o $@ $^ -Wl,--oformat=elf32-i386
+       $(CC) $(LDFLAGS) -o $@ $^ -lgcc -Wl,--oformat=elf32-i386
 
 stage3/LOADER.BIN: $(STAGE3_OBJ)
-       $(CC) $(LDFLAGS) -o $@ $^
+       $(CC) $(LDFLAGS) -o $@ $^ -lgcc
 
 stage3/loader.o: stage3/loader.s stage3/*.s
        $(AS) $(ASFLAGS) -f elf -g dwarf2 -DGIT_REVISION=\"$(GIT_REV)\" -o $@ $<
diff --git a/boot/x86/stage3/elf.c b/boot/x86/stage3/elf.c
new file mode 100644 (file)
index 0000000..d3d215f
--- /dev/null
@@ -0,0 +1,78 @@
+/* Code for parsing ELF binaries (the kernel) */
+
+#include <elf.h>
+#include <paging.h>
+#include <stdint.h>
+
+int check_elf_header(struct elf_header *header)
+{
+       if (header->magic != ELF_MAGIC)
+               return ELF_HEADER_INVALID;
+       if (header->bits != ELF_32BIT)
+               return MISMATCHED_SYSTEM;
+       if (header->endianness != ELF_LITTLE_ENDIAN)
+               return MISMATCHED_SYSTEM;
+       if (header->header_ver != 1)
+               return ELF_HEADER_INVALID;
+       if (header->os_abi != ELF_SYSV_ABI)
+               return MISMATCHED_SYSTEM;
+       if (header->type != ELF_TYPE_EXECUTABLE)
+               return NOT_EXECUTABLE;
+       if (header->machine != ELF_X86_ISA)
+               return MISMATCHED_SYSTEM;
+       if (header->elf_ver != 1)
+               return ELF_HEADER_INVALID;
+
+       return 0;
+}
+
+int setup_elf_executable(struct elf_header *header)
+{
+       /* Check the header. */
+       int header_valid = check_elf_header(header);
+       if (header_valid)
+               return header_valid;
+
+       /* Get the program header. The casting is necessary because without it
+        * the code will add some multiple of the program header offset rather
+        * than the actual offset. */
+       struct program_header *prog_hdr = (struct program_header *) ((uint32_t) header+header->program_hdr);
+       for (int i=0; i<header->ph_entry_count; i++) {
+               /* We're only interested in loadable segments. */
+               if (prog_hdr[i].seg_type != ELF_PT_LOAD)
+                       continue;
+
+               /* Calculate the memory range. */
+               void *phys_start, *phys_end, *virt_end;
+               int flags;
+               phys_start = (void *) header + prog_hdr[i].file_offset;
+               if (prog_hdr[i].memsz & 0xfff) { /* Align if needed. */
+                       phys_end = phys_start+((prog_hdr[i].memsz+0x1000) & ~0xfff);
+                       virt_end = prog_hdr[i].vaddr+((prog_hdr[i].memsz+0x1000) & ~0xfff);
+               }
+               else {
+                       phys_end = phys_start+prog_hdr[i].memsz;
+                       virt_end = prog_hdr[i].vaddr+prog_hdr[i].memsz;
+               }
+
+               flags = prog_hdr[i].flags & ELF_FLAG_WRITABLE;
+
+               /* Map the range. */
+               int ret = map_range(phys_start, phys_end, prog_hdr[i].vaddr, virt_end, flags);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int execute_elf(struct elf_header *header, void *arg1, int arg2)
+{
+       /* Check the header and map all needed memory ranges. */
+       int ret = setup_elf_executable(header);
+       if (ret)
+               return ret;
+
+       /* Execute the kernel. */
+       header->entry(arg1, arg2);
+}
diff --git a/boot/x86/stage3/elf.h b/boot/x86/stage3/elf.h
new file mode 100644 (file)
index 0000000..9692633
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef BOOT_X86_ELF_H
+#define BOOT_X86_ELF_H
+
+#include <stdint.h>
+
+/* Some constants we will test against. */
+#define ELF_MAGIC 0x464c457f
+#define ELF_32BIT 0x1
+#define ELF_LITTLE_ENDIAN 0x1
+#define ELF_SYSV_ABI 0x0
+#define ELF_TYPE_EXECUTABLE 0x2
+#define ELF_X86_ISA 0x3
+
+#define ELF_PT_LOAD 0x1
+
+#define ELF_FLAG_EXECUTABLE 0x1
+#define ELF_FLAG_WRITABLE 0x2
+#define ELF_FLAG_READABLE 0x4
+
+/* Return error codes for the functions. */
+#define ELF_HEADER_INVALID 5
+#define MISMATCHED_SYSTEM 6
+#define NOT_EXECUTABLE 7
+
+struct elf_header {
+       uint32_t magic;
+       uint8_t bits;
+       uint8_t endianness;
+       uint8_t header_ver; /* always 1 */
+       uint8_t os_abi;
+       uint8_t padding[8];
+       uint16_t type;
+       uint16_t machine;
+       uint32_t elf_ver; /* always 1 */
+       void (*entry)(void *, int);
+       uint32_t program_hdr; /* offset into file */
+       uint32_t section_hdr;
+       uint32_t flags;
+       uint16_t hdr_size;
+       uint16_t ph_entry_size;
+       uint16_t ph_entry_count;
+       uint16_t sh_entry_size;
+       uint16_t sh_entry_count;
+       uint16_t sh_name_index;
+};
+
+struct program_header {
+       uint32_t seg_type;
+       uint32_t file_offset;
+       void *vaddr;
+       uint32_t undefined;
+       uint32_t filesz;
+       uint32_t memsz;
+       uint32_t flags;
+       uint32_t align;
+};
+
+extern int execute_elf(struct elf_header *header, void *arg1, int arg2);
+
+#endif
index 9ed71021c5d42b291033df366f9231f3b8522b44..87598f4b0c93a6d95cbb1aa21dcf6a87723ff1da 100644 (file)
@@ -1,9 +1,7 @@
 bits 16
 cpu 686
 
-extern enable_paging
-extern map_range
-extern set_up_page_directory
+extern run_kernel
 
 section .text
 %include "../fat32/fat32-structs.s"
@@ -63,9 +61,11 @@ _start:
        pop si
        pop cx
        print kernel_found
+       mov eax, [es:di+dir_entry.filesize]
+       mov [KERNEL_SIZE], eax
        mov ax, [es:di+dir_entry.firstclushi]
        shl eax, 16
-       mov ax, [es:di+(dir_entry.firstcluslo)]
+       mov ax, [es:di+dir_entry.firstcluslo]
        call print_dword
        mov edi, 0x100000
        print kernel_loading
@@ -177,47 +177,18 @@ in_protected:
        mov fs, ax
        mov gs, ax
 
-       push dword 0
-       push 0xc0003000
-       push 0xc0000000
-       push 0x103000
+       push dword [KERNEL_SIZE]
        push 0x100000
-       call map_range
-       cmp eax, 0
-       jne .error
-       add esp, 20
-       push dword 2
-       push 0xc0009000
-       push 0xc0003000
-       push 0x109000
-       push 0x103000
-       call map_range
-       cmp eax, 0
-       jne .error
-       add esp, 20
-       push dword 2
-       push 0x100000
-       push dword 0
-       push 0x100000
-       push dword 0
-       call map_range
-       cmp eax, 0
-       jne .error
-       add esp, 20
-
-       call set_up_page_directory
-       call enable_paging
-
-       jmp 0xc0000000
-       nop
-.error:
-       mov dword [0xb8000], 0xcf28cf3a
-       hlt
-       jmp $-1
+       push ebx
+       push edi
+       call run_kernel
+
+section .bss
+KERNEL_SIZE: resd 1
 
 section .rodata
 
-kernel_name: db "KERNEL  BIN"
+kernel_name: db "KERNEL  ELF"
 begin: db "Nameless Bootloader revision ", GIT_REVISION, 0xd, 0xa, 0
 a20_enabled: db "A20 has been enabled", 0xd, 0xa, "Searching for kernel...", 0xd, 0xa, 0
 a20_fail: db "Failed to enable A20, giving up!", 0xd, 0xa, 0
@@ -225,7 +196,7 @@ crit_err: db "A critical error occurred, dumping registers now: ", 0xd, 0xa, 0
 kernel_found: db "Found kernel at cluster ", 0
 kernel_loading: db 0xd, 0xa, "Loading kernel...", 0xd, 0xa, 0
 kernel_loaded: db "Kernel successfully loaded.", 0xd, 0xa, "Setting up kernel environment and running kernel...", 0xd, 0xa, 0
-missing_kernel: db "Could not find KERNEL.BIN", 0xd, 0xa, 0
+missing_kernel: db "Could not find KERNEL.ELF", 0xd, 0xa, 0
 eax_s: db "EAX: ", 0
 ebx_s: db "EBX: ", 0
 ecx_s: db "ECX: ", 0
index 0482dedf9f98d63f3f2e28f7e53c431ec4c715fb..5a0e5f707db9806fcf84a69930da23810f2369f2 100644 (file)
@@ -1,12 +1,9 @@
 /* Code for enabling paging */
 
 #include <stdint.h>
+#include <paging.h>
 
-#define ADDRESS_NOT_ALIGNED 1
-#define INVL_ADDRESS 2
-#define ADDRESS_ALREADY_MAPPED 3
-#define ADDRESS_RANGE_MISMATCHED 4
-
+/* Preallocated page tables. */
 static uint32_t page_directory[1024] __attribute__((aligned(4096))) __attribute__((section(".data")));
 static uint32_t page_table_firstmb[1024] __attribute__((aligned(4096))) __attribute__((section(".data")));
 static uint32_t page_table_kernel[1024] __attribute__((aligned(4096))) __attribute__((section(".data")));
diff --git a/boot/x86/stage3/paging.h b/boot/x86/stage3/paging.h
new file mode 100644 (file)
index 0000000..487943e
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef BOOT_X86_PAGING_H
+#define BOOT_X86_PAGING_H
+
+#define ADDRESS_NOT_ALIGNED 1
+#define INVL_ADDRESS 2
+#define ADDRESS_ALREADY_MAPPED 3
+#define ADDRESS_RANGE_MISMATCHED 4
+
+extern int map_range(void *phys_start, void *phys_end, void *virt_start, void *virt_end, int flags);
+extern void set_up_page_directory();
+extern void enable_paging();
+
+#endif
diff --git a/boot/x86/stage3/prekernel.c b/boot/x86/stage3/prekernel.c
new file mode 100644 (file)
index 0000000..280c350
--- /dev/null
@@ -0,0 +1,62 @@
+#include <elf.h>
+#include <paging.h>
+
+/* This bit of code will be run right after the segment registers have been set
+ * up for protected mode. */
+
+const char *error_messages[] = {
+       "Tried to page an invalid address, the bootloader is buggy!",
+       "Tried to page an invalid address, the bootloader is buggy!",
+       "Tried to page an invalid address, the bootloader is buggy!",
+       "Tried to page an invalid address, the bootloader is buggy!",
+       "KERNEL.ELF is not a valid ELF file!",
+       "KERNEL.ELF is a non-x86 or userspace executable!",
+       "KERNEL.ELF is not an executable ELF file!"
+};
+
+void printerr(const char *error)
+{
+       char *message = (char *) error;
+       for (int i=0;;i++) {
+               char temp = *message;
+               if (!temp) return;
+               *(char *) (0xb8000+i*2) = temp;
+               *(char *) (0xb8000+1+i*2) = 0xcf;
+               message++;
+       }
+}
+
+void halt()
+{
+loop:
+       asm("hlt":);
+       goto loop;
+}
+
+void run_kernel(void *e820_map, unsigned int e820_map_size, struct elf_header *kernel, unsigned int kernel_size)
+{
+       /* Before trying to parse our kernel ELF, let's set up paging. */
+       unsigned int bootloader_mem_size = 0x100000 + kernel_size;
+
+       /* Align the size to a 4K boundary. */
+       if (bootloader_mem_size & 0xfff) {
+               bootloader_mem_size += 0x1000;
+               bootloader_mem_size &= ~0xfff;
+       }
+
+       /* Map the bootloader memory. */
+       int ret = map_range(0, (void *) bootloader_mem_size, 0, (void *) bootloader_mem_size, 2);
+       if (ret) {
+               printerr(error_messages[ret-1]);
+               halt();
+       }
+       set_up_page_directory();
+       enable_paging();
+
+       /* Parse and run the kernel ELF. */
+       ret = execute_elf(kernel, e820_map, e820_map_size);
+       if (ret) {
+               printerr(error_messages[ret-1]);
+               halt();
+       }
+}
index c38ac764d8eb68e74f8f97413881d3b69ec0273a..d6490a1159587a81cda63257b3ee3e9028fdaf68 100644 (file)
@@ -2,12 +2,31 @@ bits 32
 extern kmain
 extern __STACK_BOTTOM__
 
+section .text
 global _start
 _start:
+       ; Here, the stack will look like:
+       ; return address, map pointer, map size.
+
+       ; Skip the return address.
+       add esp, 4
+       ; Save the kernel arguments.
+       ; Can this be done without the global variables?
+       pop dword [e820_map]
+       pop dword [e820_map_size]
+       sub esp, 12
+       ; Do the stack switch.
        mov ebp, __STACK_BOTTOM__
        mov esp, ebp
-       push ebx
-       push edi
+       ; Restore the kernel arguments.
+       push dword [e820_map_size]
+       push dword [e820_map]
+       ; Call the actual kernel.
        call kmain
        hlt
        jmp $-1
+
+; Temporarily save arguments here to survive the upcoming stack switch.
+section .bss
+e820_map: resd 1
+e820_map_size: resd 1
index a7300751065656c085993d56b48971614c8637fc..a6fb8e48ed2d2289fde99b19e8bb976ac75d004c 100644 (file)
@@ -1,5 +1,4 @@
 ENTRY(_start)
-OUTPUT_FORMAT(binary)
 
 SECTIONS
 {
index 28b3427ca0063b2dd83fb2402fba23d28111264c..ddd8960718596f83cdd3a98f83d8d85c81cd0e9f 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 # Shell script to install Nameless to a disk.
 
-BINARIES=( boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN kernel/kernel.bin )
+BINARIES=( boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN kernel/kernel.elf )
 
 check_binaries() {
        for i in "${BINARIES[@]}"; do
@@ -72,7 +72,7 @@ install_blkdev() {
                exit 1
        fi
        cp boot/x86/stage3/LOADER.BIN $mountpoint/LOADER.BIN
-       cp kernel/kernel.bin $mountpoint/KERNEL.BIN
+       cp kernel/kernel.elf $mountpoint/KERNEL.ELF
        if ! [ $? -eq 0 ]; then
                echo An error occurred!
                exit 1