1use core::arch::asm;
4#[cfg(not(feature = "host-test"))]
5use core::arch::x86_64::{__cpuid, __cpuid_count};
6#[cfg(all(feature = "host-test", not(target_os = "none")))]
7use core::cell::Cell;
8#[cfg(feature = "host-test")]
9use core::sync::atomic::{AtomicUsize, Ordering};
10
11#[cfg(not(feature = "host-test"))]
12use ax_memory_addr::MemoryAddr;
13use ax_memory_addr::{PhysAddr, VirtAddr};
14#[cfg(kernel_tls)]
15use x86::msr;
16#[cfg(not(feature = "host-test"))]
17use x86::{controlregs, tlb};
18#[cfg(not(all(feature = "host-test", not(target_os = "none"))))]
19use x86_64::instructions::interrupts;
20#[cfg(all(feature = "uspace", not(feature = "host-test")))]
21use x86_64::instructions::tlb::Pcid;
22#[cfg(not(feature = "host-test"))]
23use x86_64::instructions::tlb::{InvPcidCommand, flush_pcid};
24
25#[cfg(feature = "uspace")]
26use crate::InstalledAddressSpace;
27#[cfg(all(feature = "uspace", not(feature = "host-test")))]
28use crate::InstalledAddressSpaceMode;
29#[cfg(kernel_tls)]
30use crate::KernelTlsBase;
31
32#[cfg(not(feature = "host-test"))]
33const PCID_CAPACITY: u32 = 1 << 12;
34#[cfg(all(feature = "uspace", not(feature = "host-test")))]
35const CR3_NOFLUSH: u64 = 1 << 63;
36
37#[cfg(not(feature = "host-test"))]
38fn pcid_invpcid_supported() -> bool {
39 let basic = __cpuid(1);
40 let maximum = __cpuid(0).eax;
41 let extended = (maximum >= 7).then(|| __cpuid_count(7, 0));
42 basic.ecx & (1 << 17) != 0 && extended.is_some_and(|features| features.ebx & (1 << 10) != 0)
43}
44
45#[cfg(not(feature = "host-test"))]
46fn pcid_enabled() -> bool {
47 unsafe { controlregs::cr4() }.contains(controlregs::Cr4::CR4_ENABLE_PCID)
49}
50
51#[cfg(all(feature = "uspace", not(feature = "host-test")))]
52fn ensure_pcid_enabled() -> bool {
53 if !pcid_invpcid_supported() {
54 return false;
55 }
56 let mut cr4 = unsafe { controlregs::cr4() };
58 if cr4.contains(controlregs::Cr4::CR4_ENABLE_PCID) {
59 return true;
60 }
61 if !cr4.contains(controlregs::Cr4::CR4_ENABLE_GLOBAL_PAGES) {
62 return false;
63 }
64 if unsafe { controlregs::cr3() } & 0xfff != 0 {
67 return false;
68 }
69 cr4.insert(controlregs::Cr4::CR4_ENABLE_PCID);
70 unsafe { controlregs::cr4_write(cr4) };
72 true
73}
74
75pub fn address_space_tag_capacity(_cpu_count: usize) -> u32 {
80 #[cfg(feature = "host-test")]
81 {
82 1
83 }
84 #[cfg(not(feature = "host-test"))]
85 {
86 let pge = unsafe { controlregs::cr4() }.contains(controlregs::Cr4::CR4_ENABLE_GLOBAL_PAGES);
88 if pge && pcid_invpcid_supported() {
89 PCID_CAPACITY
90 } else {
91 1
92 }
93 }
94}
95
96#[cfg(feature = "uspace")]
109pub unsafe fn install_user_address_space(address_space: InstalledAddressSpace) {
110 address_space.validate_architecture_support();
111 #[cfg(feature = "host-test")]
112 HOST_PAGE_TABLE_ROOT.store(address_space.root().as_usize(), Ordering::Release);
113 #[cfg(not(feature = "host-test"))]
114 {
115 let root = address_space.root().as_usize() as u64;
116 let tagged = matches!(address_space.mode(), InstalledAddressSpaceMode::Tagged)
117 && u32::from(address_space.hardware_tag()) < PCID_CAPACITY
118 && ensure_pcid_enabled();
119 if tagged {
120 let Ok(pcid) = Pcid::new(address_space.hardware_tag()) else {
121 unsafe { controlregs::cr3_write(root) };
124 return;
125 };
126 unsafe { flush_pcid(InvPcidCommand::Single(pcid)) };
128 unsafe {
130 controlregs::cr3_write(root | u64::from(address_space.hardware_tag()) | CR3_NOFLUSH)
131 };
132 } else {
133 if pcid_enabled() && pcid_invpcid_supported() {
134 unsafe { flush_pcid(InvPcidCommand::All) };
137 }
138 unsafe { controlregs::cr3_write(root) };
140 }
141 }
142}
143
144#[cfg(feature = "host-test")]
145static HOST_PAGE_TABLE_ROOT: AtomicUsize = AtomicUsize::new(0);
146
147#[cfg(all(feature = "host-test", not(target_os = "none")))]
148std::thread_local! {
149 static HOST_IRQS_ENABLED: Cell<bool> = const { Cell::new(true) };
150}
151
152#[inline]
154pub fn enable_irqs() {
155 #[cfg(all(feature = "host-test", not(target_os = "none")))]
156 HOST_IRQS_ENABLED.set(true);
157 #[cfg(not(all(feature = "host-test", not(target_os = "none"))))]
158 interrupts::enable();
159}
160
161#[inline]
163pub fn disable_irqs() {
164 #[cfg(all(feature = "host-test", not(target_os = "none")))]
165 HOST_IRQS_ENABLED.set(false);
166 #[cfg(not(all(feature = "host-test", not(target_os = "none"))))]
167 interrupts::disable();
168}
169
170#[inline]
172pub fn irqs_enabled() -> bool {
173 #[cfg(all(feature = "host-test", not(target_os = "none")))]
174 return HOST_IRQS_ENABLED.get();
175 #[cfg(not(all(feature = "host-test", not(target_os = "none"))))]
176 interrupts::are_enabled()
177}
178
179#[inline]
183pub fn wait_for_irqs() {
184 unsafe { asm!("hlt") }
185}
186
187#[inline]
193pub fn wait_for_irqs_disabled() {
194 debug_assert!(!irqs_enabled());
195 unsafe { asm!("sti; hlt", options(nostack)) }
196}
197
198#[inline]
200pub fn halt() {
201 disable_irqs();
202 wait_for_irqs(); }
204
205#[inline]
212pub fn read_user_page_table() -> PhysAddr {
213 #[cfg(feature = "host-test")]
214 return PhysAddr::from(HOST_PAGE_TABLE_ROOT.load(Ordering::Acquire));
215
216 #[cfg(not(feature = "host-test"))]
217 pa!(unsafe { controlregs::cr3() } as usize).align_down_4k()
218}
219
220#[inline]
227pub fn read_kernel_page_table() -> PhysAddr {
228 read_user_page_table()
229}
230
231#[inline]
243pub unsafe fn write_user_page_table(root_paddr: PhysAddr) {
244 #[cfg(feature = "host-test")]
245 {
246 HOST_PAGE_TABLE_ROOT.store(root_paddr.as_usize(), Ordering::Release);
247 }
248 #[cfg(not(feature = "host-test"))]
249 unsafe {
250 controlregs::cr3_write(root_paddr.as_usize() as _)
251 }
252}
253
254#[inline]
266pub unsafe fn write_kernel_page_table(root_paddr: PhysAddr) {
267 unsafe { write_user_page_table(root_paddr) }
268}
269
270#[inline]
272pub fn flush_icache_all() {}
273
274#[inline]
279pub fn flush_tlb(vaddr: Option<VirtAddr>) {
280 #[cfg(feature = "host-test")]
281 let _ = vaddr;
282 #[cfg(not(feature = "host-test"))]
283 {
284 if let Some(vaddr) = vaddr {
285 unsafe { tlb::flush(vaddr.into()) }
286 } else if pcid_enabled() && pcid_invpcid_supported() {
287 unsafe { flush_pcid(InvPcidCommand::All) }
289 } else {
290 unsafe { tlb::flush_all() }
291 }
292 }
293}
294
295#[inline]
300pub fn update_mmu_cache(_vaddr: VirtAddr) {}
301
302#[inline]
306#[cfg(kernel_tls)]
307pub fn read_thread_pointer() -> KernelTlsBase {
308 KernelTlsBase::new(unsafe { msr::rdmsr(msr::IA32_FS_BASE) as usize })
309}
310
311#[inline]
319#[cfg(kernel_tls)]
320pub unsafe fn write_thread_pointer(kernel_tls: KernelTlsBase) {
321 unsafe { msr::wrmsr(msr::IA32_FS_BASE, kernel_tls.as_usize() as u64) }
322}
323
324#[cfg(feature = "uspace")]
325core::arch::global_asm!(include_str!("user_copy.S"), include_str!("user_atomic.S"),);
326
327#[cfg(feature = "uspace")]
328unsafe extern "C" {
329 pub fn user_copy(dst: *mut u8, src: *const u8, size: usize) -> usize;
339}
340
341#[cfg(feature = "uspace")]
351#[inline]
352pub unsafe fn user_access_ok_page(_vaddr: usize, _access: crate::UserAccessType) -> bool {
353 false
354}
355
356#[cfg(all(test, feature = "host-test"))]
357mod tests {
358 use super::*;
359
360 #[test]
361 fn host_irq_mask_is_isolated_per_execution_thread() {
362 assert!(irqs_enabled());
363 disable_irqs();
364 assert!(!irqs_enabled());
365
366 std::thread::spawn(|| {
367 assert!(irqs_enabled());
368 disable_irqs();
369 assert!(!irqs_enabled());
370 enable_irqs();
371 assert!(irqs_enabled());
372 })
373 .join()
374 .unwrap();
375
376 assert!(!irqs_enabled());
377 enable_irqs();
378 assert!(irqs_enabled());
379 }
380}