axhal/
mem.rs

1//! Physical memory management.
2
3use core::fmt;
4
5use axconfig::plat::{PHYS_MEMORY_BASE, PHYS_MEMORY_SIZE, PHYS_VIRT_OFFSET};
6
7#[doc(no_inline)]
8pub use memory_addr::{MemoryAddr, PAGE_SIZE_4K, PhysAddr, VirtAddr};
9
10bitflags::bitflags! {
11    /// The flags of a physical memory region.
12    pub struct MemRegionFlags: usize {
13        /// Readable.
14        const READ          = 1 << 0;
15        /// Writable.
16        const WRITE         = 1 << 1;
17        /// Executable.
18        const EXECUTE       = 1 << 2;
19        /// Device memory. (e.g., MMIO regions)
20        const DEVICE        = 1 << 4;
21        /// Uncachable memory. (e.g., framebuffer)
22        const UNCACHED      = 1 << 5;
23        /// Reserved memory, do not use for allocation.
24        const RESERVED      = 1 << 6;
25        /// Free memory for allocation.
26        const FREE          = 1 << 7;
27    }
28}
29
30impl fmt::Debug for MemRegionFlags {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        fmt::Debug::fmt(&self.0, f)
33    }
34}
35
36/// A physical memory region.
37#[derive(Debug)]
38pub struct MemRegion {
39    /// The start physical address of the region.
40    pub paddr: PhysAddr,
41    /// The size in bytes of the region.
42    pub size: usize,
43    /// The region flags, see [`MemRegionFlags`].
44    pub flags: MemRegionFlags,
45    /// The region name, used for identification.
46    pub name: &'static str,
47}
48
49/// Converts a virtual address to a physical address.
50///
51/// It assumes that there is a linear mapping with the offset
52/// [`PHYS_VIRT_OFFSET`], that maps all the physical memory to the virtual
53/// space at the address plus the offset. So we have
54/// `paddr = vaddr - PHYS_VIRT_OFFSET`.
55#[inline]
56pub const fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
57    assert!(
58        vaddr.as_usize() >= PHYS_VIRT_OFFSET,
59        "Converted address is invalid, check if the virtual address is in kernel space"
60    );
61    pa!(vaddr.as_usize() - PHYS_VIRT_OFFSET)
62}
63
64/// Converts a physical address to a virtual address.
65///
66/// It assumes that there is a linear mapping with the offset
67/// [`PHYS_VIRT_OFFSET`], that maps all the physical memory to the virtual
68/// space at the address plus the offset. So we have
69/// `vaddr = paddr + PHYS_VIRT_OFFSET`.
70#[inline]
71pub const fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
72    va!(paddr.as_usize() + PHYS_VIRT_OFFSET)
73}
74
75/// Returns an iterator over all physical memory regions.
76pub fn memory_regions() -> impl Iterator<Item = MemRegion> {
77    kernel_image_regions().chain(crate::platform::mem::platform_regions())
78}
79
80/// Returns the memory regions of the kernel image (code and data sections).
81fn kernel_image_regions() -> impl Iterator<Item = MemRegion> {
82    [
83        MemRegion {
84            paddr: virt_to_phys((_stext as usize).into()),
85            size: _etext as usize - _stext as usize,
86            flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::EXECUTE,
87            name: ".text",
88        },
89        MemRegion {
90            paddr: virt_to_phys((_srodata as usize).into()),
91            size: _erodata as usize - _srodata as usize,
92            flags: MemRegionFlags::RESERVED | MemRegionFlags::READ,
93            name: ".rodata",
94        },
95        MemRegion {
96            paddr: virt_to_phys((_sdata as usize).into()),
97            size: _edata as usize - _sdata as usize,
98            flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
99            name: ".data .tdata .tbss .percpu",
100        },
101        MemRegion {
102            paddr: virt_to_phys((boot_stack as usize).into()),
103            size: boot_stack_top as usize - boot_stack as usize,
104            flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
105            name: "boot stack",
106        },
107        MemRegion {
108            paddr: virt_to_phys((_sbss as usize).into()),
109            size: _ebss as usize - _sbss as usize,
110            flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
111            name: ".bss",
112        },
113    ]
114    .into_iter()
115}
116
117/// Returns the default MMIO memory regions (from [`axconfig::MMIO_REGIONS`]).
118#[allow(dead_code)]
119pub(crate) fn default_mmio_regions() -> impl Iterator<Item = MemRegion> {
120    axconfig::devices::MMIO_REGIONS.iter().map(|reg| MemRegion {
121        paddr: reg.0.into(),
122        size: reg.1,
123        flags: MemRegionFlags::RESERVED
124            | MemRegionFlags::DEVICE
125            | MemRegionFlags::READ
126            | MemRegionFlags::WRITE,
127        name: "mmio",
128    })
129}
130
131/// Returns the default free memory regions (kernel image end to physical memory end).
132#[allow(dead_code)]
133pub(crate) fn default_free_regions() -> impl Iterator<Item = MemRegion> {
134    let start = virt_to_phys((_ekernel as usize).into()).align_up_4k();
135    let end = pa!(PHYS_MEMORY_BASE + PHYS_MEMORY_SIZE).align_down_4k();
136    core::iter::once(MemRegion {
137        paddr: start,
138        size: end.as_usize() - start.as_usize(),
139        flags: MemRegionFlags::FREE | MemRegionFlags::READ | MemRegionFlags::WRITE,
140        name: "free memory",
141    })
142}
143
144/// Fills the `.bss` section with zeros.
145#[allow(dead_code)]
146pub(crate) fn clear_bss() {
147    unsafe {
148        core::slice::from_raw_parts_mut(_sbss as usize as *mut u8, _ebss as usize - _sbss as usize)
149            .fill(0);
150    }
151}
152
153unsafe extern "C" {
154    fn _stext();
155    fn _etext();
156    fn _srodata();
157    fn _erodata();
158    fn _sdata();
159    fn _edata();
160    fn _sbss();
161    fn _ebss();
162    fn _ekernel();
163    fn boot_stack();
164    fn boot_stack_top();
165}