#[cfg(feature = "newlib")]
use core::slice;
use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use hermit_entry::boot_info::{BootInfo, PlatformInfo, RawBootInfo};
use hermit_sync::InterruptSpinMutex;
use x86::controlregs::{cr0, cr0_write, cr4, Cr0};
use self::serial::SerialPort;
use crate::arch::mm::{PhysAddr, VirtAddr};
use crate::arch::x86_64::kernel::core_local::*;
use crate::env::{self, is_uhyve};
#[cfg(feature = "acpi")]
pub mod acpi;
pub mod apic;
pub mod core_local;
pub mod gdt;
pub mod interrupts;
#[cfg(all(not(feature = "pci"), feature = "tcp"))]
pub mod mmio;
#[cfg(feature = "pci")]
pub mod pci;
pub mod pic;
pub mod pit;
pub mod processor;
pub mod scheduler;
pub mod serial;
#[cfg(target_os = "none")]
mod start;
pub mod switch;
pub mod systemtime;
#[cfg(feature = "vga")]
mod vga;
#[cfg_attr(target_os = "none", link_section = ".data")]
static mut RAW_BOOT_INFO: Option<&'static RawBootInfo> = None;
static mut BOOT_INFO: Option<BootInfo> = None;
pub fn boot_info() -> &'static BootInfo {
unsafe { BOOT_INFO.as_ref().unwrap() }
}
#[cfg(feature = "smp")]
pub fn raw_boot_info() -> &'static RawBootInfo {
unsafe { RAW_BOOT_INFO.unwrap() }
}
static COM1: InterruptSpinMutex<Option<SerialPort>> = InterruptSpinMutex::new(None);
pub fn get_ram_address() -> PhysAddr {
PhysAddr(boot_info().hardware_info.phys_addr_range.start)
}
pub fn get_base_address() -> VirtAddr {
VirtAddr(boot_info().load_info.kernel_image_addr_range.start)
}
pub fn get_image_size() -> usize {
let range = &boot_info().load_info.kernel_image_addr_range;
(range.end - range.start) as usize
}
pub fn get_limit() -> usize {
boot_info().hardware_info.phys_addr_range.end as usize
}
pub fn get_tls_start() -> VirtAddr {
VirtAddr(
boot_info()
.load_info
.tls_info
.as_ref()
.map(|tls_info| tls_info.start)
.unwrap_or_default(),
)
}
pub fn get_tls_filesz() -> usize {
boot_info()
.load_info
.tls_info
.as_ref()
.map(|tls_info| tls_info.filesz)
.unwrap_or_default() as usize
}
pub fn get_tls_memsz() -> usize {
boot_info()
.load_info
.tls_info
.as_ref()
.map(|tls_info| tls_info.memsz)
.unwrap_or_default() as usize
}
pub fn get_tls_align() -> usize {
boot_info()
.load_info
.tls_info
.as_ref()
.map(|tls_info| tls_info.align)
.unwrap_or_default() as usize
}
pub fn get_mbinfo() -> VirtAddr {
match boot_info().platform_info {
PlatformInfo::Multiboot {
multiboot_info_addr,
..
} => VirtAddr(multiboot_info_addr.get()),
PlatformInfo::LinuxBootParams { .. } => VirtAddr(0),
PlatformInfo::Uhyve { .. } => VirtAddr(0),
}
}
#[cfg(feature = "smp")]
pub fn get_possible_cpus() -> u32 {
use core::cmp;
match boot_info().platform_info {
PlatformInfo::LinuxBootParams { .. } => apic::local_apic_id_count(),
PlatformInfo::Multiboot { .. } => apic::local_apic_id_count(),
PlatformInfo::Uhyve { num_cpus, .. } => cmp::max(
u32::try_from(num_cpus.get()).unwrap(),
get_processor_count(),
),
}
}
#[cfg(feature = "smp")]
pub fn get_processor_count() -> u32 {
CPU_ONLINE.load(Ordering::Acquire)
}
#[cfg(not(feature = "smp"))]
pub fn get_processor_count() -> u32 {
1
}
pub fn is_uhyve_with_pci() -> bool {
match boot_info().platform_info {
PlatformInfo::Multiboot { .. } => false,
PlatformInfo::LinuxBootParams { .. } => false,
PlatformInfo::Uhyve { has_pci, .. } => has_pci,
}
}
pub fn get_cmdsize() -> usize {
match boot_info().platform_info {
PlatformInfo::Multiboot { command_line, .. } => command_line
.map(|command_line| command_line.len())
.unwrap_or_default(),
PlatformInfo::LinuxBootParams { command_line, .. } => command_line
.map(|command_line| command_line.len())
.unwrap_or_default(),
PlatformInfo::Uhyve { .. } => 0,
}
}
pub fn get_cmdline() -> VirtAddr {
match boot_info().platform_info {
PlatformInfo::Multiboot { command_line, .. } => VirtAddr(
command_line
.map(|command_line| command_line.as_ptr() as u64)
.unwrap_or_default(),
),
PlatformInfo::LinuxBootParams { command_line, .. } => VirtAddr(
command_line
.map(|command_line| command_line.as_ptr() as u64)
.unwrap_or_default(),
),
PlatformInfo::Uhyve { .. } => VirtAddr(0),
}
}
pub fn message_output_init() {
CoreLocal::install();
let base = boot_info().hardware_info.serial_port_base.unwrap().get();
let serial_port = unsafe { SerialPort::new(base) };
*COM1.lock() = Some(serial_port);
}
#[cfg(target_os = "none")]
pub fn output_message_buf(buf: &[u8]) {
COM1.lock().as_mut().unwrap().send(buf);
#[cfg(feature = "vga")]
for &byte in buf {
vga::write_byte(byte);
}
}
#[cfg(not(target_os = "none"))]
pub fn output_message_buf(buf: &[u8]) {
use std::io::Write;
std::io::stderr().write_all(buf).unwrap();
}
#[cfg(target_os = "none")]
pub fn boot_processor_init() {
processor::detect_features();
processor::configure();
if cfg!(feature = "vga") && !env::is_uhyve() {
#[cfg(feature = "vga")]
vga::init();
}
crate::mm::init();
crate::mm::print_information();
env::init();
gdt::add_current_core();
interrupts::load_idt();
pic::init();
processor::detect_frequency();
processor::print_information();
unsafe {
trace!("Cr0: {:#x}, Cr4: {:#x}", cr0(), cr4());
}
interrupts::install();
systemtime::init();
if is_uhyve_with_pci() || !is_uhyve() {
#[cfg(feature = "pci")]
pci::init();
}
if !env::is_uhyve() {
#[cfg(feature = "acpi")]
acpi::init();
}
apic::init();
scheduler::install_timer_handler();
finish_processor_init();
interrupts::enable();
}
#[cfg(target_os = "none")]
pub fn boot_application_processors() {
#[cfg(feature = "smp")]
apic::boot_application_processors();
apic::print_information();
}
#[cfg(all(target_os = "none", feature = "smp"))]
pub fn application_processor_init() {
CoreLocal::install();
processor::configure();
gdt::add_current_core();
interrupts::load_idt();
apic::init_x2apic();
apic::init_local_apic();
unsafe {
trace!("Cr0: {:#x}, Cr4: {:#x}", cr0(), cr4());
}
interrupts::enable();
finish_processor_init();
}
fn finish_processor_init() {
if env::is_uhyve() {
apic::add_local_apic_id(core_id() as u8);
apic::init_next_processor_variables();
}
CPU_ONLINE.fetch_add(1, Ordering::Release);
}
pub fn print_statistics() {
interrupts::print_statistics();
}
pub static CPU_ONLINE: AtomicU32 = AtomicU32::new(0);
pub static CURRENT_STACK_ADDRESS: AtomicU64 = AtomicU64::new(0);
#[cfg(target_os = "none")]
#[inline(never)]
#[no_mangle]
unsafe extern "C" fn pre_init(boot_info: &'static RawBootInfo, cpu_id: u32) -> ! {
unsafe {
let mut cr0 = cr0();
cr0.remove(Cr0::CR0_CACHE_DISABLE | Cr0::CR0_NOT_WRITE_THROUGH);
cr0_write(cr0);
}
unsafe {
RAW_BOOT_INFO = Some(boot_info);
BOOT_INFO = Some(BootInfo::from(*boot_info));
}
if cpu_id == 0 {
crate::boot_processor_main()
} else {
#[cfg(not(feature = "smp"))]
{
error!("SMP support deactivated");
loop {
processor::halt();
}
}
#[cfg(feature = "smp")]
crate::application_processor_main();
}
}
#[cfg(all(test, not(target_os = "none")))]
mod tests {
use super::*;
#[test]
fn test_output() {
output_message_buf(b"test message\n");
}
}