blob: c316dd2251ecea264e2ce7aeb789eed563661a34 [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
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +010019use crate::helpers::{self, dbm_available, page_4kb_of, RangeExt, PVMFW_PAGE_SIZE, SIZE_4MB};
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000020use crate::mmu;
Jakob Vukalovicb99905d2023-04-20 15:46:02 +010021use aarch64_paging::paging::{Attributes, Descriptor, MemoryRegion as VaRange};
Andrew Walbran848decf2022-12-15 14:39:38 +000022use alloc::alloc::alloc_zeroed;
23use alloc::alloc::dealloc;
24use alloc::alloc::handle_alloc_error;
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -070025use alloc::boxed::Box;
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +000026use alloc::vec::Vec;
Andrew Walbran87933f32023-05-09 15:29:06 +000027use buddy_system_allocator::{FrameAllocator, LockedFrameAllocator};
Andrew Walbran848decf2022-12-15 14:39:38 +000028use core::alloc::Layout;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000029use core::cmp::max;
30use core::cmp::min;
31use core::fmt;
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010032use core::iter::once;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000033use core::num::NonZeroUsize;
34use core::ops::Range;
Andrew Walbran848decf2022-12-15 14:39:38 +000035use core::ptr::NonNull;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000036use core::result;
Alice Wang90e6f162023-04-17 13:49:45 +000037use hyp::get_hypervisor;
Pierre-Clément Tosi90238c52023-04-27 17:59:10 +000038use log::trace;
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +010039use log::{debug, error};
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -070040use once_cell::race::OnceBox;
Jakob Vukalovic85a00d72023-04-20 09:51:10 +010041use spin::mutex::SpinMutex;
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000042use tinyvec::ArrayVec;
Alice Wang81399f52023-05-26 14:23:43 +000043use vmbase::{dsb, isb, read_sysreg, tlbi, write_sysreg};
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000044
Jiyong Park0ee65392023-03-27 20:52:45 +090045/// Base of the system's contiguous "main" memory.
46pub const BASE_ADDR: usize = 0x8000_0000;
47/// First address that can't be translated by a level 1 TTBR0_EL1.
48pub const MAX_ADDR: usize = 1 << 40;
49
Andrew Walbran0d8b54d2022-12-08 16:32:33 +000050pub type MemoryRange = Range<usize>;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000051
Jakob Vukalovic85a00d72023-04-20 09:51:10 +010052pub static MEMORY: SpinMutex<Option<MemoryTracker>> = SpinMutex::new(None);
53unsafe impl Send for MemoryTracker {}
54
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010055#[derive(Clone, Copy, Debug, Default, PartialEq)]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000056enum MemoryType {
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000057 #[default]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000058 ReadOnly,
59 ReadWrite,
60}
61
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000062#[derive(Clone, Debug, Default)]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000063struct MemoryRegion {
64 range: MemoryRange,
65 mem_type: MemoryType,
66}
67
68impl MemoryRegion {
69 /// True if the instance overlaps with the passed range.
70 pub fn overlaps(&self, range: &MemoryRange) -> bool {
Andrew Walbran19690632022-12-07 16:41:30 +000071 overlaps(&self.range, range)
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000072 }
73
74 /// True if the instance is fully contained within the passed range.
75 pub fn is_within(&self, range: &MemoryRange) -> bool {
Srivatsa Vaddagiric25d68e2023-04-19 22:56:33 -070076 self.as_ref().is_within(range)
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000077 }
78}
79
80impl AsRef<MemoryRange> for MemoryRegion {
81 fn as_ref(&self) -> &MemoryRange {
82 &self.range
83 }
84}
85
Andrew Walbran19690632022-12-07 16:41:30 +000086/// Returns true if one range overlaps with the other at all.
87fn overlaps<T: Copy + Ord>(a: &Range<T>, b: &Range<T>) -> bool {
88 max(a.start, b.start) < min(a.end, b.end)
89}
90
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000091/// Tracks non-overlapping slices of main memory.
92pub struct MemoryTracker {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000093 total: MemoryRange,
94 page_table: mmu::PageTable,
Andrew Walbran19690632022-12-07 16:41:30 +000095 regions: ArrayVec<[MemoryRegion; MemoryTracker::CAPACITY]>,
96 mmio_regions: ArrayVec<[MemoryRange; MemoryTracker::MMIO_CAPACITY]>,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000097}
98
99/// Errors for MemoryTracker operations.
100#[derive(Debug, Clone)]
101pub enum MemoryTrackerError {
102 /// Tried to modify the memory base address.
103 DifferentBaseAddress,
104 /// Tried to shrink to a larger memory size.
105 SizeTooLarge,
106 /// Tracked regions would not fit in memory size.
107 SizeTooSmall,
108 /// Reached limit number of tracked regions.
109 Full,
110 /// Region is out of the tracked memory address space.
111 OutOfRange,
112 /// New region overlaps with tracked regions.
113 Overlaps,
114 /// Region couldn't be mapped.
115 FailedToMap,
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100116 /// Region couldn't be unmapped.
117 FailedToUnmap,
Alice Wang90e6f162023-04-17 13:49:45 +0000118 /// Error from the interaction with the hypervisor.
119 Hypervisor(hyp::Error),
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000120 /// Failure to set `SHARED_MEMORY`.
121 SharedMemorySetFailure,
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700122 /// Failure to set `SHARED_POOL`.
123 SharedPoolSetFailure,
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100124 /// Invalid page table entry.
125 InvalidPte,
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100126 /// Failed to flush memory region.
127 FlushRegionFailed,
128 /// Failed to set PTE dirty state.
129 SetPteDirtyFailed,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000130}
131
132impl fmt::Display for MemoryTrackerError {
133 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134 match self {
135 Self::DifferentBaseAddress => write!(f, "Received different base address"),
136 Self::SizeTooLarge => write!(f, "Tried to shrink to a larger memory size"),
137 Self::SizeTooSmall => write!(f, "Tracked regions would not fit in memory size"),
138 Self::Full => write!(f, "Reached limit number of tracked regions"),
139 Self::OutOfRange => write!(f, "Region is out of the tracked memory address space"),
140 Self::Overlaps => write!(f, "New region overlaps with tracked regions"),
141 Self::FailedToMap => write!(f, "Failed to map the new region"),
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100142 Self::FailedToUnmap => write!(f, "Failed to unmap the new region"),
Alice Wang90e6f162023-04-17 13:49:45 +0000143 Self::Hypervisor(e) => e.fmt(f),
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000144 Self::SharedMemorySetFailure => write!(f, "Failed to set SHARED_MEMORY"),
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700145 Self::SharedPoolSetFailure => write!(f, "Failed to set SHARED_POOL"),
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100146 Self::InvalidPte => write!(f, "Page table entry is not valid"),
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100147 Self::FlushRegionFailed => write!(f, "Failed to flush memory region"),
148 Self::SetPteDirtyFailed => write!(f, "Failed to set PTE dirty state"),
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000149 }
150 }
151}
152
Alice Wang90e6f162023-04-17 13:49:45 +0000153impl From<hyp::Error> for MemoryTrackerError {
154 fn from(e: hyp::Error) -> Self {
155 Self::Hypervisor(e)
Andrew Walbran19690632022-12-07 16:41:30 +0000156 }
157}
158
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000159type Result<T> = result::Result<T, MemoryTrackerError>;
160
Andrew Walbran87933f32023-05-09 15:29:06 +0000161static SHARED_POOL: OnceBox<LockedFrameAllocator<32>> = OnceBox::new();
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000162static SHARED_MEMORY: SpinMutex<Option<MemorySharer>> = SpinMutex::new(None);
163
164/// Allocates memory on the heap and shares it with the host.
165///
166/// Unshares all pages when dropped.
167pub struct MemorySharer {
168 granule: usize,
169 shared_regions: Vec<(usize, Layout)>,
170}
171
172impl MemorySharer {
173 const INIT_CAP: usize = 10;
174
175 pub fn new(granule: usize) -> Self {
176 assert!(granule.is_power_of_two());
177 Self { granule, shared_regions: Vec::with_capacity(Self::INIT_CAP) }
178 }
179
180 /// Get from the global allocator a granule-aligned region that suits `hint` and share it.
Andrew Walbran87933f32023-05-09 15:29:06 +0000181 pub fn refill(&mut self, pool: &mut FrameAllocator<32>, hint: Layout) {
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000182 let layout = hint.align_to(self.granule).unwrap().pad_to_align();
183 assert_ne!(layout.size(), 0);
184 // SAFETY - layout has non-zero size.
185 let Some(shared) = NonNull::new(unsafe { alloc_zeroed(layout) }) else {
186 handle_alloc_error(layout);
187 };
188
189 let base = shared.as_ptr() as usize;
190 let end = base.checked_add(layout.size()).unwrap();
191 trace!("Sharing memory region {:#x?}", base..end);
192 for vaddr in (base..end).step_by(self.granule) {
193 let vaddr = NonNull::new(vaddr as *mut _).unwrap();
194 get_hypervisor().mem_share(virt_to_phys(vaddr).try_into().unwrap()).unwrap();
195 }
196 self.shared_regions.push((base, layout));
197
Andrew Walbran87933f32023-05-09 15:29:06 +0000198 pool.add_frame(base, end);
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000199 }
200}
201
202impl Drop for MemorySharer {
203 fn drop(&mut self) {
204 while let Some((base, layout)) = self.shared_regions.pop() {
205 let end = base.checked_add(layout.size()).unwrap();
206 trace!("Unsharing memory region {:#x?}", base..end);
207 for vaddr in (base..end).step_by(self.granule) {
208 let vaddr = NonNull::new(vaddr as *mut _).unwrap();
209 get_hypervisor().mem_unshare(virt_to_phys(vaddr).try_into().unwrap()).unwrap();
210 }
211
212 // SAFETY - The region was obtained from alloc_zeroed() with the recorded layout.
213 unsafe { dealloc(base as *mut _, layout) };
214 }
215 }
216}
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700217
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000218impl MemoryTracker {
219 const CAPACITY: usize = 5;
Andrew Walbran19690632022-12-07 16:41:30 +0000220 const MMIO_CAPACITY: usize = 5;
Pierre-Clément Tosi164a6f52023-04-18 19:29:11 +0100221 const PVMFW_RANGE: MemoryRange = (BASE_ADDR - SIZE_4MB)..BASE_ADDR;
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100222 // TCR_EL1.{HA,HD} bits controlling hardware management of access and dirty state
223 const TCR_EL1_HA_HD_BITS: usize = 3 << 39;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000224
225 /// Create a new instance from an active page table, covering the maximum RAM size.
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100226 pub fn new(mut page_table: mmu::PageTable) -> Self {
227 // Activate dirty state management first, otherwise we may get permission faults immediately
228 // after activating the new page table. This has no effect before the new page table is
229 // activated because none of the entries in the initial idmap have the DBM flag.
230 Self::set_dbm_enabled(true);
231
232 debug!("Activating dynamic page table...");
233 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
234 // aware of so activating it shouldn't have any visible effect.
235 unsafe { page_table.activate() };
236 debug!("... Success!");
237
Andrew Walbran19690632022-12-07 16:41:30 +0000238 Self {
Jiyong Park0ee65392023-03-27 20:52:45 +0900239 total: BASE_ADDR..MAX_ADDR,
Andrew Walbran19690632022-12-07 16:41:30 +0000240 page_table,
241 regions: ArrayVec::new(),
242 mmio_regions: ArrayVec::new(),
243 }
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000244 }
245
246 /// Resize the total RAM size.
247 ///
248 /// This function fails if it contains regions that are not included within the new size.
249 pub fn shrink(&mut self, range: &MemoryRange) -> Result<()> {
250 if range.start != self.total.start {
251 return Err(MemoryTrackerError::DifferentBaseAddress);
252 }
253 if self.total.end < range.end {
254 return Err(MemoryTrackerError::SizeTooLarge);
255 }
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000256 if !self.regions.iter().all(|r| r.is_within(range)) {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000257 return Err(MemoryTrackerError::SizeTooSmall);
258 }
259
260 self.total = range.clone();
261 Ok(())
262 }
263
264 /// Allocate the address range for a const slice; returns None if failed.
265 pub fn alloc_range(&mut self, range: &MemoryRange) -> Result<MemoryRange> {
Andrew Walbranda65ab12022-12-07 15:10:13 +0000266 let region = MemoryRegion { range: range.clone(), mem_type: MemoryType::ReadOnly };
267 self.check(&region)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000268 self.page_table.map_rodata(range).map_err(|e| {
269 error!("Error during range allocation: {e}");
270 MemoryTrackerError::FailedToMap
271 })?;
Andrew Walbranda65ab12022-12-07 15:10:13 +0000272 self.add(region)
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000273 }
274
275 /// Allocate the address range for a mutable slice; returns None if failed.
276 pub fn alloc_range_mut(&mut self, range: &MemoryRange) -> Result<MemoryRange> {
Andrew Walbranda65ab12022-12-07 15:10:13 +0000277 let region = MemoryRegion { range: range.clone(), mem_type: MemoryType::ReadWrite };
278 self.check(&region)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000279 self.page_table.map_data(range).map_err(|e| {
280 error!("Error during mutable range allocation: {e}");
281 MemoryTrackerError::FailedToMap
282 })?;
Andrew Walbranda65ab12022-12-07 15:10:13 +0000283 self.add(region)
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000284 }
285
286 /// Allocate the address range for a const slice; returns None if failed.
287 pub fn alloc(&mut self, base: usize, size: NonZeroUsize) -> Result<MemoryRange> {
288 self.alloc_range(&(base..(base + size.get())))
289 }
290
291 /// Allocate the address range for a mutable slice; returns None if failed.
292 pub fn alloc_mut(&mut self, base: usize, size: NonZeroUsize) -> Result<MemoryRange> {
293 self.alloc_range_mut(&(base..(base + size.get())))
294 }
295
Andrew Walbran19690632022-12-07 16:41:30 +0000296 /// Checks that the given range of addresses is within the MMIO region, and then maps it
297 /// appropriately.
298 pub fn map_mmio_range(&mut self, range: MemoryRange) -> Result<()> {
299 // MMIO space is below the main memory region.
Pierre-Clément Tosi164a6f52023-04-18 19:29:11 +0100300 if range.end > self.total.start || overlaps(&Self::PVMFW_RANGE, &range) {
Andrew Walbran19690632022-12-07 16:41:30 +0000301 return Err(MemoryTrackerError::OutOfRange);
302 }
303 if self.mmio_regions.iter().any(|r| overlaps(r, &range)) {
304 return Err(MemoryTrackerError::Overlaps);
305 }
306 if self.mmio_regions.len() == self.mmio_regions.capacity() {
307 return Err(MemoryTrackerError::Full);
308 }
309
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100310 self.page_table.map_device_lazy(&range).map_err(|e| {
Andrew Walbran19690632022-12-07 16:41:30 +0000311 error!("Error during MMIO device mapping: {e}");
312 MemoryTrackerError::FailedToMap
313 })?;
314
Andrew Walbran19690632022-12-07 16:41:30 +0000315 if self.mmio_regions.try_push(range).is_some() {
316 return Err(MemoryTrackerError::Full);
317 }
318
319 Ok(())
320 }
321
Andrew Walbranda65ab12022-12-07 15:10:13 +0000322 /// Checks that the given region is within the range of the `MemoryTracker` and doesn't overlap
323 /// with any other previously allocated regions, and that the regions ArrayVec has capacity to
324 /// add it.
325 fn check(&self, region: &MemoryRegion) -> Result<()> {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000326 if !region.is_within(&self.total) {
327 return Err(MemoryTrackerError::OutOfRange);
328 }
Andrew Walbranda65ab12022-12-07 15:10:13 +0000329 if self.regions.iter().any(|r| r.overlaps(&region.range)) {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000330 return Err(MemoryTrackerError::Overlaps);
331 }
Andrew Walbranda65ab12022-12-07 15:10:13 +0000332 if self.regions.len() == self.regions.capacity() {
333 return Err(MemoryTrackerError::Full);
334 }
335 Ok(())
336 }
337
338 fn add(&mut self, region: MemoryRegion) -> Result<MemoryRange> {
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000339 if self.regions.try_push(region).is_some() {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000340 return Err(MemoryTrackerError::Full);
341 }
342
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000343 Ok(self.regions.last().unwrap().as_ref().clone())
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000344 }
Andrew Walbran19690632022-12-07 16:41:30 +0000345
346 /// Unmaps all tracked MMIO regions from the MMIO guard.
347 ///
348 /// Note that they are not unmapped from the page table.
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100349 pub fn mmio_unmap_all(&mut self) -> Result<()> {
350 for range in &self.mmio_regions {
351 self.page_table
352 .modify_range(range, &mmio_guard_unmap_page)
353 .map_err(|_| MemoryTrackerError::FailedToUnmap)?;
Andrew Walbran19690632022-12-07 16:41:30 +0000354 }
Andrew Walbran19690632022-12-07 16:41:30 +0000355 Ok(())
356 }
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700357
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000358 /// Initialize the shared heap to dynamically share memory from the global allocator.
359 pub fn init_dynamic_shared_pool(&mut self) -> Result<()> {
360 let granule = get_hypervisor().memory_protection_granule()?;
361 let previous = SHARED_MEMORY.lock().replace(MemorySharer::new(granule));
362 if previous.is_some() {
363 return Err(MemoryTrackerError::SharedMemorySetFailure);
364 }
365
366 SHARED_POOL
Andrew Walbran87933f32023-05-09 15:29:06 +0000367 .set(Box::new(LockedFrameAllocator::new()))
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000368 .map_err(|_| MemoryTrackerError::SharedPoolSetFailure)?;
369
370 Ok(())
371 }
372
373 /// Initialize the shared heap from a static region of memory.
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700374 ///
375 /// Some hypervisors such as Gunyah do not support a MemShare API for guest
376 /// to share its memory with host. Instead they allow host to designate part
377 /// of guest memory as "shared" ahead of guest starting its execution. The
378 /// shared memory region is indicated in swiotlb node. On such platforms use
379 /// a separate heap to allocate buffers that can be shared with host.
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000380 pub fn init_static_shared_pool(&mut self, range: Range<usize>) -> Result<()> {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700381 let size = NonZeroUsize::new(range.len()).unwrap();
382 let range = self.alloc_mut(range.start, size)?;
Andrew Walbran87933f32023-05-09 15:29:06 +0000383 let shared_pool = LockedFrameAllocator::<32>::new();
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700384
Andrew Walbran87933f32023-05-09 15:29:06 +0000385 shared_pool.lock().insert(range);
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700386
387 SHARED_POOL
388 .set(Box::new(shared_pool))
389 .map_err(|_| MemoryTrackerError::SharedPoolSetFailure)?;
390
391 Ok(())
392 }
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000393
394 /// Unshares any memory that may have been shared.
395 pub fn unshare_all_memory(&mut self) {
396 drop(SHARED_MEMORY.lock().take());
397 }
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100398
399 /// Handles translation fault for blocks flagged for lazy MMIO mapping by enabling the page
400 /// table entry and MMIO guard mapping the block. Breaks apart a block entry if required.
401 pub fn handle_mmio_fault(&mut self, addr: usize) -> Result<()> {
402 let page_range = page_4kb_of(addr)..page_4kb_of(addr) + PVMFW_PAGE_SIZE;
403 self.page_table
404 .modify_range(&page_range, &verify_lazy_mapped_block)
405 .map_err(|_| MemoryTrackerError::InvalidPte)?;
406 get_hypervisor().mmio_guard_map(page_range.start)?;
407 // Maps a single device page, breaking up block mappings if necessary.
408 self.page_table.map_device(&page_range).map_err(|_| MemoryTrackerError::FailedToMap)
409 }
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100410
411 /// Flush all memory regions marked as writable-dirty.
412 fn flush_dirty_pages(&mut self) -> Result<()> {
413 // Collect memory ranges for which dirty state is tracked.
414 let writable_regions =
415 self.regions.iter().filter(|r| r.mem_type == MemoryType::ReadWrite).map(|r| &r.range);
416 let payload_range = mmu::PageTable::appended_payload_range();
417 // Execute a barrier instruction to ensure all hardware updates to the page table have been
418 // observed before reading PTE flags to determine dirty state.
419 dsb!("ish");
420 // Now flush writable-dirty pages in those regions.
421 for range in writable_regions.chain(once(&payload_range)) {
422 self.page_table
423 .modify_range(range, &flush_dirty_range)
424 .map_err(|_| MemoryTrackerError::FlushRegionFailed)?;
425 }
426 Ok(())
427 }
428
429 /// Handles permission fault for read-only blocks by setting writable-dirty state.
430 /// In general, this should be called from the exception handler when hardware dirty
431 /// state management is disabled or unavailable.
432 pub fn handle_permission_fault(&mut self, addr: usize) -> Result<()> {
433 self.page_table
434 .modify_range(&(addr..addr + 1), &mark_dirty_block)
435 .map_err(|_| MemoryTrackerError::SetPteDirtyFailed)
436 }
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100437
438 fn set_dbm_enabled(enabled: bool) {
439 if dbm_available() {
440 let mut tcr = read_sysreg!("tcr_el1");
441 if enabled {
442 tcr |= Self::TCR_EL1_HA_HD_BITS
443 } else {
444 tcr &= !Self::TCR_EL1_HA_HD_BITS
445 };
446 // Safe because it writes to a system register and does not affect Rust.
447 unsafe { write_sysreg!("tcr_el1", tcr) }
448 isb!();
449 }
450 }
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000451}
452
453impl Drop for MemoryTracker {
454 fn drop(&mut self) {
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100455 Self::set_dbm_enabled(false);
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100456 self.flush_dirty_pages().unwrap();
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100457 self.unshare_all_memory();
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000458 }
459}
Andrew Walbran19690632022-12-07 16:41:30 +0000460
Andrew Walbran2b0c7fb2023-05-09 12:16:20 +0000461/// Allocates a memory range of at least the given size and alignment that is shared with the host.
462/// Returns a pointer to the buffer.
Pierre-Clément Tosi2d5bc582023-05-03 11:23:11 +0000463pub fn alloc_shared(layout: Layout) -> hyp::Result<NonNull<u8>> {
464 assert_ne!(layout.size(), 0);
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000465 let Some(buffer) = try_shared_alloc(layout) else {
Andrew Walbran848decf2022-12-15 14:39:38 +0000466 handle_alloc_error(layout);
467 };
468
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000469 trace!("Allocated shared buffer at {buffer:?} with {layout:?}");
Andrew Walbran848decf2022-12-15 14:39:38 +0000470 Ok(buffer)
471}
472
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000473fn try_shared_alloc(layout: Layout) -> Option<NonNull<u8>> {
474 let mut shared_pool = SHARED_POOL.get().unwrap().lock();
475
Andrew Walbran87933f32023-05-09 15:29:06 +0000476 if let Some(buffer) = shared_pool.alloc_aligned(layout) {
477 Some(NonNull::new(buffer as _).unwrap())
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000478 } else if let Some(shared_memory) = SHARED_MEMORY.lock().as_mut() {
479 shared_memory.refill(&mut shared_pool, layout);
Andrew Walbran87933f32023-05-09 15:29:06 +0000480 shared_pool.alloc_aligned(layout).map(|buffer| NonNull::new(buffer as _).unwrap())
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000481 } else {
482 None
483 }
484}
485
Andrew Walbran848decf2022-12-15 14:39:38 +0000486/// Unshares and deallocates a memory range which was previously allocated by `alloc_shared`.
487///
Andrew Walbran2b0c7fb2023-05-09 12:16:20 +0000488/// The layout passed in must be the same layout passed to the original `alloc_shared` call.
Andrew Walbran848decf2022-12-15 14:39:38 +0000489///
490/// # Safety
491///
Andrew Walbran2b0c7fb2023-05-09 12:16:20 +0000492/// The memory must have been allocated by `alloc_shared` with the same layout, and not yet
Andrew Walbran848decf2022-12-15 14:39:38 +0000493/// deallocated.
Pierre-Clément Tosi2d5bc582023-05-03 11:23:11 +0000494pub unsafe fn dealloc_shared(vaddr: NonNull<u8>, layout: Layout) -> hyp::Result<()> {
Andrew Walbran87933f32023-05-09 15:29:06 +0000495 SHARED_POOL.get().unwrap().lock().dealloc_aligned(vaddr.as_ptr() as usize, layout);
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700496
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000497 trace!("Deallocated shared buffer at {vaddr:?} with {layout:?}");
Andrew Walbran848decf2022-12-15 14:39:38 +0000498 Ok(())
499}
500
Andrew Walbran848decf2022-12-15 14:39:38 +0000501/// Returns the intermediate physical address corresponding to the given virtual address.
502///
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000503/// As we use identity mapping for everything, this is just a cast, but it's useful to use it to be
504/// explicit about where we are converting from virtual to physical address.
505pub fn virt_to_phys(vaddr: NonNull<u8>) -> usize {
506 vaddr.as_ptr() as _
507}
508
509/// Returns a pointer for the virtual address corresponding to the given non-zero intermediate
510/// physical address.
511///
512/// Panics if `paddr` is 0.
513pub fn phys_to_virt(paddr: usize) -> NonNull<u8> {
514 NonNull::new(paddr as _).unwrap()
Andrew Walbran848decf2022-12-15 14:39:38 +0000515}
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100516
517/// Checks whether a PTE at given level is a page or block descriptor.
518#[inline]
519fn is_leaf_pte(flags: &Attributes, level: usize) -> bool {
520 const LEAF_PTE_LEVEL: usize = 3;
521 if flags.contains(Attributes::TABLE_OR_PAGE) {
522 level == LEAF_PTE_LEVEL
523 } else {
524 level < LEAF_PTE_LEVEL
525 }
526}
527
528/// Checks whether block flags indicate it should be MMIO guard mapped.
529fn verify_lazy_mapped_block(
530 _range: &VaRange,
531 desc: &mut Descriptor,
532 level: usize,
533) -> result::Result<(), ()> {
534 let flags = desc.flags().expect("Unsupported PTE flags set");
535 if !is_leaf_pte(&flags, level) {
536 return Ok(()); // Skip table PTEs as they aren't tagged with MMIO_LAZY_MAP_FLAG.
537 }
538 if flags.contains(mmu::MMIO_LAZY_MAP_FLAG) && !flags.contains(Attributes::VALID) {
539 Ok(())
540 } else {
541 Err(())
542 }
543}
544
545/// MMIO guard unmaps page
546fn mmio_guard_unmap_page(
547 va_range: &VaRange,
548 desc: &mut Descriptor,
549 level: usize,
550) -> result::Result<(), ()> {
551 let flags = desc.flags().expect("Unsupported PTE flags set");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100552 if !is_leaf_pte(&flags, level) {
553 return Ok(());
554 }
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100555 // This function will be called on an address range that corresponds to a device. Only if a
556 // page has been accessed (written to or read from), will it contain the VALID flag and be MMIO
557 // guard mapped. Therefore, we can skip unmapping invalid pages, they were never MMIO guard
558 // mapped anyway.
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100559 if flags.contains(Attributes::VALID) {
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100560 assert!(
561 flags.contains(mmu::MMIO_LAZY_MAP_FLAG),
562 "Attempting MMIO guard unmap for non-device pages"
563 );
564 assert_eq!(
565 va_range.len(),
566 PVMFW_PAGE_SIZE,
567 "Failed to break down block mapping before MMIO guard mapping"
568 );
569 let page_base = va_range.start().0;
570 assert_eq!(page_base % PVMFW_PAGE_SIZE, 0);
571 // Since mmio_guard_map takes IPAs, if pvmfw moves non-ID address mapping, page_base
572 // should be converted to IPA. However, since 0x0 is a valid MMIO address, we don't use
573 // virt_to_phys here, and just pass page_base instead.
574 get_hypervisor().mmio_guard_unmap(page_base).map_err(|e| {
575 error!("Error MMIO guard unmapping: {e}");
576 })?;
577 }
578 Ok(())
579}
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100580
581/// Flushes a memory range the descriptor refers to, if the descriptor is in writable-dirty state.
582fn flush_dirty_range(
583 va_range: &VaRange,
584 desc: &mut Descriptor,
585 level: usize,
586) -> result::Result<(), ()> {
587 // Only flush ranges corresponding to dirty leaf PTEs.
588 let flags = desc.flags().ok_or(())?;
589 if !is_leaf_pte(&flags, level) {
590 return Ok(());
591 }
592 if !flags.contains(Attributes::READ_ONLY) {
593 helpers::flush_region(va_range.start().0, va_range.len());
594 }
595 Ok(())
596}
597
598/// Clears read-only flag on a PTE, making it writable-dirty. Used when dirty state is managed
599/// in software to handle permission faults on read-only descriptors.
600fn mark_dirty_block(
601 va_range: &VaRange,
602 desc: &mut Descriptor,
603 level: usize,
604) -> result::Result<(), ()> {
605 let flags = desc.flags().ok_or(())?;
606 if !is_leaf_pte(&flags, level) {
607 return Ok(());
608 }
609 if flags.contains(Attributes::DBM) {
610 assert!(flags.contains(Attributes::READ_ONLY), "unexpected PTE writable state");
611 desc.modify_flags(Attributes::empty(), Attributes::READ_ONLY);
612 // Updating the read-only bit of a PTE requires TLB invalidation.
613 // A TLB maintenance instruction is only guaranteed to be complete after a DSB instruction.
614 // An ISB instruction is required to ensure the effects of completed TLB maintenance
615 // instructions are visible to instructions fetched afterwards.
616 // See ARM ARM E2.3.10, and G5.9.
617 tlbi!("vale1", mmu::PageTable::ASID, va_range.start().0);
618 dsb!("ish");
619 isb!();
620 Ok(())
621 } else {
622 Err(())
623 }
624}