Skip to main content

kernel_api/mapping/
config.rs

1#[cfg(feature = "full")] pub use full::*;
2
3use crate::newtype_enum;
4
5newtype_enum! {
6	/// The type of memory being mapped.
7	///
8	/// In certain build configurations this will be stored as metadata with each relevant page table
9	/// entry, and will be shown in relevant log entries.
10	///
11	/// Constants are provided for typical memory buffer uses and will be printed by name in logs,
12	/// but any custom value can be used and will have it's raw value printed.
13	pub enum Ty: pub u8 => {
14		/// Unknown.
15		UNKNOWN = 0,
16		/// Framebuffer memory.
17		FB = 1,
18		/// Kernel non-executable data.
19		KERNEL_DATA = 2,
20		/// Kernel executable code.
21		KERNEL_CODE = 3,
22		/// Kernel thread-local data.
23		KERNEL_TLS = 4,
24		/// Kernel thread stack.
25		KERNEL_STACK = 6,
26		/// Other kernel data.
27		KERNEL_OTHER = 5,
28		/// Memory used for the [page map region](`crate::memory#page-map-region`).
29		MEM_MAP = 7,
30		/// Bootloader executable code.
31		LOADER_CODE = 8,
32		/// Bootloader non-executable data.
33		LOADER_DATA = 9,
34		#[doc(hidden)] BGRT_BMP_HEADER = 10,
35		#[doc(hidden)] IOAPIC_REGISTERS = 11,
36		#[doc(hidden)] APIC_REGISTERS = 12,
37		#[doc(hidden)] HPET_HEADER = 13,
38		#[doc(hidden)] HPET_FULL = 14,
39		#[doc(hidden)] PHYSMAP_OTHER = 15,
40		#[doc(hidden)] ACPI_SDT_HEADER = 16,
41		#[doc(hidden)] ACPI_RSDP = 17,
42		#[doc(hidden)] ACPI_HPET = 18,
43		#[doc(hidden)] ACPI_FADT = 19,
44		#[doc(hidden)] ACPI_BGRT = 20,
45		#[doc(hidden)] BYTE_ARRAY = 21,
46		/// Userspace thread stack.
47		USER_STACK = 24,
48		/// Page table memory.
49		PAGE_TABLE = 25,
50		/// Userspace anonymous memory.
51		USER_MMAP = 26,
52		/// Userspace memory mapped to MMIO registers.
53		USER_MMIO = 27,
54		/// Userspace executable code and data.
55		USER_CODE = 28,
56		/// Memory used to buffer syscall data between processes.
57		USER_PACKET_BUFFER = 29,
58		/// [Kasan](`crate::memory::asan`) shadow memory.
59		SHADOW_MEM = 30,
60		/// Kernel heap memory.
61		HEAP = 31,
62	}
63}
64
65#[cfg(feature = "full")]
66mod full {
67	use alloc::borrow::Cow;
68	use alloc::boxed::Box;
69	use alloc::sync::Arc;
70	use core::mem::ManuallyDrop;
71	use core::num::NonZero;
72	use core::ops::DerefMut;
73	use core::ptr;
74	use log::trace;
75	use crate::mapping::{MapPageError, Mappable, Mapping, Ty};
76	use crate::address_space;
77	use crate::address_space::{AddressSpace, MappingKey, Userspace};
78	use crate::allocator::{highmem, AllocError, DynPmm};
79	use crate::mapping::full::Backing;
80	use crate::memory::{RawFrame, RawPage};
81	use crate::syscall::handle::Handle;
82
83	/// The protection that should be applied to the mapping.
84	#[derive(Debug, Copy, Clone)]
85	pub struct Protection {
86		/// If the data in the mapping can be executed.
87		pub executable: bool,
88		/// If the mapping can be written to.
89		pub writable: bool,
90		/// If the mapping can be accessed from userspace.
91		pub user_accessible: bool,
92	}
93
94	impl Protection {
95		const fn new() -> Self {
96			Self {
97				executable: false,
98				writable: false,
99				user_accessible: false
100			}
101		}
102	}
103
104	impl Default for Protection {
105		fn default() -> Self {
106			Self::new()
107		}
108	}
109
110	/// The caching strategy to use for the mapping.
111	#[derive(Debug, Copy, Clone)]
112	#[non_exhaustive]
113	pub enum Caching {
114		/// The fastest cache strategy.
115		/// This should be used for the majority of mappings.
116		Normal,
117		/// All access bypasses the cache.
118		/// This should only be used when preserving the exact sequence
119		/// of memory accesses is important.
120		Mmio,
121		/// Reads bypass the cache. Small sequential writes are buffered
122		/// to be written in a single large access.
123		WriteCombine,
124	}
125
126	impl Caching {
127		pub const fn new() -> Self {
128			Self::Normal
129		}
130	}
131
132	impl Default for Caching {
133		fn default() -> Self {
134			Self::new()
135		}
136	}
137
138	/// The strategy used to place the allocation within the address space.
139	#[derive(Debug)]
140	#[non_exhaustive]
141	pub enum Location<T> {
142		/// The memory can be allocated at any available address in the address space.
143		Any,
144		/// The allocator call should fail if the specifically requested address is unavailable.
145		At(T),
146	}
147
148	#[derive(Debug)]
149	enum AllocatorTy {
150		Vmo(Arc<Handle>),
151		Pmm(DynPmm<'static, false>),
152	}
153
154	/// The returned type when creating a userspace mapping.
155	#[derive(Debug)]
156	#[non_exhaustive]
157	pub struct MappingMeta<T: DerefMut<Target = Mapping<Box<dyn Mappable + Send>, Userspace>>> {
158		/// A key to access the mapping in future.
159		pub key: MappingKey,
160		/// An accessor to modify the mapping.
161		pub mapping: T,
162	}
163
164	/// A [`Mapping`] builder, providing control over the way the underlying buffer is mapped.
165	#[derive(Debug)]
166	#[must_use = "mapping config does nothing unless mapped"]
167	pub struct Config {
168		physical_location: Location<RawFrame>,
169		virtual_location: Location<RawPage>,
170		page_count: NonZero<usize>,
171		physical_allocator: AllocatorTy,
172		protection: Protection,
173		caching: Caching,
174		reason: Ty,
175	}
176
177	impl Config {
178		/// Creates a [`Mapping`] in the [kernel address space](address_space#kernel-address-space).
179		///
180		/// The type parameter `R` is used to determine how the physical and virtual allocations are aligned, as
181		/// well as some configuration values if left as default values. This can be used to require extra virtual
182		/// memory to be allocated and left unmapped to act as a guard page. See the [module level documentation](`super`)
183		/// for more information.
184		///
185		/// # Errors
186		///
187		/// Propagates an [`AllocError`] if the virtual or physical memory allocators have an allocation failure.
188		pub fn map<R: Mappable + Default>(self) -> Result<Mapping<R, address_space::Kernel>, AllocError> {
189			trace!("create mmap({}) with {self:#?}", core::any::type_name::<R>());
190
191			let address_space = address_space::Kernel {};
192			let raw = R::default();
193
194			self.do_map(raw, address_space)
195		}
196
197		/*#[cfg(feature = "use_std")]
198		pub fn map<R: Mappable + Default>(self) -> Result<Mapping<R, address_space::Kernel>, AllocError> {
199			trace!("create mmap({}) with {self:#?}", core::any::type_name::<R>());
200
201			let address_space = address_space::Kernel {};
202			let raw = R::default();
203
204			let Location::Any = self.physical_location else {
205				panic!("specific location not supported under test yet");
206			};
207			let Location::Any = self.virtual_location else {
208				panic!("specific location not supported under test yet");
209			};
210
211			let virtual_len = raw.virtual_size(self.page_count.get());
212
213			let mut prot = libc::PROT_READ;
214			if self.protection.writable { prot |= libc::PROT_WRITE; }
215			if self.protection.executable { prot |= libc::PROT_EXEC; }
216
217			let base = unsafe {
218				let res = libc::mmap(
219					ptr::null_mut(),
220					virtual_len.get() * PAGE_SIZE,
221					prot,
222					libc::MAP_ANON,
223					0,
224					0
225				);
226				if res.is_null() { return Err(AllocError::default()); }
227				res
228			};
229
230			let guard_below = raw.base_virtual_offset();
231			unsafe {
232				libc::mprotect(
233					base,
234					(guard_below as usize) * PAGE_SIZE,
235					libc::PROT_NONE,
236				);
237			}
238			let guard_above = virtual_len.get() - self.page_count.get() - (guard_below as usize);
239			unsafe {
240				libc::mprotect(
241					base.byte_add(((guard_below as usize) + self.page_count.get()) * PAGE_SIZE),
242					(guard_above as usize) * PAGE_SIZE,
243					libc::PROT_NONE,
244				);
245			}
246
247			Ok(Mapping {
248				raw,
249				address_space: ManuallyDrop::new(address_space),
250				backing: base,
251				caching: self.caching,
252				protection: self.protection,
253			})
254		}*/
255
256		/// Creates a [`Mapping`] in a [user address space](address_space#user-address-space).
257		///
258		/// On success, returns a [`MappingMeta`] which allows access to the created mapping.
259		///
260		/// The type parameter `R` is used to determine how the physical and virtual allocations are aligned, as
261		/// well as some configuration values if left as default values. This can be used to require extra virtual
262		/// memory to be allocated and left unmapped to act as a guard page. See the [module level documentation](`super`)
263		/// for more information.
264		///
265		/// # Errors
266		///
267		/// Propagates an [`AllocError`] if the virtual or physical memory allocators have an allocation failure.
268		#[expect(clippy::type_complexity, reason = "cannot simplify further")]
269		pub fn map_in<'a, R: Mappable + Default + Send + 'static>(self, name: Cow<'static, str>, address_space: &'a AddressSpace) -> Result<MappingMeta<impl DerefMut<Target = Mapping<Box<dyn Mappable + Send>, Userspace>> + 'a>, AllocError> {
270			trace!("create mmap({}) with {self:#?}", core::any::type_name::<R>());
271			let userspace = Userspace {
272				inner: AddressSpace::clone(address_space),
273			};
274			let raw = R::default();
275
276			let map = ManuallyDrop::new(self.do_map(raw, userspace)?);
277			let map = Mapping {
278				// SAFETY: pointer is derived directly from reference
279				raw: Box::new(unsafe { ptr::read(&raw const map.raw) }) as Box<dyn Mappable + Send>,
280				// SAFETY: pointer is derived directly from reference
281				address_space: unsafe { ptr::read(&raw const map.address_space) },
282				// SAFETY: pointer is derived directly from reference
283				backing: unsafe { ptr::read(&raw const map.backing) },
284				caching: map.caching,
285				virtual_start: map.virtual_start,
286				protection: map.protection,
287			};
288
289			let (key, mapping) = address_space.push_mapping(name, map);
290			Ok(MappingMeta { key, mapping })
291		}
292
293		/// # Errors
294		///
295		/// Propagates an [`AllocError`] if the virtual or physical memory allocators have an allocation failure.
296		fn do_map<R: Mappable, A: address_space::Ty>(self, raw: R, address_space: A) -> Result<Mapping<R, A>, AllocError> {
297			let Self {
298				physical_allocator,
299				..
300			} = self;
301
302			let (base, backing) = match (physical_allocator, self.physical_location) {
303				(AllocatorTy::Pmm(allocator), Location::At(frame)) => {
304					let frames = allocator.allocate_at(frame, self.page_count)?;
305					(frames.base(), Backing::Contiguous(frames))
306				},
307				(AllocatorTy::Pmm(allocator), Location::Any) => {
308					let frames = allocator.allocate(self.page_count)?;
309					(frames.base(), Backing::Contiguous(frames))
310				},
311				(AllocatorTy::Vmo(vmo), _) => {
312					let base_addr = crate::bridge::handle::kernel_syscall_blocking(
313						&vmo,
314						6,
315						1,
316						[ 0 /* offset */, self.page_count.get() * 4096 /* len */, 0, 0 /* unused args */],
317					)?;
318					(RawFrame::new(base_addr as usize), Backing::Vmo { handle: vmo, frame_count: self.page_count.get() })
319				}
320			};
321
322			let virtual_len = raw.virtual_size(self.page_count.get());
323			let pages = match self.virtual_location {
324				Location::At(frame) => address_space.allocator().allocate_contiguous_at(frame, virtual_len.get())?,
325				Location::Any => address_space.allocator().allocate_contiguous(virtual_len.get())?,
326			};
327			let virtual_valid_start = pages + raw.base_virtual_offset();
328
329			match address_space.map_contiguous(
330				virtual_valid_start,
331				base,
332				self.page_count.get(),
333				self.reason,
334				self.protection,
335				self.caching,
336			) {
337				Ok(()) => {}
338				Err(MapPageError::AllocError) => return Err(AllocError::default()),
339				Err(MapPageError::AlreadyMapped(ty)) => unreachable!("unallocated memory already allocated as {ty:?}"),
340			}
341
342			Ok(Mapping {
343				raw,
344				address_space: ManuallyDrop::new(address_space),
345				backing: ManuallyDrop::new(backing),
346				caching: self.caching,
347				virtual_start: pages,
348				protection: self.protection,
349			})
350		}
351
352		/// Constructs a new `Config` for creating a [`Mapping`] with size `page_count` number of pages.
353		///
354		/// The following defaults are used:
355		/// - Any physical memory location, including discontiguous memory
356		/// - Any virtual memory location
357		/// - [`highmem`](`crate::memory#highmem`) used to allocate physical memory
358		/// - Read-only
359		/// - Inaccessible from userspace
360		/// - [`Normal`](`Caching::Normal`) cache strategy
361		///
362		/// Any unspecified defaults are not guaranteed, and should be explicitly specified
363		/// with the relevant methods if specific behaviour is required.
364		pub const fn new(page_count: NonZero<usize>, ty: Ty) -> Self {
365			Self {
366				physical_location: Location::Any,
367				virtual_location: Location::Any,
368				page_count,
369				physical_allocator: AllocatorTy::Pmm(highmem().into()),
370				protection: Protection::new(),
371				caching: Caching::new(),
372				reason: ty,
373			}
374		}
375
376		/// Sets the caching method used for the mapping.
377		///
378		/// See [`Caching`] for more information.
379		pub const fn caching(mut self, caching: Caching) -> Self {
380			self.caching = caching;
381			self
382		}
383
384		/// Sets the protection bits used when creating the mapping.
385		pub const fn protection(mut self, writable: bool, executable: bool, user_accessible: bool) -> Self {
386			self.protection = Protection { executable, writable, user_accessible };
387			self
388		}
389
390		/// Uses `allocator` to supply the physical memory for the mapping.
391		pub fn with_allocator<const RAM_ONLY: bool>(self, allocator: impl Into<DynPmm<'static, RAM_ONLY>>) -> Self {
392			Self {
393				physical_allocator: AllocatorTy::Pmm(allocator.into().into()),
394				.. self
395			}
396		}
397
398		/// Requires the mapping to be at a specific physical address.
399		pub const fn physical_location(mut self, at: RawFrame) -> Self {
400			self.physical_location = Location::At(at);
401			self
402		}
403
404		/// Requires the mapping to be at a specific virtual address.
405		pub const fn virtual_location(mut self, at: RawPage) -> Self {
406			self.virtual_location = Location::At(at);
407			self
408		}
409	}
410}