blob: 0f1892deb2054017dc4b382eb1d262fb3f2013e9 [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
17use crate::helpers;
18use crate::mmu;
19use core::cmp::max;
20use core::cmp::min;
21use core::fmt;
22use core::mem;
23use core::mem::MaybeUninit;
24use core::num::NonZeroUsize;
25use core::ops::Range;
26use core::result;
27use log::error;
28
29type MemoryRange = Range<usize>;
30
31#[derive(Clone, Copy, Debug)]
32enum MemoryType {
33 ReadOnly,
34 ReadWrite,
35}
36
37#[derive(Clone, Debug)]
38struct MemoryRegion {
39 range: MemoryRange,
40 mem_type: MemoryType,
41}
42
43impl MemoryRegion {
44 /// True if the instance overlaps with the passed range.
45 pub fn overlaps(&self, range: &MemoryRange) -> bool {
46 let our: &MemoryRange = self.as_ref();
47 max(our.start, range.start) < min(our.end, range.end)
48 }
49
50 /// True if the instance is fully contained within the passed range.
51 pub fn is_within(&self, range: &MemoryRange) -> bool {
52 let our: &MemoryRange = self.as_ref();
53 self.as_ref() == &(max(our.start, range.start)..min(our.end, range.end))
54 }
55}
56
57impl AsRef<MemoryRange> for MemoryRegion {
58 fn as_ref(&self) -> &MemoryRange {
59 &self.range
60 }
61}
62
63/// Tracks non-overlapping slices of main memory.
64pub struct MemoryTracker {
65 // TODO: Use tinyvec::ArrayVec
66 count: usize,
67 regions: [MaybeUninit<MemoryRegion>; MemoryTracker::CAPACITY],
68 total: MemoryRange,
69 page_table: mmu::PageTable,
70}
71
72/// Errors for MemoryTracker operations.
73#[derive(Debug, Clone)]
74pub enum MemoryTrackerError {
75 /// Tried to modify the memory base address.
76 DifferentBaseAddress,
77 /// Tried to shrink to a larger memory size.
78 SizeTooLarge,
79 /// Tracked regions would not fit in memory size.
80 SizeTooSmall,
81 /// Reached limit number of tracked regions.
82 Full,
83 /// Region is out of the tracked memory address space.
84 OutOfRange,
85 /// New region overlaps with tracked regions.
86 Overlaps,
87 /// Region couldn't be mapped.
88 FailedToMap,
89}
90
91impl fmt::Display for MemoryTrackerError {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 match self {
94 Self::DifferentBaseAddress => write!(f, "Received different base address"),
95 Self::SizeTooLarge => write!(f, "Tried to shrink to a larger memory size"),
96 Self::SizeTooSmall => write!(f, "Tracked regions would not fit in memory size"),
97 Self::Full => write!(f, "Reached limit number of tracked regions"),
98 Self::OutOfRange => write!(f, "Region is out of the tracked memory address space"),
99 Self::Overlaps => write!(f, "New region overlaps with tracked regions"),
100 Self::FailedToMap => write!(f, "Failed to map the new region"),
101 }
102 }
103}
104
105type Result<T> = result::Result<T, MemoryTrackerError>;
106
107impl MemoryTracker {
108 const CAPACITY: usize = 5;
109 /// Base of the system's contiguous "main" memory.
110 const BASE: usize = 0x8000_0000;
111 /// First address that can't be translated by a level 1 TTBR0_EL1.
112 const MAX_ADDR: usize = 1 << 39;
113
114 /// Create a new instance from an active page table, covering the maximum RAM size.
115 pub fn new(page_table: mmu::PageTable) -> Self {
116 Self {
117 total: Self::BASE..Self::MAX_ADDR,
118 count: 0,
119 page_table,
120 // SAFETY - MaybeUninit items (of regions) do not require initialization.
121 regions: unsafe { MaybeUninit::uninit().assume_init() },
122 }
123 }
124
125 /// Resize the total RAM size.
126 ///
127 /// This function fails if it contains regions that are not included within the new size.
128 pub fn shrink(&mut self, range: &MemoryRange) -> Result<()> {
129 if range.start != self.total.start {
130 return Err(MemoryTrackerError::DifferentBaseAddress);
131 }
132 if self.total.end < range.end {
133 return Err(MemoryTrackerError::SizeTooLarge);
134 }
135 if !self.regions().iter().all(|r| r.is_within(range)) {
136 return Err(MemoryTrackerError::SizeTooSmall);
137 }
138
139 self.total = range.clone();
140 Ok(())
141 }
142
143 /// Allocate the address range for a const slice; returns None if failed.
144 pub fn alloc_range(&mut self, range: &MemoryRange) -> Result<MemoryRange> {
145 self.page_table.map_rodata(range).map_err(|e| {
146 error!("Error during range allocation: {e}");
147 MemoryTrackerError::FailedToMap
148 })?;
149 self.add(MemoryRegion { range: range.clone(), mem_type: MemoryType::ReadOnly })
150 }
151
152 /// Allocate the address range for a mutable slice; returns None if failed.
153 pub fn alloc_range_mut(&mut self, range: &MemoryRange) -> Result<MemoryRange> {
154 self.page_table.map_data(range).map_err(|e| {
155 error!("Error during mutable range allocation: {e}");
156 MemoryTrackerError::FailedToMap
157 })?;
158 self.add(MemoryRegion { range: range.clone(), mem_type: MemoryType::ReadWrite })
159 }
160
161 /// Allocate the address range for a const slice; returns None if failed.
162 pub fn alloc(&mut self, base: usize, size: NonZeroUsize) -> Result<MemoryRange> {
163 self.alloc_range(&(base..(base + size.get())))
164 }
165
166 /// Allocate the address range for a mutable slice; returns None if failed.
167 pub fn alloc_mut(&mut self, base: usize, size: NonZeroUsize) -> Result<MemoryRange> {
168 self.alloc_range_mut(&(base..(base + size.get())))
169 }
170
171 fn regions(&self) -> &[MemoryRegion] {
172 // SAFETY - The first self.count regions have been properly initialized.
173 unsafe { mem::transmute::<_, &[MemoryRegion]>(&self.regions[..self.count]) }
174 }
175
176 fn add(&mut self, region: MemoryRegion) -> Result<MemoryRange> {
177 if !region.is_within(&self.total) {
178 return Err(MemoryTrackerError::OutOfRange);
179 }
180 if self.regions().iter().any(|r| r.overlaps(region.as_ref())) {
181 return Err(MemoryTrackerError::Overlaps);
182 }
183 if self.regions.len() == self.count {
184 return Err(MemoryTrackerError::Full);
185 }
186
187 let region = self.regions[self.count].write(region);
188 self.count += 1;
189 Ok(region.as_ref().clone())
190 }
191}
192
193impl Drop for MemoryTracker {
194 fn drop(&mut self) {
195 for region in self.regions().iter() {
196 match region.mem_type {
197 MemoryType::ReadWrite => {
198 // TODO: Use page table's dirty bit to only flush pages that were touched.
199 helpers::flush_region(region.range.start, region.range.len())
200 }
201 MemoryType::ReadOnly => {}
202 }
203 }
204 }
205}