use core::arch::asm;
#[cfg(not(feature = "host-test"))]
use core::arch::x86_64::{__cpuid, __cpuid_count};
#[cfg(all(feature = "host-test", not(target_os = "none")))]
use core::cell::Cell;
#[cfg(feature = "host-test")]
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(not(feature = "host-test"))]
use ax_memory_addr::MemoryAddr;
use ax_memory_addr::{PhysAddr, VirtAddr};
#[cfg(feature = "tls")]
use x86::msr;
#[cfg(not(feature = "host-test"))]
use x86::{controlregs, tlb};
#[cfg(not(all(feature = "host-test", not(target_os = "none"))))]
use x86_64::instructions::interrupts;
#[cfg(all(feature = "uspace", not(feature = "host-test")))]
use x86_64::instructions::tlb::Pcid;
#[cfg(not(feature = "host-test"))]
use x86_64::instructions::tlb::{InvPcidCommand, flush_pcid};
#[cfg(feature = "uspace")]
use crate::InstalledAddressSpace;
#[cfg(all(feature = "uspace", not(feature = "host-test")))]
use crate::InstalledAddressSpaceMode;
#[cfg(feature = "tls")]
use crate::KernelTlsBase;
#[cfg(not(feature = "host-test"))]
const PCID_CAPACITY: u32 = 1 << 12;
#[cfg(all(feature = "uspace", not(feature = "host-test")))]
const CR3_NOFLUSH: u64 = 1 << 63;
#[cfg(not(feature = "host-test"))]
fn pcid_invpcid_supported() -> bool {
let basic = __cpuid(1);
let maximum = __cpuid(0).eax;
let extended = (maximum >= 7).then(|| __cpuid_count(7, 0));
basic.ecx & (1 << 17) != 0 && extended.is_some_and(|features| features.ebx & (1 << 10) != 0)
}
#[cfg(not(feature = "host-test"))]
fn pcid_enabled() -> bool {
unsafe { controlregs::cr4() }.contains(controlregs::Cr4::CR4_ENABLE_PCID)
}
#[cfg(all(feature = "uspace", not(feature = "host-test")))]
fn ensure_pcid_enabled() -> bool {
if !pcid_invpcid_supported() {
return false;
}
let mut cr4 = unsafe { controlregs::cr4() };
if cr4.contains(controlregs::Cr4::CR4_ENABLE_PCID) {
return true;
}
if !cr4.contains(controlregs::Cr4::CR4_ENABLE_GLOBAL_PAGES) {
return false;
}
if unsafe { controlregs::cr3() } & 0xfff != 0 {
return false;
}
cr4.insert(controlregs::Cr4::CR4_ENABLE_PCID);
unsafe { controlregs::cr4_write(cr4) };
true
}
pub fn address_space_tag_capacity(_cpu_count: usize) -> u32 {
#[cfg(feature = "host-test")]
{
1
}
#[cfg(not(feature = "host-test"))]
{
let pge = unsafe { controlregs::cr4() }.contains(controlregs::Cr4::CR4_ENABLE_GLOBAL_PAGES);
if pge && pcid_invpcid_supported() {
PCID_CAPACITY
} else {
1
}
}
}
#[cfg(feature = "uspace")]
pub unsafe fn install_user_address_space(address_space: InstalledAddressSpace) {
address_space.validate_architecture_support();
#[cfg(feature = "host-test")]
HOST_PAGE_TABLE_ROOT.store(address_space.root().as_usize(), Ordering::Release);
#[cfg(not(feature = "host-test"))]
{
let root = address_space.root().as_usize() as u64;
let tagged = matches!(address_space.mode(), InstalledAddressSpaceMode::Tagged)
&& u32::from(address_space.hardware_tag()) < PCID_CAPACITY
&& ensure_pcid_enabled();
if tagged {
let Ok(pcid) = Pcid::new(address_space.hardware_tag()) else {
unsafe { controlregs::cr3_write(root) };
return;
};
unsafe { flush_pcid(InvPcidCommand::Single(pcid)) };
unsafe {
controlregs::cr3_write(root | u64::from(address_space.hardware_tag()) | CR3_NOFLUSH)
};
} else {
if pcid_enabled() && pcid_invpcid_supported() {
unsafe { flush_pcid(InvPcidCommand::All) };
}
unsafe { controlregs::cr3_write(root) };
}
}
}
#[cfg(feature = "host-test")]
static HOST_PAGE_TABLE_ROOT: AtomicUsize = AtomicUsize::new(0);
#[cfg(all(feature = "host-test", not(target_os = "none")))]
std::thread_local! {
static HOST_IRQS_ENABLED: Cell<bool> = const { Cell::new(true) };
}
#[inline]
pub fn enable_irqs() {
#[cfg(all(feature = "host-test", not(target_os = "none")))]
HOST_IRQS_ENABLED.set(true);
#[cfg(not(all(feature = "host-test", not(target_os = "none"))))]
interrupts::enable();
}
#[inline]
pub fn disable_irqs() {
#[cfg(all(feature = "host-test", not(target_os = "none")))]
HOST_IRQS_ENABLED.set(false);
#[cfg(not(all(feature = "host-test", not(target_os = "none"))))]
interrupts::disable();
}
#[inline]
pub fn irqs_enabled() -> bool {
#[cfg(all(feature = "host-test", not(target_os = "none")))]
return HOST_IRQS_ENABLED.get();
#[cfg(not(all(feature = "host-test", not(target_os = "none"))))]
interrupts::are_enabled()
}
#[inline]
pub fn wait_for_irqs() {
unsafe { asm!("hlt") }
}
#[inline]
pub fn wait_for_irqs_disabled() {
debug_assert!(!irqs_enabled());
unsafe { asm!("sti; hlt", options(nostack)) }
}
#[inline]
pub fn halt() {
disable_irqs();
wait_for_irqs(); }
#[inline]
pub fn read_user_page_table() -> PhysAddr {
#[cfg(feature = "host-test")]
return PhysAddr::from(HOST_PAGE_TABLE_ROOT.load(Ordering::Acquire));
#[cfg(not(feature = "host-test"))]
pa!(unsafe { controlregs::cr3() } as usize).align_down_4k()
}
#[inline]
pub fn read_kernel_page_table() -> PhysAddr {
read_user_page_table()
}
#[inline]
pub unsafe fn write_user_page_table(root_paddr: PhysAddr) {
#[cfg(feature = "host-test")]
{
HOST_PAGE_TABLE_ROOT.store(root_paddr.as_usize(), Ordering::Release);
}
#[cfg(not(feature = "host-test"))]
unsafe {
controlregs::cr3_write(root_paddr.as_usize() as _)
}
}
#[inline]
pub unsafe fn write_kernel_page_table(root_paddr: PhysAddr) {
unsafe { write_user_page_table(root_paddr) }
}
#[inline]
pub fn flush_icache_all() {}
#[inline]
pub fn flush_tlb(vaddr: Option<VirtAddr>) {
#[cfg(feature = "host-test")]
let _ = vaddr;
#[cfg(not(feature = "host-test"))]
{
if let Some(vaddr) = vaddr {
unsafe { tlb::flush(vaddr.into()) }
} else if pcid_enabled() && pcid_invpcid_supported() {
unsafe { flush_pcid(InvPcidCommand::All) }
} else {
unsafe { tlb::flush_all() }
}
}
}
#[inline]
pub fn update_mmu_cache(_vaddr: VirtAddr) {}
#[inline]
#[cfg(feature = "tls")]
pub fn read_thread_pointer() -> KernelTlsBase {
KernelTlsBase::new(unsafe { msr::rdmsr(msr::IA32_FS_BASE) as usize })
}
#[inline]
#[cfg(feature = "tls")]
pub unsafe fn write_thread_pointer(kernel_tls: KernelTlsBase) {
unsafe { msr::wrmsr(msr::IA32_FS_BASE, kernel_tls.as_usize() as u64) }
}
#[cfg(feature = "uspace")]
core::arch::global_asm!(include_str!("user_copy.S"), include_str!("user_atomic.S"),);
#[cfg(feature = "uspace")]
unsafe extern "C" {
pub fn user_copy(dst: *mut u8, src: *const u8, size: usize) -> usize;
}
#[cfg(feature = "uspace")]
#[inline]
pub unsafe fn user_access_ok_page(_vaddr: usize, _access: crate::UserAccessType) -> bool {
false
}
#[cfg(all(test, feature = "host-test"))]
mod tests {
use super::*;
#[test]
fn host_irq_mask_is_isolated_per_execution_thread() {
assert!(irqs_enabled());
disable_irqs();
assert!(!irqs_enabled());
std::thread::spawn(|| {
assert!(irqs_enabled());
disable_irqs();
assert!(!irqs_enabled());
enable_irqs();
assert!(irqs_enabled());
})
.join()
.unwrap();
assert!(!irqs_enabled());
enable_irqs();
assert!(irqs_enabled());
}
}