axhal/
mem.rs

1//! Physical memory management.
2
3use heapless::Vec;
4use lazyinit::LazyInit;
5
6use axplat::mem::{check_sorted_ranges_overlap, ranges_difference};
7
8pub use axplat::mem::{MemRegionFlags, PhysMemRegion};
9pub use axplat::mem::{
10    mmio_ranges, phys_ram_ranges, phys_to_virt, reserved_phys_ram_ranges, total_ram_size,
11    virt_to_phys,
12};
13pub use memory_addr::{PAGE_SIZE_4K, PhysAddr, PhysAddrRange, VirtAddr, VirtAddrRange, pa, va};
14
15const MAX_REGIONS: usize = 128;
16
17static ALL_MEM_REGIONS: LazyInit<Vec<PhysMemRegion, MAX_REGIONS>> = LazyInit::new();
18
19/// Returns an iterator over all physical memory regions.
20pub fn memory_regions() -> impl Iterator<Item = PhysMemRegion> {
21    ALL_MEM_REGIONS.iter().cloned()
22}
23
24/// Fills the `.bss` section with zeros.
25///
26/// It requires the symbols `_sbss` and `_ebss` to be defined in the linker script.
27///
28/// # Safety
29///
30/// This function is unsafe because it writes `.bss` section directly.
31pub unsafe fn clear_bss() {
32    unsafe {
33        core::slice::from_raw_parts_mut(_sbss as usize as *mut u8, _ebss as usize - _sbss as usize)
34            .fill(0);
35    }
36}
37
38/// Initializes physical memory regions.
39pub fn init() {
40    let mut all_regions = Vec::new();
41    let mut push = |r: PhysMemRegion| {
42        if r.size > 0 {
43            all_regions.push(r).expect("too many memory regions");
44        }
45    };
46
47    // Push regions in kernel image
48    push(PhysMemRegion {
49        paddr: virt_to_phys((_stext as usize).into()),
50        size: _etext as usize - _stext as usize,
51        flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::EXECUTE,
52        name: ".text",
53    });
54    push(PhysMemRegion {
55        paddr: virt_to_phys((_srodata as usize).into()),
56        size: _erodata as usize - _srodata as usize,
57        flags: MemRegionFlags::RESERVED | MemRegionFlags::READ,
58        name: ".rodata",
59    });
60    push(PhysMemRegion {
61        paddr: virt_to_phys((_sdata as usize).into()),
62        size: _edata as usize - _sdata as usize,
63        flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
64        name: ".data .tdata .tbss .percpu",
65    });
66    push(PhysMemRegion {
67        paddr: virt_to_phys((boot_stack as usize).into()),
68        size: boot_stack_top as usize - boot_stack as usize,
69        flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
70        name: "boot stack",
71    });
72    push(PhysMemRegion {
73        paddr: virt_to_phys((_sbss as usize).into()),
74        size: _ebss as usize - _sbss as usize,
75        flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
76        name: ".bss",
77    });
78
79    // Push MMIO & reserved regions
80    for &(start, size) in mmio_ranges() {
81        push(PhysMemRegion::new_mmio(start, size, "mmio"));
82    }
83    for &(start, size) in reserved_phys_ram_ranges() {
84        push(PhysMemRegion::new_reserved(start, size, "reserved"));
85    }
86
87    // Combine kernel image range and reserved ranges
88    let kernel_start = virt_to_phys(va!(_skernel as usize)).as_usize();
89    let kernel_size = _ekernel as usize - _skernel as usize;
90    let mut reserved_ranges = reserved_phys_ram_ranges()
91        .iter()
92        .cloned()
93        .chain(core::iter::once((kernel_start, kernel_size))) // kernel image range is also reserved
94        .collect::<Vec<_, MAX_REGIONS>>();
95
96    // Remove all reserved ranges from RAM ranges, and push the remaining as free memory
97    reserved_ranges.sort_unstable_by_key(|&(start, _size)| start);
98    ranges_difference(phys_ram_ranges(), &reserved_ranges, |(start, size)| {
99        push(PhysMemRegion::new_ram(start, size, "free memory"));
100    })
101    .inspect_err(|(a, b)| error!("Reserved memory region {:#x?} overlaps with {:#x?}", a, b))
102    .unwrap();
103
104    // Check overlapping
105    all_regions.sort_unstable_by_key(|r| r.paddr);
106    check_sorted_ranges_overlap(all_regions.iter().map(|r| (r.paddr.into(), r.size)))
107        .inspect_err(|(a, b)| error!("Physical memory region {:#x?} overlaps with {:#x?}", a, b))
108        .unwrap();
109
110    ALL_MEM_REGIONS.init_once(all_regions);
111}
112
113unsafe extern "C" {
114    fn _stext();
115    fn _etext();
116    fn _srodata();
117    fn _erodata();
118    fn _sdata();
119    fn _edata();
120    fn _sbss();
121    fn _ebss();
122    fn _skernel();
123    fn _ekernel();
124    fn boot_stack();
125    fn boot_stack_top();
126}