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}