From 16b46766a7082e7f25b1ce435f435fdc1c83d1cc Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Mon, 29 Jul 2024 22:18:42 +0800 Subject: [PATCH] [mm]: Introduce module axmm for virtual memory management --- .github/workflows/build.yml | 2 +- .github/workflows/test.yml | 2 + Cargo.lock | 25 ++++-- Cargo.toml | 2 + modules/axconfig/defconfig.toml | 4 + modules/axhal/Cargo.toml | 4 +- modules/axmm/Cargo.toml | 20 +++++ modules/axmm/src/aspace.rs | 139 +++++++++++++++++++++++++++++++ modules/axmm/src/lib.rs | 71 ++++++++++++++++ modules/axruntime/Cargo.toml | 4 +- modules/axruntime/src/lib.rs | 31 +------ modules/axruntime/src/mp.rs | 2 +- platforms/aarch64-bsta1000b.toml | 4 + platforms/aarch64-qemu-virt.toml | 4 + platforms/aarch64-raspi4.toml | 4 + platforms/riscv64-qemu-virt.toml | 4 + platforms/x86_64-pc-oslab.toml | 4 + platforms/x86_64-qemu-q35.toml | 4 + 18 files changed, 289 insertions(+), 41 deletions(-) create mode 100644 modules/axmm/Cargo.toml create mode 100644 modules/axmm/src/aspace.rs create mode 100644 modules/axmm/src/lib.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 04f5b86a90..b3932d64b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,7 +64,7 @@ jobs: continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }} run: make ARCH=${{ matrix.arch }} A=examples/httpserver - name: Build shell - continue-on-error: ${{ matrix.rust-toolchain == 'shell' }} + continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }} run: make ARCH=${{ matrix.arch }} A=examples/shell - uses: ./.github/workflows/actions/setup-musl diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 161eb320b4..baae1f9d86 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,7 @@ on: [push, pull_request] env: qemu-version: 8.2.0 rust-toolchain: nightly-2024-05-02 + arceos-apps: b36b9d5 jobs: unit-test: @@ -43,4 +44,5 @@ jobs: run: | make disk_img git clone https://github.com/arceos-org/arceos-apps.git + cd arceos-apps && git reset --hard ${{ env.arceos-apps }} && cd .. make -C arceos-apps test AX_ROOT=$(pwd) ARCH=${{ matrix.arch }} diff --git a/Cargo.lock b/Cargo.lock index 5ead360e29..b97000cf43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,6 +408,19 @@ dependencies = [ "log", ] +[[package]] +name = "axmm" +version = "0.1.0" +dependencies = [ + "axconfig", + "axerrno", + "axhal", + "kspin", + "lazyinit", + "log", + "memory_addr", +] + [[package]] name = "axnet" version = "0.1.0" @@ -437,12 +450,12 @@ dependencies = [ "axfs", "axhal", "axlog", + "axmm", "axnet", "axtask", "chrono", "crate_interface", "kernel_guard", - "lazyinit", "percpu", ] @@ -1003,9 +1016,9 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "page_table_entry" -version = "0.1.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d268ed455b6d9bb54bdbd801838090823fdba51b46292503ac72ef25326ebab7" +checksum = "d75bcbfcef16b8ede39c4ff975e8dc12556eece0554667ed5dd2dd3ba958a5ad" dependencies = [ "aarch64-cpu", "bitflags 2.6.0", @@ -1015,13 +1028,15 @@ dependencies = [ [[package]] name = "page_table_multiarch" -version = "0.1.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1445dd432e6b83ccf27008835c7963cfdc107852f6f15edc7a3523dcf4da1ac" +checksum = "4235f68d9c35dd143b4c005764bd002361407687b9cc7c57465e9f8c77f7fbd3" dependencies = [ "log", "memory_addr", "page_table_entry", + "riscv", + "x86", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6b3b3f3f7a..77422d9bf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "modules/axfs", "modules/axhal", "modules/axlog", + "modules/axmm", "modules/axnet", "modules/axruntime", "modules/axsync", @@ -53,6 +54,7 @@ axdriver = { path = "modules/axdriver" } axfs = { path = "modules/axfs" } axhal = { path = "modules/axhal" } axlog = { path = "modules/axlog" } +axmm = { path = "modules/axmm" } axnet = { path = "modules/axnet" } axruntime = { path = "modules/axruntime" } axsync = { path = "modules/axsync" } diff --git a/modules/axconfig/defconfig.toml b/modules/axconfig/defconfig.toml index e13488d9e5..d7eabb35d1 100644 --- a/modules/axconfig/defconfig.toml +++ b/modules/axconfig/defconfig.toml @@ -16,6 +16,10 @@ kernel-base-vaddr = "0" # Linear mapping offset, for quick conversions between physical and virtual # addresses. phys-virt-offset = "0" +# Kernel address space base. +kernel-aspace-base = "0" +# Kernel address space size. +kernel-aspace-size = "0" # MMIO regions with format (`base_paddr`, `size`). mmio-regions = [] # VirtIO MMIO regions with format (`base_paddr`, `size`). diff --git a/modules/axhal/Cargo.toml b/modules/axhal/Cargo.toml index 5170aeb497..4680b79fbd 100644 --- a/modules/axhal/Cargo.toml +++ b/modules/axhal/Cargo.toml @@ -32,8 +32,8 @@ percpu = "0.1" memory_addr = "0.2" handler_table = "0.1" crate_interface = "0.1" -page_table_entry = "0.1" -page_table_multiarch = { version = "0.1", optional = true } +page_table_entry = "0.3" +page_table_multiarch = { version = "0.3", optional = true } axlog = { workspace = true } axconfig = { workspace = true } axalloc = { workspace = true, optional = true } diff --git a/modules/axmm/Cargo.toml b/modules/axmm/Cargo.toml new file mode 100644 index 0000000000..773e7f7935 --- /dev/null +++ b/modules/axmm/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "axmm" +version.workspace = true +edition = "2021" +authors = ["Yuekai Jia "] +description = "ArceOS virtual memory management module" +license.workspace = true +homepage.workspace = true +repository = "https://github.com/arceos-org/arceos/tree/main/modules/axmm" +documentation = "https://arceos-org.github.io/arceos/axmm/index.html" + +[dependencies] +axhal = { workspace = true, features = ["paging"] } +axconfig = { workspace = true } + +log = "0.4" +axerrno = "0.1" +lazyinit = "0.2" +memory_addr = "0.2" +kspin = "0.1" diff --git a/modules/axmm/src/aspace.rs b/modules/axmm/src/aspace.rs new file mode 100644 index 0000000000..9d8d16f617 --- /dev/null +++ b/modules/axmm/src/aspace.rs @@ -0,0 +1,139 @@ +use core::fmt; + +use axerrno::{ax_err, AxError, AxResult}; +use axhal::paging::{MappingFlags, PageTable}; +use memory_addr::{is_aligned_4k, PhysAddr, VirtAddr, VirtAddrRange}; + +use crate::paging_err_to_ax_err; + +/// The virtual memory address space. +pub struct AddrSpace { + va_range: VirtAddrRange, + pt: PageTable, +} + +impl AddrSpace { + /// Returns the address space base. + pub const fn base(&self) -> VirtAddr { + self.va_range.start + } + + /// Returns the address space end. + pub const fn end(&self) -> VirtAddr { + self.va_range.end + } + + /// Returns the address space size. + pub const fn size(&self) -> usize { + self.va_range.size() + } + + /// Returns the reference to the inner page table. + pub const fn page_table(&self) -> &PageTable { + &self.pt + } + + /// Returns the root physical address of the inner page table. + pub const fn page_table_root(&self) -> PhysAddr { + self.pt.root_paddr() + } + + /// Checks if the address space contains the given address range. + pub const fn contains_range(&self, start: VirtAddr, size: usize) -> bool { + self.va_range + .contains_range(VirtAddrRange::from_start_size(start, size)) + } + + /// Creates a new empty address space. + pub(crate) fn new_empty(base: VirtAddr, size: usize) -> AxResult { + Ok(Self { + va_range: VirtAddrRange::from_start_size(base, size), + pt: PageTable::try_new().map_err(|_| AxError::NoMemory)?, + }) + } + + /// Add a new linear mapping. + /// + /// The mapping is linear, i.e., `start_vaddr` is mapped to `start_paddr`, + /// and `start_vaddr + size` is mapped to `start_paddr + size`. + /// + /// The `flags` parameter indicates the mapping permissions and attributes. + /// + /// Returns an error if the address range is out of the address space or not + /// aligned. + pub fn map_linear( + &mut self, + start_vaddr: VirtAddr, + start_paddr: PhysAddr, + size: usize, + flags: MappingFlags, + ) -> AxResult { + if !self.contains_range(start_vaddr, size) { + return ax_err!(InvalidInput, "address out of range"); + } + if !start_vaddr.is_aligned_4k() || !start_paddr.is_aligned_4k() || !is_aligned_4k(size) { + return ax_err!(InvalidInput, "address not aligned"); + } + + let offset = start_vaddr.as_usize() - start_paddr.as_usize(); + self.pt + .map_region( + start_vaddr, + |va| PhysAddr::from(va.as_usize() - offset), + size, + flags, + false, // allow_huge + false, // flush_tlb_by_page + ) + .map_err(paging_err_to_ax_err)? + .flush(); + Ok(()) + } + + /// Removes mappings within the specified virtual address range. + /// + /// Returns an error if the address range is out of the address space or not + /// aligned. + pub fn unmap(&mut self, start: VirtAddr, size: usize) -> AxResult { + if !self.contains_range(start, size) { + return ax_err!(InvalidInput, "address out of range"); + } + if !start.is_aligned_4k() || !is_aligned_4k(size) { + return ax_err!(InvalidInput, "address not aligned"); + } + + self.pt + .unmap_region(start, size, true) + .map_err(paging_err_to_ax_err)? + .ignore(); + Ok(()) + } + + /// Updates mapping within the specified virtual address range. + /// + /// Returns an error if the address range is out of the address space or not + /// aligned. + pub fn protect(&mut self, start: VirtAddr, size: usize, flags: MappingFlags) -> AxResult { + if !self.contains_range(start, size) { + return ax_err!(InvalidInput, "address out of range"); + } + if !start.is_aligned_4k() || !is_aligned_4k(size) { + return ax_err!(InvalidInput, "address not aligned"); + } + + self.pt + .protect_region(start, size, flags, true) + .map_err(paging_err_to_ax_err)? + .ignore(); + Ok(()) + } +} + +impl fmt::Debug for AddrSpace { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("AddrSpace") + .field("va_range", &self.va_range) + .field("page_table_root", &self.pt.root_paddr()) + .finish() + } +} diff --git a/modules/axmm/src/lib.rs b/modules/axmm/src/lib.rs new file mode 100644 index 0000000000..be0dd633e6 --- /dev/null +++ b/modules/axmm/src/lib.rs @@ -0,0 +1,71 @@ +//! [ArceOS](https://github.com/arceos-org/arceos) memory management module. + +#![no_std] + +#[macro_use] +extern crate log; +extern crate alloc; + +mod aspace; + +pub use self::aspace::AddrSpace; + +use axerrno::{AxError, AxResult}; +use axhal::mem::phys_to_virt; +use axhal::paging::PagingError; +use kspin::SpinNoIrq; +use lazyinit::LazyInit; +use memory_addr::{PhysAddr, VirtAddr}; + +static KERNEL_ASPACE: LazyInit> = LazyInit::new(); + +fn paging_err_to_ax_err(err: PagingError) -> AxError { + warn!("Paging error: {:?}", err); + match err { + PagingError::NoMemory => AxError::NoMemory, + PagingError::NotAligned => AxError::InvalidInput, + PagingError::NotMapped => AxError::NotFound, + PagingError::AlreadyMapped => AxError::AlreadyExists, + PagingError::MappedToHugePage => AxError::InvalidInput, + } +} + +/// Creates a new address space for kernel itself. +pub fn new_kernel_aspace() -> AxResult { + let mut aspace = AddrSpace::new_empty( + VirtAddr::from(axconfig::KERNEL_ASPACE_BASE), + axconfig::KERNEL_ASPACE_SIZE, + )?; + for r in axhal::mem::memory_regions() { + aspace.map_linear(phys_to_virt(r.paddr), r.paddr, r.size, r.flags.into())?; + } + Ok(aspace) +} + +/// Returns the globally unique kernel address space. +pub fn kernel_aspace() -> &'static SpinNoIrq { + &KERNEL_ASPACE +} + +/// Returns the root physical address of the kernel page table. +pub fn kernel_page_table_root() -> PhysAddr { + KERNEL_ASPACE.lock().page_table_root() +} + +/// Initializes virtual memory management. +/// +/// It mainly sets up the kernel virtual memory address space and recreate a +/// fine-grained kernel page table. +pub fn init_memory_management() { + info!("Initialize virtual memory management..."); + + let kernel_aspace = new_kernel_aspace().expect("failed to initialize kernel address space"); + debug!("kernel address space init OK: {:#x?}", kernel_aspace); + KERNEL_ASPACE.init_once(SpinNoIrq::new(kernel_aspace)); + unsafe { axhal::arch::write_page_table_root(kernel_page_table_root()) }; +} + +/// Initializes kernel paging for secondary CPUs. +pub fn init_memory_management_secondary() { + unsafe { axhal::arch::write_page_table_root(kernel_page_table_root()) }; +} diff --git a/modules/axruntime/Cargo.toml b/modules/axruntime/Cargo.toml index 3b9900585d..b904fe6b89 100644 --- a/modules/axruntime/Cargo.toml +++ b/modules/axruntime/Cargo.toml @@ -16,7 +16,7 @@ smp = ["axhal/smp"] irq = ["axhal/irq", "axtask?/irq", "percpu", "kernel_guard"] tls = ["axhal/tls", "axtask?/tls"] alloc = ["axalloc"] -paging = ["axhal/paging", "lazyinit"] +paging = ["axhal/paging", "axmm"] multitask = ["axtask/multitask"] fs = ["axdriver", "axfs"] @@ -29,6 +29,7 @@ axhal = { workspace = true } axlog = { workspace = true } axconfig = { workspace = true } axalloc = { workspace = true, optional = true } +axmm = { workspace = true, optional = true } axdriver = { workspace = true, optional = true } axfs = { workspace = true, optional = true } axnet = { workspace = true, optional = true } @@ -38,6 +39,5 @@ axtask = { workspace = true, optional = true } crate_interface = "0.1" percpu = { version = "0.1", optional = true } kernel_guard = { version = "0.1", optional = true } -lazyinit = { version = "0.2", optional = true } chrono = { version = "0.4.38", default-features = false } diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index 78eeec5f9f..71bc673dda 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -146,10 +146,7 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { init_allocator(); #[cfg(feature = "paging")] - { - info!("Initialize kernel page table..."); - remap_kernel_memory().expect("remap kernel memoy failed"); - } + axmm::init_memory_management(); info!("Initialize platform devices..."); axhal::platform_init(); @@ -234,32 +231,6 @@ fn init_allocator() { } } -#[cfg(feature = "paging")] -fn remap_kernel_memory() -> Result<(), axhal::paging::PagingError> { - use axhal::mem::{memory_regions, phys_to_virt}; - use axhal::paging::PageTable; - use lazyinit::LazyInit; - - static KERNEL_PAGE_TABLE: LazyInit = LazyInit::new(); - - if axhal::cpu::this_cpu_is_bsp() { - let mut kernel_page_table = PageTable::try_new()?; - for r in memory_regions() { - kernel_page_table.map_region( - phys_to_virt(r.paddr), - r.paddr, - r.size, - r.flags.into(), - true, - )?; - } - KERNEL_PAGE_TABLE.init_once(kernel_page_table); - } - - unsafe { axhal::arch::write_page_table_root(KERNEL_PAGE_TABLE.root_paddr()) }; - Ok(()) -} - #[cfg(feature = "irq")] fn init_interrupt() { use axhal::time::TIMER_IRQ_NUM; diff --git a/modules/axruntime/src/mp.rs b/modules/axruntime/src/mp.rs index 768ef5188a..f642438223 100644 --- a/modules/axruntime/src/mp.rs +++ b/modules/axruntime/src/mp.rs @@ -35,7 +35,7 @@ pub extern "C" fn rust_main_secondary(cpu_id: usize) -> ! { info!("Secondary CPU {:x} started.", cpu_id); #[cfg(feature = "paging")] - super::remap_kernel_memory().unwrap(); + axmm::init_memory_management_secondary(); axhal::platform_init_secondary(); diff --git a/platforms/aarch64-bsta1000b.toml b/platforms/aarch64-bsta1000b.toml index ac257a5af1..01bf709f00 100644 --- a/platforms/aarch64-bsta1000b.toml +++ b/platforms/aarch64-bsta1000b.toml @@ -16,6 +16,10 @@ kernel-base-vaddr = "0xffff_0000_8100_0000" # Linear mapping offset, for quick conversions between physical and virtual # addresses. phys-virt-offset = "0xffff_0000_0000_0000" +# Kernel address space base. +kernel-aspace-base = "0xffff_0000_0000_0000" +# Kernel address space size. +kernel-aspace-size = "0x0000_ffff_ffff_f000" # MMIO regions with format (`base_paddr`, `size`). mmio-regions = [ ["0x20008000", "0x1000"], # uart8250 UART0 diff --git a/platforms/aarch64-qemu-virt.toml b/platforms/aarch64-qemu-virt.toml index 0cc554866b..c32af21436 100644 --- a/platforms/aarch64-qemu-virt.toml +++ b/platforms/aarch64-qemu-virt.toml @@ -16,6 +16,10 @@ kernel-base-vaddr = "0xffff_0000_4008_0000" # Linear mapping offset, for quick conversions between physical and virtual # addresses. phys-virt-offset = "0xffff_0000_0000_0000" +# Kernel address space base. +kernel-aspace-base = "0xffff_0000_0000_0000" +# Kernel address space size. +kernel-aspace-size = "0x0000_ffff_ffff_f000" # MMIO regions with format (`base_paddr`, `size`). mmio-regions = [ ["0x0900_0000", "0x1000"], # PL011 UART diff --git a/platforms/aarch64-raspi4.toml b/platforms/aarch64-raspi4.toml index 31ab571195..a1323cf9da 100644 --- a/platforms/aarch64-raspi4.toml +++ b/platforms/aarch64-raspi4.toml @@ -16,6 +16,10 @@ kernel-base-vaddr = "0xffff_0000_0008_0000" # Linear mapping offset, for quick conversions between physical and virtual # addresses. phys-virt-offset = "0xffff_0000_0000_0000" +# Kernel address space base. +kernel-aspace-base = "0xffff_0000_0000_0000" +# Kernel address space size. +kernel-aspace-size = "0x0000_ffff_ffff_f000" # MMIO regions with format (`base_paddr`, `size`). mmio-regions = [ ["0xFE20_1000", "0x1000"], # PL011 UART diff --git a/platforms/riscv64-qemu-virt.toml b/platforms/riscv64-qemu-virt.toml index 840091f934..f081eaf1eb 100644 --- a/platforms/riscv64-qemu-virt.toml +++ b/platforms/riscv64-qemu-virt.toml @@ -16,6 +16,10 @@ kernel-base-vaddr = "0xffff_ffc0_8020_0000" # Linear mapping offset, for quick conversions between physical and virtual # addresses. phys-virt-offset = "0xffff_ffc0_0000_0000" +# Kernel address space base. +kernel-aspace-base = "0xffff_ffc0_0000_0000" +# Kernel address space size. +kernel-aspace-size = "0x0000_003f_ffff_f000" # MMIO regions with format (`base_paddr`, `size`). mmio-regions = [ ["0x0010_1000", "0x1000"], # RTC diff --git a/platforms/x86_64-pc-oslab.toml b/platforms/x86_64-pc-oslab.toml index 03363d962f..198026ae04 100644 --- a/platforms/x86_64-pc-oslab.toml +++ b/platforms/x86_64-pc-oslab.toml @@ -16,6 +16,10 @@ kernel-base-vaddr = "0xffff_ff80_0020_0000" # Linear mapping offset, for quick conversions between physical and virtual # addresses. phys-virt-offset = "0xffff_ff80_0000_0000" +# Kernel address space base. +kernel-aspace-base = "0xffff_ff80_0000_0000" +# Kernel address space size. +kernel-aspace-size = "0x0000_007f_ffff_f000" # MMIO regions with format (`base_paddr`, `size`). mmio-regions = [ ["0xfec0_0000", "0x1000"], # IO APIC diff --git a/platforms/x86_64-qemu-q35.toml b/platforms/x86_64-qemu-q35.toml index da931dafab..b960232779 100644 --- a/platforms/x86_64-qemu-q35.toml +++ b/platforms/x86_64-qemu-q35.toml @@ -16,6 +16,10 @@ kernel-base-vaddr = "0xffff_ff80_0020_0000" # Linear mapping offset, for quick conversions between physical and virtual # addresses. phys-virt-offset = "0xffff_ff80_0000_0000" +# Kernel address space base. +kernel-aspace-base = "0xffff_ff80_0000_0000" +# Kernel address space size. +kernel-aspace-size = "0x0000_007f_ffff_f000" # MMIO regions with format (`base_paddr`, `size`). mmio-regions = [ ["0xb000_0000", "0x1000_0000"], # PCI config space