Skip to main content

ax_cpu/
mmu.rs

1//! Local address translation hardware operations.
2
3#[cfg(feature = "uspace")]
4pub use crate::arch::current::asm::install_user_address_space;
5pub use crate::arch::current::asm::{
6    address_space_tag_capacity, flush_tlb, read_kernel_page_table, read_user_page_table,
7    write_kernel_page_table, write_user_page_table,
8};
9
10/// Hardware translation state installed while the caller owns its page tables.
11/// Software identities, tag generations and mapping epochs belong to the runtime.
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub struct HardwareAddressSpace {
14    root: ax_memory_addr::PhysAddr,
15    tag: u16,
16}
17
18impl HardwareAddressSpace {
19    /// Carries a page-table root and hardware tag; tag zero requests a full flush.
20    /// Creating this value does not access or activate the page tables.
21    pub const fn new(root: ax_memory_addr::PhysAddr, tag: u16) -> Self {
22        Self { root, tag }
23    }
24
25    /// Returns the physical root address.
26    pub const fn root(self) -> ax_memory_addr::PhysAddr {
27        self.root
28    }
29
30    /// Returns the hardware tag, with zero reserved for the full-flush path.
31    pub const fn hardware_tag(self) -> u16 {
32        self.tag
33    }
34}
35
36/// Invalidates every page touched by a byte range on the current CPU.
37///
38/// Zero length is a no-op. Unaligned endpoints include both boundary pages;
39/// overflowing or sufficiently large ranges use full local invalidation.
40/// Local IRQ exclusion keeps the bounded sequence on one CPU. Cross-CPU
41/// shootdown, mapping generations and memory reclamation belong to the caller.
42pub fn flush_tlb_range(start: crate::VirtAddr, size: usize) {
43    flush_range_with(start, size, flush_tlb);
44}
45
46pub(crate) fn flush_range_with(
47    start: crate::VirtAddr,
48    size: usize,
49    flush_tlb: impl Fn(Option<crate::VirtAddr>),
50) {
51    if size == 0 {
52        return;
53    }
54    struct RestoreIrqs(bool);
55    impl Drop for RestoreIrqs {
56        fn drop(&mut self) {
57            if self.0 {
58                crate::interrupt::enable_irqs();
59            }
60        }
61    }
62    let _restore = RestoreIrqs(crate::interrupt::irqs_enabled());
63    crate::interrupt::disable_irqs();
64    let Some(last) = start.as_usize().checked_add(size - 1) else {
65        flush_tlb(None);
66        return;
67    };
68    const PAGE_SIZE: usize = ax_memory_addr::PAGE_SIZE_4K;
69    let first = start.as_usize() & !(PAGE_SIZE - 1);
70    let pages = (last - first) / PAGE_SIZE + 1;
71    if pages > crate::arch::current::TLB_RANGE_PAGE_LIMIT {
72        flush_tlb(None);
73        return;
74    }
75    // Each base lies at or below the checked inclusive endpoint, so neither
76    // the multiplication nor address addition can wrap in this bounded loop.
77    for page in 0..pages {
78        flush_tlb(Some(crate::VirtAddr::from_usize(first + page * PAGE_SIZE)));
79    }
80}
81
82/// Publishes a local page-fault mapping before the faulting access is retried.
83/// The address is normalized to the architecture's base-page boundary. This
84/// does not provide cross-CPU invalidation or an address-space ownership token.
85pub fn update_mmu_cache(vaddr: crate::VirtAddr) {
86    let base = vaddr.as_usize() & !(ax_memory_addr::PAGE_SIZE_4K - 1);
87    crate::arch::current::asm::update_mmu_cache(crate::VirtAddr::from_usize(base));
88}
89
90#[cfg(target_arch = "aarch64")]
91pub use El1 as Native;
92
93#[cfg(target_arch = "aarch64")]
94pub use crate::arch::current::mmu::{El1, El2};
95
96/// Native kernel translation regime selected by the target architecture.
97#[cfg(not(target_arch = "aarch64"))]
98pub struct Native;
99
100#[cfg(not(target_arch = "aarch64"))]
101impl Native {
102    /// Returns the current kernel page-table root.
103    pub fn read_kernel_page_table() -> crate::PhysAddr {
104        read_kernel_page_table()
105    }
106    /// Installs a native kernel root without implicit invalidation.
107    ///
108    /// # Safety
109    /// All current code, stack and data mappings must remain valid, and the
110    /// owner must retain the tables and arrange required TLB invalidation.
111    pub unsafe fn write_kernel_page_table(root: crate::PhysAddr) {
112        // SAFETY: the caller retains the native address-space installation contract.
113        unsafe { write_kernel_page_table(root) };
114    }
115    /// Invalidates a native translation or the entire native local TLB.
116    pub fn flush_tlb(address: Option<crate::VirtAddr>) {
117        flush_tlb(address);
118    }
119    /// Invalidates native translations intersecting a byte range.
120    pub fn flush_tlb_range(start: crate::VirtAddr, size: usize) {
121        flush_tlb_range(start, size);
122    }
123    /// Returns the current CPU's native hardware address-space tag capacity.
124    pub fn address_space_tag_capacity() -> u32 {
125        address_space_tag_capacity()
126    }
127}
128
129#[cfg(target_arch = "riscv64")]
130pub use crate::arch::current::mmu::{Satp, SatpMode, install_page_table, read_satp};