[boot] Add initial stubs for loading kernel ELF

This commit is contained in:
Justin C. Miller
2020-05-02 23:58:41 -07:00
parent ec794f4f99
commit f78a99927a
7 changed files with 68 additions and 314 deletions

View File

@@ -60,6 +60,7 @@ modules:
- src/boot/error.cpp - src/boot/error.cpp
- src/boot/fs.cpp - src/boot/fs.cpp
- src/boot/hardware.cpp - src/boot/hardware.cpp
- src/boot/loader.cpp
- src/boot/memory.cpp - src/boot/memory.cpp
nulldrv: nulldrv:

View File

@@ -1,46 +1,30 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#ifndef ELF_VERSION namespace boot {
#define ELF_VERSION 1 namespace elf {
#endif
#ifndef ELF_WORDSIZE constexpr uint8_t version = 1;
#define ELF_WORDSIZE 2 constexpr uint8_t word_size = 2;
#endif constexpr uint8_t endianness = 1;
constexpr uint8_t os_abi = 1;
#ifndef ELF_ENDIAN constexpr uint16_t machine = 0x3e;
#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; const unsigned ELF_PT_LOAD = 1;
const unsigned ELF_ST_PROGBITS = 1; const unsigned ELF_ST_PROGBITS = 1;
const unsigned ELF_ST_NOBITS = 8; const unsigned ELF_ST_NOBITS = 8;
const unsigned long ELF_SHF_ALLOC = 0x2; const unsigned long ELF_SHF_ALLOC = 0x2;
struct elf_ident struct header
{ {
char magic[4]; char magic[4];
uint8_t word_size; uint8_t word_size;
uint8_t endianness; uint8_t endianness;
uint8_t version; uint8_t header_version;
uint8_t os_abi; uint8_t os_abi;
uint64_t reserved; uint64_t reserved;
} __attribute__ ((packed));
struct elf_header
{
struct elf_ident ident;
uint16_t type; uint16_t type;
uint16_t machine; uint16_t machine;
@@ -64,7 +48,7 @@ struct elf_header
uint16_t sh_str_idx; uint16_t sh_str_idx;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct elf_program_header struct program_header
{ {
uint32_t type; uint32_t type;
uint32_t flags; uint32_t flags;
@@ -79,7 +63,7 @@ struct elf_program_header
uint64_t align; uint64_t align;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct elf_section_header struct section_header
{ {
uint32_t name; uint32_t name;
uint32_t type; uint32_t type;
@@ -92,3 +76,6 @@ struct elf_section_header
uint64_t align; uint64_t align;
uint64_t entry_size; uint64_t entry_size;
} __attribute__ ((packed)); } __attribute__ ((packed));
} // namespace elf
} // namespace boot

View File

@@ -1,193 +1,33 @@
#include "elf.h" #include <uefi/boot_services.h>
#include "guids.h" #include <uefi/types.h>
#include "loader.h" #include "loader.h"
#include "memory.h" #include "console.h"
#include "utility.h" #include "elf.h"
#include "error.h"
#define PAGE_SIZE 0x1000 namespace boot {
namespace loader {
static wchar_t kernel_name[] = KERNEL_FILENAME; static bool
static wchar_t initrd_name[] = INITRD_FILENAME; is_elfheader_valid(const elf::header *header)
EFI_STATUS
loader_alloc_aligned(
EFI_BOOT_SERVICES *bootsvc,
EFI_MEMORY_TYPE mem_type,
size_t *length,
void **pages)
{ {
EFI_STATUS status; return false;
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<uint64_t>(aligned) -
reinterpret_cast<uint64_t>(addr)) /
PAGE_SIZE;
if (before) {
con_debug(L" Freeing %d initial pages", before);
bootsvc->FreePages(addr, before);
} }
size_t after = page_count - before; kernel::entrypoint
if (after) { load_elf(
EFI_PHYSICAL_ADDRESS end = const void *data,
reinterpret_cast<EFI_PHYSICAL_ADDRESS>( size_t size,
reinterpret_cast<uint64_t>(aligned) + uefi::boot_services *bs)
page_count * PAGE_SIZE);
con_debug(L" Freeing %d remaining pages", after);
bootsvc->FreePages(end, after);
}
*pages = (void *)aligned;
return EFI_SUCCESS;
}
EFI_STATUS
loader_alloc_pages(
EFI_BOOT_SERVICES *bootsvc,
EFI_MEMORY_TYPE mem_type,
size_t *length,
void **pages)
{ {
EFI_STATUS status; status_line status(L"Loading kernel ELF binary");
size_t page_count = ((*length - 1) / PAGE_SIZE) + 1; if (size < sizeof(elf::header) ||
EFI_PHYSICAL_ADDRESS addr = (EFI_PHYSICAL_ADDRESS)*pages; !is_elfheader_valid(reinterpret_cast<const elf::header*>(data)))
error::raise(uefi::status::load_error, L"Kernel ELF not valid");
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;
/*
struct elf_program_header prog_header; struct elf_program_header prog_header;
for (int i = 0; i < header.ph_num; ++i) { for (int i = 0; i < header.ph_num; ++i) {
@@ -242,57 +82,11 @@ loader_load_elf(
status = file->Close(file); status = file->Close(file);
CHECK_EFI_STATUS_OR_RETURN(status, L"Closing file handle"); CHECK_EFI_STATUS_OR_RETURN(status, L"Closing file handle");
return EFI_SUCCESS; return reinterpret_cast<kernel::entrypoint>(kernel.entrypoint());
*/
return nullptr;
} }
} // namespace loader
EFI_STATUS } // namespace boot
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;
}

