#include "elf.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "disk.h"
#include "memory.h"
#include "paging32.h"
#include "paging64.h"
#include "tty.h"
#define BOOT_PAGE_WRITE (1 << 1)
#define BOOT_PAGE_PAT_HUGE (1ULL << 12)
static bool has_long_mode(void) {
cpuid_regs_t regs = { 0 };
cpuid(0x80000000, ®s);
if (regs.eax < CPUID_EXTENDED_INFO) {
return false;
}
cpuid(CPUID_EXTENDED_INFO, ®s);
return (regs.edx & CPUID_EI_LM) != 0;
}
static u64 map_top(const e820_map_t *map) {
u64 top = 0;
for (size_t i = 0; i < map->count; i++) {
const e820_entry_t *entry = &map->entries[i];
if (!entry->size) {
continue;
}
u64 end = entry->address + entry->size;
if (end > top) {
top = end;
}
}
return top;
}
static void commit_log(boot_info_t *info) {
if (!info) {
return;
}
size_t len = 0;
size_t cap = 0;
const char *buf = boot_log_buffer(&len, &cap);
if (!buf || !cap) {
return;
}
info->boot_log_paddr = (u64)(uintptr_t)buf;
info->boot_log_len = (u32)len;
info->boot_log_cap = (u32)cap;
}
static elf_header_t *read_kernel(bool want_64, bool *is_64) {
const char *path = boot_kernel_path(want_64);
elf_header_t *kernel = read_rootfs(path);
if (!kernel) {
return NULL;
}
elf_validity_t validity = elf_verify(kernel);
if (validity) {
log_error("invalid kernel ELF %s (error %d)", path, validity);
free(kernel);
return NULL;
}
if (want_64) {
if (kernel->machine != EM_X86_64) {
log_error("kernel arch mismatch at %s: expected x86_64, machine=%#x", path, kernel->machine);
free(kernel);
return NULL;
}
*is_64 = true;
return kernel;
}
if (kernel->machine != EM_X86) {
log_error("kernel arch mismatch at %s: expected x86_32, machine=%#x", path, kernel->machine);
free(kernel);
return NULL;
}
*is_64 = false;
return kernel;
}
void load_kernel(boot_info_t *info) {
bool want_64 = has_long_mode();
bool is_64 = false;
elf_header_t *kernel = read_kernel(want_64, &is_64);
if (!kernel) {
panic("no suitable kernel image found");
}
if (!is_64) {
log_info("loading x86_32 kernel");
setup_paging_32();
u32 entry = load_elf_sections_32(kernel);
u64 mem_top = map_top(&info->memory_map);
u32 phys_top = (u32)mem_top;
if (mem_top > 0xffffffffULL) {
phys_top = 0xffffffffU;
}
identity_map_32(phys_top, 0, false);
init_paging_32();
tty_disable_bios_output();
u32 stack_paddr = (u32)(uintptr_t)mmap_alloc_top(KERNEL_STACK_SIZE, E820_KERNEL, (size_t)(4 * KIB), phys_top);
u32 stack = stack_paddr + KERNEL_STACK_SIZE;
u32 boot_info = (u32)(uintptr_t)info;
log_debug("kernel entry %#x", (unsigned int)entry);
commit_log(info);
jump_to_kernel_32(entry, boot_info, stack);
} else {
log_info("loading x86_64 kernel");
setup_paging_64();
u64 entry = load_elf_sections_64(kernel);
identity_map_64(PROTECTED_MODE_TOP, 0, false);
identity_map_64(PROTECTED_MODE_TOP, LINEAR_MAP_OFFSET_64, true);
if (info->video.mode == VIDEO_GRAPHICS && info->video.framebuffer) {
u64 pitch = info->video.bytes_per_line;
if (!pitch) {
pitch = (u64)info->video.width * info->video.bytes_per_pixel;
}
u64 fb_size = pitch * info->video.height;
if (fb_size) {
u64 flags = BOOT_PAGE_WRITE | BOOT_PAGE_PAT_HUGE;
u64 fb_base = ALIGN_DOWN(info->video.framebuffer, 2 * MIB);
u64 fb_end = ALIGN(info->video.framebuffer + fb_size, 2 * MIB);
u64 map_end = fb_end;
if (map_end > PROTECTED_MODE_TOP) {
map_end = PROTECTED_MODE_TOP;
}
for (u64 a = fb_base; a < map_end; a += 2 * MIB) {
map_page_64(2 * MIB, a, a, flags, false);
map_page_64(2 * MIB, a + LINEAR_MAP_OFFSET_64, a, flags, true);
}
}
}
pat_init();
init_paging_64();
tty_disable_bios_output();
u64 stack_paddr = (u64)(uintptr_t)mmap_alloc(KERNEL_STACK_SIZE, E820_KERNEL, 0);
u64 stack = stack_paddr + KERNEL_STACK_SIZE + LINEAR_MAP_OFFSET_64;
u64 boot_info = (uintptr_t)info + LINEAR_MAP_OFFSET_64;
log_debug("kernel entry %#llx", entry);
commit_log(info);
jump_to_kernel_64(entry, boot_info, stack);
}
}