Skip to main content

kernel_api/mapping/
mod.rs

1//! Memory mappings.
2//!
3//! Each memory mapping is made of a virtual allocation and a physical allocation,
4//! where the size of the virtual allocation is equal to or larger than the size
5//! of the physical allocation.
6//!
7//! The physical memory is then mapped to a space within the virtual allocation,
8//! and the rest of the virtual allocation is left reserved but inaccessible.
9//! The size of the physical allocation (and thus usable memory) is the value
10//! requested by users of the mapping API.
11//!
12//! The size of and offset within the virtual allocation is controlled by implementations
13//! of [`Mappable`], as shown below.
14//!
15//! ```text
16//! ---+------------------------------+---
17//!    |      virtual allocation      |
18//! ---+------------------------------+---
19//!     <----------------------------> `virtual_size()`
20//!     <---> `base_virtual_offset()`
21//!          +---------------------+
22//!          | physical allocation |
23//!          +---------------------+
24//! ```
25//!
26//! A [`Mapping`] can be created with [`Config::map`]:
27//!
28//! ```
29//! use kernel_api::mapping::{Config, Ty, Mmap};
30//! use core::num::NonZero;
31//!
32//! let mapping = Config::new(NonZero::new(1).unwrap(), Ty::KERNEL_OTHER)
33//!                   .map::<Mmap>()?;
34//! # Ok::<(), kernel_api::allocator::AllocError>::(())
35//! ```
36//!
37//! Data can then be read from and written to the mapping:
38//!
39//! ```
40//! use kernel_api::mapping::{Config, Ty, Mmap};
41//! use core::num::NonZero;
42//!
43//! let mut mapping = Config::new(NonZero::new(1).unwrap(), Ty::KERNEL_OTHER)
44//!                       .protection(/* writable: */ true, false, false)
45//!                       .map::<Mmap>()?;
46//!
47//! for ptr in mapping.as_mut_ptr_range() {
48//!     unsafe { *ptr = 1 };
49//! }
50//!
51//! for ptr in mapping.as_ptr_range() {
52//!     assert_eq!(unsafe { *ptr }, 1);
53//! }
54//! # Ok::<(), kernel_api::allocator::AllocError>::(())
55//! ```
56
57mod config;
58pub use config::*;
59
60#[cfg(feature = "full")] mod mappable;
61#[cfg(feature = "full")] pub use mappable::*;
62#[cfg(feature = "full")] pub use full::*;
63
64#[cfg(feature = "full")]
65mod full {
66	use alloc::sync::Arc;
67	use super::*;
68	use core::fmt::{Debug, Formatter};
69	use crate::address_space;
70	use core::mem::ManuallyDrop;
71	use core::num::NonZero;
72	use core::ops::Range;
73	use log::{debug, info, warn};
74	use crate::allocator::{AllocError, DynPmm};
75	use crate::memory::{Frames, RawFrame, RawPage, PAGE_SIZE};
76	use crate::ptr::User;
77	use crate::syscall::handle::Handle;
78
79	#[derive(Debug)]
80	pub enum MapPageError {
81		AlreadyMapped(Ty),
82		AllocError,
83	}
84
85	impl From<AllocError> for MapPageError {
86		fn from(_value: AllocError) -> Self {
87			Self::AllocError
88		}
89	}
90
91	/// Used to track if the memory underlying the mapping is contiguous
92	pub(super) enum Backing {
93		/// The underlying physical memory is contiguous, and starts at the contained frame
94		Contiguous(Frames<false>),
95
96		/// The underlying physical memory is discontiguous, but all allocated by the same
97		Discontiguous { pmm: DynPmm<'static, false>, frame_count: usize },
98
99		/// The mapping is backed by a VMO handle
100		Vmo { handle: Arc<Handle>, frame_count: usize },
101	}
102
103	impl Backing {
104		fn frame_len(&self) -> usize {
105			match self {
106				Backing::Contiguous(frames) => frames.count(),
107				Backing::Discontiguous { frame_count, .. } => *frame_count,
108				Backing::Vmo { frame_count, .. } => *frame_count,
109			}
110		}
111
112		fn byte_len(&self) -> usize {
113			self.frame_len() * PAGE_SIZE
114		}
115
116		fn pmm(&self) -> &DynPmm<'static, false> {
117			match self {
118				Backing::Contiguous(frames) => frames.pmm(),
119				Backing::Discontiguous { pmm, .. } => pmm,
120				Backing::Vmo { .. } => todo!(),
121			}
122		}
123	}
124
125	/// An owned memory mapping.
126	///
127	/// This will allocate any required memory when created, and register any lazily mapped memory as such.
128	/// It will also manage the page tables to correctly unmap the memory when dropped, and deallocate any
129	/// memory previously allocated.
130	///
131	/// See the [module level documentation](`super`) for more information.
132	pub struct Mapping<R: Mappable, A: address_space::Ty> {
133		pub(super) raw: R,
134
135		/// The address space mapped into
136		pub(super) address_space: ManuallyDrop<A>,
137
138		pub(super) backing: ManuallyDrop<Backing>,
139		//#[cfg(feature = "use_std")] pub(super) backing: *mut libc::c_void,
140
141		pub(super) caching: Caching,
142
143		pub(super) virtual_start: RawPage,
144
145		/// The protection used when mapping pages into this mapping
146		pub(super) protection: Protection,
147	}
148
149	impl<R: Mappable> Mapping<R, address_space::Kernel> {
150		/// Returns the two unsafe mutable pointers that span the range of the mapping.
151		///
152		/// The returned range is half-open, which means that the end pointer points *one past* the last element of the slice.
153		/// This way, an empty slice is represented by two equal pointers, and the difference between the two pointers
154		/// represents the size of the slice.
155		#[must_use]
156		pub fn as_mut_ptr_range(&mut self) -> Range<*mut u8> {
157			let start = self.virtual_valid_start().as_ptr();
158			Range {
159				start,
160				end: unsafe { start.byte_add(self.byte_len()) }
161			}
162		}
163
164		/// Returns the two unsafe pointers that span the range of the mapping.
165		///
166		/// The returned range is half-open, which means that the end pointer points *one past* the last element of the slice.
167		/// This way, an empty slice is represented by two equal pointers, and the difference between the two pointers
168		/// represents the size of the slice.
169		#[must_use]
170		pub fn as_ptr_range(&self) -> Range<*const u8> {
171			let start = self.virtual_valid_start().as_ptr().cast_const();
172			Range {
173				start,
174				end: unsafe { start.byte_add(self.byte_len()) }
175			}
176		}
177
178		/// Returns an unsafe mutable to the beginning of the mapping.
179		#[must_use]
180		pub fn as_mut_ptr(&mut self) -> *mut u8 {
181			self.as_mut_ptr_range().start
182		}
183
184		/// Returns an unsafe pointer to the beginning of the mapping.
185		#[must_use]
186		pub fn as_ptr(&self) -> *const u8 {
187			self.as_ptr_range().start
188		}
189
190		/// Creates a `Mapping` directly from its raw components.
191		///
192		/// # Safety
193		///
194		/// A section of virtual memory starting at `base_page` must be allocation by the kernel global virtual allocator.
195		/// This section must be equal in length to `R::virtual_size(frames.count())`.
196		/// The section of virtual memory from `base_page + R::base_virtual_offset()` must be contiguously mapped to the
197		/// physical memory owned by `frames`, with the cache and protection attributes given in `caching` and `protection`.
198		///
199		/// These requirements are always upheld by the return values of [`into_raw_parts`]. Other mapping sources are
200		/// allowed if all the invariants are upheld.
201		//#[cfg(not(feature = "use_std"))]
202		#[must_use]
203		pub unsafe fn from_raw_parts<const RAM: bool, T>(
204			frames: Frames<RAM, T>,
205			base_page: RawPage,
206			protection: Protection,
207			caching: Caching,
208		) -> Self where R: Default {
209			Self {
210				raw: R::default(),
211				address_space: ManuallyDrop::new(address_space::Kernel {}),
212				// SAFETY: `from_raw_tuple` called directly on result of `into_raw`
213				backing: ManuallyDrop::new(Backing::Contiguous(unsafe { Frames::<false>::from_raw_tuple(Frames::into_raw(frames)) })),
214				virtual_start: base_page,
215				protection,
216				caching,
217			}
218		}
219
220		/// Decomposes a `Mapping` into its raw components.
221		//#[cfg(not(feature = "use_std"))]
222		pub fn into_raw_parts(self) -> (Option<Frames<false>>, RawPage, Protection, Caching) {
223			let mut this = ManuallyDrop::new(self);
224			(
225				// SAFETY: `ManuallyDrop::take` is being called in function consuming `self` so
226				//  cannot be called again
227				match unsafe { ManuallyDrop::take(&mut this.backing) } {
228					Backing::Contiguous(frames) => Some(frames),
229					Backing::Discontiguous { .. } => None,
230					Backing::Vmo { .. } => todo!(),
231				},
232				this.virtual_start,
233				this.protection,
234				this.caching,
235			)
236		}
237	}
238
239	#[cfg(not(feature = "use_std"))]
240	impl<R: Mappable> Mapping<R, address_space::Userspace> {
241		/// Returns the two mutable [user pointers](`crate::ptr#user-pointers`) that span the range of the mapping.
242		///
243		/// The returned range is half-open, which means that the end pointer points *one past* the last element of the slice.
244		/// This way, an empty slice is represented by two equal pointers, and the difference between the two pointers
245		/// represents the size of the slice.
246		pub fn as_mut_ptr_range(&mut self) -> Range<User<'_, *mut u8>> {
247			let start = self.virtual_valid_start().as_ptr();
248			let end = unsafe { start.byte_add(self.byte_len()) };
249			let start = User::<*mut u8>::new(start, &self.address_space.inner);
250			let end = User::<*mut u8>::new(end, &self.address_space.inner);
251			start..end
252		}
253
254		/// Returns the two [user pointers](`crate::ptr#user-pointers`) that span the range of the mapping.
255		///
256		/// The returned range is half-open, which means that the end pointer points *one past* the last element of the slice.
257		/// This way, an empty slice is represented by two equal pointers, and the difference between the two pointers
258		/// represents the size of the slice.
259		pub fn as_ptr_range(&self) -> Range<User<'_, *const u8>> {
260			let start = self.virtual_valid_start().as_ptr().cast_const();
261			// SAFETY: `start` points to the beginning of an allocation which is `self.byte_len` bytes long
262			let end = unsafe { start.byte_add(self.byte_len()) };
263			// SAFETY: `start` points to a `u8` which has no invalid bit patterns
264			let start = unsafe { User::<*const u8>::new(start, &self.address_space.inner) };
265			// SAFETY: `end` points to a `u8` which has no invalid bit patterns
266			let end = unsafe { User::<*const u8>::new(end, &self.address_space.inner) };
267			start..end
268		}
269
270		/// Returns a mutable [user pointer](`crate::ptr#user-pointers`) to the beginning of the mapping.
271		pub fn as_mut_ptr(&mut self) -> User<'_, *mut u8> {
272			self.as_mut_ptr_range().start
273		}
274
275		/// Returns a [user pointer](`crate::ptr#user-pointers`) to the beginning of the mapping.
276		pub fn as_ptr(&self) -> User<'_, *const u8> {
277			self.as_ptr_range().start
278		}
279	}
280
281	impl<R: Mappable, A: address_space::Ty> Mapping<R, A> {
282		/// Add `extra_length` pages to the end of the existing mapping.
283		///
284		/// The mapping is grown in place, such that any addresses pointing within the existing mapping
285		/// remain valid.
286		///
287		/// The underlying physical memory may no longer be contiguous after this operation.
288		///
289		/// # Errors
290		///
291		/// Returns an [`AllocError`] describing the allocation that failed.
292		///
293		/// # Examples
294		///
295		/// ```
296		/// use kernel_api::mapping::{Config, Ty, Mmap};
297		/// use kernel_api::memory::PAGE_SIZE;
298		/// use core::num::NonZero;
299		///
300		/// let mut mapping = Config::new(NonZero::new(1).unwrap(), Ty::KERNEL_OTHER)
301		///                       .map::<Mmap>()?;
302		/// assert_eq!(mapping.page_len(), 1);
303		///
304		/// mapping.grow_in_place_by(1)?;
305		/// assert_eq!(mapping.page_len(), 2);
306		/// # Ok::<(), kernel_api::allocator::AllocError>::(())
307		/// ```
308		pub fn grow_in_place_by(&mut self, extra_length: usize) -> Result<(), AllocError> {
309			let Some(extra_length) = NonZero::new(extra_length) else { return Ok(()); };
310			let extra_frames = self.backing.pmm().allocate(extra_length)?;
311
312			let extra_pages = self.address_space.allocator()
313			                      .allocate_contiguous_at(
314				                      // FIXME: this only works is `virtual_size()` obeys superposition
315				                      self.virtual_start + self.raw.virtual_size(self.page_len()).get(),
316				                      extra_length.get(),
317			                      )?;
318
319			debug!("growing mmap({})", core::any::type_name::<R>());
320
321			match self.address_space.map_contiguous(
322				self.virtual_valid_start() + self.page_len(),
323				extra_frames.into_raw().0.start,
324				extra_length.get(),
325				Ty(0),
326				self.protection,
327				self.caching,
328			) {
329				Ok(_) => Ok(()),
330				Err(MapPageError::AllocError) => {
331					self.address_space.allocator()
332							.deallocate_contiguous(extra_pages, extra_length.get());
333					Err(AllocError::default())
334				}
335				Err(MapPageError::AlreadyMapped(ty)) => unreachable!("unallocated memory already allocated as {ty:?}"),
336			}?;
337
338			let new_backing = Backing::Discontiguous {
339				pmm: *self.backing.pmm(),
340				// fixme: provide some kind of warning on overflow instead of just leaking memory?
341				frame_count: self.backing.frame_len().checked_add(extra_length.get()).unwrap()
342			};
343			self.backing = ManuallyDrop::new(new_backing); // don't drop the old backing since that could deallocate in-use frames
344
345			Ok(())
346		}
347
348		/// The valid length of this mapping in bytes.
349		///
350		/// # Examples
351		///
352		/// ```
353		/// use kernel_api::mapping::{Config, Ty, Mmap};
354		/// use kernel_api::memory::PAGE_SIZE;
355		/// use core::num::NonZero;
356		///
357		/// let mapping = Config::new(NonZero::new(1).unwrap(), Ty::KERNEL_OTHER)
358		///                   .map::<Mmap>()?;
359		/// assert_eq!(mapping.byte_len(), 1 * PAGE_SIZE);
360		/// # Ok::<(), kernel_api::allocator::AllocError>::(())
361		/// ```
362		pub fn byte_len(&self) -> usize {
363			self.backing.byte_len()
364		}
365
366		/// The valid length of this mapping in pages.
367		///
368		/// # Examples
369		///
370		/// ```
371		/// use kernel_api::mapping::{Config, Ty, Mmap};
372		/// use core::num::NonZero;
373		///
374		/// let mapping = Config::new(NonZero::new(1).unwrap(), Ty::KERNEL_OTHER)
375		///                   .map::<Mmap>()?;
376		/// assert_eq!(mapping.page_len(), 1);
377		/// # Ok::<(), kernel_api::allocator::AllocError>::(())
378		/// ```
379		pub fn page_len(&self) -> usize {
380			self.backing.frame_len()
381		}
382
383		/// Returns the [`RawFrame`] that this mapping is mapped to.
384		///
385		/// This only makes sense for a physically contiguous mapping, and will return
386		/// `None` in other situations.
387		///
388		/// # Examples
389		///
390		/// ```
391		/// use kernel_api::mapping::{Config, Ty, Mmap};
392		/// use core::num::NonZero;
393		///
394		/// let mapping = Config::new(NonZero::new(1).unwrap(), Ty::KERNEL_OTHER)
395		///                   .map::<Mmap>()?;
396		/// assert!(mapping.physical_start().is_some());
397		/// # Ok::<(), kernel_api::allocator::AllocError>::(())
398		/// ```
399		///
400		/// ```ignore (incomplete)
401		/// use kernel_api::mapping::{Config, Ty, Mmap};
402		/// use core::num::NonZero;
403		///
404		/// let mapping = Config::new(NonZero::new(1).unwrap(), Ty::KERNEL_OTHER)
405		///                   .with_vmo(get_vmo(), 0)
406		///                   .map::<Mmap>()?;
407		/// assert!(mapping.physical_start().is_none());
408		/// # Ok::<(), kernel_api::allocator::AllocError>::(())
409		/// ```
410		pub fn physical_start(&self) -> Option<RawFrame> {
411			match &*self.backing {
412				Backing::Contiguous(frames) => Some(frames.base()),
413				Backing::Discontiguous { .. } => None,
414				Backing::Vmo { handle, .. } => Some({
415					RawFrame::new(crate::bridge::handle::kernel_syscall_blocking(
416						handle,
417						6,
418						1,
419						[0 /* offset */, self.page_len() * PAGE_SIZE /* len */, 0, 0 /* unused args */],
420					).ok()? as usize)
421				}),
422			}
423		}
424
425		/// The first page in the mapping that is mapped to physical memory.
426		/// The region of virtual memory from `virtual_valid_start()` to `virtual_valid_start() + physical_length` is mapped.
427		pub fn virtual_valid_start(&self) -> RawPage { self.virtual_start + self.raw.base_virtual_offset() }
428
429		pub fn set_writable(&mut self, writable: bool) {
430			self.protection.writable = writable;
431			todo!()
432		}
433	}
434
435	impl<R: Mappable, A: address_space::Ty> Debug for Mapping<R, A> {
436		fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
437			f.debug_struct("Mapping")
438			 .field_with(
439				 "backing",
440				 |f| {
441					 #[cfg(not(feature = "use_std"))] match &*self.backing {
442						 Backing::Contiguous(frame) => Debug::fmt(frame, f),
443						 Backing::Discontiguous { frame_count, .. } => write!(f, "Discontiguous {{ frame_count: {frame_count} }}"),
444						 Backing::Vmo { handle, frame_count } => write!(f, "Vmo {{ handle: {handle:?}, frame_count: {frame_count} }}"),
445					 }
446					 #[cfg(feature = "use_std")] Ok(())
447				 }
448			 )
449			 .field("address_space", &"<address space>")
450			 .field("protection", &self.protection)
451			 .field("caching", &self.caching)
452			 .finish_non_exhaustive()
453		}
454	}
455
456	impl<R: Mappable, A: address_space::Ty> Drop for Mapping<R, A> {
457		fn drop(&mut self) {
458			info!("drop mmap({})", core::any::type_name::<R>());
459
460			// SAFETY: `ManuallyDrop::take` called in the `drop` impl so can't be called again
461			match unsafe { ManuallyDrop::take(&mut self.backing) } {
462				Backing::Contiguous(frames) => drop(frames),
463				Backing::Discontiguous { .. } => {
464					warn!("ignoring discontiguous page drop");
465				}
466				Backing::Vmo { handle, .. } => drop(handle),
467			}
468		}
469	}
470}