[boot] Add initial stubs for loading kernel ELF
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -1,46 +1,30 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
|
||||
@@ -1,193 +1,33 @@
|
||||
#include "elf.h"
|
||||
#include "guids.h"
|
||||
#include <uefi/boot_services.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
#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<uint64_t>(aligned) -
|
||||
reinterpret_cast<uint64_t>(addr)) /
|
||||
PAGE_SIZE;
|
||||
|
||||
if (before) {
|
||||
con_debug(L" Freeing %d initial pages", before);
|
||||
bootsvc->FreePages(addr, before);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t after = page_count - before;
|
||||
if (after) {
|
||||
EFI_PHYSICAL_ADDRESS end =
|
||||
reinterpret_cast<EFI_PHYSICAL_ADDRESS>(
|
||||
reinterpret_cast<uint64_t>(aligned) +
|
||||
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)
|
||||
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<const elf::header*>(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>(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
|
||||
|
||||
@@ -1,35 +1,11 @@
|
||||
#pragma once
|
||||
#include <efi/efi.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#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
|
||||
|
||||
@@ -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);
|
||||
|
||||
kernel::args::module *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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -59,4 +59,7 @@ __attribute__((aligned(alignof(max_align_t))));
|
||||
#pragma pack(pop)
|
||||
|
||||
} // namespace args
|
||||
|
||||
using entrypoint = void (*)(args::header *);
|
||||
|
||||
} // namespace kernel
|
||||
|
||||
Reference in New Issue
Block a user