Skip to main content

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*/