~sircmpwn/helios

ref: 02d0490487c7a0fb4b0367b95819e808b98f87fb helios/boot/+aarch64/loader.ha -rw-r--r-- 1.9 KiB
02d04904Drew DeVault objects::endpoint: always capalloc on receiver 10 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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;
};