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}