boot: Add EFISTUB and refactor boot protocols

This commit is contained in:
2026-10-27 22:50:43 +01:00
parent 6827dc309e
commit 196543c3d3
40 changed files with 1923 additions and 208 deletions
+120
View File
@@ -0,0 +1,120 @@
// SPDX-License-Identifier: ISC
#include <bits/elf.h>
#include <string.h>
#include "loader.h"
static bool in_image(const struct kernel_image* image, uint64_t vaddr, uint64_t len) {
return vaddr >= image->virt && len <= image->size && vaddr - image->virt <= image->size - len;
}
void* kernel_ptr(const struct kernel_image* image, uint64_t vaddr) {
return loader_ptr(image->phys + (vaddr - image->virt));
}
static bool apply_relocations(const struct kernel_image* image, uint64_t dynamic_vaddr) {
if (!in_image(image, dynamic_vaddr, sizeof(elf64_dyn_t)))
return false;
uint64_t rela = 0;
uint64_t rela_size = 0;
uint64_t rela_ent = sizeof(elf64_rela_t);
for (const elf64_dyn_t* dyn = kernel_ptr(image, dynamic_vaddr); dyn->d_tag != DT_NULL; dyn++) {
switch (dyn->d_tag) {
case DT_RELA:
rela = dyn->d_un.d_val;
break;
case DT_RELASZ:
rela_size = dyn->d_un.d_val;
break;
case DT_RELAENT:
rela_ent = dyn->d_un.d_val;
break;
}
}
if (!rela)
return true;
if (rela_ent < sizeof(elf64_rela_t) || !in_image(image, rela, rela_size))
return false;
for (uint64_t off = 0; off + rela_ent <= rela_size; off += rela_ent) {
const elf64_rela_t* r = kernel_ptr(image, rela + off);
const uint32_t type = ELF64_R_TYPE(r->r_info);
if (type == R_NONE)
continue;
if (type != R_RELATIVE)
return false;
if (!in_image(image, r->r_offset, sizeof(uint64_t)))
return false;
*(uint64_t*)kernel_ptr(image, r->r_offset) = (uint64_t)r->r_addend;
}
return true;
}
bool elf_load(const uint8_t* elf, size_t size, struct kernel_image* out) {
elf64_ehdr_t ehdr;
if (size < sizeof(ehdr))
return false;
memcpy(&ehdr, elf, sizeof(ehdr));
if (memcmp(ehdr.e_ident, ELF_MAG, sizeof(ELF_MAG)) || ehdr.e_ident[EI_CLASS] != ELF_ARCH_CLASS ||
ehdr.e_ident[EI_DATA] != ELF_ARCH_DATA)
return false;
if (ehdr.e_machine != ELF_ARCH_MACHINE)
return false;
if (ehdr.e_phentsize != sizeof(elf64_phdr_t) || ehdr.e_phoff > size ||
(size - ehdr.e_phoff) / sizeof(elf64_phdr_t) < ehdr.e_phnum)
return false;
const elf64_phdr_t* phdrs = (const elf64_phdr_t*)(elf + ehdr.e_phoff);
uint64_t lo = UINT64_MAX;
uint64_t hi = 0;
uint64_t dynamic_vaddr = 0;
uint64_t info_vaddr = 0;
uint64_t info_size = 0;
for (size_t i = 0; i < ehdr.e_phnum; i++) {
const elf64_phdr_t* ph = &phdrs[i];
if (ph->p_type == PT_DYNAMIC)
dynamic_vaddr = ph->p_vaddr;
if (ph->p_type == PT_BOOT_INFO) {
info_vaddr = ph->p_vaddr;
info_size = ph->p_memsz;
}
if (ph->p_type != PT_LOAD || ph->p_memsz == 0)
continue;
if (ph->p_filesz > ph->p_memsz || ph->p_offset > size || size - ph->p_offset < ph->p_filesz)
return false;
if (ALIGN_DOWN(ph->p_vaddr, PAGE_SIZE) < lo)
lo = ALIGN_DOWN(ph->p_vaddr, PAGE_SIZE);
if (ALIGN_UP(ph->p_vaddr + ph->p_memsz, PAGE_SIZE) > hi)
hi = ALIGN_UP(ph->p_vaddr + ph->p_memsz, PAGE_SIZE);
}
if (lo >= hi)
return false;
out->virt = lo;
out->size = hi - lo;
out->entry = ehdr.e_entry;
if (!in_image(out, out->entry, 1))
return false;
if (info_size != sizeof(struct boot_info) || !in_image(out, info_vaddr, info_size))
return false;
if (!loader_alloc(PHYS_STATIC, out->size, &out->phys))
return false;
for (size_t i = 0; i < ehdr.e_phnum; i++) {
const elf64_phdr_t* ph = &phdrs[i];
if (ph->p_type != PT_LOAD || ph->p_memsz == 0)
continue;
memcpy(kernel_ptr(out, ph->p_vaddr), elf + ph->p_offset, ph->p_filesz);
}
out->info = kernel_ptr(out, info_vaddr);
if (dynamic_vaddr)
return apply_relocations(out, dynamic_vaddr);
return true;
}
+53
View File
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: ISC
#include <stddef.h>
#include <stdint.h>
#include <string.h>
size_t strlen(const char* str) {
if (!str)
return 0;
size_t len = 0;
while (str[len])
len++;
return len;
}
int memcmp(const void* s1, const void* s2, size_t size) {
const uint8_t* a = s1;
const uint8_t* b = s2;
for (size_t i = 0; i < size; i++) {
if (a[i] != b[i])
return a[i] < b[i] ? -1 : 1;
}
return 0;
}
void* memcpy(void* restrict dest, const void* restrict src, size_t n) {
uint8_t* d = dest;
const uint8_t* s = src;
for (size_t i = 0; i < n; i++)
d[i] = s[i];
return dest;
}
void* memmove(void* dstptr, const void* srcptr, size_t size) {
uint8_t* d = dstptr;
const uint8_t* s = srcptr;
if (d < s) {
for (size_t i = 0; i < size; i++)
d[i] = s[i];
} else {
for (size_t i = size; i > 0; i--)
d[i - 1] = s[i - 1];
}
return dstptr;
}
void* memset(void* dest, int value, size_t n) {
uint8_t* d = dest;
for (size_t i = 0; i < n; i++)
d[i] = (uint8_t)value;
return dest;
}
+81
View File
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: ISC
#include "loader.h"
#include <string.h>
#include "loader_arch.h"
extern const uint8_t __kernel_start[];
extern const uint8_t __kernel_end[];
bool loader_load_kernel(struct kernel_image* out, const char* name) {
if (!elf_load(__kernel_start, __kernel_end - __kernel_start, out))
return false;
struct boot_info* info = out->info;
memcpy(info->loader, name, MIN(strlen(name), sizeof(info->loader) - 1));
info->hhdm_base = ARCH_HHDM_BASE;
info->phys_base = out->phys;
info->virt_base = out->virt;
return true;
}
bool loader_add_file(const struct kernel_image* kernel, const char* path, uint64_t base, uint64_t length) {
struct boot_info* info = kernel->info;
if (info->num_files == ARRAY_SIZE(info->files))
return false;
struct boot_file* file = &info->files[info->num_files++];
file->data = base;
file->length = length;
const size_t path_len = MIN(strlen(path), sizeof(file->path) - 1);
memcpy(file->path, path, path_len);
file->path[path_len] = 0;
return true;
}
void loader_set_mem_map(const struct kernel_image* kernel, struct phys_mem* map, size_t len) {
for (size_t i = 1; i < len; i++) {
const struct phys_mem entry = map[i];
size_t j = i;
while (j > 0 && map[j - 1].address > entry.address) {
map[j] = map[j - 1];
j--;
}
map[j] = entry;
}
size_t merged = 0;
for (size_t i = 0; i < len; i++) {
if (!map[i].length)
continue;
struct phys_mem* prev = merged ? &map[merged - 1] : nullptr;
if (prev && prev->usage == map[i].usage && prev->address + prev->length == map[i].address)
prev->length += map[i].length;
else
map[merged++] = map[i];
}
struct boot_info* info = kernel->info;
info->num_mem_maps = MIN(merged, ARRAY_SIZE(info->mem_map));
memcpy(info->mem_map, map, info->num_mem_maps * sizeof(*map));
}
bool loader_page_tables(
const struct kernel_image* kernel,
uint64_t mem_top,
uint64_t trampoline_phys,
uint64_t* out_root
) {
uint64_t root;
if (!loader_alloc(PHYS_RECLAIMABLE, PAGE_SIZE, &root))
return false;
const uint64_t trampoline_virt = ALIGN_DOWN((uint64_t)arch_enter_kernel, PAGE_SIZE);
if (!arch_paging_map(root, ARCH_HHDM_BASE, 0, ALIGN_UP(mem_top, ARCH_HUGE_PAGE_SIZE), ARCH_HUGE_PAGE_SIZE) ||
!arch_paging_map(root, kernel->virt, kernel->phys, kernel->size, PAGE_SIZE) ||
!arch_paging_map(root, trampoline_virt, ALIGN_DOWN(trampoline_phys, PAGE_SIZE), 2 * PAGE_SIZE, PAGE_SIZE))
return false;
*out_root = root;
return true;
}
+41
View File
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: ISC
#pragma once
#include <zinnia/boot.h>
#include <zinnia/utils.h>
#include <stddef.h>
#include <stdint.h>
constexpr uint64_t PAGE_SIZE = 0x1000;
struct kernel_image {
uint64_t phys;
uint64_t virt;
uint64_t size;
uint64_t entry;
struct boot_info* info;
};
bool loader_alloc(enum phys_mem_usage usage, size_t size, uint64_t* out);
void* loader_ptr(uint64_t phys);
bool elf_load(const uint8_t* elf, size_t size, struct kernel_image* out);
void* kernel_ptr(const struct kernel_image* image, uint64_t vaddr);
bool loader_load_kernel(struct kernel_image* out, const char* name);
bool loader_add_file(const struct kernel_image* kernel, const char* path, uint64_t base, uint64_t length);
void loader_set_mem_map(const struct kernel_image* kernel, struct phys_mem* map, size_t len);
bool loader_page_tables(
const struct kernel_image* kernel,
uint64_t mem_top,
uint64_t trampoline_phys,
uint64_t* out_root
);
bool arch_check_cpu();
bool arch_paging_map(uint64_t root, uint64_t virt, uint64_t phys, uint64_t size, uint64_t page_size);
[[noreturn]]
void arch_enter_kernel(uint64_t root, uint64_t entry);
+13
View File
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: ISC
.intel_syntax noprefix
.text
.globl arch_enter_kernel
.type arch_enter_kernel, @function
.balign 16
arch_enter_kernel:
cli
mov cr3, rdi
jmp rsi
.size arch_enter_kernel, . - arch_enter_kernel
+8
View File
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: ISC
#pragma once
#include <stdint.h>
constexpr uint64_t ARCH_HHDM_BASE = 0xffff'8000'0000'0000;
constexpr uint64_t ARCH_HUGE_PAGE_SIZE = 0x20'0000;
+11
View File
@@ -0,0 +1,11 @@
loader_sources += files(
'enter.S',
'paging.c',
)
loader_c_args += [
'-mno-red-zone',
'-masm=intel',
]
loader_objcopy_format = 'elf64-x86-64'
+45
View File
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: ISC
#include <bits/pmap.h>
#include <x86_64/defs.h>
#include "loader.h"
#include "loader_arch.h"
static bool next_table(uint64_t* table, size_t index, uint64_t** out) {
if (!(table[index] & ARCH_FLAG_PRESENT)) {
uint64_t page;
if (!loader_alloc(PHYS_RECLAIMABLE, PAGE_SIZE, &page))
return false;
table[index] = page | ARCH_FLAG_PRESENT | ARCH_FLAG_READ_WRITE;
}
*out = loader_ptr(table[index] & ARCH_PTE_ADDR_MASK);
return true;
}
bool arch_check_cpu() {
uint64_t cr4;
asm volatile("mov %0, cr4" : "=r"(cr4));
if (cr4 & CR4_LA57)
return false;
return true;
}
bool arch_paging_map(uint64_t root, uint64_t virt, uint64_t phys, uint64_t size, uint64_t page_size) {
const int leaf_level = page_size == ARCH_HUGE_PAGE_SIZE ? 1 : 0;
for (uint64_t off = 0; off < size; off += page_size) {
const uint64_t va = virt + off;
uint64_t* table = loader_ptr(root);
for (int level = 3; level > leaf_level; level--) {
if (!next_table(table, (va >> (12 + 9 * level)) & 0x1ff, &table))
return false;
}
uint64_t entry = (phys + off) | ARCH_FLAG_PRESENT | ARCH_FLAG_READ_WRITE;
if (leaf_level == 1)
entry |= ARCH_FLAG_SIZE;
table[(va >> (12 + 9 * leaf_level)) & 0x1ff] = entry;
}
return true;
}