Skip to main content

kernel_api/ptr/
user_ptr.rs

1use crate::ptr::{impls, PointerError};
2use core::fmt;
3use core::fmt::Formatter;
4use core::mem::MaybeUninit;
5use crate::address_space::AddressSpace;
6use crate::memory::VirtualAddress;
7
8/// A pointer to a potentially invalid address, tied to a specific address space
9///
10/// While the pointer may safely point to an invalid or unaligned address, in the case that
11/// it points to a valid address and is read from, then it is unsound for the address to not
12/// hold a valid bit-pattern for the type `T`.
13///
14/// For example, it is always sound to create a `User<*const u8>` regardless of the address it points
15/// to, and always safe to call `read()` on it, but it would be unsound to create a
16/// `User<*const bool>`if it's not guaranteed that the pointed value is either `1` or `0`.
17///
18/// Additionally, to make the API require less `unsafe` for common cases, `User<*mut T>` is always
19/// safe to construct, with the additional requirement that it becomes write-only.
20// todo: maybe don't allow access to kernel addresses
21#[derive(Clone, Copy)]
22pub struct User<'a, T> {
23	ptr: T,
24	address_space: Option<&'a AddressSpace>,
25}
26
27// SAFETY: All access goes through checked functions which ensure the pointer is valid
28// to dereference in the current address space. Therefore, when used on a different thread
29// the pointer will either be fine to use (as long as the type it points to is safe to
30// read from another thread) or it will return an error
31unsafe impl<T: Send + ?Sized> Send for User<'_, *mut T> {}
32
33// SAFETY: see above comment on `*mut T`
34unsafe impl<T: Send + ?Sized> Send for User<'_, *const T> {}
35
36impl<T: fmt::Pointer> fmt::Pointer for User<'_, T> {
37	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
38		fmt::Pointer::fmt(&self.ptr, f)
39	}
40}
41
42impl<T: fmt::Pointer> fmt::Debug for User<'_, T> {
43	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
44		fmt::Pointer::fmt(&self.ptr, f)
45	}
46}
47
48/// Produces a null pointer
49///
50/// See [`null_mut()`] for more details
51impl<T: core::ptr::Thin + ?Sized> Default for User<'_, *mut T> {
52	fn default() -> Self {
53		null_mut()
54	}
55}
56
57/// Produces a null pointer
58///
59/// See [`null()`] for more details
60impl<T: core::ptr::Thin + ?Sized> Default for User<'_, *const T> {
61	fn default() -> Self {
62		null()
63	}
64}
65
66/// Produces a null [`User<*mut T>`]
67///
68/// This will always return an error when writing to
69pub const fn null_mut<T: core::ptr::Thin + ?Sized>() -> User<'static, *mut T> {
70	User {
71		ptr: core::ptr::null_mut(),
72		address_space: None,
73	}
74}
75
76/// Produces a null [`User<*const T>`]
77///
78/// This will always return an error when reading from
79pub const fn null<T: core::ptr::Thin + ?Sized>() -> User<'static, *const T> {
80	User {
81		ptr: core::ptr::null(),
82		address_space: None,
83	}
84}
85
86/// Pointer equality is by address space, and [`<*mut T>::eq`].
87impl<T: ?Sized> PartialEq for User<'_, *mut T> {
88	#[expect(ambiguous_wide_pointer_comparisons, reason = "want same behaviour as `PartialEq` on raw pointer")]
89	fn eq(&self, other: &Self) -> bool {
90		let ptr_eq = self.ptr == other.ptr;
91		let address_space_eq = self.address_space.is_some_and(
92			|this| other.address_space.is_some_and(|other| AddressSpace::ptr_eq(this, other))
93		);
94		ptr_eq && address_space_eq
95	}
96}
97
98/// Pointer equality is by address space, and [`<*const T>::eq`].
99impl<T: ?Sized> PartialEq for User<'_, *const T> {
100	#[expect(ambiguous_wide_pointer_comparisons, reason = "want same behaviour as `PartialEq` on raw pointer")]
101	fn eq(&self, other: &Self) -> bool {
102		let ptr_eq = self.ptr == other.ptr;
103		let address_space_eq = self.address_space.is_some_and(
104			|this| other.address_space.is_some_and(|other| AddressSpace::ptr_eq(this, other))
105		);
106		ptr_eq && address_space_eq
107	}
108}
109
110/*
111impl<T: ?Sized> PartialOrd for User<'_, *mut T> {
112	#[expect(ambiguous_wide_pointer_comparisons, reason = "want same behaviour as `PartialOrd` on raw pointer")]
113	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
114		if self.address_space != other.address_space { None }
115		else { self.ptr.partial_cmp(&other.ptr) }
116	}
117}
118
119impl<T: ?Sized> PartialOrd for User<'_, *const T> {
120	#[expect(ambiguous_wide_pointer_comparisons, reason = "want same behaviour as `PartialOrd` on raw pointer")]
121	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
122		if self.address_space != other.address_space { None }
123		else { self.ptr.partial_cmp(&other.ptr) }
124	}
125}
126*/
127
128impl<T: ?Sized> Eq for User<'_, *const T> {}
129impl<T: ?Sized> Eq for User<'_, *mut T> {}
130
131impl<'a, T: ?Sized> User<'a, *const T> {
132	/// Creates a new `User<*const T>` with the provided address tied to the provided address space
133	///
134	/// # Safety
135	///
136	/// `addr`, if valid, must point to a valid bit pattern for `T` in the current address space.
137	/// This means that in almost all cases, it is unsound to create a `User<*const T>` where `T`
138	/// has a niche.
139	///
140	/// For all types with no invalid bit-patterns (i.e. all numeric types) it is sound to create
141	/// a `User<*const T>`.
142	pub(crate) unsafe fn new(ptr: *const T, address_space: &'a AddressSpace) -> Self where T: Sized {
143		Self {
144			ptr,
145			address_space: Some(address_space),
146		}
147	}
148
149	/// Returns `true` if the pointer has either a null address or address space
150	pub const fn is_null(&self) -> bool {
151		self.ptr.is_null() || self.address_space.is_none()
152	}
153
154	/// Adds a signed offset to a pointer.
155	///
156	/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
157	/// offset of `3 * size_of::<T>()` bytes.
158	///
159	/// # Safety
160	///
161	/// See safety requirements for [`<*const T>::offset()`]
162	pub const unsafe fn offset(self, count: isize) -> Self where T: Sized {
163		Self {
164			// SAFETY: this function has the same safety requirements as `<*const T>::offset()`
165			ptr: unsafe { self.ptr.offset(count) },
166			address_space: self.address_space,
167		}
168	}
169
170	/// Adds a signed offset in bytes to a pointer.
171	///
172	/// # Safety
173	///
174	/// See safety requirements for [`<*const T>::byte_offset()`]
175	pub const unsafe fn byte_offset(self, count: isize) -> Self {
176		Self {
177			// SAFETY: this function has the same safety requirements as `<*const T>::byte_offset()`
178			ptr: unsafe { self.ptr.byte_offset(count) },
179			address_space: self.address_space,
180		}
181	}
182
183	/// Adds an offset to a pointer.
184	///
185	/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
186	/// offset of `3 * size_of::<T>()` bytes.
187	///
188	/// # Safety
189	///
190	/// See safety requirements for [`<*const T>::add()`]
191	pub const unsafe fn add(self, count: usize) -> Self where T: Sized {
192		Self {
193			// SAFETY: this function has the same safety requirements as `<*const T>::add()`
194			ptr: unsafe { self.ptr.add(count) },
195			address_space: self.address_space,
196		}
197	}
198
199	/// Adds an offset in bytes to a pointer.
200	///
201	/// # Safety
202	///
203	/// See safety requirements for [`<*const T>::byte_add()`]
204	pub const unsafe fn byte_add(self, count: usize) -> Self {
205		Self {
206			// SAFETY: this function has the same safety requirements as `<*const T>::byte_add()`
207			ptr: unsafe { self.ptr.byte_add(count) },
208			address_space: self.address_space,
209		}
210	}
211
212	/// Subtracts an offset from a pointer.
213	///
214	/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
215	/// offset of `3 * size_of::<T>()` bytes.
216	///
217	/// # Safety
218	///
219	/// See safety requirements for [`<*const T>::sub()`]
220	pub const unsafe fn sub(self, count: usize) -> Self where T: Sized {
221		Self {
222			// SAFETY: this function has the same safety requirements as `<*const T>::sub()`
223			ptr: unsafe { self.ptr.sub(count) },
224			address_space: self.address_space,
225		}
226	}
227
228	/// Subtracts an offset in bytes from a pointer.
229	///
230	/// # Safety
231	///
232	/// See safety requirements for [`<*const T>::byte_sub()`]
233	pub const unsafe fn byte_sub(self, count: usize) -> Self {
234		Self {
235			// SAFETY: this function has the same safety requirements as `<*const T>::byte_sub()`
236			ptr: unsafe { self.ptr.byte_sub(count) },
237			address_space: self.address_space,
238		}
239	}
240
241	/*
242	/// Tries to read the value pointed to by the pointer
243	///
244	/// # Errors
245	///
246	/// If the pointer is invalid due to unmapped memory or similar, returns [`PointerError`]
247	// todo: do we need the `Copy` bound
248	pub fn read(self) -> Result<T, PointerError> where T: Sized + Copy {
249		assert!(
250			crate::bridge::address_space::is_current(self.address_space),
251			"Address space of User<*> should match current address space",
252		);
253		
254		match size_of::<T>() {
255			1 => unsafe {
256				impls::checked_read_1(self.ptr.cast())
257						.map(|val| (&val as *const MaybeUninit<u8>).cast::<T>().read())
258			},
259			2 => unsafe {
260				impls::checked_read_2(self.ptr.cast())
261						.map(|val| (&val as *const MaybeUninit<u16>).cast::<T>().read())
262			},
263			4 => unsafe {
264				impls::checked_read_4(self.ptr.cast())
265						.map(|val| (&val as *const MaybeUninit<u32>).cast::<T>().read())
266			},
267			#[cfg(target_pointer_width = "64")] 8 => unsafe {
268				impls::checked_read_8(self.ptr.cast())
269						.map(|val| (&val as *const MaybeUninit<u64>).cast::<T>().read())
270			},
271			size => {
272				let mut buf = MaybeUninit::<T>::uninit();
273				impls::checked_memcpy(self.ptr.cast(), buf.as_mut_ptr().cast(), size)
274						.map(|_| unsafe { buf.assume_init() })
275			}
276		}.ok_or(PointerError {})
277	}*/
278
279	pub fn read_other_address_space(self) -> Result<T, PointerError> where T: Sized + Copy {
280		todo!()
281	}
282
283	/// Casts a pointer to another type
284	///
285	/// # Safety
286	///
287	/// If `self` has a valid address, then it must point to a valid bit pattern for `U` in the current
288	/// address space.
289	/// This means that in almost all cases, it is unsound to cast to a `User<*const U>` where `U`
290	/// has a niche.
291	///
292	/// For all types with no invalid bit-patterns (i.e. all numeric types) it is sound to cast to
293	/// a `User<*const U>`.
294	pub const unsafe fn cast<U>(self) -> User<'a, *const U> {
295		User {
296			ptr: self.ptr.cast(),
297			address_space: self.address_space,
298		}
299	}
300
301	/// Casts to a writable pointer
302	pub const fn cast_mut(self) -> User<'a, *mut T> {
303		User {
304			ptr: self.ptr.cast_mut(),
305			address_space: self.address_space,
306		}
307	}
308
309	pub fn is_aligned_to(self, align: usize) -> bool {
310		self.ptr.is_aligned_to(align)
311	}
312
313	pub fn addr(self) -> VirtualAddress {
314		VirtualAddress::new(self.ptr.addr())
315	}
316
317	pub fn align_offset(self, align: usize) -> usize where T: Sized {
318		self.ptr.align_offset(align)
319	}
320}
321
322impl<'a, T: ?Sized> User<'a, *mut T> {
323	/// Creates a new `User<*mut T>` with the provided address tied to the provided address space
324	pub(crate) fn new(ptr: *mut T, address_space: &'a AddressSpace) -> Self where T: Sized {
325		Self {
326			ptr,
327			address_space: Some(address_space),
328		}
329	}
330
331	/// Returns `true` if the pointer has either a null address or address space
332	pub const fn is_null(&self) -> bool {
333		self.ptr.is_null() || self.address_space.is_none()
334	}
335
336	/// Adds a signed offset to a pointer.
337	///
338	/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
339	/// offset of `3 * size_of::<T>()` bytes.
340	///
341	/// # Safety
342	///
343	/// See safety requirements for [`<*mut T>::offset()`]
344	pub const unsafe fn offset(self, count: isize) -> Self where T: Sized {
345		Self {
346			// SAFETY: this function has the same safety requirements as `<*mut T>::offset()`
347			ptr: unsafe { self.ptr.offset(count) },
348			address_space: self.address_space,
349		}
350	}
351
352	/// Adds a signed offset in bytes to a pointer.
353	///
354	/// # Safety
355	///
356	/// See safety requirements for [`<*mut T>::byte_offset()`]
357	pub const unsafe fn byte_offset(self, count: isize) -> Self {
358		Self {
359			// SAFETY: this function has the same safety requirements as `<*mut T>::byte_offset()`
360			ptr: unsafe { self.ptr.byte_offset(count) },
361			address_space: self.address_space,
362		}
363	}
364
365	/// Adds an offset to a pointer.
366	///
367	/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
368	/// offset of `3 * size_of::<T>()` bytes.
369	///
370	/// # Safety
371	///
372	/// See safety requirements for [`<*mut T>::add()`]
373	pub const unsafe fn add(self, count: usize) -> Self where T: Sized {
374		Self {
375			// SAFETY: this function has the same safety requirements as `<*mut T>::add()`
376			ptr: unsafe { self.ptr.add(count) },
377			address_space: self.address_space,
378		}
379	}
380
381	/// Adds an offset in bytes to a pointer.
382	///
383	/// # Safety
384	///
385	/// See safety requirements for [`<*mut T>::byte_add()`]
386	pub const unsafe fn byte_add(self, count: usize) -> Self {
387		Self {
388			// SAFETY: this function has the same safety requirements as `<*mut T>::byte_add()`
389			ptr: unsafe { self.ptr.byte_add(count) },
390			address_space: self.address_space,
391		}
392	}
393
394	/// Subtracts an offset from a pointer.
395	///
396	/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
397	/// offset of `3 * size_of::<T>()` bytes.
398	///
399	/// # Safety
400	///
401	/// See safety requirements for [`<*mut T>::sub()`]
402	pub const unsafe fn sub(self, count: usize) -> Self where T: Sized {
403		Self {
404			// SAFETY: this function has the same safety requirements as `<*mut T>::sub()`
405			ptr: unsafe { self.ptr.sub(count) },
406			address_space: self.address_space,
407		}
408	}
409
410	/// Subtracts an offset in bytes from a pointer.
411	///
412	/// # Safety
413	///
414	/// See safety requirements for [`<*mut T>::byte_sub()`]
415	pub const unsafe fn byte_sub(self, count: usize) -> Self {
416		Self {
417			// SAFETY: this function has the same safety requirements as `<*mut T>::byte_sub()`
418			ptr: unsafe { self.ptr.byte_sub(count) },
419			address_space: self.address_space,
420		}
421	}
422
423	/*
424	/// Tries to write to the value pointed to by the pointer
425	///
426	/// # Errors
427	///
428	/// If the pointer is invalid due to unmapped memory or similar, returns [`PointerError`]
429	pub fn write(self, value: T) -> Result<(), PointerError> where T: Sized {
430		trace!("direct write <val> -> {:#p}", self);
431		
432		assert!(
433			crate::bridge::address_space::is_current(self.address_space),
434			"Address space of User<*> should match current address space",
435		);
436			
437		match size_of::<T>() {
438			1 => unsafe {
439				impls::checked_write_1(self.ptr.cast(), (&value as *const T).cast::<MaybeUninit<u8>>().read())
440			},
441			2 => unsafe {
442				impls::checked_write_2(self.ptr.cast(), (&value as *const T).cast::<MaybeUninit<u16>>().read())
443			},
444			4 => unsafe {
445				impls::checked_write_4(self.ptr.cast(), (&value as *const T).cast::<MaybeUninit<u32>>().read())
446			},
447			#[cfg(target_pointer_width = "64")] 8 => unsafe {
448				impls::checked_write_8(self.ptr.cast(), (&value as *const T).cast::<MaybeUninit<u64>>().read())
449			},
450			size => {
451				impls::checked_memcpy((&value as *const T).cast(), self.ptr.cast(), size)
452			}
453		}.ok_or(PointerError {})
454	}*/
455
456	pub fn write_other_address_space(self, value: T) -> Result<(), PointerError> where T: Sized {
457		unsafe { self.copy_from_other_address_space(&raw const value, 1)? };
458		core::mem::forget(value);
459		Ok(())
460	}
461
462	/// # Safety
463	///
464	/// `src` must be [valid](`core::ptr#safety`) for reads of `count * size_of::<T>()` bytes or that number must be 0.
465	///
466	/// Like [`core::ptr::read`], `copy_from_other_address_space` creates a bitwise copy of `T`, regardless of
467	/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the values
468	/// in the region beginning at `*start` and the region pointed to by `self` can
469	/// [violate memory safety](`core::ptr::read#ownership-of-the-returned-value`).
470	// FIXME(soundness): doesn't check if `self` points to conventional memory and therefore may write outside the page map region
471	pub unsafe fn copy_from_other_address_space(self, start: *const T, count: usize) -> Result<(), PointerError> where T: Sized {
472		let Some(address_space) = self.address_space else {
473			return Err(PointerError {});
474		};
475
476		let dest_start = self.ptr.cast::<u8>();
477		let mut chunk_start = dest_start;
478		let end = unsafe { self.ptr.add(count).cast::<u8>() };
479
480		loop {
481			let chunk_end = {
482				let chunk_page_end = VirtualAddress::from(chunk_start).align_down_to_page() + 1usize;
483				if chunk_page_end.addr >= end.addr() {
484					end
485				} else {
486					chunk_page_end.as_ptr()
487				}
488			};
489
490			let physical = crate::bridge::address_space::user::translate_addr(address_space, VirtualAddress::from(chunk_start))
491					.ok_or(PointerError {})?;
492			let physical = physical.to_virtual().as_ptr();
493
494			let chunk_size = unsafe { chunk_end.offset_from_unsigned(chunk_start) };
495
496			unsafe {
497				let offset = chunk_start.offset_from(dest_start);
498				core::ptr::copy_nonoverlapping(
499					start.cast::<u8>().byte_offset(offset),
500					physical,
501					chunk_size,
502				);
503			}
504
505			if chunk_end == end { break; }
506			else { chunk_start = chunk_end; }
507		}
508
509		Ok(())
510	}
511
512	/// Casts a pointer to another type
513	pub const fn cast<U>(self) -> User<'a, *mut U> {
514		User {
515			ptr: self.ptr.cast(),
516			address_space: self.address_space,
517		}
518	}
519
520	/// Casts to a readable pointer
521	///
522	/// # Safety
523	///
524	/// `addr`, if valid, must point to a valid bit pattern for `T` in the current address space.
525	/// This means that in almost all cases, it is unsound to cast to a `User<*const T>` where `T`
526	/// has a niche.
527	///
528	/// For all types with no invalid bit-patterns (i.e. all numeric types) it is sound to cast to
529	/// a `User<*const T>`.
530	pub const unsafe fn cast_const(self) -> User<'a, *const T> {
531		User {
532			ptr: self.ptr.cast_const(),
533			address_space: self.address_space,
534		}
535	}
536
537	pub fn is_aligned_to(self, align: usize) -> bool {
538		self.ptr.is_aligned_to(align)
539	}
540
541	pub fn addr(self) -> VirtualAddress {
542		VirtualAddress::new(self.ptr.addr())
543	}
544
545	pub fn align_offset(self, align: usize) -> usize where T: Sized {
546		self.ptr.align_offset(align)
547	}
548}
549
550impl<'a, T> User<'a, *const [T]> {
551	pub const fn len(self) -> usize { self.ptr.len() }
552	
553	pub const fn is_empty(self) -> bool { self.ptr.is_empty() }
554	
555	pub const fn as_ptr(self) -> User<'a, *const T> {
556		User {
557			ptr: self.ptr.as_ptr(),
558			address_space: self.address_space,
559		}
560	}
561
562	/*
563	pub fn read_to_box(self) -> Result<Box<[T]>, PointerError> {
564		let mut buf = Box::new_uninit_slice(self.len());
565		let size = self.read_to_buffer(&mut buf)?;
566		assert_eq!(size, buf.len(), "box should be big enough");
567		Ok(unsafe { buf.assume_init() })
568	}
569	
570	pub fn read_to_buffer(self, buffer: &mut [MaybeUninit<T>]) -> Result<usize, PointerError> {
571		assert!(
572			crate::bridge::address_space::is_current(self.address_space),
573			"Address space of User<*> should match current address space",
574		);
575		
576		let count = min(buffer.len(), self.len());
577		
578		impls::checked_memcpy(
579			self.ptr.cast(),
580			buffer.as_mut_ptr().cast(),
581			size_of::<T>() * count
582		).ok_or(PointerError {})?;
583		
584		Ok(count)
585	}*/
586
587	/*
588	pub fn read_to_buffer_other_address_space(self, buffer: &mut [MaybeUninit<T>]) -> Result<usize, PointerError> {
589		todo!()
590	}*/
591}
592
593impl<'a, T> User<'a, *mut [T]> {
594	pub const fn len(self) -> usize { self.ptr.len() }
595
596	pub const fn is_empty(self) -> bool { self.ptr.is_empty() }
597
598	pub const fn as_mut_ptr(self) -> User<'a, *mut T> {
599		User {
600			ptr: self.ptr.as_mut_ptr(),
601			address_space: self.address_space,
602		}
603	}
604
605	/*
606	pub fn write_from_slice(self, slice: &[T]) -> Result<usize, PointerError> {
607		assert!(
608			crate::bridge::address_space::is_current(self.address_space),
609			"Address space of User<*> should match current address space",
610		);
611
612		let count = min(slice.len(), self.len());
613
614		impls::checked_memcpy(
615			slice.as_ptr().cast(),
616			self.ptr.cast(),
617			size_of::<T>() * count
618		).ok_or(PointerError {})?;
619
620		Ok(count)
621	}
622
623
624	pub fn write_from_slice_other_address_space(self, slice: &[T]) -> Result<usize, PointerError> {
625		todo!()
626	}*/
627	
628	pub fn fill(self, value: u8) -> Result<(), PointerError> {
629		impls::checked_fill(
630			MaybeUninit::new(value),
631			self.ptr.cast(),
632			size_of::<T>() * self.len(),
633		).ok_or(PointerError {})?;
634		
635		Ok(())
636	}
637}
638
639/// # Safety
640///
641/// If `data` points to accessible memory, it must point to `len` elements of type `T`.
642/// See [`User::<*const T>::new`](`User::<*const T>::new#safety`) for more details.
643pub unsafe fn slice_from_raw_parts<T>(data: User<*const T>, len: usize) -> User<*const [T]> {
644	let ptr = core::ptr::slice_from_raw_parts(data.ptr, len);
645	User {
646		ptr,
647		address_space: data.address_space,
648	}
649}
650
651pub fn slice_from_raw_parts_mut<T>(data: User<*mut T>, len: usize) -> User<*mut [T]> {
652	let ptr = core::ptr::slice_from_raw_parts_mut(data.ptr, len);
653	User {
654		ptr,
655		address_space: data.address_space,
656	}
657}