ostd/mm/heap/
slot.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Heap slots for allocations.
4
5use core::{alloc::AllocError, ptr::NonNull};
6
7use crate::{
8    impl_frame_meta_for,
9    mm::{
10        FrameAllocOptions, PAGE_SIZE, Paddr, Segment, Vaddr, kspace::LINEAR_MAPPING_BASE_VADDR,
11        paddr_to_vaddr,
12    },
13};
14
15/// A slot that will become or has been turned from a heap allocation.
16///
17/// Heap slots can come from [`Slab`] or directly from a typed [`Segment`].
18///
19/// Heap slots can be used to fulfill heap allocations requested by the allocator.
20/// Upon deallocation, the deallocated memory also becomes a heap slot.
21///
22/// The size of the heap slot must match the slot size of the [`Slab`] or the
23/// size of the [`Segment`].
24///
25/// [`Slab`]: super::Slab
26pub struct HeapSlot {
27    /// The address of the slot.
28    addr: NonNull<u8>,
29    /// The type and size of the slot.
30    info: SlotInfo,
31}
32
33/// The type and size of the heap slot that should be used for the allocation.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum SlotInfo {
36    /// The slot is from a [`super::Slab`].
37    ///
38    /// The size of the slot and the corresponding slab are provided.
39    /// Both values are identical.
40    SlabSlot(usize),
41    /// The slot is from a [`Segment`].
42    ///
43    /// The size of the slot and the corresponding segment are provided.
44    /// Both values are identical.
45    LargeSlot(usize),
46}
47
48impl SlotInfo {
49    /// Gets the size of the slot.
50    pub fn size(&self) -> usize {
51        match self {
52            Self::SlabSlot(size) => *size,
53            Self::LargeSlot(size) => *size,
54        }
55    }
56}
57
58impl HeapSlot {
59    /// Creates a new pointer to a heap slot.
60    ///
61    /// # Safety
62    ///
63    /// The pointer to the slot must either:
64    ///  - be a free slot in a [`super::Slab`], or
65    ///  - be a free slot in a [`Segment`].
66    ///
67    /// If the pointer is from a [`super::Slab`] or [`Segment`], the slot must
68    /// have a size that matches the slot size of the slab or segment respectively.
69    pub(super) unsafe fn new(addr: NonNull<u8>, info: SlotInfo) -> Self {
70        Self { addr, info }
71    }
72
73    /// Allocates a large slot.
74    ///
75    /// This function allocates in units of [`PAGE_SIZE`] bytes.
76    ///
77    /// This function returns an error if the frame allocation fails.
78    ///
79    /// # Panics
80    ///
81    /// This function panics if the size is not a multiple of [`PAGE_SIZE`].
82    pub fn alloc_large(size: usize) -> Result<Self, AllocError> {
83        assert_eq!(size % PAGE_SIZE, 0);
84        let nframes = size / PAGE_SIZE;
85        let segment = FrameAllocOptions::new()
86            .zeroed(false)
87            .alloc_segment_with(nframes, |_| LargeAllocFrameMeta)
88            .map_err(|_| {
89                log::error!("Failed to allocate a large slot");
90                AllocError
91            })?;
92
93        let paddr_range = segment.into_raw();
94        let vaddr = paddr_to_vaddr(paddr_range.start);
95
96        Ok(Self {
97            addr: NonNull::new(vaddr as *mut u8).unwrap(),
98            info: SlotInfo::LargeSlot(size),
99        })
100    }
101
102    /// Deallocates a large slot.
103    ///
104    /// # Panics
105    ///
106    /// This function aborts if the slot was not allocated with
107    /// [`HeapSlot::alloc_large`], as it requires specific memory management
108    /// operations that only apply to large slots.
109    pub fn dealloc_large(self) {
110        let SlotInfo::LargeSlot(size) = self.info else {
111            log::error!(
112                "Deallocating a large slot that was not allocated with `HeapSlot::alloc_large`"
113            );
114            crate::panic::abort();
115        };
116
117        debug_assert_eq!(size % PAGE_SIZE, 0);
118        debug_assert_eq!(self.paddr() % PAGE_SIZE, 0);
119        let range = self.paddr()..self.paddr() + size;
120
121        // SAFETY: The segment was once forgotten when allocated.
122        drop(unsafe { Segment::<LargeAllocFrameMeta>::from_raw(range) });
123    }
124
125    /// Gets the physical address of the slot.
126    pub fn paddr(&self) -> Paddr {
127        self.addr.as_ptr() as Vaddr - LINEAR_MAPPING_BASE_VADDR
128    }
129
130    /// Gets the size of the slot.
131    pub fn size(&self) -> usize {
132        match self.info {
133            SlotInfo::SlabSlot(size) => size,
134            SlotInfo::LargeSlot(size) => size,
135        }
136    }
137
138    /// Gets the type and size of the slot.
139    pub fn info(&self) -> SlotInfo {
140        self.info
141    }
142
143    /// Gets the pointer to the slot.
144    pub fn as_ptr(&self) -> *mut u8 {
145        self.addr.as_ptr()
146    }
147}
148
149/// The frames allocated for a large allocation.
150#[derive(Debug)]
151pub struct LargeAllocFrameMeta;
152
153impl_frame_meta_for!(LargeAllocFrameMeta);