Nothing to see here, move along
1#![no_std]
2#![no_main]
3#![feature(abi_x86_interrupt)]
4#![allow(clippy::single_match)]
5
6mod acpi;
7mod arch;
8mod cap;
9mod console;
10mod dashboard;
11mod error;
12mod flight;
13mod iommu;
14mod ipc;
15mod irq;
16mod log;
17mod mem;
18mod panic;
19mod pci;
20mod proc;
21#[cfg(lancer_test)]
22mod qemu;
23mod ring;
24mod sched;
25mod static_vec;
26mod sync;
27mod syscall;
28#[cfg(lancer_test)]
29mod test_harness;
30#[cfg(lancer_test)]
31mod tests;
32mod types;
33mod wcet;
34
35use wcet::region::{WcetGuard, WcetTracker};
36#[cfg(not(lancer_test))]
37use wcet::tsc;
38
39#[unsafe(no_mangle)]
40#[unsafe(naked)]
41extern "C" fn kmain() -> ! {
42 core::arch::naked_asm!(
43 "mov rax, cr0",
44 "and ax, 0xFFFB",
45 "or ax, 0x0002",
46 "mov cr0, rax",
47 "mov rax, cr4",
48 "or ax, 0x0600",
49 "mov cr4, rax",
50 "jmp {entry}",
51 entry = sym kmain_inner,
52 )
53}
54
55extern "C" fn kmain_inner() -> ! {
56 arch::serial::init();
57 arch::syscall::init_bsp_percpu();
58
59 dashboard::dash().add_section("kern", 3);
60 dashboard::dash().add_section("mem", 3);
61 dashboard::dash().add_section("gdt", 1);
62 dashboard::dash().add_section("idt", 1);
63 dashboard::dash().add_section("phys", 2);
64 dashboard::dash().add_section("virt", 1);
65 dashboard::dash().add_section("apic", 2);
66 dashboard::dash().add_section("acpi", 1);
67 dashboard::dash().add_section("ioapic", 2);
68 dashboard::dash().add_section("pci", 1);
69 dashboard::dash().add_section("iommu", 1);
70 dashboard::dash().add_section("cpu", 4);
71 dashboard::dash().add_section("sys", 1);
72 dashboard::dash().add_section("cap", 1);
73 dashboard::dash().add_section("sched", 1);
74 #[cfg(not(lancer_test))]
75 dashboard::dash().add_section("proc", 2);
76 #[cfg(not(lancer_test))]
77 dashboard::dash().add_section("fb", 1);
78 #[cfg(not(lancer_test))]
79 dashboard::dash().add_section("boot", 6);
80
81 #[cfg(not(lancer_test))]
82 let boot_start = tsc::read_tsc();
83 let mut serial_wcet: WcetTracker<16> = WcetTracker::new("serial_init");
84 let mut memmap_wcet: WcetTracker<16> = WcetTracker::new("memmap_parse");
85 let mut gdt_wcet: WcetTracker<16> = WcetTracker::new("gdt_init");
86 let mut idt_wcet: WcetTracker<16> = WcetTracker::new("idt_init");
87 let mut phys_wcet: WcetTracker<16> = WcetTracker::new("phys_alloc_init");
88 let mut virt_wcet: WcetTracker<16> = WcetTracker::new("virt_mem_init");
89 let mut apic_wcet: WcetTracker<16> = WcetTracker::new("apic_init");
90
91 serial_wcet.record(0);
92
93 phase_begin!(kern);
94 arch::boot::validate_base_revision();
95 show!(kern, "limine base revision validated");
96
97 let hhdm_offset = arch::boot::hhdm_offset();
98 show!(kern, "hhdm offset {:#x}", hhdm_offset);
99
100 let (phys_base, virt_base) = arch::boot::executable_address();
101 show!(kern, "kernel phys {:#x} virt {:#x}", phys_base, virt_base);
102 phase_end!(kern);
103
104 phase_begin!(mem);
105 {
106 let _guard = WcetGuard::new(&mut memmap_wcet);
107 let memory_map = arch::boot::memory_map();
108 show!(mem, "memory map {} entries", memory_map.len());
109 mem::init(memory_map, hhdm_offset);
110 }
111 proc::address_space::capture_boot_pml4();
112 console::init(16);
113 {
114 let stats = mem::phys::frame_stats();
115 show!(mem, "{} total, {} free", stats.0, stats.1);
116 }
117 phase_end!(mem);
118
119 phase_begin!(gdt);
120 {
121 let _guard = WcetGuard::new(&mut gdt_wcet);
122 arch::gdt::init();
123 }
124 show!(gdt, "loaded");
125 phase_end!(gdt);
126
127 phase_begin!(idt);
128 {
129 let _guard = WcetGuard::new(&mut idt_wcet);
130 arch::idt::init();
131 }
132 show!(idt, "32 exception handlers + apic vectors");
133 phase_end!(idt);
134
135 phase_begin!(phys);
136 {
137 let _guard = WcetGuard::new(&mut phys_wcet);
138 }
139 {
140 let stats = mem::phys::frame_stats();
141 show!(phys, "{} frames available", stats.1);
142 }
143 phase_end!(phys);
144
145 phase_begin!(virt);
146 let mut mapper = unsafe { arch::paging::init(hhdm_offset) };
147 let mut allocator = mem::phys::BitmapFrameAllocator;
148 {
149 let _guard = WcetGuard::new(&mut virt_wcet);
150 mem::virt::test_mapping(&mut mapper, &mut allocator);
151 }
152 show!(virt, "page tables initialized");
153 phase_end!(virt);
154
155 {
156 let mut ptable = proc::PROCESSES.lock();
157 ptable.init(&mut allocator);
158 }
159
160 phase_begin!(apic);
161 {
162 let _guard = WcetGuard::new(&mut apic_wcet);
163 let apic_base = unsafe { x2apic::lapic::xapic_base() };
164 mem::virt::map_mmio(&mut mapper, &mut allocator, apic_base, hhdm_offset).expect("map_mmio");
165 arch::apic::init(hhdm_offset);
166 }
167 show!(apic, "lapic enabled, timer configured");
168 phase_end!(apic);
169
170 phase_begin!(acpi);
171 let acpi_info = match arch::boot::rsdp_address() {
172 Some(rsdp) => {
173 show!(acpi, "rsdp at {:#x}", rsdp);
174 acpi::parse(rsdp, &mut mapper, &mut allocator, hhdm_offset)
175 }
176 None => {
177 show!(acpi, "no rsdp from bootloader");
178 None
179 }
180 };
181 phase_end!(acpi);
182
183 phase_begin!(ioapic);
184 let (com1_gsi, kbd_gsi) = match &acpi_info {
185 Some(info) => {
186 mem::virt::map_mmio(&mut mapper, &mut allocator, info.ioapic_addr, hhdm_offset)
187 .expect("map_mmio");
188 arch::ioapic::init(info.ioapic_addr);
189 let com1 = info.gsi_for_irq(4);
190 arch::ioapic::route_irq(com1, arch::idt::COM1_VECTOR, 0);
191 let kbd = info.gsi_for_irq(1);
192 arch::ioapic::route_irq(kbd, arch::idt::KBD_VECTOR, 0);
193 show!(
194 ioapic,
195 "com1 gsi {} -> vec {}",
196 com1,
197 arch::idt::COM1_VECTOR
198 );
199 show!(ioapic, "kbd gsi {} -> vec {}", kbd, arch::idt::KBD_VECTOR);
200 (Some(com1), Some(kbd))
201 }
202 None => {
203 show!(ioapic, "skipped, no acpi");
204 (None, None)
205 }
206 };
207 phase_end!(ioapic);
208
209 phase_begin!(pci);
210 match &acpi_info {
211 Some(info) if !info.mcfg_entries.is_empty() => {
212 acpi::mcfg::cache_mcfg_entries(&info.mcfg_entries);
213 pci::enumerate(&info.mcfg_entries, &mut mapper, &mut allocator, hhdm_offset);
214 let count = pci::DEVICE_TABLE.lock().len();
215 show!(pci, "{} devices enumerated", count);
216 }
217 _ => {
218 show!(pci, "skipped, no mcfg");
219 }
220 }
221 phase_end!(pci);
222
223 phase_begin!(iommu);
224 match &acpi_info {
225 Some(info) if info.dmar_info.is_some() => {
226 let dmar = info.dmar_info.as_ref().unwrap();
227 match iommu::init(dmar, &mut mapper, &mut allocator, hhdm_offset) {
228 Ok(()) => match iommu::enable(&mut allocator, hhdm_offset) {
229 Ok(()) => show!(iommu, "enabled"),
230 Err(e) => {
231 show!(iommu, error, "enable failed {:?}", e);
232 phase_fail!(iommu);
233 }
234 },
235 Err(e) => {
236 show!(iommu, error, "init failed {:?}", e);
237 phase_fail!(iommu);
238 }
239 }
240 }
241 _ => {
242 show!(iommu, "no dmar table");
243 }
244 }
245 match dashboard::domain_to_section("iommu") {
246 Some(idx)
247 if !matches!(
248 dashboard::dash().section_status(idx),
249 lancer_log::phase::PhaseStatus::Failed
250 ) =>
251 {
252 phase_end!(iommu)
253 }
254 _ => {}
255 }
256
257 let virtio_net_gsi = match &acpi_info {
258 Some(info) => {
259 let dev_table = pci::DEVICE_TABLE.lock();
260 dev_table
261 .iter()
262 .find(|d| d.vendor_id == 0x1AF4 && d.class_code == 0x02 && d.subclass == 0x00)
263 .map(|d| {
264 let irq = d.interrupt_line;
265 let gsi = info.gsi_for_irq(irq);
266 match d.msix_cap.is_some() {
267 true => {}
268 false => {
269 arch::ioapic::route_irq(gsi, arch::idt::VIRTIO_NET_VECTOR, 0);
270 }
271 }
272 gsi
273 })
274 }
275 None => None,
276 };
277
278 syscall::platform::store_platform_info(com1_gsi, kbd_gsi, virtio_net_gsi);
279
280 phase_begin!(cpu);
281 {
282 use x86_64::registers::control::{Cr0, Cr0Flags, Cr4, Cr4Flags};
283 use x86_64::registers::model_specific::{Efer, EferFlags};
284
285 unsafe {
286 Efer::update(|flags| *flags |= EferFlags::NO_EXECUTE_ENABLE);
287 }
288 show!(cpu, "nxe enabled");
289
290 unsafe {
291 Cr0::update(|f| {
292 f.remove(Cr0Flags::EMULATE_COPROCESSOR);
293 f.remove(Cr0Flags::TASK_SWITCHED);
294 f.insert(Cr0Flags::MONITOR_COPROCESSOR);
295 f.insert(Cr0Flags::NUMERIC_ERROR);
296 });
297 Cr4::update(|f| {
298 f.insert(Cr4Flags::OSFXSR);
299 f.insert(Cr4Flags::OSXMMEXCPT_ENABLE);
300 });
301 }
302 show!(cpu, "fpu/sse enabled");
303
304 arch::xsave::init();
305 arch::xsave::propagate_to_percpu();
306
307 let leaf7 = core::arch::x86_64::__cpuid_count(7, 0);
308 let has_smep = leaf7.ebx & (1 << 7) != 0;
309 let has_smap = leaf7.ebx & (1 << 20) != 0;
310
311 if has_smep {
312 unsafe { Cr4::update(|f| *f |= Cr4Flags::SUPERVISOR_MODE_EXECUTION_PROTECTION) };
313 show!(cpu, "smep enabled");
314 }
315
316 if has_smap {
317 unsafe { Cr4::update(|f| *f |= Cr4Flags::SUPERVISOR_MODE_ACCESS_PREVENTION) };
318 arch::syscall::set_smap_active(true);
319 show!(cpu, "smap enabled");
320 }
321 }
322 phase_end!(cpu);
323
324 phase_begin!(sys);
325 arch::syscall::init();
326 show!(sys, "syscall/sysret configured");
327 phase_end!(sys);
328
329 phase_begin!(cap);
330 cap::init(&mut allocator);
331 show!(cap, "initialized");
332 phase_end!(cap);
333
334 phase_begin!(sched);
335 sched::init();
336 wcet::init();
337 sched::seed_timer_wheel();
338 x86_64::instructions::interrupts::enable();
339 show!(sched, "scheduler + interrupts enabled");
340 phase_end!(sched);
341
342 #[cfg(lancer_test)]
343 {
344 dashboard::dash().transition_to_streaming();
345 let _ = (com1_gsi, kbd_gsi, virtio_net_gsi);
346 {
347 let mut ptable = proc::PROCESSES.lock();
348 let created = ptable
349 .allocate(&mut allocator)
350 .expect("Failed to create process 0");
351 assert!(created.pid().raw() == 0, "First process must be pid 0");
352 ptable.start(created).expect("Created -> Ready");
353 }
354 {
355 let memory_map = arch::boot::memory_map();
356 let reserved = cap::boot_untyped::build_reserved_regions();
357 let boot_untypeds = {
358 let ptable = proc::PROCESSES.lock();
359 let mut pool = cap::pool::POOL.lock_after(&ptable);
360 cap::boot_untyped::create_boot_untypeds(memory_map, &reserved, &mut pool)
361 };
362
363 cap::boot_untyped::store_metadata(boot_untypeds);
364 }
365 crate::test_harness::run_all();
366 }
367
368 #[cfg(not(lancer_test))]
369 {
370 phase_begin!(proc);
371
372 fn extract_filename(path: &[u8]) -> &[u8] {
373 let last_slash = (0..path.len()).rev().find(|&i| path[i] == b'/');
374 match last_slash {
375 Some(pos) => &path[pos + 1..],
376 None => path,
377 }
378 }
379
380 let modules = arch::boot::modules().unwrap_or(&[]);
381 show!(proc, "{} modules available", modules.len());
382
383 let init_file = modules
384 .iter()
385 .find(|file| {
386 let filename = extract_filename(file.path().to_bytes());
387 filename.len() == 4 && filename.iter().zip(b"init").all(|(a, b)| a == b)
388 })
389 .expect("init module not found");
390
391 let init_pid = {
392 let mut ptable = proc::PROCESSES.lock();
393 let created = ptable
394 .allocate(&mut allocator)
395 .expect("failed to create init process");
396 let pid = created.pid();
397 ptable.start(created).expect("Created -> Ready");
398 drop(ptable);
399
400 let data =
401 unsafe { core::slice::from_raw_parts(init_file.addr(), init_file.size() as usize) };
402 proc::loader::spawn_module(pid, data, &mut allocator).expect("init module load failed");
403 {
404 let mut ptable = proc::PROCESSES.lock();
405 let exec = ptable.exec_mut(pid).expect("init pid missing");
406 exec.set_name(b"init");
407 exec.saved_context.rdi = u64::MAX;
408 exec.seal_context();
409 }
410 pid
411 };
412
413 show!(proc, "init loaded as pid {}", init_pid.raw());
414 phase_end!(proc);
415
416 phase_begin!(fb);
417 match arch::boot::framebuffer() {
418 Some(fb) => show!(
419 fb,
420 "{}x{} {}bpp pitch {}",
421 fb.width(),
422 fb.height(),
423 fb.bpp(),
424 fb.pitch()
425 ),
426 None => show!(fb, "no framebuffer available"),
427 }
428 phase_end!(fb);
429
430 phase_begin!(boot);
431 proc::bootstrap::bootstrap_self_cap(init_pid, 10).expect("init self-cap failed");
432
433 {
434 const KERNEL_FRAME_RESERVE: usize = 5120;
435 let reserve_base = allocator
436 .allocate_contiguous(KERNEL_FRAME_RESERVE)
437 .expect("kernel frame reserve allocation failed");
438
439 let memory_map = arch::boot::memory_map();
440 let reserved = cap::boot_untyped::build_reserved_regions();
441 let boot_untypeds = {
442 let ptable = proc::PROCESSES.lock();
443 let mut pool = cap::pool::POOL.lock_after(&ptable);
444 cap::boot_untyped::create_boot_untypeds(memory_map, &reserved, &mut pool)
445 };
446
447 cap::boot_untyped::seal_bitmap(&boot_untypeds);
448
449 (0..KERNEL_FRAME_RESERVE).for_each(|i| {
450 let phys = x86_64::PhysAddr::new(reserve_base.as_u64() + (i as u64) * 4096);
451 allocator.deallocate_frame(
452 x86_64::structures::paging::PhysFrame::containing_address(phys),
453 );
454 });
455
456 let total_bytes: u64 = boot_untypeds
457 .as_slice()
458 .iter()
459 .filter(|u| !u.is_device)
460 .map(|u| 1u64 << u.size_bits)
461 .fold(0u64, u64::saturating_add);
462 show!(
463 boot,
464 "{} boot untypeds, {}MB usable exported, {}KB kernel reserve",
465 boot_untypeds.len(),
466 total_bytes / (1024 * 1024),
467 (KERNEL_FRAME_RESERVE * 4096) / 1024,
468 );
469
470 cap::boot_untyped::install_boot_untypeds(init_pid, &boot_untypeds)
471 .expect("install boot untypeds");
472 cap::boot_untyped::store_metadata(boot_untypeds);
473 }
474
475 proc::address_space::sync_kernel_entries();
476 phase_end!(boot);
477
478 let boot_end = tsc::read_tsc();
479 let boot_cycles = boot_end - boot_start;
480
481 dashboard::dash().transition_to_streaming();
482 show!(
483 boot,
484 "complete {} cycles {}ns",
485 boot_cycles,
486 tsc::cycles_to_ns(boot_cycles)
487 );
488
489 sched::rescue();
490 }
491}