Skip to main content

kernel_api/mapping/
mod.rs

1//! RAII memory mappings
2
3mod config;
4pub use config::*;
5
6#[cfg(feature = "full")] mod mappable;
7#[cfg(feature = "full")] pub use mappable::*;
8#[cfg(feature = "full")] pub use full::*;
9
10#[cfg(feature = "full")]
11mod full {
12	use alloc::sync::Arc;
13	use super::*;
14	use core::fmt::{Debug, Formatter};
15	use crate::{address_space, dbg};
16	use core::mem::ManuallyDrop;
17	use core::num::NonZero;
18	use core::ops::Range;
19	use log::{debug, info, warn};
20	use crate::allocator::{AllocError, DynPmm};
21	use crate::memory::{Frames, RawFrame, RawPage, PAGE_SIZE};
22	use crate::ptr::User;
23	use crate::syscall::handle::Handle;
24
25	#[derive(Debug)]
26	pub enum MapPageError {
27		AlreadyMapped(Ty),
28		AllocError,
29	}
30
31	impl From<AllocError> for MapPageError {
32		fn from(_value: AllocError) -> Self {
33			Self::AllocError
34		}
35	}
36
37	/// Used to track if the memory underlying the mapping is contiguous
38	pub(super) enum Backing {
39		/// The underlying physical memory is contiguous, and starts at the contained frame
40		Contiguous(Frames<false>),
41
42		/// The underlying physical memory is discontiguous, but all allocated by the same
43		Discontiguous { pmm: DynPmm<'static, false>, frame_count: usize },
44
45		/// The mapping is backed by a VMO handle
46		Vmo { handle: Arc<Handle>, frame_count: usize },
47	}
48
49	impl Backing {
50		fn frame_len(&self) -> usize {
51			match self {
52				Backing::Contiguous(frames) => frames.count(),
53				Backing::Discontiguous { frame_count, .. } => *frame_count,
54				Backing::Vmo { frame_count, .. } => *frame_count,
55			}
56		}
57
58		fn byte_len(&self) -> usize {
59			self.frame_len() * PAGE_SIZE
60		}
61
62		fn pmm(&self) -> &DynPmm<'static, false> {
63			match self {
64				Backing::Contiguous(frames) => frames.pmm(),
65				Backing::Discontiguous { pmm, .. } => pmm,
66				Backing::Vmo { .. } => todo!(),
67			}
68		}
69	}
70
71	/// The raw type underlying all memory mappings.
72	///
73	/// This will allocate any required memory when created, and register any lazily mapped memory as such.
74	/// It will also manage the page tables to correctly unmap the memory when dropped.
75	pub struct Mapping<R: Mappable, A: address_space::Ty> {
76		pub(super) raw: R,
77
78		/// The address space mapped into
79		pub(super) address_space: ManuallyDrop<A>,
80
81		pub(super) backing: ManuallyDrop<Backing>,
82		//#[cfg(feature = "use_std")] pub(super) backing: *mut libc::c_void,
83
84		pub(super) caching: Caching,
85
86		pub(super) virtual_start: RawPage,
87
88		/// The protection used when mapping pages into this mapping
89		pub(super) protection: Protection,
90	}
91
92	impl<R: Mappable> Mapping<R, address_space::Kernel> {
93		pub fn as_mut_ptr_range(&mut self) -> Range<*mut u8> {
94			let start = self.virtual_valid_start().as_ptr();
95			Range {
96				start,
97				end: unsafe { start.byte_add(self.byte_len()) }
98			}
99		}
100
101		pub fn as_ptr_range(&self) -> Range<*const u8> {
102			let start = self.virtual_valid_start().as_ptr().cast_const();
103			Range {
104				start,
105				end: unsafe { start.byte_add(self.byte_len()) }
106			}
107		}
108
109		pub fn as_mut_ptr(&mut self) -> *mut u8 {
110			self.as_mut_ptr_range().start
111		}
112
113		pub fn as_ptr(&self) -> *const u8 {
114			self.as_ptr_range().start
115		}
116
117		//#[cfg(not(feature = "use_std"))]
118		pub unsafe fn from_raw_parts<const RAM: bool, T>(
119			frames: Frames<RAM, T>,
120			base_page: RawPage,
121			protection: Protection,
122			caching: Caching,
123		) -> Self where R: Default {
124			Self {
125				raw: R::default(),
126				address_space: ManuallyDrop::new(address_space::Kernel {}),
127				backing: ManuallyDrop::new(Backing::Contiguous(unsafe { Frames::<false>::from_raw_tuple(Frames::into_raw(frames)) })),
128				virtual_start: base_page,
129				protection,
130				caching,
131			}
132		}
133
134		//#[cfg(not(feature = "use_std"))]
135		pub fn into_raw_parts(self) -> (Option<Frames<false>>, RawPage, Protection, Caching) {
136			let mut this = ManuallyDrop::new(self);
137			(
138				match unsafe { ManuallyDrop::take(&mut this.backing) } {
139					Backing::Contiguous(frames) => Some(frames),
140					Backing::Discontiguous { .. } => None,
141					Backing::Vmo { .. } => todo!(),
142				},
143				this.virtual_start,
144				this.protection,
145				this.caching,
146			)
147		}
148	}
149
150	#[cfg(not(feature = "use_std"))]
151	impl<R: Mappable> Mapping<R, address_space::Userspace> {
152		pub fn as_mut_ptr_range(&mut self) -> Range<User<'_, *mut u8>> {
153			let start = self.virtual_valid_start().as_ptr();
154			let end = unsafe { start.byte_add(self.byte_len()) };
155			let start = User::<*mut u8>::new(start, &self.address_space.inner);
156			let end = User::<*mut u8>::new(end, &self.address_space.inner);
157			start..end
158		}
159
160		pub fn as_ptr_range(&self) -> Range<User<'_, *const u8>> {
161			let start = self.virtual_valid_start().as_ptr().cast_const();
162			let end = unsafe { start.byte_add(self.byte_len()) };
163			let start = unsafe { User::<*const u8>::new(start, &self.address_space.inner) };
164			let end = unsafe { User::<*const u8>::new(end, &self.address_space.inner) };
165			start..end
166		}
167
168		pub fn as_mut_ptr(&mut self) -> User<'_, *mut u8> {
169			self.as_mut_ptr_range().start
170		}
171
172		pub fn as_ptr(&self) -> User<'_, *const u8> {
173			self.as_ptr_range().start
174		}
175	}
176
177	impl<R: Mappable, A: address_space::Ty> Mapping<R, A> {
178		pub fn grow_in_place_by(&mut self, extra_length: usize) -> Result<(), AllocError> {
179			let Some(extra_length) = NonZero::new(extra_length) else { return Ok(()); };
180			let extra_frames = self.backing.pmm().allocate(extra_length)?;
181
182			let extra_pages = self.address_space.allocator()
183			                      .allocate_contiguous_at(
184				                      self.virtual_start + self.raw.virtual_size(self.page_len()).get(),
185				                      extra_length.get(),
186			                      )?;
187
188			debug!("growing mmap({})", core::any::type_name::<R>());
189			let _ = dbg!(self.virtual_start);
190			let _ = dbg!(self.virtual_valid_start());
191			let _ = dbg!(self.page_len());
192			let _ = dbg!(extra_length);
193			let _ = dbg!(self.raw.virtual_size(self.page_len()));
194
195			match self.address_space.map_contiguous(
196				self.virtual_valid_start() + self.page_len(),
197				extra_frames.into_raw().0.start,
198				extra_length.get(),
199				Ty(0),
200				self.protection,
201				self.caching,
202			) {
203				Ok(_) => Ok(()),
204				Err(MapPageError::AllocError) => {
205					self.address_space.allocator()
206							.deallocate_contiguous(extra_pages, extra_length.get());
207					Err(AllocError::default())
208				}
209				Err(MapPageError::AlreadyMapped(ty)) => unreachable!("unallocated memory already allocated as {ty:?}"),
210			}?;
211
212			let new_backing = Backing::Discontiguous {
213				pmm: *self.backing.pmm(),
214				frame_count: self.backing.frame_len().checked_add(extra_length.get()).unwrap()
215			};
216			self.backing = ManuallyDrop::new(new_backing); // don't drop the old backing since that could deallocate in-use frames
217
218			Ok(())
219		}
220
221		pub fn byte_len(&self) -> usize {
222			self.backing.byte_len()
223		}
224
225		pub fn page_len(&self) -> usize {
226			self.backing.frame_len()
227		}
228
229		pub fn physical_start(&self) -> Option<RawFrame> {
230			match &*self.backing {
231				Backing::Contiguous(frames) => Some(frames.base()),
232				Backing::Discontiguous { .. } => None,
233				Backing::Vmo { handle, .. } => Some({
234					RawFrame::new(crate::bridge::handle::kernel_syscall_blocking(
235						handle,
236						6,
237						1,
238						[0 /* offset */, self.page_len() * PAGE_SIZE /* len */, 0, 0 /* unused args */],
239					).ok()? as usize)
240				}),
241			}
242		}
243
244		/// The first page in the mapping that is mapped to physical memory.
245		/// The region of virtual memory from `virtual_valid_start()` to `virtual_valid_start() + physical_length` is mapped.
246		pub fn virtual_valid_start(&self) -> RawPage { self.virtual_start + self.raw.base_virtual_offset() }
247
248		pub fn set_writable(&mut self, writable: bool) {
249			self.protection.writable = writable;
250			todo!()
251		}
252	}
253
254	impl<R: Mappable, A: address_space::Ty> Debug for Mapping<R, A> {
255		fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
256			f.debug_struct("Mapping")
257			 .field_with(
258				 "backing",
259				 |f| {
260					 #[cfg(not(feature = "use_std"))] match &*self.backing {
261						 Backing::Contiguous(frame) => Debug::fmt(frame, f),
262						 Backing::Discontiguous { frame_count, .. } => write!(f, "Discontiguous {{ frame_count: {frame_count} }}"),
263						 Backing::Vmo { handle, frame_count } => write!(f, "Vmo {{ handle: {handle:?}, frame_count: {frame_count} }}"),
264					 }
265					 #[cfg(feature = "use_std")] Ok(())
266				 }
267			 )
268			 .field("address_space", &"<address space>")
269			 .field("protection", &self.protection)
270			 .field("caching", &self.caching)
271			 .finish_non_exhaustive()
272		}
273	}
274
275	impl<R: Mappable, A: address_space::Ty> Drop for Mapping<R, A> {
276		fn drop(&mut self) {
277			info!("drop mmap({})", core::any::type_name::<R>());
278
279			match unsafe { ManuallyDrop::take(&mut self.backing) } {
280				Backing::Contiguous(frames) => drop(frames),
281				Backing::Discontiguous { pmm, frame_count } => {
282					warn!("ignoring discontiguous page drop");
283				}
284				Backing::Vmo { handle, .. } => drop(handle),
285			}
286		}
287	}
288}