View File

@@ -1,35 +1,11 @@
#pragma once #pragma once
#include <efi/efi.h>
#include <stddef.h>
#define PAGE_SIZE 0x1000 #include "kernel_args.h"
#ifndef KERNEL_PHYS_ADDRESS namespace boot {
#define KERNEL_PHYS_ADDRESS 0x100000 namespace loader {
#endif
#ifndef KERNEL_VIRT_ADDRESS kernel::entrypoint load_elf(const void *data, size_t size, uefi::boot_services *bs);
#define KERNEL_VIRT_ADDRESS 0xFFFFFF0000000000
#endif
#ifndef KERNEL_FILENAME } // namespace loader
#define KERNEL_FILENAME L"kernel.elf" } // namespace boot
#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);

View File

@@ -11,6 +11,7 @@
#include "error.h" #include "error.h"
#include "fs.h" #include "fs.h"
#include "hardware.h" #include "hardware.h"
#include "loader.h"
#include "memory.h" #include "memory.h"
#include "kernel_args.h" #include "kernel_args.h"
@@ -38,7 +39,6 @@ struct kernel_header {
uint32_t gitsha; uint32_t gitsha;
}; };
#pragma pack(pop) #pragma pack(pop)
using kernel_entry = void (*)(kernel_args *);
*/ */
namespace boot { namespace boot {
@@ -84,7 +84,8 @@ detect_debug_mode(EFI_RUNTIME_SERVICES *run, kernel_args *header) {
return EFI_SUCCESS; return EFI_SUCCESS;
} }
*/ */
void
kernel::args::module *
load_module( load_module(
fs::file &disk, fs::file &disk,
kernel::args::header *args, kernel::args::header *args,
@@ -100,9 +101,10 @@ load_module(
module.location = file.load(&module.size); module.location = file.load(&module.size);
console::print(L" Loaded at: 0x%lx, %d bytes\r\n", module.location, 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) bootloader_main_uefi(uefi::handle image, uefi::system_table *st, console &con)
{ {
error::uefi_handler handler(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); 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"initrd", L"initrd.img", kernel::args::type::initrd);
kernel::args::module *kernel =
load_module(disk, args, L"kernel", L"jsix.elf", kernel::args::type::kernel); load_module(disk, args, L"kernel", L"jsix.elf", kernel::args::type::kernel);
return uefi::status::success; 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; struct loader_data load;
load.data_length = data_length; load.data_length = data_length;
@@ -253,9 +242,10 @@ efi_main(uefi::handle image_handle, uefi::system_table *st)
error::cpu_assert_handler handler; error::cpu_assert_handler handler;
console con(st->boot_services, st->con_out); 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); while(1);
return uefi::status::success; return uefi::status::unsupported;
} }

View File

@@ -13,7 +13,10 @@ inline constexpr size_t bytes_to_pages(size_t bytes) {
return ((bytes - 1) / page_size) + 1; 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); void mark_pointer_fixup(void **p);
kernel::args::header * allocate_args_structure(uefi::boot_services *bs, size_t max_modules); kernel::args::header * allocate_args_structure(uefi::boot_services *bs, size_t max_modules);

View File

@@ -59,4 +59,7 @@ __attribute__((aligned(alignof(max_align_t))));
#pragma pack(pop) #pragma pack(pop)
} // namespace args } // namespace args
using entrypoint = void (*)(args::header *);
} // namespace kernel } // namespace kernel