blob: f4fc3b13ec2646609b033dd8a8d2541c8f87a5e9 [file] [log] [blame]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +00001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Low-level allocation and tracking of main memory.
16
Andrew Walbran848decf2022-12-15 14:39:38 +000017#![deny(unsafe_op_in_unsafe_fn)]
18
Alice Wang4dd20932023-05-26 13:47:16 +000019use crate::helpers::{self, page_4kb_of, RangeExt, PVMFW_PAGE_SIZE, SIZE_4MB};
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000020use aarch64_paging::idmap::IdMap;
Jakob Vukalovicb99905d2023-04-20 15:46:02 +010021use aarch64_paging::paging::{Attributes, Descriptor, MemoryRegion as VaRange};
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000022use aarch64_paging::MapError;
Andrew Walbran848decf2022-12-15 14:39:38 +000023use alloc::alloc::alloc_zeroed;
24use alloc::alloc::dealloc;
25use alloc::alloc::handle_alloc_error;
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -070026use alloc::boxed::Box;
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +000027use alloc::vec::Vec;
Andrew Walbran87933f32023-05-09 15:29:06 +000028use buddy_system_allocator::{FrameAllocator, LockedFrameAllocator};
Andrew Walbran848decf2022-12-15 14:39:38 +000029use core::alloc::Layout;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000030use core::cmp::max;
31use core::cmp::min;
32use core::fmt;
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010033use core::iter::once;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000034use core::num::NonZeroUsize;
35use core::ops::Range;
Andrew Walbran848decf2022-12-15 14:39:38 +000036use core::ptr::NonNull;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000037use core::result;
Alice Wang90e6f162023-04-17 13:49:45 +000038use hyp::get_hypervisor;
Pierre-Clément Tosi90238c52023-04-27 17:59:10 +000039use log::trace;
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +010040use log::{debug, error};
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -070041use once_cell::race::OnceBox;
Jakob Vukalovic85a00d72023-04-20 09:51:10 +010042use spin::mutex::SpinMutex;
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000043use tinyvec::ArrayVec;
Pierre-Clément Tosi3d4c5c32023-05-31 16:57:06 +000044use vmbase::{
45 dsb, isb, layout,
46 memory::{set_dbm_enabled, PageTable, MMIO_LAZY_MAP_FLAG},
47 tlbi,
48};
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000049
Jiyong Park0ee65392023-03-27 20:52:45 +090050/// Base of the system's contiguous "main" memory.
51pub const BASE_ADDR: usize = 0x8000_0000;
52/// First address that can't be translated by a level 1 TTBR0_EL1.
53pub const MAX_ADDR: usize = 1 << 40;
54
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000055const PT_ROOT_LEVEL: usize = 1;
56const PT_ASID: usize = 1;
57
Andrew Walbran0d8b54d2022-12-08 16:32:33 +000058pub type MemoryRange = Range<usize>;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000059
Jakob Vukalovic85a00d72023-04-20 09:51:10 +010060pub static MEMORY: SpinMutex<Option<MemoryTracker>> = SpinMutex::new(None);
61unsafe impl Send for MemoryTracker {}
62
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010063#[derive(Clone, Copy, Debug, Default, PartialEq)]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000064enum MemoryType {
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000065 #[default]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000066 ReadOnly,
67 ReadWrite,
68}
69
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000070#[derive(Clone, Debug, Default)]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000071struct MemoryRegion {
72 range: MemoryRange,
73 mem_type: MemoryType,
74}
75
76impl MemoryRegion {
77 /// True if the instance overlaps with the passed range.
78 pub fn overlaps(&self, range: &MemoryRange) -> bool {
Andrew Walbran19690632022-12-07 16:41:30 +000079 overlaps(&self.range, range)
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000080 }
81
82 /// True if the instance is fully contained within the passed range.
83 pub fn is_within(&self, range: &MemoryRange) -> bool {
Srivatsa Vaddagiric25d68e2023-04-19 22:56:33 -070084 self.as_ref().is_within(range)
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000085 }
86}
87
88impl AsRef<MemoryRange> for MemoryRegion {
89 fn as_ref(&self) -> &MemoryRange {
90 &self.range
91 }
92}
93
Andrew Walbran19690632022-12-07 16:41:30 +000094/// Returns true if one range overlaps with the other at all.
95fn overlaps<T: Copy + Ord>(a: &Range<T>, b: &Range<T>) -> bool {
96 max(a.start, b.start) < min(a.end, b.end)
97}
98
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000099/// Tracks non-overlapping slices of main memory.
100pub struct MemoryTracker {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000101 total: MemoryRange,
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000102 page_table: PageTable,
Andrew Walbran19690632022-12-07 16:41:30 +0000103 regions: ArrayVec<[MemoryRegion; MemoryTracker::CAPACITY]>,
104 mmio_regions: ArrayVec<[MemoryRange; MemoryTracker::MMIO_CAPACITY]>,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000105}
106
107/// Errors for MemoryTracker operations.
108#[derive(Debug, Clone)]
109pub enum MemoryTrackerError {
110 /// Tried to modify the memory base address.
111 DifferentBaseAddress,
112 /// Tried to shrink to a larger memory size.
113 SizeTooLarge,
114 /// Tracked regions would not fit in memory size.
115 SizeTooSmall,
116 /// Reached limit number of tracked regions.
117 Full,
118 /// Region is out of the tracked memory address space.
119 OutOfRange,
120 /// New region overlaps with tracked regions.
121 Overlaps,
122 /// Region couldn't be mapped.
123 FailedToMap,
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100124 /// Region couldn't be unmapped.
125 FailedToUnmap,
Alice Wang90e6f162023-04-17 13:49:45 +0000126 /// Error from the interaction with the hypervisor.
127 Hypervisor(hyp::Error),
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000128 /// Failure to set `SHARED_MEMORY`.
129 SharedMemorySetFailure,
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700130 /// Failure to set `SHARED_POOL`.
131 SharedPoolSetFailure,
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100132 /// Invalid page table entry.
133 InvalidPte,
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100134 /// Failed to flush memory region.
135 FlushRegionFailed,
136 /// Failed to set PTE dirty state.
137 SetPteDirtyFailed,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000138}
139
140impl fmt::Display for MemoryTrackerError {
141 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142 match self {
143 Self::DifferentBaseAddress => write!(f, "Received different base address"),
144 Self::SizeTooLarge => write!(f, "Tried to shrink to a larger memory size"),
145 Self::SizeTooSmall => write!(f, "Tracked regions would not fit in memory size"),
146 Self::Full => write!(f, "Reached limit number of tracked regions"),
147 Self::OutOfRange => write!(f, "Region is out of the tracked memory address space"),
148 Self::Overlaps => write!(f, "New region overlaps with tracked regions"),
149 Self::FailedToMap => write!(f, "Failed to map the new region"),
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100150 Self::FailedToUnmap => write!(f, "Failed to unmap the new region"),
Alice Wang90e6f162023-04-17 13:49:45 +0000151 Self::Hypervisor(e) => e.fmt(f),
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000152 Self::SharedMemorySetFailure => write!(f, "Failed to set SHARED_MEMORY"),
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700153 Self::SharedPoolSetFailure => write!(f, "Failed to set SHARED_POOL"),
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100154 Self::InvalidPte => write!(f, "Page table entry is not valid"),
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100155 Self::FlushRegionFailed => write!(f, "Failed to flush memory region"),
156 Self::SetPteDirtyFailed => write!(f, "Failed to set PTE dirty state"),
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000157 }
158 }
159}
160
Alice Wang90e6f162023-04-17 13:49:45 +0000161impl From<hyp::Error> for MemoryTrackerError {
162 fn from(e: hyp::Error) -> Self {
163 Self::Hypervisor(e)
Andrew Walbran19690632022-12-07 16:41:30 +0000164 }
165}
166
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000167type Result<T> = result::Result<T, MemoryTrackerError>;
168
Andrew Walbran87933f32023-05-09 15:29:06 +0000169static SHARED_POOL: OnceBox<LockedFrameAllocator<32>> = OnceBox::new();
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000170static SHARED_MEMORY: SpinMutex<Option<MemorySharer>> = SpinMutex::new(None);
171
172/// Allocates memory on the heap and shares it with the host.
173///
174/// Unshares all pages when dropped.
175pub struct MemorySharer {
176 granule: usize,
177 shared_regions: Vec<(usize, Layout)>,
178}
179
180impl MemorySharer {
181 const INIT_CAP: usize = 10;
182
183 pub fn new(granule: usize) -> Self {
184 assert!(granule.is_power_of_two());
185 Self { granule, shared_regions: Vec::with_capacity(Self::INIT_CAP) }
186 }
187
188 /// Get from the global allocator a granule-aligned region that suits `hint` and share it.
Andrew Walbran87933f32023-05-09 15:29:06 +0000189 pub fn refill(&mut self, pool: &mut FrameAllocator<32>, hint: Layout) {
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000190 let layout = hint.align_to(self.granule).unwrap().pad_to_align();
191 assert_ne!(layout.size(), 0);
192 // SAFETY - layout has non-zero size.
193 let Some(shared) = NonNull::new(unsafe { alloc_zeroed(layout) }) else {
194 handle_alloc_error(layout);
195 };
196
197 let base = shared.as_ptr() as usize;
198 let end = base.checked_add(layout.size()).unwrap();
199 trace!("Sharing memory region {:#x?}", base..end);
200 for vaddr in (base..end).step_by(self.granule) {
201 let vaddr = NonNull::new(vaddr as *mut _).unwrap();
202 get_hypervisor().mem_share(virt_to_phys(vaddr).try_into().unwrap()).unwrap();
203 }
204 self.shared_regions.push((base, layout));
205
Andrew Walbran87933f32023-05-09 15:29:06 +0000206 pool.add_frame(base, end);
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000207 }
208}
209
210impl Drop for MemorySharer {
211 fn drop(&mut self) {
212 while let Some((base, layout)) = self.shared_regions.pop() {
213 let end = base.checked_add(layout.size()).unwrap();
214 trace!("Unsharing memory region {:#x?}", base..end);
215 for vaddr in (base..end).step_by(self.granule) {
216 let vaddr = NonNull::new(vaddr as *mut _).unwrap();
217 get_hypervisor().mem_unshare(virt_to_phys(vaddr).try_into().unwrap()).unwrap();
218 }
219
220 // SAFETY - The region was obtained from alloc_zeroed() with the recorded layout.
221 unsafe { dealloc(base as *mut _, layout) };
222 }
223 }
224}
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700225
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000226impl MemoryTracker {
227 const CAPACITY: usize = 5;
Andrew Walbran19690632022-12-07 16:41:30 +0000228 const MMIO_CAPACITY: usize = 5;
Pierre-Clément Tosi164a6f52023-04-18 19:29:11 +0100229 const PVMFW_RANGE: MemoryRange = (BASE_ADDR - SIZE_4MB)..BASE_ADDR;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000230
231 /// Create a new instance from an active page table, covering the maximum RAM size.
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000232 pub fn new(mut page_table: PageTable) -> Self {
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100233 // Activate dirty state management first, otherwise we may get permission faults immediately
234 // after activating the new page table. This has no effect before the new page table is
235 // activated because none of the entries in the initial idmap have the DBM flag.
Alice Wang4dd20932023-05-26 13:47:16 +0000236 set_dbm_enabled(true);
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100237
238 debug!("Activating dynamic page table...");
239 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
240 // aware of so activating it shouldn't have any visible effect.
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000241 unsafe { page_table.activate() }
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100242 debug!("... Success!");
243
Andrew Walbran19690632022-12-07 16:41:30 +0000244 Self {
Jiyong Park0ee65392023-03-27 20:52:45 +0900245 total: BASE_ADDR..MAX_ADDR,
Andrew Walbran19690632022-12-07 16:41:30 +0000246 page_table,
247 regions: ArrayVec::new(),
248 mmio_regions: ArrayVec::new(),
249 }
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000250 }
251
252 /// Resize the total RAM size.
253 ///
254 /// This function fails if it contains regions that are not included within the new size.
255 pub fn shrink(&mut self, range: &MemoryRange) -> Result<()> {
256 if range.start != self.total.start {
257 return Err(MemoryTrackerError::DifferentBaseAddress);
258 }
259 if self.total.end < range.end {
260 return Err(MemoryTrackerError::SizeTooLarge);
261 }
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000262 if !self.regions.iter().all(|r| r.is_within(range)) {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000263 return Err(MemoryTrackerError::SizeTooSmall);
264 }
265
266 self.total = range.clone();
267 Ok(())
268 }
269
270 /// Allocate the address range for a const slice; returns None if failed.
271 pub fn alloc_range(&mut self, range: &MemoryRange) -> Result<MemoryRange> {
Andrew Walbranda65ab12022-12-07 15:10:13 +0000272 let region = MemoryRegion { range: range.clone(), mem_type: MemoryType::ReadOnly };
273 self.check(&region)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000274 self.page_table.map_rodata(range).map_err(|e| {
275 error!("Error during range allocation: {e}");
276 MemoryTrackerError::FailedToMap
277 })?;
Andrew Walbranda65ab12022-12-07 15:10:13 +0000278 self.add(region)
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000279 }
280
281 /// Allocate the address range for a mutable slice; returns None if failed.
282 pub fn alloc_range_mut(&mut self, range: &MemoryRange) -> Result<MemoryRange> {
Andrew Walbranda65ab12022-12-07 15:10:13 +0000283 let region = MemoryRegion { range: range.clone(), mem_type: MemoryType::ReadWrite };
284 self.check(&region)?;
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000285 self.page_table.map_data_dbm(range).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000286 error!("Error during mutable range allocation: {e}");
287 MemoryTrackerError::FailedToMap
288 })?;
Andrew Walbranda65ab12022-12-07 15:10:13 +0000289 self.add(region)
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000290 }
291
292 /// Allocate the address range for a const slice; returns None if failed.
293 pub fn alloc(&mut self, base: usize, size: NonZeroUsize) -> Result<MemoryRange> {
294 self.alloc_range(&(base..(base + size.get())))
295 }
296
297 /// Allocate the address range for a mutable slice; returns None if failed.
298 pub fn alloc_mut(&mut self, base: usize, size: NonZeroUsize) -> Result<MemoryRange> {
299 self.alloc_range_mut(&(base..(base + size.get())))
300 }
301
Andrew Walbran19690632022-12-07 16:41:30 +0000302 /// Checks that the given range of addresses is within the MMIO region, and then maps it
303 /// appropriately.
304 pub fn map_mmio_range(&mut self, range: MemoryRange) -> Result<()> {
305 // MMIO space is below the main memory region.
Pierre-Clément Tosi164a6f52023-04-18 19:29:11 +0100306 if range.end > self.total.start || overlaps(&Self::PVMFW_RANGE, &range) {
Andrew Walbran19690632022-12-07 16:41:30 +0000307 return Err(MemoryTrackerError::OutOfRange);
308 }
309 if self.mmio_regions.iter().any(|r| overlaps(r, &range)) {
310 return Err(MemoryTrackerError::Overlaps);
311 }
312 if self.mmio_regions.len() == self.mmio_regions.capacity() {
313 return Err(MemoryTrackerError::Full);
314 }
315
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100316 self.page_table.map_device_lazy(&range).map_err(|e| {
Andrew Walbran19690632022-12-07 16:41:30 +0000317 error!("Error during MMIO device mapping: {e}");
318 MemoryTrackerError::FailedToMap
319 })?;
320
Andrew Walbran19690632022-12-07 16:41:30 +0000321 if self.mmio_regions.try_push(range).is_some() {
322 return Err(MemoryTrackerError::Full);
323 }
324
325 Ok(())
326 }
327
Andrew Walbranda65ab12022-12-07 15:10:13 +0000328 /// Checks that the given region is within the range of the `MemoryTracker` and doesn't overlap
329 /// with any other previously allocated regions, and that the regions ArrayVec has capacity to
330 /// add it.
331 fn check(&self, region: &MemoryRegion) -> Result<()> {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000332 if !region.is_within(&self.total) {
333 return Err(MemoryTrackerError::OutOfRange);
334 }
Andrew Walbranda65ab12022-12-07 15:10:13 +0000335 if self.regions.iter().any(|r| r.overlaps(&region.range)) {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000336 return Err(MemoryTrackerError::Overlaps);
337 }
Andrew Walbranda65ab12022-12-07 15:10:13 +0000338 if self.regions.len() == self.regions.capacity() {
339 return Err(MemoryTrackerError::Full);
340 }
341 Ok(())
342 }
343
344 fn add(&mut self, region: MemoryRegion) -> Result<MemoryRange> {
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000345 if self.regions.try_push(region).is_some() {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000346 return Err(MemoryTrackerError::Full);
347 }
348
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000349 Ok(self.regions.last().unwrap().as_ref().clone())
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000350 }
Andrew Walbran19690632022-12-07 16:41:30 +0000351
352 /// Unmaps all tracked MMIO regions from the MMIO guard.
353 ///
354 /// Note that they are not unmapped from the page table.
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100355 pub fn mmio_unmap_all(&mut self) -> Result<()> {
356 for range in &self.mmio_regions {
357 self.page_table
358 .modify_range(range, &mmio_guard_unmap_page)
359 .map_err(|_| MemoryTrackerError::FailedToUnmap)?;
Andrew Walbran19690632022-12-07 16:41:30 +0000360 }
Andrew Walbran19690632022-12-07 16:41:30 +0000361 Ok(())
362 }
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700363
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000364 /// Initialize the shared heap to dynamically share memory from the global allocator.
365 pub fn init_dynamic_shared_pool(&mut self) -> Result<()> {
366 let granule = get_hypervisor().memory_protection_granule()?;
367 let previous = SHARED_MEMORY.lock().replace(MemorySharer::new(granule));
368 if previous.is_some() {
369 return Err(MemoryTrackerError::SharedMemorySetFailure);
370 }
371
372 SHARED_POOL
Andrew Walbran87933f32023-05-09 15:29:06 +0000373 .set(Box::new(LockedFrameAllocator::new()))
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000374 .map_err(|_| MemoryTrackerError::SharedPoolSetFailure)?;
375
376 Ok(())
377 }
378
379 /// Initialize the shared heap from a static region of memory.
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700380 ///
381 /// Some hypervisors such as Gunyah do not support a MemShare API for guest
382 /// to share its memory with host. Instead they allow host to designate part
383 /// of guest memory as "shared" ahead of guest starting its execution. The
384 /// shared memory region is indicated in swiotlb node. On such platforms use
385 /// a separate heap to allocate buffers that can be shared with host.
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000386 pub fn init_static_shared_pool(&mut self, range: Range<usize>) -> Result<()> {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700387 let size = NonZeroUsize::new(range.len()).unwrap();
388 let range = self.alloc_mut(range.start, size)?;
Andrew Walbran87933f32023-05-09 15:29:06 +0000389 let shared_pool = LockedFrameAllocator::<32>::new();
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700390
Andrew Walbran87933f32023-05-09 15:29:06 +0000391 shared_pool.lock().insert(range);
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700392
393 SHARED_POOL
394 .set(Box::new(shared_pool))
395 .map_err(|_| MemoryTrackerError::SharedPoolSetFailure)?;
396
397 Ok(())
398 }
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000399
400 /// Unshares any memory that may have been shared.
401 pub fn unshare_all_memory(&mut self) {
402 drop(SHARED_MEMORY.lock().take());
403 }
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100404
405 /// Handles translation fault for blocks flagged for lazy MMIO mapping by enabling the page
406 /// table entry and MMIO guard mapping the block. Breaks apart a block entry if required.
407 pub fn handle_mmio_fault(&mut self, addr: usize) -> Result<()> {
408 let page_range = page_4kb_of(addr)..page_4kb_of(addr) + PVMFW_PAGE_SIZE;
409 self.page_table
410 .modify_range(&page_range, &verify_lazy_mapped_block)
411 .map_err(|_| MemoryTrackerError::InvalidPte)?;
412 get_hypervisor().mmio_guard_map(page_range.start)?;
413 // Maps a single device page, breaking up block mappings if necessary.
414 self.page_table.map_device(&page_range).map_err(|_| MemoryTrackerError::FailedToMap)
415 }
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100416
417 /// Flush all memory regions marked as writable-dirty.
418 fn flush_dirty_pages(&mut self) -> Result<()> {
419 // Collect memory ranges for which dirty state is tracked.
420 let writable_regions =
421 self.regions.iter().filter(|r| r.mem_type == MemoryType::ReadWrite).map(|r| &r.range);
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000422 let payload_range = appended_payload_range();
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100423 // Execute a barrier instruction to ensure all hardware updates to the page table have been
424 // observed before reading PTE flags to determine dirty state.
425 dsb!("ish");
426 // Now flush writable-dirty pages in those regions.
427 for range in writable_regions.chain(once(&payload_range)) {
428 self.page_table
429 .modify_range(range, &flush_dirty_range)
430 .map_err(|_| MemoryTrackerError::FlushRegionFailed)?;
431 }
432 Ok(())
433 }
434
435 /// Handles permission fault for read-only blocks by setting writable-dirty state.
436 /// In general, this should be called from the exception handler when hardware dirty
437 /// state management is disabled or unavailable.
438 pub fn handle_permission_fault(&mut self, addr: usize) -> Result<()> {
439 self.page_table
440 .modify_range(&(addr..addr + 1), &mark_dirty_block)
441 .map_err(|_| MemoryTrackerError::SetPteDirtyFailed)
442 }
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000443}
444
445impl Drop for MemoryTracker {
446 fn drop(&mut self) {
Alice Wang4dd20932023-05-26 13:47:16 +0000447 set_dbm_enabled(false);
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100448 self.flush_dirty_pages().unwrap();
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100449 self.unshare_all_memory();
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000450 }
451}
Andrew Walbran19690632022-12-07 16:41:30 +0000452
Andrew Walbran2b0c7fb2023-05-09 12:16:20 +0000453/// Allocates a memory range of at least the given size and alignment that is shared with the host.
454/// Returns a pointer to the buffer.
Pierre-Clément Tosi2d5bc582023-05-03 11:23:11 +0000455pub fn alloc_shared(layout: Layout) -> hyp::Result<NonNull<u8>> {
456 assert_ne!(layout.size(), 0);
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000457 let Some(buffer) = try_shared_alloc(layout) else {
Andrew Walbran848decf2022-12-15 14:39:38 +0000458 handle_alloc_error(layout);
459 };
460
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000461 trace!("Allocated shared buffer at {buffer:?} with {layout:?}");
Andrew Walbran848decf2022-12-15 14:39:38 +0000462 Ok(buffer)
463}
464
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000465fn try_shared_alloc(layout: Layout) -> Option<NonNull<u8>> {
466 let mut shared_pool = SHARED_POOL.get().unwrap().lock();
467
Andrew Walbran87933f32023-05-09 15:29:06 +0000468 if let Some(buffer) = shared_pool.alloc_aligned(layout) {
469 Some(NonNull::new(buffer as _).unwrap())
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000470 } else if let Some(shared_memory) = SHARED_MEMORY.lock().as_mut() {
471 shared_memory.refill(&mut shared_pool, layout);
Andrew Walbran87933f32023-05-09 15:29:06 +0000472 shared_pool.alloc_aligned(layout).map(|buffer| NonNull::new(buffer as _).unwrap())
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000473 } else {
474 None
475 }
476}
477
Andrew Walbran848decf2022-12-15 14:39:38 +0000478/// Unshares and deallocates a memory range which was previously allocated by `alloc_shared`.
479///
Andrew Walbran2b0c7fb2023-05-09 12:16:20 +0000480/// The layout passed in must be the same layout passed to the original `alloc_shared` call.
Andrew Walbran848decf2022-12-15 14:39:38 +0000481///
482/// # Safety
483///
Andrew Walbran2b0c7fb2023-05-09 12:16:20 +0000484/// The memory must have been allocated by `alloc_shared` with the same layout, and not yet
Andrew Walbran848decf2022-12-15 14:39:38 +0000485/// deallocated.
Pierre-Clément Tosi2d5bc582023-05-03 11:23:11 +0000486pub unsafe fn dealloc_shared(vaddr: NonNull<u8>, layout: Layout) -> hyp::Result<()> {
Andrew Walbran87933f32023-05-09 15:29:06 +0000487 SHARED_POOL.get().unwrap().lock().dealloc_aligned(vaddr.as_ptr() as usize, layout);
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700488
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000489 trace!("Deallocated shared buffer at {vaddr:?} with {layout:?}");
Andrew Walbran848decf2022-12-15 14:39:38 +0000490 Ok(())
491}
492
Andrew Walbran848decf2022-12-15 14:39:38 +0000493/// Returns the intermediate physical address corresponding to the given virtual address.
494///
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000495/// As we use identity mapping for everything, this is just a cast, but it's useful to use it to be
496/// explicit about where we are converting from virtual to physical address.
497pub fn virt_to_phys(vaddr: NonNull<u8>) -> usize {
498 vaddr.as_ptr() as _
499}
500
501/// Returns a pointer for the virtual address corresponding to the given non-zero intermediate
502/// physical address.
503///
504/// Panics if `paddr` is 0.
505pub fn phys_to_virt(paddr: usize) -> NonNull<u8> {
506 NonNull::new(paddr as _).unwrap()
Andrew Walbran848decf2022-12-15 14:39:38 +0000507}
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100508
509/// Checks whether a PTE at given level is a page or block descriptor.
510#[inline]
511fn is_leaf_pte(flags: &Attributes, level: usize) -> bool {
512 const LEAF_PTE_LEVEL: usize = 3;
513 if flags.contains(Attributes::TABLE_OR_PAGE) {
514 level == LEAF_PTE_LEVEL
515 } else {
516 level < LEAF_PTE_LEVEL
517 }
518}
519
520/// Checks whether block flags indicate it should be MMIO guard mapped.
521fn verify_lazy_mapped_block(
522 _range: &VaRange,
523 desc: &mut Descriptor,
524 level: usize,
525) -> result::Result<(), ()> {
526 let flags = desc.flags().expect("Unsupported PTE flags set");
527 if !is_leaf_pte(&flags, level) {
528 return Ok(()); // Skip table PTEs as they aren't tagged with MMIO_LAZY_MAP_FLAG.
529 }
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000530 if flags.contains(MMIO_LAZY_MAP_FLAG) && !flags.contains(Attributes::VALID) {
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100531 Ok(())
532 } else {
533 Err(())
534 }
535}
536
537/// MMIO guard unmaps page
538fn mmio_guard_unmap_page(
539 va_range: &VaRange,
540 desc: &mut Descriptor,
541 level: usize,
542) -> result::Result<(), ()> {
543 let flags = desc.flags().expect("Unsupported PTE flags set");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100544 if !is_leaf_pte(&flags, level) {
545 return Ok(());
546 }
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100547 // This function will be called on an address range that corresponds to a device. Only if a
548 // page has been accessed (written to or read from), will it contain the VALID flag and be MMIO
549 // guard mapped. Therefore, we can skip unmapping invalid pages, they were never MMIO guard
550 // mapped anyway.
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100551 if flags.contains(Attributes::VALID) {
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100552 assert!(
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000553 flags.contains(MMIO_LAZY_MAP_FLAG),
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100554 "Attempting MMIO guard unmap for non-device pages"
555 );
556 assert_eq!(
557 va_range.len(),
558 PVMFW_PAGE_SIZE,
559 "Failed to break down block mapping before MMIO guard mapping"
560 );
561 let page_base = va_range.start().0;
562 assert_eq!(page_base % PVMFW_PAGE_SIZE, 0);
563 // Since mmio_guard_map takes IPAs, if pvmfw moves non-ID address mapping, page_base
564 // should be converted to IPA. However, since 0x0 is a valid MMIO address, we don't use
565 // virt_to_phys here, and just pass page_base instead.
566 get_hypervisor().mmio_guard_unmap(page_base).map_err(|e| {
567 error!("Error MMIO guard unmapping: {e}");
568 })?;
569 }
570 Ok(())
571}
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100572
573/// Flushes a memory range the descriptor refers to, if the descriptor is in writable-dirty state.
574fn flush_dirty_range(
575 va_range: &VaRange,
576 desc: &mut Descriptor,
577 level: usize,
578) -> result::Result<(), ()> {
579 // Only flush ranges corresponding to dirty leaf PTEs.
580 let flags = desc.flags().ok_or(())?;
581 if !is_leaf_pte(&flags, level) {
582 return Ok(());
583 }
584 if !flags.contains(Attributes::READ_ONLY) {
585 helpers::flush_region(va_range.start().0, va_range.len());
586 }
587 Ok(())
588}
589
590/// Clears read-only flag on a PTE, making it writable-dirty. Used when dirty state is managed
591/// in software to handle permission faults on read-only descriptors.
592fn mark_dirty_block(
593 va_range: &VaRange,
594 desc: &mut Descriptor,
595 level: usize,
596) -> result::Result<(), ()> {
597 let flags = desc.flags().ok_or(())?;
598 if !is_leaf_pte(&flags, level) {
599 return Ok(());
600 }
601 if flags.contains(Attributes::DBM) {
602 assert!(flags.contains(Attributes::READ_ONLY), "unexpected PTE writable state");
603 desc.modify_flags(Attributes::empty(), Attributes::READ_ONLY);
604 // Updating the read-only bit of a PTE requires TLB invalidation.
605 // A TLB maintenance instruction is only guaranteed to be complete after a DSB instruction.
606 // An ISB instruction is required to ensure the effects of completed TLB maintenance
607 // instructions are visible to instructions fetched afterwards.
608 // See ARM ARM E2.3.10, and G5.9.
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000609 tlbi!("vale1", PT_ASID, va_range.start().0);
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100610 dsb!("ish");
611 isb!();
612 Ok(())
613 } else {
614 Err(())
615 }
616}
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000617
618/// Returns memory range reserved for the appended payload.
619pub fn appended_payload_range() -> Range<usize> {
620 let start = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
621 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
622 let end = helpers::align_up(start, helpers::SIZE_2MB).unwrap();
623 start..end
624}
625
626/// Region allocated for the stack.
627pub fn stack_range() -> Range<usize> {
628 const STACK_PAGES: usize = 8;
629
630 layout::stack_range(STACK_PAGES * PVMFW_PAGE_SIZE)
631}
632
633pub fn init_page_table() -> result::Result<PageTable, MapError> {
634 let mut page_table: PageTable = IdMap::new(PT_ASID, PT_ROOT_LEVEL).into();
635
636 // Stack and scratch ranges are explicitly zeroed and flushed before jumping to payload,
637 // so dirty state management can be omitted.
638 page_table.map_data(&layout::scratch_range())?;
639 page_table.map_data(&stack_range())?;
640 page_table.map_code(&layout::text_range())?;
641 page_table.map_rodata(&layout::rodata_range())?;
642 page_table.map_data_dbm(&appended_payload_range())?;
Alice Wang807fa592023-06-02 09:54:43 +0000643 if let Err(e) = page_table.map_device(&layout::console_uart_range()) {
644 error!("Failed to remap the UART as a dynamic page table entry: {e}");
645 return Err(e);
646 }
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000647 Ok(page_table)
648}