Skip to main content

ax_hal/
mem.rs

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