blob: 0a2444f6ab086d8c3a864c3ff4067c1a4ae12ff9 [file] [log] [blame]
Alice Wangf47b2342023-06-02 11:51:57 +00001// Copyright 2023, 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//! Shared memory management.
16
17use super::util::virt_to_phys;
18use alloc::alloc::{alloc_zeroed, dealloc, handle_alloc_error};
19use alloc::vec::Vec;
20use buddy_system_allocator::FrameAllocator;
21use core::alloc::Layout;
22use core::ptr::NonNull;
23use hyp::get_hypervisor;
24use log::trace;
25
26/// Allocates memory on the heap and shares it with the host.
27///
28/// Unshares all pages when dropped.
29pub struct MemorySharer {
30 granule: usize,
31 shared_regions: Vec<(usize, Layout)>,
32}
33
34impl MemorySharer {
35 /// Constructs a new `MemorySharer` instance with the specified granule size and capacity.
36 /// `granule` must be a power of 2.
37 pub fn new(granule: usize, capacity: usize) -> Self {
38 assert!(granule.is_power_of_two());
39 Self { granule, shared_regions: Vec::with_capacity(capacity) }
40 }
41
42 /// Get from the global allocator a granule-aligned region that suits `hint` and share it.
43 pub fn refill(&mut self, pool: &mut FrameAllocator<32>, hint: Layout) {
44 let layout = hint.align_to(self.granule).unwrap().pad_to_align();
45 assert_ne!(layout.size(), 0);
46 // SAFETY - layout has non-zero size.
47 let Some(shared) = NonNull::new(unsafe { alloc_zeroed(layout) }) else {
48 handle_alloc_error(layout);
49 };
50
51 let base = shared.as_ptr() as usize;
52 let end = base.checked_add(layout.size()).unwrap();
53 trace!("Sharing memory region {:#x?}", base..end);
54 for vaddr in (base..end).step_by(self.granule) {
55 let vaddr = NonNull::new(vaddr as *mut _).unwrap();
56 get_hypervisor().mem_share(virt_to_phys(vaddr).try_into().unwrap()).unwrap();
57 }
58 self.shared_regions.push((base, layout));
59
60 pool.add_frame(base, end);
61 }
62}
63
64impl Drop for MemorySharer {
65 fn drop(&mut self) {
66 while let Some((base, layout)) = self.shared_regions.pop() {
67 let end = base.checked_add(layout.size()).unwrap();
68 trace!("Unsharing memory region {:#x?}", base..end);
69 for vaddr in (base..end).step_by(self.granule) {
70 let vaddr = NonNull::new(vaddr as *mut _).unwrap();
71 get_hypervisor().mem_unshare(virt_to_phys(vaddr).try_into().unwrap()).unwrap();
72 }
73
74 // SAFETY - The region was obtained from alloc_zeroed() with the recorded layout.
75 unsafe { dealloc(base as *mut _, layout) };
76 }
77 }
78}