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