axmm/backend/
alloc.rs

1use axalloc::global_allocator;
2use axhal::mem::{phys_to_virt, virt_to_phys};
3use axhal::paging::{MappingFlags, PageSize, PageTable};
4use memory_addr::{PAGE_SIZE_4K, PageIter4K, PhysAddr, VirtAddr};
5
6use super::Backend;
7
8fn alloc_frame(zeroed: bool) -> Option<PhysAddr> {
9    let vaddr = VirtAddr::from(global_allocator().alloc_pages(1, PAGE_SIZE_4K).ok()?);
10    if zeroed {
11        unsafe { core::ptr::write_bytes(vaddr.as_mut_ptr(), 0, PAGE_SIZE_4K) };
12    }
13    let paddr = virt_to_phys(vaddr);
14    Some(paddr)
15}
16
17fn dealloc_frame(frame: PhysAddr) {
18    let vaddr = phys_to_virt(frame);
19    global_allocator().dealloc_pages(vaddr.as_usize(), 1);
20}
21
22impl Backend {
23    /// Creates a new allocation mapping backend.
24    pub const fn new_alloc(populate: bool) -> Self {
25        Self::Alloc { populate }
26    }
27
28    pub(crate) fn map_alloc(
29        &self,
30        start: VirtAddr,
31        size: usize,
32        flags: MappingFlags,
33        pt: &mut PageTable,
34        populate: bool,
35    ) -> bool {
36        debug!(
37            "map_alloc: [{:#x}, {:#x}) {:?} (populate={})",
38            start,
39            start + size,
40            flags,
41            populate
42        );
43        if populate {
44            // allocate all possible physical frames for populated mapping.
45            let mut cursor = pt.cursor();
46            for addr in PageIter4K::new(start, start + size).unwrap() {
47                if let Some(frame) = alloc_frame(true)
48                    && cursor.map(addr, frame, PageSize::Size4K, flags).is_err()
49                {
50                    // Mapping failed; roll back any previously mapped pages in this range
51                    // and deallocate their frames to avoid leaks and partial mappings.
52                    for rollback_addr in PageIter4K::new(start, addr).unwrap() {
53                        if let Ok((mapped_frame, _, page_size)) = cursor.unmap(rollback_addr) {
54                            // We only expect 4K pages here, but avoid touching huge pages.
55                            if !page_size.is_huge() {
56                                dealloc_frame(mapped_frame);
57                            }
58                        }
59                    }
60                    // Deallocate the just-allocated frame that failed to map.
61                    dealloc_frame(frame);
62                    return false;
63                }
64            }
65            true
66        } else {
67            // Map to a empty entry for on-demand mapping.
68            let flags = MappingFlags::empty();
69            pt.cursor()
70                .map_region(start, |_| 0.into(), size, flags, false)
71                .is_ok()
72        }
73    }
74
75    pub(crate) fn unmap_alloc(
76        &self,
77        start: VirtAddr,
78        size: usize,
79        pt: &mut PageTable,
80        _populate: bool,
81    ) -> bool {
82        debug!("unmap_alloc: [{:#x}, {:#x})", start, start + size);
83        for addr in PageIter4K::new(start, start + size).unwrap() {
84            if let Ok((frame, _, page_size)) = pt.cursor().unmap(addr) {
85                // Deallocate the physical frame if there is a mapping in the
86                // page table.
87                if page_size.is_huge() {
88                    return false;
89                }
90                dealloc_frame(frame);
91            }
92        }
93        true
94    }
95
96    pub(crate) fn handle_page_fault_alloc(
97        &self,
98        vaddr: VirtAddr,
99        orig_flags: MappingFlags,
100        pt: &mut PageTable,
101        populate: bool,
102    ) -> bool {
103        if populate {
104            false // Populated mappings should not trigger page faults.
105        } else if let Some(frame) = alloc_frame(true) {
106            // Allocate a physical frame lazily and map it to the fault address.
107            // `vaddr` does not need to be aligned. It will be automatically
108            // aligned during `pt.remap` regardless of the page size.
109            let res = pt.cursor().remap(vaddr, frame, orig_flags);
110            if let Err(e) = &res {
111                debug!(
112                    "handle_page_fault_alloc: remap failed for {:#x}: {:?}",
113                    vaddr, e
114                );
115                dealloc_frame(frame);
116            }
117            res.is_ok()
118        } else {
119            false
120        }
121    }
122}