axmm/
lib.rs

1//! [ArceOS](https://github.com/arceos-org/arceos) memory management module.
2
3#![no_std]
4
5#[macro_use]
6extern crate log;
7extern crate alloc;
8
9mod aspace;
10mod backend;
11
12pub use self::aspace::AddrSpace;
13pub use self::backend::Backend;
14
15use axerrno::{AxError, AxResult};
16use axhal::mem::phys_to_virt;
17use kspin::SpinNoIrq;
18use lazyinit::LazyInit;
19use memory_addr::{PhysAddr, VirtAddr, va};
20use memory_set::MappingError;
21
22static KERNEL_ASPACE: LazyInit<SpinNoIrq<AddrSpace>> = LazyInit::new();
23
24fn mapping_err_to_ax_err(err: MappingError) -> AxError {
25    warn!("Mapping error: {:?}", err);
26    match err {
27        MappingError::InvalidParam => AxError::InvalidInput,
28        MappingError::AlreadyExists => AxError::AlreadyExists,
29        MappingError::BadState => AxError::BadState,
30    }
31}
32
33/// Creates a new address space for kernel itself.
34pub fn new_kernel_aspace() -> AxResult<AddrSpace> {
35    let mut aspace = AddrSpace::new_empty(
36        va!(axconfig::plat::KERNEL_ASPACE_BASE),
37        axconfig::plat::KERNEL_ASPACE_SIZE,
38    )?;
39    for r in axhal::mem::memory_regions() {
40        aspace.map_linear(phys_to_virt(r.paddr), r.paddr, r.size, r.flags.into())?;
41    }
42    Ok(aspace)
43}
44
45/// Creates a new address space for user processes.
46pub fn new_user_aspace(base: VirtAddr, size: usize) -> AxResult<AddrSpace> {
47    let mut aspace = AddrSpace::new_empty(base, size)?;
48    if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "loongarch64") {
49        // ARMv8 (aarch64) and LoongArch64 use separate page tables for user space
50        // (aarch64: TTBR0_EL1, LoongArch64: PGDL), so there is no need to copy the
51        // kernel portion to the user page table.
52        aspace.copy_mappings_from(&kernel_aspace().lock())?;
53    }
54    Ok(aspace)
55}
56
57/// Returns the globally unique kernel address space.
58pub fn kernel_aspace() -> &'static SpinNoIrq<AddrSpace> {
59    &KERNEL_ASPACE
60}
61
62/// Returns the root physical address of the kernel page table.
63pub fn kernel_page_table_root() -> PhysAddr {
64    KERNEL_ASPACE.lock().page_table_root()
65}
66
67/// Initializes virtual memory management.
68///
69/// It mainly sets up the kernel virtual memory address space and recreate a
70/// fine-grained kernel page table.
71pub fn init_memory_management() {
72    info!("Initialize virtual memory management...");
73
74    let kernel_aspace = new_kernel_aspace().expect("failed to initialize kernel address space");
75    debug!("kernel address space init OK: {:#x?}", kernel_aspace);
76    KERNEL_ASPACE.init_once(SpinNoIrq::new(kernel_aspace));
77    axhal::paging::set_kernel_page_table_root(kernel_page_table_root());
78}
79
80/// Initializes kernel paging for secondary CPUs.
81pub fn init_memory_management_secondary() {
82    axhal::paging::set_kernel_page_table_root(kernel_page_table_root());
83}