mycelium_kernel/arch/
x86_64.rs1use bootloader_api::config::{BootloaderConfig, Mapping};
2use hal_core::boot::BootInfo;
3use hal_x86_64::{
4 cpu::{self, local::GsLocalData},
5 time, vga,
6};
7pub use hal_x86_64::{
8 cpu::{entropy::seed_rng, local::LocalKey, wait_for_interrupt},
9 mm, NAME,
10};
11
12mod acpi;
13mod boot;
14mod framebuf;
15pub mod interrupt;
16mod oops;
17pub mod pci;
18pub mod shell;
19pub use self::{
20 boot::ArchInfo,
21 oops::{oops, Oops},
22};
23
24#[cfg(test)]
25mod tests;
26
27pub type MinPageSize = mm::size::Size4Kb;
28
29pub static BOOTLOADER_CONFIG: BootloaderConfig = {
30 let mut config = BootloaderConfig::new_default();
31 config.mappings.physical_memory = Some(Mapping::Dynamic);
32 config.mappings.dynamic_range_start = Some(0xFFFF_8000_0000_0000);
34 config.mappings.page_table_recursive = Some(Mapping::Dynamic);
35
36 config
37};
38
39#[cfg(target_os = "none")]
40bootloader_api::entry_point!(arch_entry, config = &BOOTLOADER_CONFIG);
41
42pub fn arch_entry(info: &'static mut bootloader_api::BootInfo) -> ! {
43 unsafe {
44 cpu::intrinsics::cli();
45 }
46
47 if let Some(offset) = info.physical_memory_offset.into_option() {
48 unsafe {
50 vga::init_with_offset(offset);
51 }
52 }
53 let (boot_info, archinfo) = boot::BootloaderApiBootInfo::from_bootloader(info);
58 crate::kernel_start(boot_info, archinfo);
59}
60
61pub fn init(_info: &impl BootInfo, archinfo: &ArchInfo) -> maitake::time::Clock {
62 pci::init_pci();
63
64 unsafe {
66 GsLocalData::init();
67 }
68 tracing::info!("set up the boot processor's local data");
69
70 if let Some(rsdp) = archinfo.rsdp_addr {
71 let acpi = acpi::acpi_tables(rsdp);
72 let platform_info = acpi.and_then(|acpi| acpi.platform_info());
73 match platform_info {
74 Ok(platform) => {
75 tracing::debug!("found ACPI platform info");
76 let irq_ctrl =
77 interrupt::enable_hardware_interrupts(Some(&platform.interrupt_model));
78 acpi::bringup_smp(&platform)
79 .expect("failed to bring up application processors! this is bad news!");
80 irq_ctrl
81 }
82 Err(error) => {
83 tracing::warn!(?error, "missing ACPI platform info");
84 interrupt::enable_hardware_interrupts(None)
85 }
86 }
87 } else {
88 tracing::warn!("no RSDP from bootloader, skipping SMP bringup");
90 interrupt::enable_hardware_interrupts(None)
91 };
92
93 time::Rdtsc::new()
94 .map_err(|error| {
95 tracing::warn!(%error, "RDTSC not supported");
96 })
97 .and_then(|rdtsc| {
98 rdtsc
99 .into_maitake_clock()
100 .inspect(|clock| tracing::info!(?clock, "calibrated RDTSC clock"))
101 .map_err(|error| tracing::warn!(%error, "could not calibrate RDTSC clock"))
102 })
103 .unwrap_or(interrupt::IDIOTIC_CLOCK)
104}
105
106#[cfg(test)]
109pub fn run_tests() {
110 use hal_x86_64::serial;
111 let com1 = serial::com1().expect("if we're running tests, there ought to be a serial port");
112 let mk = || com1.lock();
113 match mycotest::runner::run_tests(mk) {
114 Ok(()) => qemu_exit(QemuExitCode::Success),
115 Err(_) => qemu_exit(QemuExitCode::Failed),
116 }
117}
118
119#[cfg(test)]
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121#[repr(u32)]
122pub(crate) enum QemuExitCode {
123 Success = 0x10,
124 Failed = 0x11,
125}
126
127#[cfg(test)]
131pub(crate) fn qemu_exit(exit_code: QemuExitCode) -> ! {
132 let code = exit_code as u32;
133 unsafe {
134 cpu::Port::at(0xf4).writel(code);
135
136 cpu::halt()
138 }
139}