Skip to main content

kernel_api/allocator/
pmm.rs

1use core::marker::PhantomData;
2use core::num::NonZero;
3use crate::memory::{Frames, RawFrame};
4use crate::sync::RwSpinlock;
5use crate::allocator::AllocError;
6
7/// Returns the kernel's `highmem` allocator.
8/// 
9/// This should be the default allocator when allocating physical memory.
10///
11/// See the [memory module docs](`crate::memory#physical`) for more information.
12#[inline]
13#[must_use]
14pub const fn highmem() -> DynPmm<'static, true> {
15	DynPmm::from(&crate::bridge::memory::GLOBAL_HIGHMEM)
16}
17
18/// Returns the kernel's `dmamem` allocator.
19///
20/// This should be used sparingly and only when allocating DMA memory for 32-bit
21/// DMA controllers.
22///
23/// See the [memory module docs](`crate::memory#physical`) for more information.
24#[inline]
25#[must_use]
26pub const fn dmamem() -> DynPmm<'static, true> {
27	DynPmm::from(&crate::bridge::memory::GLOBAL_DMA)
28}
29
30/// Returns an allocator which uses the [`highmem`](`crate::memory#highmem`) allocator as a backing allocator but zeroes all memory.
31#[inline]
32#[must_use]
33pub const fn highmem_zero() -> DynPmm<'static, true> {
34	struct Zero;
35
36	// SAFETY: All allocated memory comes from `highmem()` which implements `Pmm<true>`
37	unsafe impl Pmm<true> for Zero {
38		fn allocate_one_raw(&self) -> Result<RawFrame, AllocError> {
39			let mut frame = highmem().allocate_one()?;
40			frame.write_filled(0);
41			Ok(Frames::into_raw(frame).0.start)
42		}
43
44		fn allocate_raw(&self, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
45			let mut frames = highmem().allocate(count)?;
46			frames.write_filled(0);
47			Ok(Frames::into_raw(frames).0.start)
48		}
49
50		fn allocate_raw_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
51			let mut frames = highmem().allocate_at(at, count)?;
52			frames.write_filled(0);
53			Ok(Frames::into_raw(frames).0.start)
54		}
55
56		unsafe fn deallocate_raw(&self, base: RawFrame, count: NonZero<usize>) {
57			// SAFETY: safety requirements of `deallocate_raw` are upheld by caller
58			unsafe { highmem().deallocate_raw(base, count) }
59		}
60	}
61	
62	DynPmm::from(&Zero)
63}
64
65/// A physical memory allocator.
66/// 
67/// If the allocator only allocates from conventional RAM, then the
68/// `RAM_ONLY` flag is set, and any returned memory can be accessed via
69/// the [page map](crate::memory#page-map-region).
70/// 
71/// # Safety
72/// 
73/// The implementation must return unaliased physical memory.
74/// Additionally, if `RAM_ONLY` is `true`, the returned memory ranges must
75/// only be from conventional memory, i.e. `EfiConventionalMemory`.
76pub unsafe trait Pmm<const RAM_ONLY: bool>: Sync + Sized { // todo: can we remove the Sync bound? it makes dyn stuff a real pain
77	                                                       // the Sized bound is chucked on to prevent `dyn Pmm` being used
78	/// Allocates a single frame.
79	///
80	/// # Errors
81	///
82	/// Returns an [`AllocError`] if the memory could not be allocated.
83	#[must_use = "memory will be leaked unless the frame is explicitly deallocated"]
84	fn allocate_one_raw(&self) -> Result<RawFrame, AllocError> { self.allocate_raw(const { NonZero::new(1).unwrap() }) }
85
86	/// Allocates `count` number of contiguous frames.
87	///
88	/// # Errors
89	///
90	/// Returns an [`AllocError`] if the memory could not be allocated. This
91	/// does not mean there is no free memory, just that is there no region
92	/// of contiguous memory large enough.
93	#[must_use = "memory will be leaked unless the frame is explicitly deallocated"]
94	fn allocate_raw(&self, count: NonZero<usize>) -> Result<RawFrame, AllocError>;
95
96	/// Allocates `count` number of contiguous frames with the first frame being at `at`.
97	///
98	/// # Errors
99	///
100	/// Returns an [`AllocError`] if the memory could not be allocated. This
101	/// does not mean there is no free memory, just that is there no region
102	/// of contiguous memory large enough starting at `at`.
103	#[must_use = "memory will be leaked unless the frame is explicitly deallocated"]
104	fn allocate_raw_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError>;
105
106	/// Deallocates `count` frames starting at `base`.
107	/// 
108	/// # Safety
109	/// 
110	/// All frames in the range `base .. (base + count)` must have been allocated by this allocator.
111	unsafe fn deallocate_raw(&self, base: RawFrame, count: NonZero<usize>);
112}
113
114// SAFETY: this implementation only exists where `Pmm` has already been implemented for T
115unsafe impl<const RAM_ONLY: bool, T: Pmm<RAM_ONLY>> Pmm<RAM_ONLY> for &T {
116	fn allocate_one_raw(&self) -> Result<RawFrame, AllocError> {
117		(**self).allocate_one_raw()
118	}
119	
120	fn allocate_raw(&self, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
121		(**self).allocate_raw(count)
122	}
123
124	fn allocate_raw_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
125		(**self).allocate_raw_at(at, count)
126	}
127
128	unsafe fn deallocate_raw(&self, base: RawFrame, count: NonZero<usize>) {
129		// SAFETY: this function has the same safety requirements as `deallocate_raw`
130		unsafe { (**self).deallocate_raw(base, count) }
131	}
132}
133
134#[derive(Debug)]
135#[doc(hidden)]
136pub struct GlobalAllocator {
137	#[doc(hidden)]
138	pub __rwlock: RwSpinlock<Option<DynPmm<'static, true>>>
139}
140
141// SAFETY: all calls are forwarded to a type that implements `Pmm` itself
142unsafe impl Pmm<true> for GlobalAllocator {
143	#[inline]
144	fn allocate_raw(&self, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
145		self.__rwlock.read()
146				.expect("no global Pmm")
147				.allocate_raw(count)
148	}
149
150	#[inline]
151	fn allocate_raw_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
152		self.__rwlock.read()
153		    .expect("no global Pmm")
154		    .allocate_raw_at(at, count)
155	}
156
157	#[inline]
158	unsafe fn deallocate_raw(&self, base: RawFrame, count: NonZero<usize>) {
159		let pmm = self.__rwlock.read()
160		    .expect("no global Pmm");
161
162		// SAFETY: this function has the same safety requirements as `pmm.deallocate_raw`
163		unsafe { pmm.deallocate_raw(base, count) }
164	}
165}
166
167/// A type erased reference to a [`Pmm`].
168///
169/// This is equivalent to `&dyn Pmm` with the addition that a `DynPmm<true>` can be converted directly
170/// to a `DynPmm<false>` by calling [`from`](`From::from`) or [`into`](`Into::into`).
171#[derive(Copy, Clone, Debug)] // this is effectively a `&dyn Pmm` therefore we can copy it like a `&` ref
172#[repr(C)] // to force consistent layout between RAM and !RAM variants
173// This is used instead of `&'a dyn Pmm<RAM>` to ensure vtable layout is the same between RAM and !RAM
174// versions so we can freely interconvert
175// INVARIANT: `data` points to a valid implementation of `Pmm<RAM>` with lifetime 'a
176// INVARIANT: all function pointers point to the implementation's corresponding method
177pub struct DynPmm<'a, const RAM: bool> {
178	data: *const (), // we could use NonNull here but we already get niche optimization from all the fn ptrs
179	allocate_raw_at: unsafe fn(*const (), at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError>,
180	allocate_one_raw: unsafe fn(*const ()) -> Result<RawFrame, AllocError>,
181	allocate_raw: unsafe fn(*const (), count: NonZero<usize>) -> Result<RawFrame, AllocError>,
182	deallocate_raw: unsafe fn(*const (), base: RawFrame, count: NonZero<usize>),
183	_phantom: PhantomData<&'a ()>,
184}
185
186// SAFETY: all implementations of Pmm are Sync, therefore &Pmm (which DynPmm is equivalent to) is Sync
187unsafe impl<const RAM: bool> Sync for DynPmm<'_, RAM> {}
188// SAFETY: all implementations of Pmm are Sync, therefore &Pmm (which DynPmm is equivalent to) is Send
189unsafe impl<const RAM: bool> Send for DynPmm<'_, RAM> {}
190
191const impl<'a, const RAM: bool, T: Pmm<RAM>> From<&'a T> for DynPmm<'a, RAM> {
192	fn from(value: &'a T) -> Self {
193		/// # Safety
194		/// `this` must be valid to convert to a reference to `T`.
195		/// # Errors
196		/// See [`Pmm::allocate_raw_at`].
197		#[inline]
198		unsafe fn allocate_raw_at_shim<const RAM: bool, T: Pmm<RAM>>(this: *const (), at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
199			// SAFETY: `this` is valid to convert to ref to `T`, upheld by caller
200			T::allocate_raw_at(unsafe { &*this.cast() }, at, count)
201		}
202
203		/// # Safety
204		/// `this` must be valid to convert to a reference to `T`.
205		/// # Errors
206		/// See [`Pmm::allocate_one_raw`].
207		#[inline]
208		unsafe fn allocate_one_raw_shim<const RAM: bool, T: Pmm<RAM>>(this: *const ()) -> Result<RawFrame, AllocError> {
209			// SAFETY: `this` is valid to convert to ref to `T`, upheld by caller
210			T::allocate_one_raw(unsafe { &*this.cast() })
211		}
212
213		/// # Safety
214		/// `this` must be valid to convert to a reference to `T`.
215		/// # Errors
216		/// See [`Pmm::allocate_raw`].
217		#[inline]
218		unsafe fn allocate_raw_shim<const RAM: bool, T: Pmm<RAM>>(this: *const (), count: NonZero<usize>) -> Result<RawFrame, AllocError> {
219			// SAFETY: `this` is valid to convert to ref to `T`, upheld by caller
220			T::allocate_raw(unsafe { &*this.cast() }, count)
221		}
222
223		/// # Safety
224		/// `this` must be valid to convert to a reference to `T`, and all safety requirements of `Pmm::deallocate_raw`
225		/// must be upheld.
226		#[inline]
227		unsafe fn deallocate_raw_shim<const RAM: bool, T: Pmm<RAM>>(this: *const (), base: RawFrame, count: NonZero<usize>) {
228			// SAFETY: `this` is valid to convert to ref to `T`, upheld by caller
229			let this = unsafe { &*this.cast() };
230			// SAFETY: safety requirements of `deallocate_raw` upheld by caller
231			unsafe { T::deallocate_raw(this, base, count) }
232		}
233
234		DynPmm {
235			data: core::ptr::from_ref(value).cast(),
236			allocate_raw_at: allocate_raw_at_shim::<RAM, T>,
237			allocate_one_raw: allocate_one_raw_shim::<RAM, T>,
238			allocate_raw: allocate_raw_shim::<RAM, T>,
239			deallocate_raw: deallocate_raw_shim::<RAM, T>,
240			_phantom: PhantomData,
241		}
242	}
243}
244
245impl<'a, const RAM: bool> DynPmm<'a, RAM> {
246	/// Converts to a `DynPmm<false>`.
247	#[must_use]
248	#[expect(clippy::unnecessary_struct_initialization, reason = "false positive since it changes type param")]
249	pub const fn into(self) -> DynPmm<'a, false> {
250		DynPmm::<false> {
251			..self
252		}
253	}
254
255	/// Allocates a single owned [frame](`Frames`).
256	///
257	/// # Errors
258	///
259	/// Returns an [`AllocError`] if the memory could not be allocated.
260	pub fn allocate_one(&self) -> Result<Frames<RAM>, AllocError> where Self: 'static {
261		let base = self.allocate_one_raw()?;
262		// SAFETY: `base` was just allocated by `self`
263		Ok(unsafe {
264			Frames::from_raw(
265				base .. (base + 1usize),
266				*self,
267			)
268		})
269	}
270
271	/// Allocates `counts` number of contiguous owned [`Frames`].
272	///
273	/// # Errors
274	///
275	/// Returns an [`AllocError`] if the memory could not be allocated. This
276	/// does not mean there is no free memory, just that is there no region
277	/// of contiguous memory large enough.
278	pub fn allocate(&self, count: NonZero<usize>) -> Result<Frames<RAM>, AllocError> where Self: 'static {
279		let base = self.allocate_raw(count)?;
280		// SAFETY: `base` was just allocated by `self`
281		Ok(unsafe {
282			Frames::from_raw(
283				base .. (base + count.get()),
284				*self,
285			)
286		})
287	}
288
289	/// Allocates `count` number of contiguous owned [`Frames`] with the first frame being at `at`.
290	///
291	/// # Errors
292	///
293	/// Returns an [`AllocError`] if the memory could not be allocated. This
294	/// does not mean there is no free memory, just that is there no region
295	/// of contiguous memory large enough starting at `at`.
296	pub fn allocate_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<Frames<RAM>, AllocError> where Self: 'static {
297		let base = self.allocate_raw_at(at, count)?;
298		// SAFETY: `base` was just allocated by `self`
299		Ok(unsafe {
300			Frames::from_raw(
301				base .. (base + count.get()),
302				*self,
303			)
304		})
305	}
306}
307
308// SAFETY: DynPmm<RAM> can only be constructed from a reference to an object
309//  already implementing DynPmm<RAM>
310unsafe impl<const RAM: bool> Pmm<RAM> for DynPmm<'_, RAM> {
311	#[inline]
312	fn allocate_one_raw(&self) -> Result<RawFrame, AllocError> {
313		// SAFETY: invariant of `DynPmm` that `self.data` is type erased reference to same type as fn pointer is from
314		unsafe { (self.allocate_one_raw)(self.data) }
315	}
316
317	#[inline]
318	fn allocate_raw(&self, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
319		// SAFETY: invariant of `DynPmm` that `self.data` is type erased reference to same type as fn pointer is from
320		unsafe { (self.allocate_raw)(self.data, count) }
321	}
322
323	#[inline]
324	fn allocate_raw_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
325		// SAFETY: invariant of `DynPmm` that `self.data` is type erased reference to same type as fn pointer is from
326		unsafe { (self.allocate_raw_at)(self.data, at, count) }
327	}
328
329	#[inline]
330	unsafe fn deallocate_raw(&self, base: RawFrame, count: NonZero<usize>) {
331		// SAFETY: invariants of `self.deallocate_raw` are same as `Pmm::deallocate_raw`, and
332		//  invariant of `DynPmm` that `self.data` is type erased reference to same type as fn pointer is from
333		unsafe { (self.deallocate_raw)(self.data, base, count) }
334	}
335}