diff --git a/src/arch/aarch64/kernel/mod.rs b/src/arch/aarch64/kernel/mod.rs index a61f1bbc4c..cb9587b0c8 100644 --- a/src/arch/aarch64/kernel/mod.rs +++ b/src/arch/aarch64/kernel/mod.rs @@ -25,7 +25,6 @@ pub(crate) use self::processor::set_oneshot_timer; use crate::arch::aarch64::kernel::core_local::*; use crate::arch::aarch64::mm::paging::{BasePageSize, PageSize}; use crate::config::*; -use crate::env; #[repr(align(8))] pub(crate) struct AlignedAtomicU32(AtomicU32); @@ -42,7 +41,7 @@ global_asm!(include_str!("start.s")); #[cfg(feature = "smp")] pub fn get_possible_cpus() -> u32 { - let fdt = env::fdt().unwrap(); + let fdt = crate::env::fdt().unwrap(); let cpu_count = fdt.cpus().count(); u32::try_from(cpu_count).unwrap() } @@ -60,9 +59,7 @@ pub fn get_processor_count() -> u32 { /// Real Boot Processor initialization as soon as we have put the first Welcome message on the screen. #[cfg(target_os = "none")] pub fn boot_processor_init() { - if !env::is_uhyve() { - processor::configure(); - } + processor::configure(); crate::mm::init(); crate::mm::print_information(); @@ -102,8 +99,14 @@ pub fn boot_next_processor() { #[allow(unused_variables)] let cpu_online = CPU_ONLINE.0.fetch_add(1, Ordering::Release); + #[allow(clippy::needless_return)] + #[cfg(feature = "uhyve")] + if crate::env::is_uhyve() { + return; + } + #[cfg(all(target_os = "none", feature = "smp"))] - if !env::is_uhyve() && get_possible_cpus() > 1 { + if get_possible_cpus() > 1 { use core::arch::asm; use core::hint::spin_loop; @@ -122,7 +125,7 @@ pub fn boot_next_processor() { trace!("Virtual address of smp_start 0x{virt_start:x}"); trace!("Physical address of smp_start 0x{phys_start:x}"); - let fdt = env::fdt().unwrap(); + let fdt = crate::env::fdt().unwrap(); let psci_node = fdt.find_node("/psci").unwrap(); let cpu_on = psci_node.property("cpu_on").unwrap().as_usize().unwrap(); diff --git a/src/arch/aarch64/kernel/processor.rs b/src/arch/aarch64/kernel/processor.rs index 3a62f128a7..c27034b922 100644 --- a/src/arch/aarch64/kernel/processor.rs +++ b/src/arch/aarch64/kernel/processor.rs @@ -262,6 +262,11 @@ pub fn supports_2mib_pages() -> bool { } pub fn configure() { + #[cfg(feature = "uhyve")] + if env::is_uhyve() { + return; + } + // TODO: PMCCNTR_EL0 is the best replacement for RDTSC on AArch64. // However, this test code showed that it's apparently not supported under uhyve yet. // Finish the boot loader for QEMU first and then run this code under QEMU, where it should be supported. diff --git a/src/arch/aarch64/kernel/systemtime.rs b/src/arch/aarch64/kernel/systemtime.rs index 8202bff934..4385b468ac 100644 --- a/src/arch/aarch64/kernel/systemtime.rs +++ b/src/arch/aarch64/kernel/systemtime.rs @@ -1,7 +1,6 @@ use core::arch::asm; use free_list::PageLayout; -use hermit_entry::boot_info::PlatformInfo; use hermit_sync::OnceCell; use memory_addresses::{PhysAddr, VirtAddr}; use time::OffsetDateTime; @@ -45,55 +44,49 @@ fn rtc_read(off: usize) -> u32 { u32::from_le(value) } -pub fn init() { - match env::boot_info().platform_info { - PlatformInfo::Uhyve { boot_time, .. } => { - PL031_ADDRESS.set(VirtAddr::zero()).unwrap(); - BOOT_TIME - .set(u64::try_from(boot_time.unix_timestamp_nanos() / 1000).unwrap()) - .unwrap(); - info!("Hermit booted on {boot_time}"); - - return; - } - _ => { - let fdt = env::fdt().unwrap(); - if let Some(pl031_node) = fdt.find_compatible(&["arm,pl031"]) { - let reg = pl031_node.reg().unwrap().next().unwrap(); - let addr = PhysAddr::from(reg.starting_address.addr()); - let size = u64::try_from(reg.size.unwrap()).unwrap(); - - debug!("Found RTC at {addr:p} (size {size:#X})"); - - let layout = PageLayout::from_size(size.try_into().unwrap()).unwrap(); - let page_range = PageAlloc::allocate(layout).unwrap(); - let pl031_address = VirtAddr::from(page_range.start()); - PL031_ADDRESS.set(pl031_address).unwrap(); - debug!("Mapping RTC to virtual address {pl031_address:p}"); - - let mut flags = PageTableEntryFlags::empty(); - flags.device().writable().execute_disable(); - paging::map::( - pl031_address, - addr, - (size / BasePageSize::SIZE).try_into().unwrap(), - flags, - ); - - let boot_time = - OffsetDateTime::from_unix_timestamp(rtc_read(reg::RTC_DR).into()).unwrap(); - info!("Hermit booted on {boot_time}"); - - BOOT_TIME - .set(u64::try_from(boot_time.unix_timestamp_nanos() / 1000).unwrap()) - .unwrap(); - - return; - } - } +fn boot_time() -> OffsetDateTime { + #[cfg(feature = "uhyve")] + if let Some(boot_time) = env::uhyve_boot_time() { + return boot_time; + } + + let fdt = env::fdt().unwrap(); + let Some(pl031_node) = fdt.find_compatible(&["arm,pl031"]) else { + error!("Could not find PL031 Real Time Clock to determine the boot time."); + return OffsetDateTime::UNIX_EPOCH; }; - BOOT_TIME.set(0).unwrap(); + let reg = pl031_node.reg().unwrap().next().unwrap(); + let addr = PhysAddr::from(reg.starting_address.addr()); + let size = u64::try_from(reg.size.unwrap()).unwrap(); + + debug!("Found RTC at {addr:p} (size {size:#X})"); + + let layout = PageLayout::from_size(size.try_into().unwrap()).unwrap(); + let page_range = PageAlloc::allocate(layout).unwrap(); + let pl031_address = VirtAddr::from(page_range.start()); + PL031_ADDRESS.set(pl031_address).unwrap(); + debug!("Mapping RTC to virtual address {pl031_address:p}"); + + let mut flags = PageTableEntryFlags::empty(); + flags.device().writable().execute_disable(); + paging::map::( + pl031_address, + addr, + (size / BasePageSize::SIZE).try_into().unwrap(), + flags, + ); + + OffsetDateTime::from_unix_timestamp(rtc_read(reg::RTC_DR).into()).unwrap() +} + +pub fn init() { + let boot_time = boot_time(); + info!("Hermit booted on {boot_time}"); + + BOOT_TIME + .set(u64::try_from(boot_time.unix_timestamp_nanos() / 1000).unwrap()) + .unwrap(); } /// Returns the current time in microseconds since UNIX epoch. diff --git a/src/arch/riscv64/kernel/mod.rs b/src/arch/riscv64/kernel/mod.rs index a24a0606b4..de549e7aeb 100644 --- a/src/arch/riscv64/kernel/mod.rs +++ b/src/arch/riscv64/kernel/mod.rs @@ -149,11 +149,14 @@ pub fn boot_next_processor() { // TODO: Old: Changing cpu_online will cause uhyve to start the next processor CPU_ONLINE.fetch_add(1, Ordering::Release); - //When running bare-metal/QEMU we use the firmware to start the next hart - if !env::is_uhyve() { - let start_addr = (start::_start as *const ()).expose_provenance(); - sbi_rt::hart_start(next_hart_id as usize, start_addr, 0).unwrap(); + #[cfg(feature = "uhyve")] + if env::is_uhyve() { + return; } + + //When running bare-metal/QEMU we use the firmware to start the next hart + let start_addr = (start::_start as *const ()).expose_provenance(); + sbi_rt::hart_start(next_hart_id as usize, start_addr, 0).unwrap(); } pub fn print_statistics() { diff --git a/src/arch/x86_64/kernel/acpi.rs b/src/arch/x86_64/kernel/acpi.rs index cfa47caa3e..e4aa03cf83 100644 --- a/src/arch/x86_64/kernel/acpi.rs +++ b/src/arch/x86_64/kernel/acpi.rs @@ -517,6 +517,11 @@ pub fn poweroff() { } pub fn init() { + #[cfg(feature = "uhyve")] + if env::is_uhyve() { + return; + } + // Detect the RSDP and get a pointer to either the XSDT (64-bit) or RSDT (32-bit), whichever is available. // Both are called RSDT in the following. let rsdp = detect_acpi().expect("Hermit requires an ACPI-compliant system"); diff --git a/src/arch/x86_64/kernel/apic.rs b/src/arch/x86_64/kernel/apic.rs index b0b07fa9f2..6695a69b30 100644 --- a/src/arch/x86_64/kernel/apic.rs +++ b/src/arch/x86_64/kernel/apic.rs @@ -1,12 +1,9 @@ -use alloc::alloc::alloc; use alloc::vec::Vec; -use core::alloc::Layout; #[cfg(feature = "smp")] use core::arch::x86_64::_mm_mfence; #[cfg(feature = "acpi")] use core::fmt; use core::hint::spin_loop; -use core::sync::atomic::Ordering; use core::{cmp, ptr}; use align_address::Align; @@ -21,7 +18,6 @@ use x86_64::registers::control::Cr3; use x86_64::registers::model_specific::Msr; use super::interrupts::IDT; -use crate::arch::x86_64::kernel::CURRENT_STACK_ADDRESS; #[cfg(feature = "acpi")] use crate::arch::x86_64::kernel::acpi; use crate::arch::x86_64::mm::paging; @@ -29,7 +25,6 @@ use crate::arch::x86_64::mm::paging::{ BasePageSize, PageSize, PageTableEntryFlags, PageTableEntryFlagsExt, }; use crate::arch::x86_64::swapgs; -use crate::config::*; use crate::mm::{PageAlloc, PageBox, PageRangeAllocator}; use crate::scheduler::CoreId; use crate::{arch, env, scheduler}; @@ -511,19 +506,24 @@ fn default_apic() -> PhysAddr { default_local } +fn apic_addr() -> PhysAddr { + #[cfg(feature = "uhyve")] + if env::is_uhyve() { + return default_apic(); + } + + detect_from_acpi() + .or_else(|()| detect_from_mp()) + .unwrap_or_else(|()| default_apic()) +} + pub fn eoi() { local_apic_write(IA32_X2APIC_EOI, APIC_EOI_ACK); } pub fn init() { // Detect CPUs and APICs. - let local_apic_physical_address = if env::is_uhyve() { - default_apic() - } else { - detect_from_acpi() - .or_else(|()| detect_from_mp()) - .unwrap_or_else(|()| default_apic()) - }; + let local_apic_physical_address = apic_addr(); // Initialize x2APIC or xAPIC, depending on what's available. if processor::supports_x2apic() { @@ -729,7 +729,15 @@ pub fn init_x2apic() { } /// Initialize the required _start variables for the next CPU to be booted. +#[cfg(feature = "smp")] pub fn init_next_processor_variables() { + use alloc::alloc::alloc; + use core::alloc::Layout; + use core::sync::atomic::Ordering; + + use crate::arch::x86_64::kernel::CURRENT_STACK_ADDRESS; + use crate::config::KERNEL_STACK_SIZE; + // Allocate stack for the CPU and pass the addresses. let layout = Layout::from_size_align(KERNEL_STACK_SIZE, BasePageSize::SIZE as usize).unwrap(); let stack = unsafe { alloc(layout) }; diff --git a/src/arch/x86_64/kernel/mod.rs b/src/arch/x86_64/kernel/mod.rs index fc5bcc2484..e0696d93a6 100644 --- a/src/arch/x86_64/kernel/mod.rs +++ b/src/arch/x86_64/kernel/mod.rs @@ -10,7 +10,7 @@ use x86_64::registers::control::{Cr0, Cr4}; pub(crate) use self::apic::{set_oneshot_timer, wakeup_core}; use crate::arch::x86_64::kernel::core_local::*; -use crate::env::{self, is_uhyve}; +use crate::env; #[cfg(feature = "acpi")] pub mod acpi; @@ -40,12 +40,12 @@ pub mod vga; #[cfg(feature = "smp")] pub fn get_possible_cpus() -> u32 { - use hermit_entry::boot_info::PlatformInfo; - - match env::boot_info().platform_info { - PlatformInfo::Uhyve { num_cpus, .. } => u32::try_from(num_cpus.get()).unwrap(), - _ => apic::local_apic_id_count(), + #[cfg(feature = "uhyve")] + if let Some(num_cpus) = env::uhyve_num_cpus() { + return num_cpus.get().try_into().unwrap(); } + + apic::local_apic_id_count() } #[cfg(feature = "smp")] @@ -64,10 +64,8 @@ pub fn boot_processor_init() { processor::detect_features(); processor::configure(); - if cfg!(feature = "vga") && !is_uhyve() { - #[cfg(feature = "vga")] - vga::init(); - } + #[cfg(feature = "vga")] + vga::init(); crate::mm::init(); crate::mm::print_information(); @@ -84,10 +82,8 @@ pub fn boot_processor_init() { interrupts::install(); systemtime::init(); - if !is_uhyve() { - #[cfg(feature = "acpi")] - acpi::init(); - } + #[cfg(feature = "acpi")] + acpi::init(); #[cfg(feature = "pci")] pci::init(); @@ -114,7 +110,8 @@ pub fn application_processor_init() { } fn finish_processor_init() { - if is_uhyve() { + #[cfg(feature = "uhyve")] + if env::is_uhyve() { // uhyve does not use apic::detect_from_acpi and therefore does not know the number of processors and // their APIC IDs in advance. // Therefore, we have to add each booted processor into the CPU_LOCAL_APIC_IDS vector ourselves. @@ -123,6 +120,7 @@ fn finish_processor_init() { // uhyve also boots each processor into _start itself and does not use apic::boot_application_processors. // Therefore, the current processor already needs to prepare the processor variables for a possible next processor. + #[cfg(feature = "smp")] apic::init_next_processor_variables(); } } @@ -132,15 +130,18 @@ pub fn boot_next_processor() { // to initialize the next processor. let cpu_online = CPU_ONLINE.fetch_add(1, Ordering::Release); - if !is_uhyve() { - if cpu_online == 0 { - #[cfg(all(target_os = "none", feature = "smp"))] - apic::boot_application_processors(); - } + #[cfg(feature = "uhyve")] + if env::is_uhyve() { + return; + } - if !cfg!(feature = "smp") { - apic::print_information(); - } + if cpu_online == 0 { + #[cfg(all(target_os = "none", feature = "smp"))] + apic::boot_application_processors(); + } + + if !cfg!(feature = "smp") { + apic::print_information(); } } diff --git a/src/arch/x86_64/kernel/processor.rs b/src/arch/x86_64/kernel/processor.rs index 0915d6365c..460c6713d8 100644 --- a/src/arch/x86_64/kernel/processor.rs +++ b/src/arch/x86_64/kernel/processor.rs @@ -8,10 +8,9 @@ use core::arch::x86_64::{ }; use core::fmt; use core::hint::spin_loop; -use core::num::{NonZero, NonZeroU32}; +use core::num::NonZero; use core::sync::atomic::{AtomicU64, Ordering}; -use hermit_entry::boot_info::PlatformInfo; use hermit_sync::Lazy; use raw_cpuid::*; use x86_64::instructions::interrupts::int3; @@ -369,18 +368,19 @@ impl CpuFrequency { } fn detect_from_hypervisor(&mut self) -> Result<(), ()> { - fn detect_from_uhyve() -> Result { - match env::boot_info().platform_info { - PlatformInfo::Uhyve { cpu_freq, .. } => Ok(u16::try_from( - cpu_freq.map(NonZeroU32::get).unwrap_or_default() / 1000, - ) - .unwrap()), - _ => Err(()), - } + #[cfg(feature = "uhyve")] + { + let cpu_freq = env::uhyve_cpu_freq().ok_or(())?.get(); + let mhz = cpu_freq / 1000; + + self.set_detected_cpu_frequency( + mhz.try_into().unwrap(), + CpuFrequencySources::Hypervisor, + ) } - // future implementations could add support for different hypervisors - // by adding or_else here - self.set_detected_cpu_frequency(detect_from_uhyve()?, CpuFrequencySources::Hypervisor) + + #[cfg(not(feature = "uhyve"))] + Err(()) } extern "x86-interrupt" fn measure_frequency_timer_handler( @@ -401,11 +401,6 @@ impl CpuFrequency { fn measure_frequency(&mut self) -> Result<(), ()> { use crate::arch::x86_64::kernel::interrupts::IDT; - // The PIC is not initialized for uhyve, so we cannot measure anything. - if env::is_uhyve() { - return Err(()); - } - // Measure the CPU frequency by counting 3 ticks of a 100Hz timer. let tick_count = 3; let measurement_frequency = 100; @@ -668,10 +663,6 @@ impl fmt::Display for CpuFeaturePrinter { } } -pub(crate) fn run_on_hypervisor() -> bool { - env::is_uhyve() || FEATURES.run_on_hypervisor -} - #[derive(Debug)] struct CpuSpeedStep { eist_available: bool, diff --git a/src/arch/x86_64/kernel/systemtime.rs b/src/arch/x86_64/kernel/systemtime.rs index cea2329330..43531e0f4d 100644 --- a/src/arch/x86_64/kernel/systemtime.rs +++ b/src/arch/x86_64/kernel/systemtime.rs @@ -1,12 +1,10 @@ use core::hint::spin_loop; -use hermit_entry::boot_info::PlatformInfo; use hermit_sync::{OnceCell, without_interrupts}; use time::OffsetDateTime; use x86_64::instructions::port::Port; use crate::arch::x86_64::kernel::processor; -use crate::env; const CMOS_COMMAND: Port = Port::new(0x70); const CMOS_DATA: Port = Port::new(0x71); @@ -175,17 +173,22 @@ impl Rtc { static BOOT_TIME: OnceCell = OnceCell::new(); +fn boot_time() -> OffsetDateTime { + #[cfg(feature = "uhyve")] + if let Some(boot_time) = crate::env::uhyve_boot_time() { + return boot_time; + } + + // Get the current time in microseconds since the epoch (1970-01-01) from the x86 RTC. + // Subtract the timer ticks to get the actual time when Hermit was booted. + let current_time = without_interrupts(|| Rtc::new().get_microseconds_since_epoch()); + let boot_micros = current_time - processor::get_timer_ticks(); + let boot_nanos = i128::from(boot_micros) * 1000; + OffsetDateTime::from_unix_timestamp_nanos(boot_nanos).unwrap() +} + pub fn init() { - let boot_time = match env::boot_info().platform_info { - PlatformInfo::Uhyve { boot_time, .. } => boot_time, - _ => { - // Get the current time in microseconds since the epoch (1970-01-01) from the x86 RTC. - // Subtract the timer ticks to get the actual time when Hermit was booted. - let current_time = without_interrupts(|| Rtc::new().get_microseconds_since_epoch()); - let boot_time = current_time - processor::get_timer_ticks(); - OffsetDateTime::from_unix_timestamp_nanos(i128::from(boot_time) * 1000).unwrap() - } - }; + let boot_time = boot_time(); info!("Hermit booted on {boot_time}"); let micros = u64::try_from(boot_time.unix_timestamp_nanos() / 1000).unwrap(); diff --git a/src/arch/x86_64/kernel/vga.rs b/src/arch/x86_64/kernel/vga.rs index ea5c0f7a89..7f763ccc8c 100644 --- a/src/arch/x86_64/kernel/vga.rs +++ b/src/arch/x86_64/kernel/vga.rs @@ -133,6 +133,11 @@ impl VgaScreen { } pub fn init() { + #[cfg(feature = "uhyve")] + if crate::env::is_uhyve() { + return; + } + VGA_SCREEN.lock().init(); } diff --git a/src/env.rs b/src/env.rs index f016f27edb..17cd879576 100644 --- a/src/env.rs +++ b/src/env.rs @@ -10,7 +10,7 @@ use ahash::RandomState; use fdt::Fdt; use hashbrown::HashMap; use hashbrown::hash_map::Iter; -use hermit_entry::boot_info::{BootInfo, PlatformInfo, RawBootInfo}; +use hermit_entry::boot_info::{BootInfo, RawBootInfo}; use hermit_sync::OnceCell; use memory_addresses::PhysAddr; @@ -44,10 +44,51 @@ struct Cli { } /// Whether Hermit is running under the "uhyve" hypervisor. +#[cfg(feature = "uhyve")] pub fn is_uhyve() -> bool { + use hermit_entry::boot_info::PlatformInfo; + matches!(boot_info().platform_info, PlatformInfo::Uhyve { .. }) } +#[cfg_attr(target_arch = "riscv64", expect(dead_code))] +#[cfg(feature = "uhyve")] +pub fn uhyve_boot_time() -> Option { + use hermit_entry::boot_info::PlatformInfo; + + match boot_info().platform_info { + PlatformInfo::Uhyve { boot_time, .. } => Some(boot_time), + _ => None, + } +} + +#[cfg_attr( + any(not(target_arch = "x86_64"), not(feature = "smp")), + expect(dead_code) +)] +#[cfg(feature = "uhyve")] +pub fn uhyve_num_cpus() -> Option> { + use hermit_entry::boot_info::PlatformInfo; + + match boot_info().platform_info { + PlatformInfo::Uhyve { num_cpus, .. } => { + Some(NonZero::new(num_cpus.get() as usize).unwrap()) + } + _ => None, + } +} + +#[cfg_attr(not(target_arch = "x86_64"), expect(dead_code))] +#[cfg(feature = "uhyve")] +pub fn uhyve_cpu_freq() -> Option> { + use hermit_entry::boot_info::PlatformInfo; + + match boot_info().platform_info { + PlatformInfo::Uhyve { cpu_freq, .. } => Some(NonZero::new(cpu_freq?.get()).unwrap()), + _ => None, + } +} + pub fn is_uefi() -> bool { fdt().is_some_and(|fdt| fdt.root().compatible().first() == "hermit,uefi") } diff --git a/src/lib.rs b/src/lib.rs index 965d925019..25745c5744 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -175,12 +175,6 @@ extern "C" fn initd(_arg: usize) { fn main(argc: i32, argv: *const *const u8, env: *const *const u8); } - if env::is_uhyve() { - info!("Hermit is running on uhyve!"); - } else { - info!("Hermit is running on common system!"); - } - // Initialize Drivers drivers::init(); // The filesystem needs to be initialized before network to allow writing packet captures to a file.