Skip to main content

kernel_api/ptr/
user_ptr.rs

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