Skip to main content

kernel_api/allocator/
pmm.rs

1use core::marker::PhantomData;
2use core::num::NonZero;
3use core::ptr::addr_of;
4use crate::memory::{Frames, RawFrame};
5use crate::sync::RwSpinlock;
6use crate::allocator::AllocError;
7
8/// Returns the kernel's `highmem` allocator
9/// 
10/// This should be the default allocator when allocating physical memory
11#[inline]
12pub const fn highmem() -> DynPmm<'static, true> {
13	DynPmm::from(&crate::bridge::memory::GLOBAL_HIGHMEM)
14}
15
16/// Returns the kernel's `dmamem` allocator
17///
18/// This should be used sparingly and only when allocating DMA memory for 32-bit
19/// DMA controllers.
20#[inline]
21pub const fn dmamem() -> DynPmm<'static, true> {
22	DynPmm::from(&crate::bridge::memory::GLOBAL_DMA)
23}
24
25/// Returns an allocator which uses the [`highmem`](highmem()) allocator as a backing allocator but zeroes all memory
26#[inline]
27pub const fn highmem_zero() -> DynPmm<'static, true> {
28	struct Zero;
29
30	unsafe impl Pmm<true> for Zero {
31		fn allocate_one_raw(&self) -> Result<RawFrame, AllocError> {
32			let mut frame = highmem().allocate_one()?;
33			frame.write_filled(0);
34			Ok(Frames::into_raw(frame).0.start)
35		}
36
37		fn allocate_raw(&self, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
38			let mut frames = highmem().allocate(count)?;
39			frames.write_filled(0);
40			Ok(Frames::into_raw(frames).0.start)
41		}
42
43		fn allocate_raw_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
44			let mut frames = highmem().allocate_at(at, count)?;
45			frames.write_filled(0);
46			Ok(Frames::into_raw(frames).0.start)
47		}
48
49		unsafe fn deallocate_raw(&self, base: RawFrame, count: NonZero<usize>) {
50			unsafe { highmem().deallocate_raw(base, count) }
51		}
52	}
53	
54	DynPmm::from(&Zero)
55}
56
57/// A physical memory allocator
58/// 
59/// If the allocator only allocates from conventional RAM, then the
60/// `RAM_ONLY` flag is set, and any returned memory can be accessed via
61/// the [page map](crate::memory#page-map-region)
62/// 
63/// # Safety
64/// 
65/// The implementation must return unaliased physical memory.
66/// Additionally, if `RAM_ONLY` is `true`, the returned memory ranges must
67/// only be from conventional memory, i.e. `EfiConventionalMemory`
68pub unsafe trait Pmm<const RAM_ONLY: bool>: Sync + Sized { // todo: can we remove the Sync bound? it makes dyn stuff a real pain
69	                                                       // the Sized bound is chucked on to prevent `dyn Pmm` being used
70	/// Allocates a single frame
71	///
72	/// # Errors
73	///
74	/// Returns an [`AllocError`] if the memory could not be allocated
75	fn allocate_one_raw(&self) -> Result<RawFrame, AllocError> { self.allocate_raw(const { NonZero::new(1).unwrap() }) }
76
77	/// Allocates `count` number of contiguous frames
78	///
79	/// # Errors
80	///
81	/// Returns an [`AllocError`] if the memory could not be allocated. This
82	/// does not mean there is no free memory, just that is there no region
83	/// of contiguous memory large enough
84	fn allocate_raw(&self, count: NonZero<usize>) -> Result<RawFrame, AllocError>;
85
86	fn allocate_raw_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError>;
87
88	/// Deallocates `count` frames starting at `base`
89	/// 
90	/// # Safety
91	/// 
92	/// All frames in the range `base .. (count + base)` must have been allocated by this allocator
93	unsafe fn deallocate_raw(&self, base: RawFrame, count: NonZero<usize>);
94}
95
96unsafe impl<'a, const RAM_ONLY: bool, T: Pmm<RAM_ONLY> + ?Sized> Pmm<RAM_ONLY> for &'a T {
97	fn allocate_one_raw(&self) -> Result<RawFrame, AllocError> {
98		(**self).allocate_one_raw()
99	}
100	
101	fn allocate_raw(&self, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
102		(**self).allocate_raw(count)
103	}
104
105	fn allocate_raw_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
106		(**self).allocate_raw_at(at, count)
107	}
108
109	unsafe fn deallocate_raw(&self, base: RawFrame, count: NonZero<usize>) {
110		(**self).deallocate_raw(base, count)
111	}
112}
113
114#[doc(hidden)]
115pub struct GlobalAllocator {
116	pub __rwlock: RwSpinlock<Option<DynPmm<'static, true>>>
117}
118
119unsafe impl Pmm<true> for GlobalAllocator {
120	#[inline]
121	fn allocate_raw(&self, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
122		self.__rwlock.read()
123				.expect("no global Pmm")
124				.allocate_raw(count)
125	}
126
127	#[inline]
128	fn allocate_raw_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
129		self.__rwlock.read()
130		    .expect("no global Pmm")
131		    .allocate_raw_at(at, count)
132	}
133
134	#[inline]
135	unsafe fn deallocate_raw(&self, base: RawFrame, count: NonZero<usize>) {
136		self.__rwlock.read()
137		    .expect("no global Pmm")
138		    .deallocate_raw(base, count)
139	}
140}
141
142#[derive(Copy, Clone, Debug)] // this is effectively a `&dyn Pmm` therefore we can copy it like a `&` ref
143#[repr(C)] // to force consistent layout between RAM and !RAM variants
144pub struct DynPmm<'a, const RAM: bool> {
145	data: *const (), // we could use NonNull here but we already get niche optimization from all the fn ptrs
146	allocate_raw_at: unsafe fn(*const (), at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError>,
147	allocate_one_raw: unsafe fn(*const ()) -> Result<RawFrame, AllocError>,
148	allocate_raw: unsafe fn(*const (), count: NonZero<usize>) -> Result<RawFrame, AllocError>,
149	deallocate_raw: unsafe fn(*const (), base: RawFrame, count: NonZero<usize>),
150	_phantom: PhantomData<&'a ()>,
151}
152
153unsafe impl<const RAM: bool> Sync for DynPmm<'_, RAM> {}
154unsafe impl<const RAM: bool> Send for DynPmm<'_, RAM> {}
155
156impl<'a, const RAM: bool, T: Pmm<RAM>> const From<&'a T> for DynPmm<'a, RAM> {
157	fn from(value: &'a T) -> Self {
158		DynPmm {
159			data: addr_of!(*value).cast(),
160			allocate_raw_at: unsafe { core::mem::transmute(T::allocate_raw_at as fn(_, _, _) -> _) },
161			allocate_one_raw: unsafe { core::mem::transmute(T::allocate_one_raw as fn(_) -> _) },
162			allocate_raw: unsafe { core::mem::transmute(T::allocate_raw as fn(_, _) -> _) },
163			deallocate_raw: unsafe { core::mem::transmute(T::deallocate_raw as unsafe fn(_, _, _)) },
164			_phantom: PhantomData,
165		}
166	}
167}
168
169impl<'a, const RAM: bool> DynPmm<'a, RAM> {
170	pub const fn into(self) -> DynPmm<'a, false> {
171		DynPmm::<false> {
172			..self
173		}
174	}
175
176	/// Allocates a single owned [frame](Frames)
177	///
178	/// # Errors
179	///
180	/// Returns an [`AllocError`] if the memory could not be allocated
181	pub fn allocate_one(&self) -> Result<Frames<RAM>, AllocError> where Self: 'static {
182		let base = self.allocate_one_raw()?;
183		Ok(unsafe {
184			Frames::from_raw(
185				base .. (base + 1usize),
186				*self,
187			)
188		})
189	}
190
191	/// Allocates a `counts` contiguous owned [`Frames`]
192	///
193	/// # Errors
194	///
195	/// Returns an [`AllocError`] if the memory could not be allocated. This
196	/// does not mean there is no free memory, just that is there no region
197	/// of contiguous memory large enough
198	pub fn allocate(&self, count: NonZero<usize>) -> Result<Frames<RAM>, AllocError> where Self: 'static {
199		let base = self.allocate_raw(count)?;
200		Ok(unsafe {
201			Frames::from_raw(
202				base .. (base + count.get()),
203				*self,
204			)
205		})
206	}
207
208	pub fn allocate_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<Frames<RAM>, AllocError> where Self: 'static {
209		let base = self.allocate_raw_at(at, count)?;
210		Ok(unsafe {
211			Frames::from_raw(
212				base .. (base + count.get()),
213				*self,
214			)
215		})
216	}
217}
218
219unsafe impl<const RAM: bool> Pmm<RAM> for DynPmm<'_, RAM> {
220	#[inline]
221	fn allocate_one_raw(&self) -> Result<RawFrame, AllocError> {
222		unsafe { (self.allocate_one_raw)(self.data) }
223	}
224
225	#[inline]
226	fn allocate_raw(&self, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
227		unsafe { (self.allocate_raw)(self.data, count) }
228	}
229
230	#[inline]
231	fn allocate_raw_at(&self, at: RawFrame, count: NonZero<usize>) -> Result<RawFrame, AllocError> {
232		unsafe { (self.allocate_raw_at)(self.data, at, count) }
233	}
234
235	#[inline]
236	unsafe fn deallocate_raw(&self, base: RawFrame, count: NonZero<usize>) {
237		unsafe { (self.deallocate_raw)(self.data, base, count) }
238	}
239}