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