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