Parse ELF and load kernel, specify mem types

* Very bare-bones ELF parsing to load the kernel
* Custom memory type values for allocated memory
This commit is contained in:
Justin C. Miller
2020-05-09 21:25:45 -07:00
parent f78a99927a
commit 9aa749e877
9 changed files with 212 additions and 205 deletions

View File

@@ -7,13 +7,13 @@ namespace elf {
constexpr uint8_t version = 1;
constexpr uint8_t word_size = 2;
constexpr uint8_t endianness = 1;
constexpr uint8_t os_abi = 1;
constexpr uint8_t os_abi = 0;
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;
const unsigned PT_LOAD = 1;
const unsigned ST_PROGBITS = 1;
const unsigned ST_NOBITS = 8;
const unsigned long SHF_ALLOC = 0x2;
struct header
{

View File

@@ -51,7 +51,7 @@ file::open(const wchar_t *path)
}
void *
file::load(size_t *out_size)
file::load(size_t *out_size, uefi::memory_type mem_type)
{
uint8_t buffer[sizeof(uefi::protos::file_info) + 100];
size_t size = sizeof(buffer);
@@ -69,8 +69,7 @@ file::load(size_t *out_size)
try_or_raise(
m_bs->allocate_pages(
uefi::allocate_type::any_pages,
uefi::memory_type::loader_data,
pages, &data),
mem_type, pages, &data),
L"Could not allocate pages to load file");
size = info->file_size;

View File

@@ -15,7 +15,9 @@ public:
~file();
file open(const wchar_t *path);
void * load(size_t *out_size);
void * load(
size_t *out_size,
uefi::memory_type mem_type = uefi::memory_type::loader_data);
private:
friend file get_boot_volume(uefi::handle, uefi::boot_services*);

View File

@@ -5,87 +5,86 @@
#include "console.h"
#include "elf.h"
#include "error.h"
#include "memory.h"
namespace boot {
namespace loader {
using memory::offset_ptr;
static bool
is_elfheader_valid(const elf::header *header)
{
return false;
return
header->magic[0] == 0x7f &&
header->magic[1] == 'E' &&
header->magic[2] == 'L' &&
header->magic[3] == 'F' &&
header->word_size == elf::word_size &&
header->endianness == elf::endianness &&
header->os_abi == elf::os_abi &&
header->machine == elf::machine &&
header->header_version == elf::version;
}
kernel::entrypoint
load_elf(
loaded_elf
load(
const void *data,
size_t size,
uefi::boot_services *bs)
{
status_line status(L"Loading kernel ELF binary");
const elf::header *header = reinterpret_cast<const elf::header*>(data);
if (size < sizeof(elf::header) ||
!is_elfheader_valid(reinterpret_cast<const elf::header*>(data)))
if (size < sizeof(elf::header) || !is_elfheader_valid(header))
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) {
uintptr_t kernel_start = 0;
uintptr_t kernel_end = 0;
for (int i = 0; i < header->ph_num; ++i) {
ptrdiff_t offset = header->ph_offset + i * header->ph_entsize;
const elf::program_header *pheader =
offset_ptr<elf::program_header>(data, offset);
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;
length = prog_header.mem_size;
void *addr = (void *)(prog_header.vaddr - KERNEL_VIRT_ADDRESS);
status = loader_alloc_pages(bootsvc, memtype_kernel, &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;
}
con_debug(L"Read %d ELF program headers", header.ph_num);
struct elf_section_header sec_header;
for (int i = 0; i < header.sh_num; ++i) {
status = file->SetPosition(file, header.sh_offset + i * header.sh_entsize);
CHECK_EFI_STATUS_OR_RETURN(status, L"Setting ELF file position");
length = header.sh_entsize;
status = file->Read(file, &length, &sec_header);
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading ELF section header");
if ((sec_header.flags & ELF_SHF_ALLOC) == 0) {
if (pheader->type != elf::PT_LOAD)
continue;
if (kernel_start == 0 || pheader->vaddr < kernel_start)
kernel_start = pheader->vaddr;
if (pheader->vaddr + pheader->mem_size > kernel_end)
kernel_end = pheader->vaddr + pheader->mem_size;
}
void *addr = (void *)(sec_header.addr - KERNEL_VIRT_ADDRESS);
void *pages = nullptr;
size_t num_pages = memory::bytes_to_pages(kernel_end - kernel_start);
try_or_raise(
bs->allocate_pages(uefi::allocate_type::any_pages,
memory::kernel_type, num_pages, &pages),
L"Failed allocating space for kernel code");
if (sec_header.type == ELF_ST_PROGBITS) {
status = file->SetPosition(file, sec_header.offset);
CHECK_EFI_STATUS_OR_RETURN(status, L"Setting ELF file position");
for (int i = 0; i < header->ph_num; ++i) {
ptrdiff_t offset = header->ph_offset + i * header->ph_entsize;
const elf::program_header *pheader =
offset_ptr<elf::program_header>(data, offset);
length = sec_header.size;
status = file->Read(file, &length, addr);
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading file");
} else if (sec_header.type == ELF_ST_NOBITS) {
bootsvc->SetMem(addr, sec_header.size, 0);
if (pheader->type != elf::PT_LOAD)
continue;
void *data_start = offset_ptr<void>(data, pheader->offset);
void *program_start = offset_ptr<void>(pages, pheader->vaddr - kernel_start);
bs->copy_mem(program_start, data_start, pheader->mem_size);
}
}
con_debug(L"Read %d ELF section headers", header.ph_num);
status = file->Close(file);
CHECK_EFI_STATUS_OR_RETURN(status, L"Closing file handle");
loaded_elf result;
result.data = pages;
result.vaddr = kernel_start;
result.entrypoint = header->entrypoint;
console::print(L" Kernel loaded at: 0x%lx\r\n", result.data);
console::print(L" Kernel virtual address: 0x%lx\r\n", result.vaddr);
console::print(L" Kernel entrypoint: 0x%lx\r\n", result.entrypoint);
return reinterpret_cast<kernel::entrypoint>(kernel.entrypoint());
*/
return nullptr;
return result;
}
} // namespace loader

View File

@@ -1,11 +1,16 @@
#pragma once
#include "kernel_args.h"
namespace boot {
namespace loader {
kernel::entrypoint load_elf(const void *data, size_t size, uefi::boot_services *bs);
struct loaded_elf
{
void *data;
uintptr_t vaddr;
uintptr_t entrypoint;
};
loaded_elf load(const void *data, size_t size, uefi::boot_services *bs);
} // namespace loader
} // namespace boot

View File

@@ -91,20 +91,20 @@ load_module(
kernel::args::header *args,
const wchar_t *name,
const wchar_t *path,
kernel::args::type type)
kernel::args::mod_type type)
{
status_line status(L"Loading module", name);
fs::file file = disk.open(path);
kernel::args::module &module = args->modules[args->num_modules++];
module.type = type;
module.location = file.load(&module.size);
module.location = file.load(&module.size, memory::module_type);
console::print(L" Loaded at: 0x%lx, %d bytes\r\n", module.location, module.size);
return &module;
}
kernel::entrypoint
loader::loaded_elf
bootloader_main_uefi(uefi::handle image, uefi::system_table *st, console &con)
{
error::uefi_handler handler(con);
@@ -123,13 +123,17 @@ bootloader_main_uefi(uefi::handle image, uefi::system_table *st, console &con)
args->acpi_table = hw::find_acpi_table(st);
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::mod_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::mod_type::kernel);
kernel::entrypoint entry = loader::load_elf(kernel->location, kernel->size, bs);
return entry;
loader::loaded_elf kernel_elf =
loader::load(kernel->location, kernel->size, bs);
memory::get_mappings(bs);
return kernel_elf;
}
/*
@@ -242,7 +246,7 @@ efi_main(uefi::handle image_handle, uefi::system_table *st)
error::cpu_assert_handler handler;
console con(st->boot_services, st->con_out);
kernel::entrypoint kernel_main =
loader::loaded_elf kernel =
bootloader_main_uefi(image_handle, st, con);
while(1);

View File

@@ -8,53 +8,43 @@
namespace boot {
namespace memory {
/*
const EFI_MEMORY_TYPE memtype_kernel = static_cast<EFI_MEMORY_TYPE>(0x80000000ul);
const EFI_MEMORY_TYPE memtype_data = static_cast<EFI_MEMORY_TYPE>(0x80000001ul);
const EFI_MEMORY_TYPE memtype_initrd = static_cast<EFI_MEMORY_TYPE>(0x80000002ul);
const EFI_MEMORY_TYPE memtype_scratch = static_cast<EFI_MEMORY_TYPE>(0x80000003ul);
#define INCREMENT_DESC(p, b) (EFI_MEMORY_DESCRIPTOR*)(((uint8_t*)(p))+(b))
*/
size_t fixup_pointer_index = 0;
void **fixup_pointers[64];
uint64_t *new_pml4 = 0;
/*
const wchar_t *memory_type_names[] = {
L"EfiReservedMemoryType",
L"EfiLoaderCode",
L"EfiLoaderData",
L"EfiBootServicesCode",
L"EfiBootServicesData",
L"EfiRuntimeServicesCode",
L"EfiRuntimeServicesData",
L"EfiConventionalMemory",
L"EfiUnusableMemory",
L"EfiACPIReclaimMemory",
L"EfiACPIMemoryNVS",
L"EfiMemoryMappedIO",
L"EfiMemoryMappedIOPortSpace",
L"EfiPalCode",
L"EfiPersistentMemory",
static const wchar_t *memory_type_names[] = {
L"reserved memory type",
L"loader code",
L"loader data",
L"boot services code",
L"boot services data",
L"runtime services code",
L"runtime services data",
L"conventional memory",
L"unusable memory",
L"acpi reclaim memory",
L"acpi memory nvs",
L"memory mapped io",
L"memory mapped io port space",
L"pal code",
L"persistent memory"
};
static const wchar_t *
memory_type_name(UINT32 value)
memory_type_name(uefi::memory_type t)
{
if (value >= (sizeof(memory_type_names) / sizeof(wchar_t *))) {
switch (value) {
case memtype_kernel: return L"Kernel Data";
case memtype_data: return L"Kernel Data";
case memtype_initrd: return L"Initial Ramdisk";
case memtype_scratch: return L"Kernel Scratch Space";
if (t < uefi::memory_type::max_memory_type) {
return memory_type_names[static_cast<uint32_t>(t)];
}
switch(t) {
case args_type: return L"jsix kernel args";
case module_type: return L"jsix bootloader module";
case kernel_type: return L"jsix kernel code";
case table_type: return L"jsix page tables";
default: return L"Bad Type Value";
}
}
return memory_type_names[value];
}
*/
void
update_marked_addresses(uefi::event, void *context)
@@ -84,15 +74,13 @@ init_pointer_fixup(uefi::boot_services *bs, uefi::runtime_services *rs)
&event),
L"Error creating memory virtualization event");
uefi::memory_type memtype = static_cast<uefi::memory_type>(0x80000003ul);
// Reserve a page for our replacement PML4, plus some pages for the kernel to use
// as page tables while it gets started.
void *addr = nullptr;
try_or_raise(
bs->allocate_pages(
uefi::allocate_type::any_pages,
memtype,
table_type,
64,
&addr),
L"Error allocating page table pages.");
@@ -111,81 +99,6 @@ mark_pointer_fixup(void **p)
}
/*
void
copy_desc(EFI_MEMORY_DESCRIPTOR *src, EFI_MEMORY_DESCRIPTOR *dst, size_t len)
{
uint8_t *srcb = (uint8_t *)src;
uint8_t *dstb = (uint8_t *)dst;
uint8_t *endb = srcb + len;
while (srcb < endb)
*dstb++ = *srcb++;
}
EFI_STATUS
memory_get_map_length(EFI_BOOT_SERVICES *bootsvc, size_t *size)
{
if (size == NULL)
return EFI_INVALID_PARAMETER;
EFI_STATUS status;
size_t key, desc_size;
uint32_t desc_version;
*size = 0;
status = bootsvc->GetMemoryMap(size, 0, &key, &desc_size, &desc_version);
if (status != EFI_BUFFER_TOO_SMALL) {
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to get memory map size");
}
return EFI_SUCCESS;
}
EFI_STATUS
memory_get_map(EFI_BOOT_SERVICES *bootsvc, struct memory_map *map)
{
EFI_STATUS status;
if (map == NULL)
return EFI_INVALID_PARAMETER;
size_t needs_size = 0;
status = memory_get_map_length(bootsvc, &needs_size);
if (EFI_ERROR(status)) return status;
if (map->length < needs_size)
return EFI_BUFFER_TOO_SMALL;
status = bootsvc->GetMemoryMap(&map->length, map->entries, &map->key, &map->size, &map->version);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to load memory map");
return EFI_SUCCESS;
}
EFI_STATUS
memory_dump_map(struct memory_map *map)
{
if (map == NULL)
return EFI_INVALID_PARAMETER;
const size_t count = map->length / map->size;
console::print(L"Memory map:\n");
console::print(L"\t Descriptor Count: %d (%d bytes)\n", count, map->length);
console::print(L"\t Descriptor Size: %d bytes\n", map->size);
console::print(L"\t Type offset: %d\n\n", offsetof(EFI_MEMORY_DESCRIPTOR, Type));
EFI_MEMORY_DESCRIPTOR *end = INCREMENT_DESC(map->entries, map->length);
EFI_MEMORY_DESCRIPTOR *d = map->entries;
while (d < end) {
int runtime = (d->Attribute & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME;
console::print(L"%s%s ", memory_type_name(d->Type), runtime ? L"*" : L" ");
console::print(L"%lx ", d->PhysicalStart);
console::print(L"%lx ", d->VirtualStart);
console::print(L"[%4d]\n", d->NumberOfPages);
d = INCREMENT_DESC(d, map->size);
}
return EFI_SUCCESS;
}
void
memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map)
{
@@ -245,9 +158,7 @@ allocate_args_structure(uefi::boot_services *bs, size_t max_modules)
max_modules * sizeof(kernel::args::module); // The module structures
try_or_raise(
bs->allocate_pool(
uefi::memory_type::loader_data,
args_size,
bs->allocate_pool(args_type, args_size,
reinterpret_cast<void**>(&args)),
L"Could not allocate argument memory");
@@ -260,6 +171,51 @@ allocate_args_structure(uefi::boot_services *bs, size_t max_modules)
return args;
}
efi_mem_map
get_mappings(uefi::boot_services *bs)
{
size_t needs_size = 0;
size_t map_key = 0;
size_t desc_size = 0;
uint32_t desc_version = 0;
uefi::status status = bs->get_memory_map(
&needs_size, nullptr, &map_key, &desc_size, &desc_version);
if (status != uefi::status::buffer_too_small)
error::raise(status, L"Error getting memory map size");
console::print(L"memory map needs %lu bytes\r\n", needs_size);
size_t buffer_size = needs_size + 10*desc_size;
uefi::memory_descriptor *buffer = nullptr;
try_or_raise(
bs->allocate_pool(
uefi::memory_type::loader_data, buffer_size,
reinterpret_cast<void**>(&buffer)),
L"Allocating space for memory map");
try_or_raise(
bs->get_memory_map(&buffer_size, buffer, &map_key, &desc_size, &desc_version),
L"Getting UEFI memory map");
efi_mem_map map;
map.length = buffer_size;
map.size = desc_size;
map.key = map_key;
map.version = desc_version;
map.entries = buffer;
for (auto desc : map) {
//for(size_t i = 0; i < map.num_entries(); ++i) {
//uefi::memory_descriptor *desc = map[i];
console::print(L" Range %lx (%lx) %x(%s) [%lu]\r\n",
desc->physical_start, desc->attribute, desc->type, memory_type_name(desc->type), desc->number_of_pages);
}
return map;
}
} // namespace boot
} // namespace memory

View File

@@ -9,6 +9,16 @@ namespace memory {
constexpr size_t page_size = 0x1000;
constexpr uefi::memory_type args_type = static_cast<uefi::memory_type>(0x80000000);
constexpr uefi::memory_type module_type = static_cast<uefi::memory_type>(0x80000001);
constexpr uefi::memory_type kernel_type = static_cast<uefi::memory_type>(0x80000002);
constexpr uefi::memory_type table_type = static_cast<uefi::memory_type>(0x80000003);
template <typename T, typename S>
static T* offset_ptr(S* input, ptrdiff_t offset) {
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(input) + offset);
}
inline constexpr size_t bytes_to_pages(size_t bytes) {
return ((bytes - 1) / page_size) + 1;
}
@@ -21,20 +31,52 @@ void mark_pointer_fixup(void **p);
kernel::args::header * allocate_args_structure(uefi::boot_services *bs, size_t max_modules);
/*
extern const EFI_MEMORY_TYPE memtype_kernel;
extern const EFI_MEMORY_TYPE memtype_data;
extern const EFI_MEMORY_TYPE memtype_initrd;
extern const EFI_MEMORY_TYPE memtype_scratch;
template <typename T>
class offset_iterator
{
T* m_t;
size_t m_off;
public:
offset_iterator(T* t, size_t offset=0) : m_t(t), m_off(offset) {}
struct memory_map {
T* operator++() { m_t = offset_ptr<T>(m_t, m_off); return m_t; }
T* operator++(int) { T* tmp = m_t; operator++(); return tmp; }
bool operator==(T* p) { return p == m_t; }
T* operator*() const { return m_t; }
operator T*() const { return m_t; }
T* operator->() const { return m_t; }
};
struct efi_mem_map {
size_t length;
size_t size;
size_t key;
uint32_t version;
EFI_MEMORY_DESCRIPTOR *entries;
uefi::memory_descriptor *entries;
inline size_t num_entries() const { return length / size; }
inline uefi::memory_descriptor * operator[](size_t i) {
size_t offset = i * size;
if (offset > length) return nullptr;
return offset_ptr<uefi::memory_descriptor>(entries, offset);
}
offset_iterator<uefi::memory_descriptor> begin() { return offset_iterator<uefi::memory_descriptor>(entries, size); }
offset_iterator<uefi::memory_descriptor> end() { return offset_ptr<uefi::memory_descriptor>(entries, length); }
};
efi_mem_map get_mappings(uefi::boot_services *bs);
enum class memory_type
{
free,
loader_used,
system_used
};
/*
EFI_STATUS memory_get_map_length(EFI_BOOT_SERVICES *bootsvc, size_t *size);
EFI_STATUS memory_get_map(EFI_BOOT_SERVICES *bootsvc, struct memory_map *map);
EFI_STATUS memory_dump_map(struct memory_map *map);

View File

@@ -10,12 +10,12 @@ namespace args {
constexpr uint32_t magic = 0x600dda7a;
constexpr uint16_t version = 1;
enum class flags : uint32_t
enum class mod_flags : uint32_t
{
debug = 0x00000001
};
enum class type : uint32_t {
enum class mod_type : uint32_t {
unknown,
kernel,
@@ -36,8 +36,8 @@ enum class mode : uint8_t {
struct module {
void *location;
size_t size;
type type;
flags flags;
mod_type type;
mod_flags flags;
};
struct header {