1#![cfg_attr(not(test), no_std)]
4#![no_main]
5#![allow(unsafe_code, dead_code)]
6extern crate alloc;
7
8mod elf;
9mod file_system;
10mod framebuffer;
11mod memory;
12mod rsdp;
13mod tracing;
14mod uefi_mmap;
15mod vmem;
16
17use crate::elf::parser::ElfHeader;
18use crate::file_system::load_file;
19use crate::framebuffer::get_framebuffer;
20use crate::memory::alloc_trampoline_stack;
21use crate::rsdp::find_rsdp_addr;
22use crate::tracing::trace_boot_info;
23use crate::uefi_mmap::exit_boot_services;
24use crate::vmem::create_kernel_pagetables;
25use alloc::boxed::Box;
26use kernel_info::boot::{KernelBootInfo, MemoryMapInfo};
27use kernel_qemu::qemu_trace;
28use kernel_vmem::addresses::{PhysicalAddress, VirtualAddress};
29use uefi::boot::PAGE_SIZE;
30use uefi::cstr16;
31use uefi::prelude::*;
32
33const TRAMPOLINE_STACK_SIZE_BYTES: usize = 64 * 1024;
35
36#[entry]
37#[allow(clippy::too_many_lines)]
38fn efi_main() -> Status {
39 if uefi::helpers::init().is_err() {
41 return Status::UNSUPPORTED;
42 }
43
44 qemu_trace!("UEFI Loader reporting to QEMU\n");
45 uefi::println!("Attempting to load kernel.elf ...");
46
47 let elf_bytes = match load_file(cstr16!("\\EFI\\Boot\\kernel.elf")) {
48 Ok(bytes) => bytes,
49 Err(status) => {
50 uefi::println!("Failed to load kernel.elf. Exiting.");
51 return status;
52 }
53 };
54
55 let Ok(parsed) = ElfHeader::parse_elf64(&elf_bytes) else {
57 uefi::println!("kernel.elf is not a valid x86_64 ELF64");
58 return Status::UNSUPPORTED;
59 };
60
61 uefi::println!("Loading kernel segments into memory ...");
62 let kernel_segments = match elf::loader::load_pt_load_segments_hi(&elf_bytes, &parsed) {
63 Ok(segments) => segments,
64 Err(e) => {
65 uefi::println!("Failed to load PT_LOAD segments: {e:?}");
66 return e.into();
67 }
68 };
69
70 uefi::println!(
71 "kernel.elf loaded successfully: entry={}, segments={}",
72 parsed.entry,
73 parsed.segments.len()
74 );
75
76 let fb = match get_framebuffer() {
77 Ok(fb) => fb,
78 Err(status) => {
79 return status;
80 }
81 };
82
83 let rsdp_addr: u64 = find_rsdp_addr();
85
86 let boot_info = KernelBootInfo {
87 mmap: MemoryMapInfo {
89 mmap_ptr: 0,
90 mmap_len: 0,
91 mmap_desc_size: 0,
92 mmap_desc_version: 0,
93 },
94 rsdp_addr,
95 fb,
96 };
97
98 let boot_info = Box::leak(Box::new(boot_info));
100 uefi::println!("Kernel boot info: {:#?}", core::ptr::from_ref(boot_info));
101
102 let tramp_code_va = VirtualAddress::new(switch_to_kernel as usize as u64);
105 let tramp_code_len: usize = PAGE_SIZE; let (tramp_stack_base_phys, tramp_stack_top_va) =
109 alloc_trampoline_stack(TRAMPOLINE_STACK_SIZE_BYTES, true);
110
111 let bi_ptr_va = VirtualAddress::from_ptr(core::ptr::from_ref::<KernelBootInfo>(boot_info));
113
114 let Ok(pml4_phys) = create_kernel_pagetables(
116 &kernel_segments,
117 tramp_code_va,
118 tramp_code_len,
119 tramp_stack_base_phys,
120 TRAMPOLINE_STACK_SIZE_BYTES,
121 bi_ptr_va,
122 ) else {
123 uefi::println!("Failed to create kernel page tables");
124 return Status::OUT_OF_RESOURCES;
125 };
126
127 boot_info.mmap = match exit_boot_services() {
128 Ok(value) => value,
129 Err(value) => return value,
130 };
131
132 unsafe {
134 trace_boot_info(boot_info, bi_ptr_va, parsed.entry, tramp_stack_top_va);
135 enable_wp_nxe_pge();
136
137 switch_to_kernel(
139 pml4_phys,
140 parsed.entry, bi_ptr_va,
142 tramp_stack_top_va,
143 )
144 }
145}
146
147#[allow(clippy::items_after_statements)]
148unsafe fn enable_wp_nxe_pge() {
149 qemu_trace!("Enabling supervisor write protection ...\n");
151 let mut cr0: u64;
152 unsafe {
153 core::arch::asm!("mov {}, cr0", out(reg) cr0, options(nomem, preserves_flags));
154 }
155 cr0 |= 1 << 16;
156 unsafe {
157 core::arch::asm!("mov cr0, {}", in(reg) cr0, options(nomem, preserves_flags));
158 }
159
160 qemu_trace!("Setting EFER.NXE ...\n");
162 const MSR_EFER: u32 = 0xC000_0080; let (mut lo, mut hi): (u32, u32);
164 unsafe {
165 core::arch::asm!("rdmsr", in("ecx") MSR_EFER, out("eax") lo, out("edx") hi, options(nomem, preserves_flags));
166 }
167 let mut efer = u64::from(hi) << 32 | u64::from(lo);
168 efer |= 1 << 11;
169 lo = u32::try_from(efer).expect("failed to cast efer to u32"); hi = (efer >> 32) as u32;
171 unsafe {
172 core::arch::asm!("wrmsr", in("ecx") MSR_EFER, in("eax") lo, in("edx") hi, options(nomem, preserves_flags));
173 }
174
175 qemu_trace!("Enabling global pages ...\n");
177 let mut cr4: u64;
178 unsafe {
179 core::arch::asm!("mov {}, cr4", out(reg) cr4, options(nomem, preserves_flags));
180 }
181 cr4 |= 1 << 7;
182 unsafe {
183 core::arch::asm!("mov cr4, {}", in(reg) cr4, options(nomem, preserves_flags));
184 }
185}
186
187type PageTablePhysicalAddress = PhysicalAddress;
188type KernelVirtualAddress = VirtualAddress;
189type BootInfoVirtualAddress = VirtualAddress;
190type TrampolineStackVirtualAddress = VirtualAddress;
191
192#[inline(never)]
198unsafe fn switch_to_kernel(
199 pml4_phys: PageTablePhysicalAddress,
200 kernel_entry_va: KernelVirtualAddress,
201 boot_info_ptr_va: BootInfoVirtualAddress,
202 tramp_stack_top_va: TrampolineStackVirtualAddress,
203) -> ! {
204 qemu_trace!("UEFI is about to jump into Kernel land. Ciao Kakao ...\n");
205 unsafe {
206 core::arch::asm!(
207 "cli",
208 "mov rsp, rdx",
210 "mov cr3, rdi",
212 "mov rdi, r8",
214 "mov rax, rsi",
216 "and rsp, -16",
218 "push 0",
220 "jmp rax",
221 in("rdi") pml4_phys.as_u64(),
222 in("rsi") kernel_entry_va.as_u64(),
223 in("rdx") tramp_stack_top_va.as_u64(),
224 in("r8") boot_info_ptr_va.as_u64(),
225 options(noreturn)
226 )
227 }
228}