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    pa!(vaddr.as_usize() - PHYS_VIRT_OFFSET)
58}
59
60/// Converts a physical address to a virtual address.
61///
62/// It assumes that there is a linear mapping with the offset
63/// [`PHYS_VIRT_OFFSET`], that maps all the physical memory to the virtual
64/// space at the address plus the offset. So we have
65/// `vaddr = paddr + PHYS_VIRT_OFFSET`.
66#[inline]
67pub const fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
68    va!(paddr.as_usize() + PHYS_VIRT_OFFSET)
69}
70
71/// Returns an iterator over all physical memory regions.
72pub fn memory_regions() -> impl Iterator<Item = MemRegion> {
73    kernel_image_regions().chain(crate::platform::mem::platform_regions())
74}
75
76/// Returns the memory regions of the kernel image (code and data sections).
77fn kernel_image_regions() -> impl Iterator<Item = MemRegion> {
78    [
79        MemRegion {
80            paddr: virt_to_phys((_stext as usize).into()),
81            size: _etext as usize - _stext as usize,
82            flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::EXECUTE,
83            name: ".text",
84        },
85        MemRegion {
86            paddr: virt_to_phys((_srodata as usize).into()),
87            size: _erodata as usize - _srodata as usize,
88            flags: MemRegionFlags::RESERVED | MemRegionFlags::READ,
89            name: ".rodata",
90        },
91        MemRegion {
92            paddr: virt_to_phys((_sdata as usize).into()),
93            size: _edata as usize - _sdata as usize,
94            flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
95            name: ".data .tdata .tbss .percpu",
96        },
97        MemRegion {
98            paddr: virt_to_phys((boot_stack as usize).into()),
99            size: boot_stack_top as usize - boot_stack as usize,
100            flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
101            name: "boot stack",
102        },
103        MemRegion {
104            paddr: virt_to_phys((_sbss as usize).into()),
105            size: _ebss as usize - _sbss as usize,
106            flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
107            name: ".bss",
108        },
109    ]
110    .into_iter()
111}
112
113/// Returns the default MMIO memory regions (from [`axconfig::MMIO_REGIONS`]).
114#[allow(dead_code)]
115pub(crate) fn default_mmio_regions() -> impl Iterator<Item = MemRegion> {
116    axconfig::devices::MMIO_REGIONS.iter().map(|reg| MemRegion {
117        paddr: reg.0.into(),
118        size: reg.1,
119        flags: MemRegionFlags::RESERVED
120            | MemRegionFlags::DEVICE
121            | MemRegionFlags::READ
122            | MemRegionFlags::WRITE,
123        name: "mmio",
124    })
125}
126
127/// Returns the default free memory regions (kernel image end to physical memory end).
128#[allow(dead_code)]
129pub(crate) fn default_free_regions() -> impl Iterator<Item = MemRegion> {
130    let start = virt_to_phys((_ekernel as usize).into()).align_up_4k();
131    let end = pa!(PHYS_MEMORY_BASE + PHYS_MEMORY_SIZE).align_down_4k();
132    core::iter::once(MemRegion {
133        paddr: start,
134        size: end.as_usize() - start.as_usize(),
135        flags: MemRegionFlags::FREE | MemRegionFlags::READ | MemRegionFlags::WRITE,
136        name: "free memory",
137    })
138}
139
140/// Fills the `.bss` section with zeros.
141#[allow(dead_code)]
142pub(crate) fn clear_bss() {
143    unsafe {
144        core::slice::from_raw_parts_mut(_sbss as usize as *mut u8, _ebss as usize - _sbss as usize)
145            .fill(0);
146    }
147}
148
149unsafe extern "C" {
150    fn _stext();
151    fn _etext();
152    fn _srodata();
153    fn _erodata();
154    fn _sdata();
155    fn _edata();
156    fn _sbss();
157    fn _ebss();
158    fn _ekernel();
159    fn boot_stack();
160    fn boot_stack_top();
161}