From 14f51436d788a2af7d2f774ad72d322d40457447 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sat, 28 Apr 2018 02:12:08 -0700 Subject: [PATCH] Load ELF file with bootloader instead of flat binary --- Makefile | 9 +-- src/boot/elf.h | 77 +++++++++++++++++++ src/boot/loader.c | 156 ++++++++++++++++++++++++++------------ src/boot/loader.h | 14 +--- src/boot/main.c | 13 +--- src/boot/memory.c | 1 - src/include/kernel_data.h | 3 - src/kernel/boot.s | 3 +- 8 files changed, 193 insertions(+), 83 deletions(-) create mode 100644 src/boot/elf.h diff --git a/Makefile b/Makefile index 77876df..1ade57a 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ ARCH_D := src/arch/$(ARCH) VERSION ?= $(shell git describe --dirty --always) GITSHA ?= $(shell git rev-parse --short HEAD) -KERNEL_FILENAME:= popcorn.bin +KERNEL_FILENAME:= popcorn.elf KERNEL_FONT := assets/fonts/tamsyn8x16r.psf MODULES := kutil @@ -169,9 +169,6 @@ $(BUILD_D)/boot.debug.efi: $(BUILD_D)/boot.elf -j .debug_aranges -j .debug_line -j .debug_macinfo \ --target=efi-app-$(ARCH) $^ $@ -$(BUILD_D)/%.bin: $(BUILD_D)/%.elf - $(OBJC) $< -O binary $@ - $(BUILD_D)/boot.dump: $(BUILD_D)/boot.efi $(OBJD) -D -S $< > $@ @@ -201,7 +198,7 @@ $(BUILD_D)/flash.img: $(OVMF) $(BUILD_D)/screenfont.psf: $(KERNEL_FONT) cp $^ $@ -$(BUILD_D)/fs.img: $(BUILD_D)/boot.efi $(BUILD_D)/kernel.bin $(BUILD_D)/screenfont.psf +$(BUILD_D)/fs.img: $(BUILD_D)/boot.efi $(BUILD_D)/kernel.elf $(BUILD_D)/screenfont.psf $(eval TEMPFILE := $(shell mktemp --suffix=.img)) dd if=/dev/zero of=$@.tmp bs=512 count=93750 $(PARTED) $@.tmp -s -a minimal mklabel gpt @@ -212,7 +209,7 @@ $(BUILD_D)/fs.img: $(BUILD_D)/boot.efi $(BUILD_D)/kernel.bin $(BUILD_D)/screenfo mmd -i $(TEMPFILE) ::/EFI mmd -i $(TEMPFILE) ::/EFI/BOOT mcopy -i $(TEMPFILE) $(BUILD_D)/boot.efi ::/EFI/BOOT/BOOTX64.efi - mcopy -i $(TEMPFILE) $(BUILD_D)/kernel.bin ::$(KERNEL_FILENAME) + mcopy -i $(TEMPFILE) $(BUILD_D)/kernel.elf ::$(KERNEL_FILENAME) mcopy -i $(TEMPFILE) $(BUILD_D)/screenfont.psf ::screenfont.psf mlabel -i $(TEMPFILE) ::Popcorn_OS dd if=$(TEMPFILE) of=$@.tmp bs=512 count=91669 seek=2048 conv=notrunc diff --git a/src/boot/elf.h b/src/boot/elf.h new file mode 100644 index 0000000..b0e1a3e --- /dev/null +++ b/src/boot/elf.h @@ -0,0 +1,77 @@ +#pragma once +#include + +#ifndef ELF_VERSION +#define ELF_VERSION 1 +#endif + +#ifndef ELF_WORDSIZE +#define ELF_WORDSIZE 2 +#endif + +#ifndef ELF_ENDIAN +#define ELF_ENDIAN 1 +#endif + +#ifndef ELF_OSABI +#define ELF_OSABI 0 +#endif + +#ifndef ELF_MACHINE +#define ELF_MACHINE 0x3e +#endif + +const unsigned ELF_PT_LOAD = 1; + +struct elf_ident +{ + char magic[4]; + + uint8_t word_size; + uint8_t endianness; + uint8_t version; + uint8_t os_abi; + + uint64_t reserved; +} __attribute__ ((packed)); + +struct elf_header +{ + struct elf_ident ident; + + uint16_t type; + uint16_t machine; + + uint32_t version; + + uint64_t entrypoint; + uint64_t ph_offset; + uint64_t sh_offset; + + uint32_t flags; + + uint16_t eh_size; + + uint16_t ph_entsize; + uint16_t ph_num; + + uint16_t sh_entsize; + uint16_t sh_num; + + uint16_t sh_str_idx; +} __attribute__ ((packed)); + +struct elf_program_header +{ + uint32_t type; + uint32_t flags; + uint64_t offset; + + uint64_t vaddr; + uint64_t paddr; + + uint64_t file_size; + uint64_t mem_size; + + uint64_t align; +} __attribute__ ((packed)); diff --git a/src/boot/loader.c b/src/boot/loader.c index c5b0886..c042846 100644 --- a/src/boot/loader.c +++ b/src/boot/loader.c @@ -1,3 +1,4 @@ +#include "elf.h" #include "guids.h" #include "loader.h" #include "memory.h" @@ -13,8 +14,7 @@ loader_alloc_pages( EFI_BOOT_SERVICES *bootsvc, EFI_MEMORY_TYPE mem_type, size_t *length, - void **pages, - void **next) + void **pages) { EFI_STATUS status; @@ -33,31 +33,26 @@ loader_alloc_pages( *length = page_count * PAGE_SIZE; *pages = (void *)addr; - *next = (void*)(addr + *length); return EFI_SUCCESS; } EFI_STATUS -loader_load_file( +loader_load_font( EFI_BOOT_SERVICES *bootsvc, EFI_FILE_PROTOCOL *root, - const CHAR16 *filename, - EFI_MEMORY_TYPE mem_type, - void **data, - size_t *length, - void **next) + struct loader_data *data) { EFI_STATUS status; EFI_FILE_PROTOCOL *file = NULL; - status = root->Open(root, &file, (CHAR16 *)filename, EFI_FILE_MODE_READ, + status = root->Open(root, &file, (CHAR16 *)font_name, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM); if (status == EFI_NOT_FOUND) return status; - CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", filename); + CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", font_name); char info[sizeof(EFI_FILE_INFO) + 100]; size_t info_length = sizeof(info); @@ -65,12 +60,16 @@ loader_load_file( status = file->GetInfo(file, &guid_file_info, &info_length, info); CHECK_EFI_STATUS_OR_RETURN(status, L"Getting file info"); - *length = ((EFI_FILE_INFO *)info)->FileSize; + data->font_length = ((EFI_FILE_INFO *)info)->FileSize; - status = loader_alloc_pages(bootsvc, mem_type, length, data, next); + status = loader_alloc_pages( + bootsvc, + KERNEL_FONT_MEMTYPE, + &data->font_length, + &data->font); CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating pages"); - status = file->Read(file, length, *data); + status = file->Read(file, &data->font_length, data->font); CHECK_EFI_STATUS_OR_RETURN(status, L"Reading file"); status = file->Close(file); @@ -78,7 +77,96 @@ loader_load_file( return EFI_SUCCESS; } - + + +EFI_STATUS +loader_load_elf( + EFI_BOOT_SERVICES *bootsvc, + EFI_FILE_PROTOCOL *root, + struct loader_data *data) +{ + EFI_STATUS status; + + EFI_FILE_PROTOCOL *file = NULL; + status = root->Open(root, &file, (CHAR16 *)kernel_name, EFI_FILE_MODE_READ, + EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM); + + if (status == EFI_NOT_FOUND) + return status; + + uint64_t length = 0; + data->kernel = 0; + data->kernel_entry = 0; + data->kernel_length = 0; + + CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", kernel_name); + + struct elf_header header; + + length = sizeof(struct elf_header); + status = file->Read(file, &length, &header); + CHECK_EFI_STATUS_OR_RETURN(status, L"Reading ELF header"); + + if (length < sizeof(struct elf_header)) + CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"Incomplete read of ELF header"); + + static const char expected[] = {0x7f, 'E', 'L', 'F'}; + for (int i = 0; i < sizeof(expected); ++i) { + if (header.ident.magic[i] != expected[i]) + CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"Bad ELF magic number"); + } + + if (header.ident.word_size != ELF_WORDSIZE) + CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"ELF load error: 32 bit ELF not supported"); + + if (header.ph_entsize != sizeof(struct elf_program_header)) + CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"ELF load error: program header size mismatch"); + + if (header.ident.version != ELF_VERSION || + header.version != ELF_VERSION) + CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"ELF load error: wrong ELF version"); + + if (header.ident.endianness != 1 || + header.ident.os_abi != 0 || + header.machine != 0x3e) + CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"ELF load error: wrong machine architecture"); + + data->kernel_entry = (void *)header.entrypoint; + + struct elf_program_header prog_header; + for (int i = 0; i < header.ph_num; ++i) { + status = file->SetPosition(file, header.ph_offset + i * header.ph_entsize); + CHECK_EFI_STATUS_OR_RETURN(status, L"Setting ELF file position"); + + length = header.ph_entsize; + status = file->Read(file, &length, &prog_header); + CHECK_EFI_STATUS_OR_RETURN(status, L"Reading ELF program header"); + + if (prog_header.type != ELF_PT_LOAD) continue; + + status = file->SetPosition(file, prog_header.offset); + CHECK_EFI_STATUS_OR_RETURN(status, L"Setting ELF file position"); + + length = prog_header.mem_size; + void *addr = (void *)(prog_header.vaddr - KERNEL_VIRT_ADDRESS); + status = loader_alloc_pages(bootsvc, KERNEL_MEMTYPE, &length, &addr); + CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating kernel pages"); + + if (data->kernel == 0) + data->kernel = addr; + data->kernel_length = (uint64_t)addr + length - (uint64_t)data->kernel; + + length = prog_header.file_size; + status = file->Read(file, &length, addr); + CHECK_EFI_STATUS_OR_RETURN(status, L"Reading file"); + } + + status = file->Close(file); + CHECK_EFI_STATUS_OR_RETURN(status, L"Closing file handle"); + + return EFI_SUCCESS; +} + EFI_STATUS loader_load_kernel( @@ -105,54 +193,26 @@ loader_load_kernel( status = fileSystem->OpenVolume(fileSystem, &root); CHECK_EFI_STATUS_OR_RETURN(status, L"OpenVolume"); - void *next = NULL; - - data->kernel = (void *)KERNEL_PHYS_ADDRESS; - status = loader_load_file( - bootsvc, - root, - kernel_name, - KERNEL_MEMTYPE, - &data->kernel, - &data->kernel_length, - &next); + status = loader_load_elf(bootsvc, root, data); if (status == EFI_NOT_FOUND) continue; CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", kernel_name); - data->font = next; - status = loader_load_file( - bootsvc, - root, - font_name, - KERNEL_FONT_MEMTYPE, - &data->font, - &data->font_length, - &next); + data->font = (void *)((uint64_t)data->kernel + data->kernel_length); + status = loader_load_font(bootsvc, root, data); CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", font_name); - data->data = next; + data->data = (void *)((uint64_t)data->font + data->font_length); data->data_length += PAGE_SIZE; // extra page for map growth status = loader_alloc_pages( bootsvc, KERNEL_DATA_MEMTYPE, &data->data_length, - &data->data, - &next); + &data->data); CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel data"); - data->log = next; - data->log_length = KERNEL_LOG_PAGES * PAGE_SIZE; - status = loader_alloc_pages( - bootsvc, - KERNEL_LOG_MEMTYPE, - &data->log_length, - &data->log, - &next); - CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel log"); - return EFI_SUCCESS; } diff --git a/src/boot/loader.h b/src/boot/loader.h index 96b50be..ea9e207 100644 --- a/src/boot/loader.h +++ b/src/boot/loader.h @@ -28,20 +28,12 @@ #define KERNEL_DATA_MEMTYPE 0x80000002 #endif -#ifndef KERNEL_LOG_MEMTYPE -#define KERNEL_LOG_MEMTYPE 0x80000003 -#endif - -#ifndef KERNEL_LOG_PAGES -#define KERNEL_LOG_PAGES 4 -#endif - #ifndef KERNEL_PT_MEMTYPE #define KERNEL_PT_MEMTYPE 0x80000004 #endif #ifndef KERNEL_FILENAME -#define KERNEL_FILENAME L"kernel.bin" +#define KERNEL_FILENAME L"kernel.elf" #endif #ifndef KERNEL_FONT @@ -50,6 +42,7 @@ struct loader_data { void *kernel; + void *kernel_entry; size_t kernel_length; void *font; @@ -57,9 +50,6 @@ struct loader_data { void *data; size_t data_length; - - void *log; - size_t log_length; }; EFI_STATUS loader_load_kernel(EFI_BOOT_SERVICES *bootsvc, struct loader_data *data); diff --git a/src/boot/main.c b/src/boot/main.c index 104721b..8415dda 100644 --- a/src/boot/main.c +++ b/src/boot/main.c @@ -27,8 +27,6 @@ struct kernel_header { uint8_t minor; uint16_t patch; uint32_t gitsha; - - void *entrypoint; }; #pragma pack(pop) @@ -77,8 +75,6 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table) // Load the kernel image from disk and check it // - void *kernel_image = NULL, *kernel_data = NULL; - uint64_t kernel_length = 0; con_printf(L"Loading kernel into memory...\r\n"); struct loader_data load; @@ -89,7 +85,6 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table) con_printf(L" %u image bytes at 0x%x\r\n", load.kernel_length, load.kernel); con_printf(L" %u font bytes at 0x%x\r\n", load.font_length, load.font); con_printf(L" %u data bytes at 0x%x\r\n", load.data_length, load.data); - con_printf(L" %u log bytes at 0x%x\r\n", load.log_length, load.log); struct kernel_header *version = (struct kernel_header *)load.kernel; if (version->magic != KERNEL_HEADER_MAGIC) { @@ -100,9 +95,9 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table) con_printf(L" Kernel version %d.%d.%d %x%s\r\n", version->major, version->minor, version->patch, version->gitsha & 0x0fffffff, version->gitsha & 0xf0000000 ? "*" : ""); - con_printf(L" Entrypoint 0x%x\r\n", version->entrypoint); + con_printf(L" Entrypoint 0x%x\r\n", load.kernel_entry); - void (*kernel_main)() = version->entrypoint; + void (*kernel_main)() = load.kernel_entry; memory_mark_pointer_fixup((void **)&kernel_main); // Set up the kernel data pages to pass to the kernel @@ -124,10 +119,6 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table) data_header->data_length = load.data_length; memory_mark_pointer_fixup((void **)&data_header->data); - data_header->log = load.log; - data_header->log_length = load.log_length; - memory_mark_pointer_fixup((void **)&data_header->log); - data_header->memory_map = (EFI_MEMORY_DESCRIPTOR *)(data_header + 1); memory_mark_pointer_fixup((void **)&data_header->memory_map); diff --git a/src/boot/memory.c b/src/boot/memory.c index 34fd337..ce9953e 100644 --- a/src/boot/memory.c +++ b/src/boot/memory.c @@ -192,7 +192,6 @@ memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map) case KERNEL_MEMTYPE: case KERNEL_FONT_MEMTYPE: case KERNEL_DATA_MEMTYPE: - case KERNEL_LOG_MEMTYPE: d->Attribute |= EFI_MEMORY_RUNTIME; default: diff --git a/src/include/kernel_data.h b/src/include/kernel_data.h index 1a0577f..c5724cb 100644 --- a/src/include/kernel_data.h +++ b/src/include/kernel_data.h @@ -22,9 +22,6 @@ struct popcorn_data { void *data; size_t data_length; - void *log; - size_t log_length; - void *memory_map; size_t memory_map_length; size_t memory_map_desc_size; diff --git a/src/kernel/boot.s b/src/kernel/boot.s index 231c4e0..67fed39 100644 --- a/src/kernel/boot.s +++ b/src/kernel/boot.s @@ -6,12 +6,11 @@ global _header _header: dd MAGIC ; Kernel header magic dw 1 ; Header version 1 - dw 1 ; Kernel header length + dw 16 ; Kernel header length db VERSION_MAJOR ; Kernel version db VERSION_MINOR dw VERSION_PATCH dd VERSION_GITSHA - dq _start ; Kernel entrypoint section .text align 16