From f78a99927a7d30f93a3e54f503e1c2faef245d4c Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sat, 2 May 2020 23:58:41 -0700 Subject: [PATCH] [boot] Add initial stubs for loading kernel ELF --- modules.yaml | 1 + src/boot/elf.h | 41 ++---- src/boot/loader.cpp | 260 ++++---------------------------------- src/boot/loader.h | 36 +----- src/boot/main.cpp | 36 ++---- src/boot/memory.h | 5 +- src/include/kernel_args.h | 3 + 7 files changed, 68 insertions(+), 314 deletions(-) diff --git a/modules.yaml b/modules.yaml index acb897f..7882ef8 100644 --- a/modules.yaml +++ b/modules.yaml @@ -60,6 +60,7 @@ modules: - src/boot/error.cpp - src/boot/fs.cpp - src/boot/hardware.cpp + - src/boot/loader.cpp - src/boot/memory.cpp nulldrv: diff --git a/src/boot/elf.h b/src/boot/elf.h index 146fe74..9743a96 100644 --- a/src/boot/elf.h +++ b/src/boot/elf.h @@ -1,46 +1,30 @@ #pragma once #include -#ifndef ELF_VERSION -#define ELF_VERSION 1 -#endif +namespace boot { +namespace elf { -#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 +constexpr uint8_t version = 1; +constexpr uint8_t word_size = 2; +constexpr uint8_t endianness = 1; +constexpr uint8_t os_abi = 1; +constexpr uint16_t machine = 0x3e; const unsigned ELF_PT_LOAD = 1; const unsigned ELF_ST_PROGBITS = 1; const unsigned ELF_ST_NOBITS = 8; const unsigned long ELF_SHF_ALLOC = 0x2; -struct elf_ident +struct header { char magic[4]; uint8_t word_size; uint8_t endianness; - uint8_t version; + uint8_t header_version; uint8_t os_abi; uint64_t reserved; -} __attribute__ ((packed)); - -struct elf_header -{ - struct elf_ident ident; uint16_t type; uint16_t machine; @@ -64,7 +48,7 @@ struct elf_header uint16_t sh_str_idx; } __attribute__ ((packed)); -struct elf_program_header +struct program_header { uint32_t type; uint32_t flags; @@ -79,7 +63,7 @@ struct elf_program_header uint64_t align; } __attribute__ ((packed)); -struct elf_section_header +struct section_header { uint32_t name; uint32_t type; @@ -92,3 +76,6 @@ struct elf_section_header uint64_t align; uint64_t entry_size; } __attribute__ ((packed)); + +} // namespace elf +} // namespace boot diff --git a/src/boot/loader.cpp b/src/boot/loader.cpp index 004337f..5f2ce60 100644 --- a/src/boot/loader.cpp +++ b/src/boot/loader.cpp @@ -1,193 +1,33 @@ -#include "elf.h" -#include "guids.h" +#include +#include + #include "loader.h" -#include "memory.h" -#include "utility.h" +#include "console.h" +#include "elf.h" +#include "error.h" -#define PAGE_SIZE 0x1000 +namespace boot { +namespace loader { -static wchar_t kernel_name[] = KERNEL_FILENAME; -static wchar_t initrd_name[] = INITRD_FILENAME; - -EFI_STATUS -loader_alloc_aligned( - EFI_BOOT_SERVICES *bootsvc, - EFI_MEMORY_TYPE mem_type, - size_t *length, - void **pages) +static bool +is_elfheader_valid(const elf::header *header) { - EFI_STATUS status; - EFI_PHYSICAL_ADDRESS addr; - - size_t alignment = PAGE_SIZE; - while (alignment < *length) - alignment *= 2; - - size_t page_count = alignment / PAGE_SIZE; - *length = alignment; - - con_debug(L"Trying to find %d aligned pages for %x", page_count, mem_type); - - status = bootsvc->AllocatePages(AllocateAnyPages, mem_type, page_count * 2, &addr); - CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating %d pages for alignment", page_count * 2); - con_debug(L" Found %d pages at %lx", page_count * 2, addr); - - EFI_PHYSICAL_ADDRESS aligned = addr; - aligned = ((aligned - 1) & ~(alignment - 1)) + alignment; - con_debug(L" Aligning %lx to %lx", addr, aligned); - - size_t before = - (reinterpret_cast(aligned) - - reinterpret_cast(addr)) / - PAGE_SIZE; - - if (before) { - con_debug(L" Freeing %d initial pages", before); - bootsvc->FreePages(addr, before); - } - - size_t after = page_count - before; - if (after) { - EFI_PHYSICAL_ADDRESS end = - reinterpret_cast( - reinterpret_cast(aligned) + - page_count * PAGE_SIZE); - con_debug(L" Freeing %d remaining pages", after); - bootsvc->FreePages(end, after); - } - - *pages = (void *)aligned; - return EFI_SUCCESS; + return false; } -EFI_STATUS -loader_alloc_pages( - EFI_BOOT_SERVICES *bootsvc, - EFI_MEMORY_TYPE mem_type, - size_t *length, - void **pages) +kernel::entrypoint +load_elf( + const void *data, + size_t size, + uefi::boot_services *bs) { - EFI_STATUS status; + status_line status(L"Loading kernel ELF binary"); - size_t page_count = ((*length - 1) / PAGE_SIZE) + 1; - EFI_PHYSICAL_ADDRESS addr = (EFI_PHYSICAL_ADDRESS)*pages; - - con_debug(L"Trying to find %d non-aligned pages for %x at %lx", - page_count, mem_type, addr); - - status = bootsvc->AllocatePages(AllocateAddress, mem_type, page_count, &addr); - CHECK_EFI_STATUS_OR_RETURN(status, - L"Allocating %d kernel pages type %x", - page_count, mem_type); - - *length = page_count * PAGE_SIZE; - *pages = (void *)addr; - - return EFI_SUCCESS; -} - -EFI_STATUS -loader_load_initrd( - 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, (wchar_t *)initrd_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", initrd_name); - - char info[sizeof(EFI_FILE_INFO) + 100]; - size_t info_length = sizeof(info); - - status = file->GetInfo(file, &guid_file_info, &info_length, info); - CHECK_EFI_STATUS_OR_RETURN(status, L"Getting file info"); - - data->initrd_length = ((EFI_FILE_INFO *)info)->FileSize; - - status = loader_alloc_aligned( - bootsvc, - memtype_initrd, - &data->initrd_length, - &data->initrd); - CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating pages"); - - status = file->Read(file, &data->initrd_length, data->initrd); - 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_elf( - EFI_BOOT_SERVICES *bootsvc, - EFI_FILE_PROTOCOL *root, - struct loader_data *data) -{ - EFI_STATUS status; - - con_debug(L"Opening kernel file %s", (wchar_t *)kernel_name); - - EFI_FILE_PROTOCOL *file = NULL; - status = root->Open(root, &file, (wchar_t *)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"); - - con_debug(L"Read %u bytes of ELF header", length); - - 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"); - - con_debug(L"ELF is valid, entrypoint %lx", header.entrypoint); - - data->kernel_entry = (void *)header.entrypoint; + if (size < sizeof(elf::header) || + !is_elfheader_valid(reinterpret_cast(data))) + error::raise(uefi::status::load_error, L"Kernel ELF not valid"); + /* struct elf_program_header prog_header; for (int i = 0; i < header.ph_num; ++i) { @@ -242,57 +82,11 @@ loader_load_elf( status = file->Close(file); CHECK_EFI_STATUS_OR_RETURN(status, L"Closing file handle"); - return EFI_SUCCESS; + return reinterpret_cast(kernel.entrypoint()); + */ + + return nullptr; } - -EFI_STATUS -loader_load_kernel( - EFI_BOOT_SERVICES *bootsvc, - struct loader_data *data) -{ - if (data == NULL) - CHECK_EFI_STATUS_OR_RETURN(EFI_INVALID_PARAMETER, L"NULL loader_data"); - - EFI_STATUS status; - EFI_HANDLE *handles = NULL; - size_t handleCount = 0; - - status = bootsvc->LocateHandleBuffer(ByProtocol, &guid_simple_filesystem, NULL, &handleCount, &handles); - CHECK_EFI_STATUS_OR_RETURN(status, L"LocateHandleBuffer"); - - for (unsigned i = 0; i < handleCount; ++i) { - EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fileSystem = NULL; - - status = bootsvc->HandleProtocol(handles[i], &guid_simple_filesystem, (void **)&fileSystem); - CHECK_EFI_STATUS_OR_RETURN(status, L"HandleProtocol"); - - EFI_FILE_PROTOCOL *root = NULL; - status = fileSystem->OpenVolume(fileSystem, &root); - CHECK_EFI_STATUS_OR_RETURN(status, L"OpenVolume"); - - status = loader_load_elf(bootsvc, root, data); - if (status == EFI_NOT_FOUND) - continue; - - CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_elf: %s", kernel_name); - - data->data = (void *)((uint64_t)data->kernel + data->kernel_length); - data->data_length += PAGE_SIZE; // extra page for map growth - - status = loader_alloc_aligned( - bootsvc, - memtype_data, - &data->data_length, - &data->data); - CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_aligned: kernel data"); - - data->initrd = (void *)((uint64_t)data->data + data->data_length); - status = loader_load_initrd(bootsvc, root, data); - CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", initrd_name); - - return EFI_SUCCESS; - } - - return EFI_NOT_FOUND; -} +} // namespace loader +} // namespace boot diff --git a/src/boot/loader.h b/src/boot/loader.h index c412db8..9f923c5 100644 --- a/src/boot/loader.h +++ b/src/boot/loader.h @@ -1,35 +1,11 @@ #pragma once -#include -#include -#define PAGE_SIZE 0x1000 +#include "kernel_args.h" -#ifndef KERNEL_PHYS_ADDRESS -#define KERNEL_PHYS_ADDRESS 0x100000 -#endif +namespace boot { +namespace loader { -#ifndef KERNEL_VIRT_ADDRESS -#define KERNEL_VIRT_ADDRESS 0xFFFFFF0000000000 -#endif +kernel::entrypoint load_elf(const void *data, size_t size, uefi::boot_services *bs); -#ifndef KERNEL_FILENAME -#define KERNEL_FILENAME L"kernel.elf" -#endif - -#ifndef INITRD_FILENAME -#define INITRD_FILENAME L"initrd.img" -#endif - -struct loader_data { - void *kernel; - void *kernel_entry; - size_t kernel_length; - - void *initrd; - size_t initrd_length; - - void *data; - size_t data_length; -}; - -EFI_STATUS loader_load_kernel(EFI_BOOT_SERVICES *bootsvc, struct loader_data *data); +} // namespace loader +} // namespace boot diff --git a/src/boot/main.cpp b/src/boot/main.cpp index 49b9641..523d9c7 100644 --- a/src/boot/main.cpp +++ b/src/boot/main.cpp @@ -11,6 +11,7 @@ #include "error.h" #include "fs.h" #include "hardware.h" +#include "loader.h" #include "memory.h" #include "kernel_args.h" @@ -38,7 +39,6 @@ struct kernel_header { uint32_t gitsha; }; #pragma pack(pop) -using kernel_entry = void (*)(kernel_args *); */ namespace boot { @@ -84,7 +84,8 @@ detect_debug_mode(EFI_RUNTIME_SERVICES *run, kernel_args *header) { return EFI_SUCCESS; } */ -void + +kernel::args::module * load_module( fs::file &disk, kernel::args::header *args, @@ -100,9 +101,10 @@ load_module( module.location = file.load(&module.size); console::print(L" Loaded at: 0x%lx, %d bytes\r\n", module.location, module.size); + return &module; } -uefi::status +kernel::entrypoint bootloader_main_uefi(uefi::handle image, uefi::system_table *st, console &con) { error::uefi_handler handler(con); @@ -122,28 +124,15 @@ bootloader_main_uefi(uefi::handle image, uefi::system_table *st, console &con) fs::file disk = fs::get_boot_volume(image, bs); load_module(disk, args, L"initrd", L"initrd.img", kernel::args::type::initrd); - load_module(disk, args, L"kernel", L"jsix.elf", kernel::args::type::kernel); - return uefi::status::success; + kernel::args::module *kernel = + load_module(disk, args, L"kernel", L"jsix.elf", kernel::args::type::kernel); + + kernel::entrypoint entry = loader::load_elf(kernel->location, kernel->size, bs); + return entry; } /* - // Compute necessary number of data pages - // - size_t data_length = 0; - status = memory_get_map_length(bootsvc, &data_length); - CHECK_EFI_STATUS_OR_FAIL(status); - - size_t header_size = sizeof(kernel_args); - const size_t header_align = alignof(kernel_args); - if (header_size % header_align) - header_size += header_align - (header_size % header_align); - - data_length += header_size; - - // Load the kernel image from disk and check it - // - console::print(L"Loading kernel into memory...\r\n"); struct loader_data load; load.data_length = data_length; @@ -253,9 +242,10 @@ efi_main(uefi::handle image_handle, uefi::system_table *st) error::cpu_assert_handler handler; console con(st->boot_services, st->con_out); - /*return*/ bootloader_main_uefi(image_handle, st, con); + kernel::entrypoint kernel_main = + bootloader_main_uefi(image_handle, st, con); while(1); - return uefi::status::success; + return uefi::status::unsupported; } diff --git a/src/boot/memory.h b/src/boot/memory.h index 7d06cf9..f6436e2 100644 --- a/src/boot/memory.h +++ b/src/boot/memory.h @@ -13,7 +13,10 @@ inline constexpr size_t bytes_to_pages(size_t bytes) { return ((bytes - 1) / page_size) + 1; } -void init_pointer_fixup(uefi::boot_services *bs, uefi::runtime_services *rs); +void init_pointer_fixup( + uefi::boot_services *bs, + uefi::runtime_services *rs); + void mark_pointer_fixup(void **p); kernel::args::header * allocate_args_structure(uefi::boot_services *bs, size_t max_modules); diff --git a/src/include/kernel_args.h b/src/include/kernel_args.h index 541288d..267aaaa 100644 --- a/src/include/kernel_args.h +++ b/src/include/kernel_args.h @@ -59,4 +59,7 @@ __attribute__((aligned(alignof(max_align_t)))); #pragma pack(pop) } // namespace args + +using entrypoint = void (*)(args::header *); + } // namespace kernel