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}