use bytes;
use efi;
use elf;
use log;
use strings;
// Loads the kernel into memory and sets up its memory mappings in the early
// kernel page tables. Returns the kernel entry point's virtual address.
fn load(mem: *allocator, image: *efi::FILE_PROTOCOL) (uintptr | efi::error) = {
let buf: [size(elf::header64)]u8 = [0...];
const n = efi::read(image, buf)?;
assert(n == size(elf::header64));
const header = &buf[0]: *elf::header64;
const ident = header.e_ident[..len(elf::MAGIC)];
if (!bytes::equal(strings::toutf8(elf::MAGIC), ident)) {
log::printfln("Kernel is not an ELF file");
return efi::STATUS::LOAD_ERROR: efi::error;
};
if (header.e_type != elf::elf_type::EXEC
|| header.e_machine != elf::elf_machine::AARCH64) {
log::printfln("Kernel is not an aarch64 executable");
return efi::STATUS::LOAD_ERROR: efi::error;
};
let buf: [size(elf::phdr64)]u8 = [0...];
for (let i = 0z; i < header.e_phnum; i += 1) {
const offs = header.e_phoff: size + (i * size(elf::phdr64));
const n = efi::read(image, buf)?;
assert(n == size(elf::phdr64));
const phdr = &buf[0]: *elf::phdr64;
if (phdr.p_type != elf::pt::LOAD) {
continue;
};
// TODO: Map empty pages if memsz exceeds filesz
let npage = phdr.p_memsz: size / PAGESIZE;
if (npage % PAGESIZE != 0) {
npage += 1;
};
efi::seek(image, phdr.p_offset)?;
for (let i = 0z; i < npage; i += 1) {
const phys = pagealloc(mem);
const offs = i: uintptr * UPAGESIZE;
const virt = phdr.p_vaddr: uintptr + offs;
// TODO: Split up kernel PHDRs and map appropriately,
// e.g. set noexec on .data/.bss
kmmap(virt, phys, PT_RW);
let nbyte = PAGESIZE;
if (i * PAGESIZE + nbyte > phdr.p_filesz: size) {
nbyte = phdr.p_filesz: size % PAGESIZE;
};
if (i * PAGESIZE + nbyte > phdr.p_filesz: size) {
continue;
};
let dest = (phys: *[*]u8)[..nbyte];
const n = efi::read(image, dest)?;
assert(n == nbyte);
};
};
return header.e_entry: uintptr;
};