blob: ca0b886a9b4be2c6dfb43502d8bf681384939183 [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;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000022use core::num::NonZeroUsize;
23use core::ops::Range;
24use core::result;
25use log::error;
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000026use tinyvec::ArrayVec;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000027
28type MemoryRange = Range<usize>;
29
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000030#[derive(Clone, Copy, Debug, Default)]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000031enum MemoryType {
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000032 #[default]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000033 ReadOnly,
34 ReadWrite,
35}
36
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000037#[derive(Clone, Debug, Default)]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000038struct 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 {
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +000065 regions: ArrayVec<[MemoryRegion; MemoryTracker::CAPACITY]>,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000066 total: MemoryRange,
67 page_table: mmu::PageTable,
68}
69
70/// Errors for MemoryTracker operations.
71#[derive(Debug, Clone)]
72pub enum MemoryTrackerError {
73 /// Tried to modify the memory base address.
74 DifferentBaseAddress,
75 /// Tried to shrink to a larger memory size.
76 SizeTooLarge,
77 /// Tracked regions would not fit in memory size.
78 SizeTooSmall,
79 /// Reached limit number of tracked regions.
80 Full,
81 /// Region is out of the tracked memory address space.
82 OutOfRange,
83 /// New region overlaps with tracked regions.
84 Overlaps,
85 /// Region couldn't be mapped.
86 FailedToMap,
87}
88
89impl fmt::Display for MemoryTrackerError {
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 match self {
92 Self::DifferentBaseAddress => write!(f, "Received different base address"),
93 Self::SizeTooLarge => write!(f, "Tried to shrink to a larger memory size"),
94 Self::SizeTooSmall => write!(f, "Tracked regions would not fit in memory size"),
95 Self::Full => write!(f, "Reached limit number of tracked regions"),
96 Self::OutOfRange => write!(f, "Region is out of the tracked memory address space"),
97 Self::Overlaps => write!(f, "New region overlaps with tracked regions"),
98 Self::FailedToMap => write!(f, "Failed to map the new region"),
99 }
100 }
101}
102
103type Result<T> = result::Result<T, MemoryTrackerError>;
104
105impl MemoryTracker {
106 const CAPACITY: usize = 5;
107 /// Base of the system's contiguous "main" memory.
108 const BASE: usize = 0x8000_0000;
109 /// First address that can't be translated by a level 1 TTBR0_EL1.
110 const MAX_ADDR: usize = 1 << 39;
111
112 /// Create a new instance from an active page table, covering the maximum RAM size.
113 pub fn new(page_table: mmu::PageTable) -> Self {
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000114 Self { total: Self::BASE..Self::MAX_ADDR, page_table, regions: ArrayVec::new() }
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000115 }
116
117 /// Resize the total RAM size.
118 ///
119 /// This function fails if it contains regions that are not included within the new size.
120 pub fn shrink(&mut self, range: &MemoryRange) -> Result<()> {
121 if range.start != self.total.start {
122 return Err(MemoryTrackerError::DifferentBaseAddress);
123 }
124 if self.total.end < range.end {
125 return Err(MemoryTrackerError::SizeTooLarge);
126 }
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000127 if !self.regions.iter().all(|r| r.is_within(range)) {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000128 return Err(MemoryTrackerError::SizeTooSmall);
129 }
130
131 self.total = range.clone();
132 Ok(())
133 }
134
135 /// Allocate the address range for a const slice; returns None if failed.
136 pub fn alloc_range(&mut self, range: &MemoryRange) -> Result<MemoryRange> {
137 self.page_table.map_rodata(range).map_err(|e| {
138 error!("Error during range allocation: {e}");
139 MemoryTrackerError::FailedToMap
140 })?;
141 self.add(MemoryRegion { range: range.clone(), mem_type: MemoryType::ReadOnly })
142 }
143
144 /// Allocate the address range for a mutable slice; returns None if failed.
145 pub fn alloc_range_mut(&mut self, range: &MemoryRange) -> Result<MemoryRange> {
146 self.page_table.map_data(range).map_err(|e| {
147 error!("Error during mutable range allocation: {e}");
148 MemoryTrackerError::FailedToMap
149 })?;
150 self.add(MemoryRegion { range: range.clone(), mem_type: MemoryType::ReadWrite })
151 }
152
153 /// Allocate the address range for a const slice; returns None if failed.
154 pub fn alloc(&mut self, base: usize, size: NonZeroUsize) -> Result<MemoryRange> {
155 self.alloc_range(&(base..(base + size.get())))
156 }
157
158 /// Allocate the address range for a mutable slice; returns None if failed.
159 pub fn alloc_mut(&mut self, base: usize, size: NonZeroUsize) -> Result<MemoryRange> {
160 self.alloc_range_mut(&(base..(base + size.get())))
161 }
162
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000163 fn add(&mut self, region: MemoryRegion) -> Result<MemoryRange> {
164 if !region.is_within(&self.total) {
165 return Err(MemoryTrackerError::OutOfRange);
166 }
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000167 if self.regions.iter().any(|r| r.overlaps(region.as_ref())) {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000168 return Err(MemoryTrackerError::Overlaps);
169 }
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000170 if self.regions.try_push(region).is_some() {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000171 return Err(MemoryTrackerError::Full);
172 }
173
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000174 Ok(self.regions.last().unwrap().as_ref().clone())
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000175 }
176}
177
178impl Drop for MemoryTracker {
179 fn drop(&mut self) {
Pierre-Clément Tosi328dfb62022-11-25 18:20:42 +0000180 for region in self.regions.iter() {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000181 match region.mem_type {
182 MemoryType::ReadWrite => {
183 // TODO: Use page table's dirty bit to only flush pages that were touched.
184 helpers::flush_region(region.range.start, region.range.len())
185 }
186 MemoryType::ReadOnly => {}
187 }
188 }
189 }
190}