1use core::ptr::{NonNull, copy_nonoverlapping};
2
3use crate::hal::mem::{PhysAddr, VirtAddr};
4
5use crate::{
6 error::Result,
7 mem::{
8 alloc::{Allocator, bestfit},
9 pfa, vmm,
10 },
11};
12
13pub struct AddressSpace {
14 begin: PhysAddr,
15 #[allow(dead_code)]
16 end: PhysAddr,
17 allocator: bestfit::BestFitAllocator,
18}
19
20#[cfg(any(feature = "metrics", metrics))]
21impl AddressSpace {
22 pub(crate) fn metrics(&self) -> crate::mem::alloc::Metrics {
23 self.allocator.metrics()
24 }
25}
26
27impl vmm::AddressSpacelike for AddressSpace {
28 fn new(pgs: usize) -> Result<Self> {
29 let begin = pfa::alloc_page(pgs).ok_or(kerr!(ENOMEM))?;
30 let end = begin
31 .checked_add(pgs * pfa::PAGE_SIZE)
32 .ok_or(kerr!(ENOMEM))?;
33
34 let mut allocator = bestfit::BestFitAllocator::new();
35 unsafe { allocator.add_range(&(begin..end))? };
36
37 Ok(Self {
38 begin,
39 end,
40 allocator,
41 })
42 }
43
44 fn map(&mut self, region: vmm::Region) -> Result<PhysAddr> {
45 let req = region.start.and_then(|virt| self.virt_to_phys(virt));
46 let align = core::mem::align_of::<u128>();
48 let start = unsafe { self.allocator.malloc::<u8>(region.len(), align, req)? };
49
50 match region.backing {
51 vmm::Backing::Anon(phys) => {
52 unsafe {
53 copy_nonoverlapping(phys.as_mut_ptr::<u8>(), start.as_ptr(), region.len())
54 };
55 }
56 vmm::Backing::Zeroed => {
57 unsafe { core::ptr::write_bytes(start.as_ptr(), 0, region.len()) };
58 }
59 vmm::Backing::Uninit => {}
60 }
61
62 Ok(start.into())
63 }
64
65 fn unmap(&mut self, region: &vmm::Region) -> Result<()> {
66 let virt = region.start.ok_or(kerr!(EINVAL))?;
67 let phys = self.virt_to_phys(virt).ok_or(kerr!(EINVAL))?;
68 let ptr = NonNull::new(phys.as_mut_ptr::<u8>()).ok_or(kerr!(EINVAL))?;
69 unsafe { self.allocator.free(ptr, region.len()) };
70 Ok(())
71 }
72
73 fn protect(&mut self, _region: &vmm::Region, _perms: vmm::Perms) -> Result<()> {
74 Ok(())
75 }
76
77 fn phys_to_virt(&self, addr: PhysAddr) -> Option<VirtAddr> {
78 if addr < self.begin || addr >= self.end {
79 return None;
80 }
81 addr.checked_sub(self.begin.as_usize())
82 .map(|phys| VirtAddr::new(phys.as_usize()))
83 }
84
85 fn virt_to_phys(&self, addr: VirtAddr) -> Option<PhysAddr> {
86 let phys = self.begin.checked_add(addr.as_usize())?;
87 if phys >= self.end {
88 return None;
89 }
90 Some(phys)
91 }
92
93 fn end(&self) -> VirtAddr {
94 VirtAddr::new(self.end.diff(self.begin))
95 }
96
97 fn activate(&self) -> Result<()> {
98 Ok(())
99 }
100}
101
102impl Drop for AddressSpace {
103 fn drop(&mut self) {
104 let pgs = self.end.diff(self.begin) / pfa::PAGE_SIZE;
107 if pgs > 0 {
108 pfa::free_page(self.begin, pgs);
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use crate::mem::vmm::{AddressSpacelike, Backing, Perms, Region};
117
118 fn make_addr_space(size: usize) -> AddressSpace {
119 let layout =
120 std::alloc::Layout::from_size_align(size, core::mem::align_of::<u128>()).unwrap();
121 let ptr = unsafe { std::alloc::alloc(layout) };
122 let begin = PhysAddr::new(ptr as usize);
123 let end = begin + size;
124 let mut allocator = bestfit::BestFitAllocator::new();
125 unsafe { allocator.add_range(&(begin..end)).unwrap() };
126 AddressSpace {
127 begin,
128 end,
129 allocator,
130 }
131 }
132
133 #[test]
134 fn unmap_returns_space_to_allocator() {
135 let mut as_ = make_addr_space(4096);
136
137 let region = Region::new(None, 2048, Backing::Uninit, Perms::Read);
138 let phys = as_.map(region).unwrap();
139
140 let virt = as_.phys_to_virt(phys).unwrap();
141 let placed = Region::new(Some(virt), 2048, Backing::Uninit, Perms::Read);
142 as_.unmap(&placed).unwrap();
143
144 let region2 = Region::new(None, 2048, Backing::Uninit, Perms::Read);
145 as_.map(region2).expect("re-map after unmap should not OOM");
146 }
147
148 #[test]
149 fn unmap_unplaced_region_rejected() {
150 let mut as_ = make_addr_space(4096);
151 let region = Region::new(None, 128, Backing::Uninit, Perms::Read);
152 assert!(as_.unmap(®ion).is_err());
153 }
154
155 #[test]
156 fn virt_to_phys_rejects_out_of_range() {
157 let as_ = make_addr_space(4096);
158 let size = as_.end.diff(as_.begin);
159 assert!(as_.virt_to_phys(VirtAddr::new(size)).is_none());
160 assert!(as_.virt_to_phys(VirtAddr::new(size + 1)).is_none());
161 assert!(as_.virt_to_phys(VirtAddr::new(usize::MAX)).is_none());
162 }
163
164 #[test]
165 fn phys_to_virt_rejects_out_of_range() {
166 let as_ = make_addr_space(4096);
167 assert!(as_.phys_to_virt(as_.end).is_none());
168 assert!(as_.phys_to_virt(as_.begin - 1).is_none());
169 assert!(as_.phys_to_virt(as_.end + 1).is_none());
170 }
171
172 #[test]
173 fn virt_phys_roundtrip() {
174 let as_ = make_addr_space(4096);
175 let v = VirtAddr::new(128);
176 let p = as_.virt_to_phys(v).unwrap();
177 assert_eq!(as_.phys_to_virt(p), Some(v));
178 }
179}