Skip to main content

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