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);