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