ostd/mm/frame/allocator.rs
1// SPDX-License-Identifier: MPL-2.0
2//! The physical memory allocator.
3use vstd::prelude::*;
4use vstd_extra::{cast_ptr::Repr, prelude::*};
5
6use crate::specs::mm::frame::meta_owners::MetaSlotStorage;
7
8use super::{Frame, meta::AnyFrameMeta, segment::Segment};
9use crate::{
10 boot::memory_region::MemoryRegionType,
11 error::Error,
12 // impl_frame_meta_for,
13 mm::{PAGE_SIZE, Paddr, paddr_to_vaddr},
14 // util::ops::range_difference,
15};
16use align_ext::AlignExt;
17use core::{alloc::Layout, ops::Range};
18
19verus! {
20
21/// Options for allocating physical memory frames.
22pub struct FrameAllocOptions {
23 zeroed: bool,
24}
25
26impl Default for FrameAllocOptions {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl FrameAllocOptions {
33 /// Creates new options for allocating the specified number of frames.
34 pub fn new() -> Self {
35 Self { zeroed: true }
36 }
37
38 /// Sets whether the allocated frames should be initialized with zeros.
39 ///
40 /// If `zeroed` is `true`, the allocated frames are filled with zeros.
41 /// If not, the allocated frames will contain sensitive data and the caller
42 /// should clear them before sharing them with other components.
43 ///
44 /// By default, the frames are zero-initialized.
45 pub fn zeroed(&mut self, zeroed: bool) {
46 self.zeroed = zeroed
47 }
48
49 /* /// Allocates a single untyped frame without metadata.
50 pub fn alloc_frame(&self) -> Result<Frame<()>, Error> {
51 self.alloc_frame_with(())
52 }*/
53 /// Allocates a single frame with additional metadata.
54 #[verifier::external_body]
55 pub fn alloc_frame_with<M: AnyFrameMeta + Repr<MetaSlotStorage>>(&self, metadata: M) -> Result<
56 Frame<M>,
57 Error,
58 > {
59 unimplemented!()/*
60 let single_layout = Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).unwrap();
61 let frame = get_global_frame_allocator()
62 .alloc(single_layout)
63 .map(|paddr| Frame::from_unused(paddr, metadata).unwrap())
64 .ok_or(Error::NoMemory)?;
65
66 if self.zeroed {
67 let addr = paddr_to_vaddr(frame.start_paddr()) as *mut u8;
68 // SAFETY: The newly allocated frame is guaranteed to be valid.
69 unsafe { core::ptr::write_bytes(addr, 0, PAGE_SIZE) }
70 }
71
72 Ok(frame)
73 */
74
75 }/*
76 /// Allocates a contiguous range of untyped frames without metadata.
77 pub fn alloc_segment(&self, nframes: usize) -> Result<Segment<()>> {
78 self.alloc_segment_with(nframes, |_| ())
79 }*/
80 /*
81 /// Allocates a contiguous range of frames with additional metadata.
82 ///
83 /// The returned [`Segment`] contains at least one frame. The method returns
84 /// an error if the number of frames is zero.
85 pub fn alloc_segment_with<M: AnyFrameMeta, F>(
86 &self,
87 nframes: usize,
88 metadata_fn: F,
89 ) -> Result<Segment<M>>
90 where
91 F: FnMut(Paddr) -> M,
92 {
93 if nframes == 0 {
94 return Err(Error::InvalidArgs);
95 }
96 let layout = Layout::from_size_align(nframes * PAGE_SIZE, PAGE_SIZE).unwrap();
97 let segment = get_global_frame_allocator()
98 .alloc(layout)
99 .map(|start| {
100 Segment::from_unused(start..start + nframes * PAGE_SIZE, metadata_fn).unwrap()
101 })
102 .ok_or(Error::NoMemory)?;
103
104 if self.zeroed {
105 let addr = paddr_to_vaddr(segment.start_paddr()) as *mut u8;
106 // SAFETY: The newly allocated segment is guaranteed to be valid.
107 unsafe { core::ptr::write_bytes(addr, 0, nframes * PAGE_SIZE) }
108 }
109
110 Ok(segment)
111 }*/
112
113}
114
115} // verus!
116/*
117#[cfg(ktest)]
118#[ktest]
119fn test_alloc_dealloc() {
120 // Here we allocate and deallocate frames in random orders to test the allocator.
121 // We expect the test to fail if the underlying implementation panics.
122 let single_options = FrameAllocOptions::new();
123 let mut contiguous_options = FrameAllocOptions::new();
124 contiguous_options.zeroed(false);
125 let mut remember_vec = Vec::new();
126 for _ in 0..10 {
127 for i in 0..10 {
128 let single_frame = single_options.alloc_frame().unwrap();
129 if i % 3 == 0 {
130 remember_vec.push(single_frame);
131 }
132 }
133 let contiguous_segment = contiguous_options.alloc_segment(10).unwrap();
134 drop(contiguous_segment);
135 remember_vec.pop();
136 }
137}
138
139/// The trait for the global frame allocator.
140///
141/// OSTD allows a customized frame allocator by the [`global_frame_allocator`]
142/// attribute, which marks a static variable of this type.
143///
144/// The API mimics the standard Rust allocator API ([`GlobalAlloc`] and
145/// [`global_allocator`]). However, this trait is much safer. Double free
146/// or freeing in-use memory through this trait only messes up the allocator's
147/// state rather than causing undefined behavior.
148///
149/// Whenever OSTD or other modules need to allocate or deallocate frames via
150/// [`FrameAllocOptions`], they are forwarded to the global frame allocator.
151/// It is not encouraged to call the global allocator directly.
152///
153/// [`global_frame_allocator`]: crate::global_frame_allocator
154/// [`GlobalAlloc`]: core::alloc::GlobalAlloc
155pub trait GlobalFrameAllocator: Sync {
156 /// Allocates a contiguous range of frames.
157 ///
158 /// The caller guarantees that `layout.size()` is aligned to [`PAGE_SIZE`].
159 ///
160 /// When any of the allocated memory is not in use, OSTD returns them by
161 /// calling [`GlobalFrameAllocator::dealloc`]. If multiple frames are
162 /// allocated, they may be returned in any order with any number of calls.
163 fn alloc(&self, layout: Layout) -> Option<Paddr>;
164
165 /// Deallocates a contiguous range of frames.
166 ///
167 /// The caller guarantees that `addr` and `size` are both aligned to
168 /// [`PAGE_SIZE`]. The deallocated memory should always be allocated by
169 /// [`GlobalFrameAllocator::alloc`]. However, if
170 /// [`GlobalFrameAllocator::alloc`] returns multiple frames, it is possible
171 /// that some of them are deallocated before others. The deallocated memory
172 /// must never overlap with any memory that is already deallocated or
173 /// added, without being allocated in between.
174 ///
175 /// The deallocated memory can be uninitialized.
176 fn dealloc(&self, addr: Paddr, size: usize);
177
178 /// Adds a contiguous range of frames to the allocator.
179 ///
180 /// The memory being added must never overlap with any memory that was
181 /// added before.
182 ///
183 /// The added memory can be uninitialized.
184 fn add_free_memory(&self, addr: Paddr, size: usize);
185}
186
187extern "Rust" {
188 /// The global frame allocator's reference exported by
189 /// [`crate::global_frame_allocator`].
190 static __GLOBAL_FRAME_ALLOCATOR_REF: &'static dyn GlobalFrameAllocator;
191}
192
193pub(super) fn get_global_frame_allocator() -> &'static dyn GlobalFrameAllocator {
194 // SAFETY: The global frame allocator is set up correctly with the
195 // `global_frame_allocator` attribute. If they use safe code only, the
196 // up-call is safe.
197 unsafe { __GLOBAL_FRAME_ALLOCATOR_REF }
198}
199
200/// Initializes the global frame allocator.
201///
202/// It just does adds the frames to the global frame allocator. Calling it
203/// multiple times would be not safe.
204///
205/// # Safety
206///
207/// This function should be called only once.
208pub(crate) unsafe fn init() {
209 let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
210
211 // Retire the early allocator.
212 let early_allocator = EARLY_ALLOCATOR.lock().take().unwrap();
213 let (range_1, range_2) = early_allocator.allocated_regions();
214
215 for region in regions.iter() {
216 if region.typ() == MemoryRegionType::Usable {
217 debug_assert!(region.base() % PAGE_SIZE == 0);
218 debug_assert!(region.len() % PAGE_SIZE == 0);
219
220 // Add global free pages to the frame allocator.
221 // Truncate the early allocated frames if there is an overlap.
222 for r1 in range_difference(&(region.base()..region.end()), &range_1) {
223 for r2 in range_difference(&r1, &range_2) {
224 log::info!("Adding free frames to the allocator: {:x?}", r2);
225 get_global_frame_allocator().add_free_memory(r2.start, r2.len());
226 }
227 }
228 }
229 }
230}
231
232/// An allocator in the early boot phase when frame metadata is not available.
233pub(super) struct EarlyFrameAllocator {
234 // We need to allocate from under 4G first since the linear mapping for
235 // the higher region is not constructed yet.
236 under_4g_range: Range<Paddr>,
237 under_4g_end: Paddr,
238
239 // And also sometimes 4G is not enough for early phase. This, if not `0..0`,
240 // is the largest region above 4G.
241 max_range: Range<Paddr>,
242 max_end: Paddr,
243}
244
245/// The global frame allocator in the early boot phase.
246///
247/// It is used to allocate frames before the frame metadata is initialized.
248/// The allocated frames are not tracked by the frame metadata. After the
249/// metadata is initialized with [`super::meta::init`], the frames are tracked
250/// with metadata and the early allocator is no longer used.
251///
252/// This is protected by the [`spin::Mutex`] rather than [`crate::sync::SpinLock`]
253/// since the latter uses CPU-local storage, which isn't available in the early
254/// boot phase. So we must make sure that no interrupts are enabled when using
255/// this allocator.
256pub(super) static EARLY_ALLOCATOR: spin::Mutex<Option<EarlyFrameAllocator>> =
257 spin::Mutex::new(None);
258
259impl EarlyFrameAllocator {
260 /// Creates a new early frame allocator.
261 ///
262 /// It uses at most 2 regions, the first is the maximum usable region below
263 /// 4 GiB. The other is the maximum usable region above 4 GiB and is only
264 /// usable when linear mapping is constructed.
265 pub fn new() -> Self {
266 let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
267
268 let mut under_4g_range = 0..0;
269 let mut max_range = 0..0;
270 for region in regions.iter() {
271 if region.typ() != MemoryRegionType::Usable {
272 continue;
273 }
274 const PADDR4G: Paddr = 0x1_0000_0000;
275 if region.base() < PADDR4G {
276 let range = region.base()..region.end().min(PADDR4G);
277 if range.len() > under_4g_range.len() {
278 under_4g_range = range;
279 }
280 }
281 if region.end() >= PADDR4G {
282 let range = region.base().max(PADDR4G)..region.end();
283 if range.len() > max_range.len() {
284 max_range = range;
285 }
286 }
287 }
288
289 log::debug!(
290 "Early frame allocator (below 4G) at: {:#x?}",
291 under_4g_range
292 );
293 if !max_range.is_empty() {
294 log::debug!("Early frame allocator (above 4G) at: {:#x?}", max_range);
295 }
296
297 Self {
298 under_4g_range: under_4g_range.clone(),
299 under_4g_end: under_4g_range.start,
300 max_range: max_range.clone(),
301 max_end: max_range.start,
302 }
303 }
304
305 /// Allocates a contiguous range of frames.
306 pub fn alloc(&mut self, layout: Layout) -> Option<Paddr> {
307 let size = layout.size().align_up(PAGE_SIZE);
308 let align = layout.align().max(PAGE_SIZE);
309
310 for (tail, end) in [
311 (&mut self.under_4g_end, self.under_4g_range.end),
312 (&mut self.max_end, self.max_range.end),
313 ] {
314 let allocated = tail.align_up(align);
315 if let Some(allocated_end) = allocated.checked_add(size)
316 && allocated_end <= end
317 {
318 *tail = allocated_end;
319 return Some(allocated);
320 }
321 }
322
323 None
324 }
325
326 pub(super) fn allocated_regions(&self) -> (Range<Paddr>, Range<Paddr>) {
327 (
328 self.under_4g_range.start..self.under_4g_end,
329 self.max_range.start..self.max_end,
330 )
331 }
332}
333
334/// Metadata for frames allocated in the early boot phase.
335///
336/// Frames allocated with [`early_alloc`] are not immediately tracked with
337/// frame metadata. But [`super::meta::init`] will track them later.
338#[derive(Debug)]
339pub(crate) struct EarlyAllocatedFrameMeta;
340
341impl_frame_meta_for!(EarlyAllocatedFrameMeta);
342
343/// Allocates a contiguous range of frames in the early boot phase.
344///
345/// The early allocated frames will not be reclaimable, until the metadata is
346/// initialized by [`super::meta::init`]. Then we can use [`Frame::from_raw`]
347/// to free the frames.
348///
349/// # Panics
350///
351/// This function panics if:
352/// - it is called before [`init_early_allocator`],
353/// - or if is called after [`init`].
354pub(crate) fn early_alloc(layout: Layout) -> Option<Paddr> {
355 let mut early_allocator = EARLY_ALLOCATOR.lock();
356 early_allocator.as_mut().unwrap().alloc(layout)
357}
358
359/// Initializes the early frame allocator.
360///
361/// [`early_alloc`] should be used after this initialization. After [`init`], the
362/// early allocator.
363///
364/// # Safety
365///
366/// This function should be called only once after the memory regions are ready.
367pub(crate) unsafe fn init_early_allocator() {
368 let mut early_allocator = EARLY_ALLOCATOR.lock();
369 *early_allocator = Some(EarlyFrameAllocator::new());
370}
371*/