Skip to main content

ostd/mm/frame/
meta.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Metadata management of frames.
4//!
5//! You can picture a globally shared, static, gigantic array of metadata
6//! initialized for each frame.
7//! Each entry in this array holds the metadata for a single frame.
8//! There would be a dedicated small
9//! "heap" space in each slot for dynamic metadata. You can store anything as
10//! the metadata of a frame as long as it's [`Sync`].
11//!
12//! # Implementation
13//!
14//! The slots are placed in the metadata pages mapped to a certain virtual
15//! address in the kernel space. So finding the metadata of a frame often
16//! comes with no costs since the translation is a simple arithmetic operation.
17
18pub(crate) mod mapping {
19    //! The metadata of each physical page is linear mapped to fixed virtual addresses
20    //! in [`FRAME_METADATA_RANGE`].
21
22    use super::MetaSlot;
23    use crate::mm::{PAGE_SIZE, Paddr, PagingConstsTrait, Vaddr, kspace::FRAME_METADATA_RANGE};
24
25    /// Converts a physical address of a base frame to the virtual address of the metadata slot.
26    pub(crate) const fn frame_to_meta<C: PagingConstsTrait>(paddr: Paddr) -> Vaddr {
27        let base = FRAME_METADATA_RANGE.start;
28        let offset = paddr / PAGE_SIZE;
29        base + offset * size_of::<MetaSlot>()
30    }
31
32    /// Converts a virtual address of the metadata slot to the physical address of the frame.
33    pub(crate) const fn meta_to_frame<C: PagingConstsTrait>(vaddr: Vaddr) -> Paddr {
34        let base = FRAME_METADATA_RANGE.start;
35        let offset = (vaddr - base) / size_of::<MetaSlot>();
36        offset * PAGE_SIZE
37    }
38}
39
40use core::{
41    alloc::Layout,
42    any::Any,
43    cell::UnsafeCell,
44    fmt::Debug,
45    mem::{ManuallyDrop, MaybeUninit},
46    sync::atomic::{AtomicU64, Ordering},
47};
48
49use crate::{
50    arch::mm::PagingConsts,
51    boot::memory_region::MemoryRegionType,
52    const_assert, info,
53    mm::{
54        CachePolicy, Infallible, PAGE_SIZE, Paddr, PageFlags, PageProperty, PrivilegedPageFlags,
55        Segment, Vaddr, VmReader,
56        frame::allocator::{self, EarlyAllocatedFrameMeta},
57        paddr_to_vaddr, page_size,
58        page_table::boot_pt,
59    },
60    panic::abort,
61    util::ops::range_difference,
62};
63
64/// The maximum number of bytes of the metadata of a frame.
65pub const FRAME_METADATA_MAX_SIZE: usize = META_SLOT_SIZE
66    - size_of::<AtomicU64>()
67    - size_of::<FrameMetaVtablePtr>()
68    - size_of::<AtomicU64>();
69/// The maximum alignment in bytes of the metadata of a frame.
70pub const FRAME_METADATA_MAX_ALIGN: usize = META_SLOT_SIZE;
71
72const META_SLOT_SIZE: usize = 64;
73
74#[repr(C)]
75pub(in crate::mm) struct MetaSlot {
76    /// The metadata of a frame.
77    ///
78    /// It is placed at the beginning of a slot because:
79    ///  - the implementation can simply cast a `*const MetaSlot`
80    ///    to a `*const AnyFrameMeta` for manipulation;
81    ///  - if the metadata need special alignment, we can provide
82    ///    at most [`FRAME_METADATA_MAX_ALIGN`] bytes of alignment;
83    ///  - the subsequent fields can utilize the padding of the
84    ///    reference count to save space.
85    ///
86    /// Don't interpret this field as an array of bytes. It is a
87    /// placeholder for the metadata of a frame.
88    storage: UnsafeCell<[u8; FRAME_METADATA_MAX_SIZE]>,
89    /// The reference count of the page.
90    ///
91    /// Specifically, the reference count has the following meaning:
92    ///  - `REF_COUNT_UNUSED`: The page is not in use.
93    ///  - `REF_COUNT_UNIQUE`: The page is owned by a [`UniqueFrame`].
94    ///  - `0`: The page is being constructed ([`Frame::from_unused`])
95    ///    or destructured ([`drop_last_in_place`]).
96    ///  - `1..REF_COUNT_MAX`: The page is in use.
97    ///  - `REF_COUNT_MAX..REF_COUNT_UNIQUE`: Illegal values to
98    ///    prevent the reference count from overflowing. Otherwise,
99    ///    overflowing the reference count will cause soundness issue.
100    ///
101    /// [`Frame::from_unused`]: super::Frame::from_unused
102    /// [`UniqueFrame`]: super::unique::UniqueFrame
103    /// [`drop_last_in_place`]: Self::drop_last_in_place
104    //
105    // Other than this field the fields should be `MaybeUninit`.
106    // See initialization in `alloc_meta_frames`.
107    pub(super) ref_count: AtomicU64,
108    /// The virtual table that indicates the type of the metadata.
109    pub(super) vtable_ptr: UnsafeCell<MaybeUninit<FrameMetaVtablePtr>>,
110    /// This is only accessed by [`crate::mm::frame::linked_list`].
111    /// It stores 0 if the frame is not in any list, otherwise it stores the
112    /// ID of the list.
113    ///
114    /// It is ugly but allows us to tell if a frame is in a specific list by
115    /// one relaxed read. Otherwise, if we store it conditionally in `storage`
116    /// we would have to ensure that the type is correct before the read, which
117    /// costs a synchronization.
118    pub(super) in_list: AtomicU64,
119}
120
121pub(super) const REF_COUNT_UNUSED: u64 = u64::MAX;
122pub(super) const REF_COUNT_UNIQUE: u64 = u64::MAX - 1;
123pub(super) const REF_COUNT_MAX: u64 = i64::MAX as u64;
124
125type FrameMetaVtablePtr = core::ptr::DynMetadata<dyn AnyFrameMeta>;
126
127const_assert!(PAGE_SIZE.is_multiple_of(META_SLOT_SIZE));
128const_assert!(size_of::<MetaSlot>() == META_SLOT_SIZE);
129
130/// All frame metadata types must implement this trait.
131///
132/// If a frame type needs specific drop behavior, it should specify
133/// when implementing this trait. When we drop the last handle to
134/// this frame, the `on_drop` method will be called. The `on_drop`
135/// method is called with the physical address of the frame.
136///
137/// The implemented structure should have a size less than or equal to
138/// [`FRAME_METADATA_MAX_SIZE`] and an alignment less than or equal to
139/// [`FRAME_METADATA_MAX_ALIGN`]. Otherwise, the metadata type cannot
140/// be used because storing it will fail compile-time assertions.
141///
142/// # Safety
143///
144/// If `on_drop` reads the page using the provided `VmReader`, the
145/// implementer must ensure that the frame is safe to read.
146pub unsafe trait AnyFrameMeta: Any + Send + Sync {
147    /// Called when the last handle to the frame is dropped.
148    fn on_drop(&mut self, _reader: &mut VmReader<Infallible>) {}
149
150    /// Whether the metadata's associated frame is untyped.
151    ///
152    /// If a type implements [`AnyUFrameMeta`], this should be `true`.
153    /// Otherwise, it should be `false`.
154    ///
155    /// [`AnyUFrameMeta`]: super::untyped::AnyUFrameMeta
156    fn is_untyped(&self) -> bool {
157        false
158    }
159}
160
161/// Checks that a frame metadata type has valid size and alignment.
162#[macro_export]
163macro_rules! check_frame_meta_layout {
164    ($t:ty) => {
165        $crate::const_assert!(size_of::<$t>() <= $crate::mm::frame::meta::FRAME_METADATA_MAX_SIZE);
166        $crate::const_assert!(
167            $crate::mm::frame::meta::FRAME_METADATA_MAX_ALIGN % align_of::<$t>() == 0
168        );
169    };
170}
171
172/// Makes a structure usable as a frame metadata.
173#[macro_export]
174macro_rules! impl_frame_meta_for {
175    // Implement without specifying the drop behavior.
176    ($t:ty) => {
177        // SAFETY: `on_drop` won't read the page.
178        unsafe impl $crate::mm::frame::meta::AnyFrameMeta for $t {}
179
180        $crate::check_frame_meta_layout!($t);
181    };
182}
183
184pub use impl_frame_meta_for;
185
186/// The error type for getting the frame from a physical address.
187#[derive(Debug)]
188pub enum GetFrameError {
189    /// The frame is in use.
190    InUse,
191    /// The frame is not in use.
192    Unused,
193    /// The frame is being initialized or destructed.
194    Busy,
195    /// The frame is private to an owner of [`UniqueFrame`].
196    ///
197    /// [`UniqueFrame`]: super::unique::UniqueFrame
198    Unique,
199    /// The provided physical address is out of bound.
200    OutOfBound,
201    /// The provided physical address is not aligned.
202    NotAligned,
203}
204
205/// Gets the reference to a metadata slot.
206pub(super) fn get_slot(paddr: Paddr) -> Result<&'static MetaSlot, GetFrameError> {
207    if !paddr.is_multiple_of(PAGE_SIZE) {
208        return Err(GetFrameError::NotAligned);
209    }
210    if paddr >= super::max_paddr() {
211        return Err(GetFrameError::OutOfBound);
212    }
213
214    let vaddr = mapping::frame_to_meta::<PagingConsts>(paddr);
215    let ptr = vaddr as *mut MetaSlot;
216
217    // SAFETY: `ptr` points to a valid `MetaSlot` that will never be
218    // mutably borrowed, so taking an immutable reference to it is safe.
219    Ok(unsafe { &*ptr })
220}
221
222impl MetaSlot {
223    /// Initializes the metadata slot of a frame assuming it is unused.
224    ///
225    /// If successful, the function returns a pointer to the metadata slot.
226    /// And the slot is initialized with the given metadata.
227    ///
228    /// The resulting reference count held by the returned pointer is
229    /// [`REF_COUNT_UNIQUE`] if `as_unique_ptr` is `true`, otherwise `1`.
230    pub(super) fn get_from_unused<M: AnyFrameMeta>(
231        paddr: Paddr,
232        metadata: M,
233        as_unique_ptr: bool,
234    ) -> Result<*const Self, GetFrameError> {
235        let slot = get_slot(paddr)?;
236
237        // `Acquire` pairs with the `Release` in `drop_last_in_place` and ensures the metadata
238        // initialization won't be reordered before this memory compare-and-exchange.
239        slot.ref_count
240            .compare_exchange(REF_COUNT_UNUSED, 0, Ordering::Acquire, Ordering::Relaxed)
241            .map_err(|val| match val {
242                REF_COUNT_UNIQUE => GetFrameError::Unique,
243                0 => GetFrameError::Busy,
244                _ => GetFrameError::InUse,
245            })?;
246
247        // SAFETY: The slot now has a reference count of `0`, other threads will
248        // not access the metadata slot so it is safe to have a mutable reference.
249        unsafe { slot.write_meta(metadata) };
250
251        if as_unique_ptr {
252            // No one can create a `Frame` instance directly from the page
253            // address, so `Relaxed` is fine here.
254            slot.ref_count.store(REF_COUNT_UNIQUE, Ordering::Relaxed);
255        } else {
256            // `Release` is used to ensure that the metadata initialization
257            // won't be reordered after this memory store.
258            slot.ref_count.store(1, Ordering::Release);
259        }
260
261        Ok(slot as *const MetaSlot)
262    }
263
264    /// Gets another owning pointer to the metadata slot from the given page.
265    pub(super) fn get_from_in_use(paddr: Paddr) -> Result<*const Self, GetFrameError> {
266        let slot = get_slot(paddr)?;
267
268        // Try to increase the reference count for an in-use frame. Otherwise fail.
269        loop {
270            match slot.ref_count.load(Ordering::Relaxed) {
271                REF_COUNT_UNUSED => return Err(GetFrameError::Unused),
272                REF_COUNT_UNIQUE => return Err(GetFrameError::Unique),
273                0 => return Err(GetFrameError::Busy),
274                last_ref_cnt => {
275                    if last_ref_cnt >= REF_COUNT_MAX {
276                        // See `Self::inc_ref_count` for the explanation.
277                        abort();
278                    }
279                    // Using `Acquire` here to pair with `get_from_unused` or
280                    // `<Frame<M> as From<UniqueFrame<M>>>::from` (who must be
281                    // performed after writing the metadata).
282                    //
283                    // It ensures that the written metadata will be visible to us.
284                    if slot
285                        .ref_count
286                        .compare_exchange_weak(
287                            last_ref_cnt,
288                            last_ref_cnt + 1,
289                            Ordering::Acquire,
290                            Ordering::Relaxed,
291                        )
292                        .is_ok()
293                    {
294                        return Ok(slot as *const MetaSlot);
295                    }
296                }
297            }
298            core::hint::spin_loop();
299        }
300    }
301
302    /// Increases the frame reference count by one.
303    ///
304    /// # Safety
305    ///
306    /// The caller must have already held a reference to the frame.
307    pub(super) unsafe fn inc_ref_count(&self) {
308        let last_ref_cnt = self.ref_count.fetch_add(1, Ordering::Relaxed);
309        debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED);
310
311        if last_ref_cnt >= REF_COUNT_MAX {
312            // This follows the same principle as the `Arc::clone` implementation to prevent the
313            // reference count from overflowing. See also
314            // <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.clone>.
315            abort();
316        }
317    }
318
319    /// Gets the corresponding frame's physical address.
320    pub(super) fn frame_paddr(&self) -> Paddr {
321        mapping::meta_to_frame::<PagingConsts>(self as *const MetaSlot as Vaddr)
322    }
323
324    /// Gets a dynamically typed pointer to the stored metadata.
325    ///
326    /// # Safety
327    ///
328    /// The caller should ensure that:
329    ///  - the stored metadata is initialized (by [`Self::write_meta`]) and valid.
330    ///
331    /// The returned pointer should not be dereferenced as mutable unless having
332    /// exclusive access to the metadata slot.
333    pub(super) unsafe fn dyn_meta_ptr(&self) -> *mut dyn AnyFrameMeta {
334        // SAFETY: The page metadata is valid to be borrowed immutably, since
335        // it will never be borrowed mutably after initialization.
336        let vtable_ptr = unsafe { *self.vtable_ptr.get() };
337
338        // SAFETY: The page metadata is initialized and valid.
339        let vtable_ptr = *unsafe { vtable_ptr.assume_init_ref() };
340
341        let meta_ptr: *mut dyn AnyFrameMeta =
342            core::ptr::from_raw_parts_mut(self as *const MetaSlot as *mut MetaSlot, vtable_ptr);
343
344        meta_ptr
345    }
346
347    /// Gets the stored metadata as type `M`.
348    ///
349    /// Calling the method should be safe, but using the returned pointer would
350    /// be unsafe. Specifically, the derefernecer should ensure that:
351    ///  - the stored metadata is initialized (by [`Self::write_meta`]) and
352    ///    valid;
353    ///  - the initialized metadata is of type `M`;
354    ///  - the returned pointer should not be dereferenced as mutable unless
355    ///    having exclusive access to the metadata slot.
356    pub(super) fn as_meta_ptr<M: AnyFrameMeta>(&self) -> *mut M {
357        self.storage.get() as *mut M
358    }
359
360    /// Writes the metadata to the slot without reading or dropping the previous value.
361    ///
362    /// # Safety
363    ///
364    /// The caller should have exclusive access to the metadata slot's fields.
365    pub(super) unsafe fn write_meta<M: AnyFrameMeta>(&self, metadata: M) {
366        const { assert!(size_of::<M>() <= FRAME_METADATA_MAX_SIZE) };
367        const { assert!(align_of::<M>() <= FRAME_METADATA_MAX_ALIGN) };
368
369        // SAFETY: Caller ensures that the access to the fields are exclusive.
370        let vtable_ptr = unsafe { &mut *self.vtable_ptr.get() };
371        vtable_ptr.write(core::ptr::metadata(&metadata as &dyn AnyFrameMeta));
372
373        let ptr = self.storage.get();
374        // SAFETY:
375        // 1. `ptr` points to the metadata storage.
376        // 2. The size and the alignment of the metadata storage is large enough to hold `M`
377        //    (guaranteed by the const assertions above).
378        // 3. We have exclusive access to the metadata storage (guaranteed by the caller).
379        unsafe { ptr.cast::<M>().write(metadata) };
380    }
381
382    /// Drops the metadata and deallocates the frame.
383    ///
384    /// # Safety
385    ///
386    /// The caller should ensure that:
387    ///  - the reference count is `0` (so we are the sole owner of the frame);
388    ///  - the metadata is initialized;
389    pub(super) unsafe fn drop_last_in_place(&self) {
390        // This should be guaranteed as a safety requirement.
391        debug_assert_eq!(self.ref_count.load(Ordering::Relaxed), 0);
392
393        // SAFETY: The caller ensures safety.
394        unsafe { self.drop_meta_in_place() };
395
396        // `Release` pairs with the `Acquire` in `Frame::from_unused` and ensures
397        // `drop_meta_in_place` won't be reordered after this memory store.
398        self.ref_count.store(REF_COUNT_UNUSED, Ordering::Release);
399    }
400
401    /// Drops the metadata of a slot in place.
402    ///
403    /// After this operation, the metadata becomes uninitialized. Any access to the
404    /// metadata is undefined behavior unless it is re-initialized by [`Self::write_meta`].
405    ///
406    /// # Safety
407    ///
408    /// The caller should ensure that:
409    ///  - the reference count is `0` (so we are the sole owner of the frame);
410    ///  - the metadata is initialized;
411    pub(super) unsafe fn drop_meta_in_place(&self) {
412        let paddr = self.frame_paddr();
413
414        // SAFETY: We have exclusive access to the frame metadata.
415        let vtable_ptr = unsafe { &mut *self.vtable_ptr.get() };
416        // SAFETY: The frame metadata is initialized and valid.
417        let vtable_ptr = unsafe { vtable_ptr.assume_init_read() };
418
419        let meta_ptr: *mut dyn AnyFrameMeta =
420            core::ptr::from_raw_parts_mut(self.storage.get(), vtable_ptr);
421
422        // SAFETY: The implementer of the frame metadata decides that if the frame
423        // is safe to be read or not.
424        let mut reader =
425            unsafe { VmReader::from_kernel_space(paddr_to_vaddr(paddr) as *const u8, PAGE_SIZE) };
426
427        // SAFETY: `ptr` points to the metadata storage which is valid to be mutably borrowed under
428        // `vtable_ptr` because the metadata is valid, the vtable is correct, and we have the exclusive
429        // access to the frame metadata.
430        unsafe {
431            // Invoke the custom `on_drop` handler.
432            (*meta_ptr).on_drop(&mut reader);
433            // Drop the frame metadata.
434            core::ptr::drop_in_place(meta_ptr);
435        }
436    }
437}
438
439/// The metadata of frames that holds metadata of frames.
440#[derive(Debug, Default)]
441pub struct MetaPageMeta {}
442
443impl_frame_meta_for!(MetaPageMeta);
444
445/// Initializes the metadata of all physical frames.
446///
447/// The function returns a list of `Frame`s containing the metadata.
448///
449/// # Safety
450///
451/// This function should be called only once and only on the BSP,
452/// before any APs are started.
453pub(crate) unsafe fn init() -> Segment<MetaPageMeta> {
454    let max_paddr = {
455        let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
456        regions
457            .iter()
458            .filter(|r| r.typ().is_physical())
459            .map(|r| r.base() + r.len())
460            .max()
461            .unwrap()
462    };
463
464    info!(
465        "Initializing frame metadata for physical memory up to {:x}",
466        max_paddr
467    );
468
469    // In RISC-V, the boot page table has mapped the 512GB memory,
470    // so we don't need to add temporary linear mapping.
471    // In LoongArch, the DWM0 has mapped the whole memory,
472    // so we don't need to add temporary linear mapping.
473    #[cfg(target_arch = "x86_64")]
474    add_temp_linear_mapping(max_paddr);
475
476    let tot_nr_frames = max_paddr / page_size::<PagingConsts>(1);
477    let (nr_meta_pages, meta_pages) = alloc_meta_frames(tot_nr_frames);
478
479    // Map the metadata frames.
480    boot_pt::with_borrow(|boot_pt| {
481        for i in 0..nr_meta_pages {
482            let frame_paddr = meta_pages + i * PAGE_SIZE;
483            let vaddr = mapping::frame_to_meta::<PagingConsts>(0) + i * PAGE_SIZE;
484            let prop = PageProperty {
485                flags: PageFlags::RW,
486                cache: CachePolicy::Writeback,
487                priv_flags: PrivilegedPageFlags::GLOBAL,
488            };
489            // SAFETY: we are doing the metadata mappings for the kernel.
490            unsafe { boot_pt.map_base_page(vaddr, frame_paddr, prop) };
491        }
492    })
493    .unwrap();
494
495    // Now the metadata frames are mapped, we can initialize the metadata.
496    super::MAX_PADDR.store(max_paddr, Ordering::Relaxed);
497
498    let meta_page_range = meta_pages..meta_pages + nr_meta_pages * PAGE_SIZE;
499
500    let (range_1, range_2) = allocator::EARLY_ALLOCATOR
501        .lock()
502        .as_ref()
503        .unwrap()
504        .allocated_regions();
505    for r in range_difference(&range_1, &meta_page_range) {
506        let early_seg = Segment::from_unused(r, |_| EarlyAllocatedFrameMeta).unwrap();
507        let _ = ManuallyDrop::new(early_seg);
508    }
509    for r in range_difference(&range_2, &meta_page_range) {
510        let early_seg = Segment::from_unused(r, |_| EarlyAllocatedFrameMeta).unwrap();
511        let _ = ManuallyDrop::new(early_seg);
512    }
513
514    mark_unusable_ranges();
515
516    Segment::from_unused(meta_page_range, |_| MetaPageMeta {}).unwrap()
517}
518
519/// Returns whether the global frame allocator is initialized.
520pub(in crate::mm) fn is_initialized() -> bool {
521    // `init` sets it with relaxed ordering somewhere in the middle. But due
522    // to the safety requirement of the `init` function, we can assume that
523    // there is no race conditions.
524    super::MAX_PADDR.load(Ordering::Relaxed) != 0
525}
526
527fn alloc_meta_frames(tot_nr_frames: usize) -> (usize, Paddr) {
528    let nr_meta_pages = tot_nr_frames
529        .checked_mul(size_of::<MetaSlot>())
530        .unwrap()
531        .div_ceil(PAGE_SIZE);
532    let paddr = allocator::early_alloc(
533        Layout::from_size_align(nr_meta_pages * PAGE_SIZE, PAGE_SIZE).unwrap(),
534    )
535    .unwrap();
536
537    let slots = paddr_to_vaddr(paddr) as *mut MetaSlot;
538
539    // Initialize the metadata slots.
540    for i in 0..tot_nr_frames {
541        // SAFETY: The memory is successfully allocated with `tot_nr_frames`
542        // slots so the index must be within the range.
543        let slot = unsafe { slots.add(i) };
544        // SAFETY: The memory is just allocated so we have exclusive access and
545        // it's valid for writing.
546        unsafe {
547            slot.write(MetaSlot {
548                storage: UnsafeCell::new([0; FRAME_METADATA_MAX_SIZE]),
549                ref_count: AtomicU64::new(REF_COUNT_UNUSED),
550                vtable_ptr: UnsafeCell::new(MaybeUninit::uninit()),
551                in_list: AtomicU64::new(0),
552            })
553        };
554    }
555
556    (nr_meta_pages, paddr)
557}
558
559/// Unusable memory metadata. Cannot be used for any purposes.
560#[derive(Debug)]
561pub struct UnusableMemoryMeta;
562impl_frame_meta_for!(UnusableMemoryMeta);
563
564/// Reserved memory metadata. Maybe later used as I/O memory.
565#[derive(Debug)]
566pub struct ReservedMemoryMeta;
567impl_frame_meta_for!(ReservedMemoryMeta);
568
569/// The metadata of physical pages that contains the kernel itself.
570#[derive(Debug, Default)]
571pub struct KernelMeta;
572impl_frame_meta_for!(KernelMeta);
573
574macro_rules! mark_ranges {
575    ($region: expr, $typ: expr) => {{
576        debug_assert!($region.base().is_multiple_of(PAGE_SIZE));
577        debug_assert!($region.len().is_multiple_of(PAGE_SIZE));
578
579        let seg = Segment::from_unused($region.base()..$region.end(), |_| $typ).unwrap();
580        let _ = ManuallyDrop::new(seg);
581    }};
582}
583
584fn mark_unusable_ranges() {
585    let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
586
587    for region in regions.iter().rev().skip_while(|r| !r.typ().is_physical()) {
588        match region.typ() {
589            MemoryRegionType::BadMemory => mark_ranges!(region, UnusableMemoryMeta),
590            MemoryRegionType::Unknown => mark_ranges!(region, ReservedMemoryMeta),
591            MemoryRegionType::NonVolatileSleep => mark_ranges!(region, UnusableMemoryMeta),
592            MemoryRegionType::Reserved => mark_ranges!(region, ReservedMemoryMeta),
593            MemoryRegionType::Kernel => mark_ranges!(region, KernelMeta),
594            MemoryRegionType::Module => mark_ranges!(region, UnusableMemoryMeta),
595            MemoryRegionType::Framebuffer => mark_ranges!(region, ReservedMemoryMeta),
596            MemoryRegionType::Reclaimable => mark_ranges!(region, UnusableMemoryMeta),
597            MemoryRegionType::Usable => {} // By default it is initialized as usable.
598        }
599    }
600}
601
602/// Adds a temporary linear mapping for the metadata frames.
603///
604/// We only assume boot page table to contain 4G linear mapping. Thus if the
605/// physical memory is huge we end up depleted of linear virtual memory for
606/// initializing metadata.
607#[cfg(target_arch = "x86_64")]
608fn add_temp_linear_mapping(max_paddr: Paddr) {
609    use align_ext::AlignExt;
610
611    use crate::mm::kspace::LINEAR_MAPPING_BASE_VADDR;
612
613    const PADDR4G: Paddr = 0x1_0000_0000;
614
615    if max_paddr <= PADDR4G {
616        return;
617    }
618
619    // TODO: We don't know if the allocator would allocate from low to high or
620    // not. So we prepare all linear mappings in the boot page table. Hope it
621    // won't drag the boot performance much.
622    let end_paddr = max_paddr.align_up(PAGE_SIZE);
623    let prange = PADDR4G..end_paddr;
624    let prop = PageProperty {
625        flags: PageFlags::RW,
626        cache: CachePolicy::Writeback,
627        priv_flags: PrivilegedPageFlags::GLOBAL,
628    };
629
630    // SAFETY: we are doing the linear mapping for the kernel.
631    unsafe {
632        boot_pt::with_borrow(|boot_pt| {
633            for paddr in prange.step_by(PAGE_SIZE) {
634                let vaddr = LINEAR_MAPPING_BASE_VADDR + paddr;
635                boot_pt.map_base_page(vaddr, paddr, prop);
636            }
637        })
638        .unwrap();
639    }
640}