blob: 73c001bee64f86cb4bc1185155cb1aa8c9845d73 [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use crate::arch_rs::{arch_copy_from_user, arch_copy_to_user};
use core::mem::MaybeUninit;
use zerocopy::{FromBytes, Immutable, IntoBytes};
use zx_status::Status;
/// A wrapper around a const pointer to user memory.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Default)]
pub struct UserInPtr<T> {
ptr: *const T,
}
impl<T> UserInPtr<T> {
/// Constructs a new `UserInPtr`.
pub const fn new(ptr: *const T) -> Self {
Self { ptr }
}
/// Returns true if the underlying pointer is null.
pub fn is_null(&self) -> bool {
self.ptr.is_null()
}
/// Returns the underlying raw pointer.
pub fn as_ptr(&self) -> *const T {
self.ptr
}
/// Returns a pointer offset by `count` bytes.
pub fn byte_offset(&self, count: isize) -> Self {
if self.ptr.is_null() {
return Self::new(core::ptr::null());
}
Self::new(self.ptr.wrapping_byte_offset(count))
}
/// Returns a pointer offset by `index` elements.
pub fn element_offset(&self, index: usize) -> Self {
if self.ptr.is_null() {
return Self::new(core::ptr::null());
}
Self::new(self.ptr.wrapping_add(index))
}
/// Reinterprets the pointer as a pointer to type `U`.
pub fn reinterpret<U>(&self) -> UserInPtr<U> {
UserInPtr::new(self.ptr.cast::<U>())
}
/// Copies a single element from userspace into `dst`.
pub fn copy_from_user<'a>(&self, dst: &'a mut MaybeUninit<T>) -> Result<&'a mut T, Status>
where
T: FromBytes,
{
// SAFETY: `dst.as_mut_ptr()` points to `size_of::<T>()` bytes of valid kernel memory. If
// `arch_copy_from_user` succeeds, `dst` is fully initialized with bytes from user memory.
// Since `T: FromBytes`, any bit pattern of size `size_of::<T>()` is a valid representation
// of `T`, making `assume_init_mut()` safe to call.
unsafe {
arch_copy_from_user(
dst.as_mut_ptr() as *mut core::ffi::c_void,
self.ptr as *const core::ffi::c_void,
core::mem::size_of::<T>(),
)?;
Ok(dst.assume_init_mut())
}
}
/// Reads and returns a single copyable element from userspace.
pub fn read(&self) -> Result<T, Status>
where
T: FromBytes,
{
let mut val = MaybeUninit::uninit();
self.copy_from_user(&mut val)?;
// SAFETY: `copy_from_user` succeeded, so `val` has been fully initialized with a valid
// byte representation of `T` (since `T: FromBytes`).
Ok(unsafe { val.assume_init() })
}
/// Copies a slice of elements from userspace into `dst`.
pub fn copy_slice_from_user<'a>(
&self,
dst: &'a mut [MaybeUninit<T>],
) -> Result<&'a mut [T], Status>
where
T: FromBytes,
{
let len_bytes = core::mem::size_of_val(dst);
// SAFETY: `dst.as_mut_ptr()` points to `len_bytes` of valid kernel memory buffer space.
// Upon successful return, all elements in `dst` are initialized with bytes from user
// memory. Since `T: FromBytes`, converting the raw pointer to a mutable slice of `T` with
// length `dst.len()` is safe.
unsafe {
arch_copy_from_user(
dst.as_mut_ptr() as *mut core::ffi::c_void,
self.ptr as *const core::ffi::c_void,
len_bytes,
)?;
Ok(core::slice::from_raw_parts_mut(dst.as_mut_ptr() as *mut T, dst.len()))
}
}
}
/// A wrapper around a mutable pointer to user memory (write-only).
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Default)]
pub struct UserOutPtr<T> {
ptr: *mut T,
}
impl<T> UserOutPtr<T> {
/// Constructs a new `UserOutPtr`.
pub const fn new(ptr: *mut T) -> Self {
Self { ptr }
}
/// Returns true if the underlying pointer is null.
pub fn is_null(&self) -> bool {
self.ptr.is_null()
}
/// Returns the underlying raw pointer.
pub fn as_ptr(&self) -> *mut T {
self.ptr
}
/// Returns a pointer offset by `count` bytes.
pub fn byte_offset(&self, count: isize) -> Self {
if self.ptr.is_null() {
return Self::new(core::ptr::null_mut());
}
Self::new(self.ptr.wrapping_byte_offset(count))
}
/// Returns a pointer offset by `index` elements.
pub fn element_offset(&self, index: usize) -> Self {
if self.ptr.is_null() {
return Self::new(core::ptr::null_mut());
}
Self::new(self.ptr.wrapping_add(index))
}
/// Reinterprets the pointer as a pointer to type `U`.
pub fn reinterpret<U>(&self) -> UserOutPtr<U> {
UserOutPtr::new(self.ptr.cast::<U>())
}
/// Copies a single element from `src` to userspace.
pub fn copy_to_user(&self, src: &T) -> Result<(), Status>
where
T: IntoBytes + Immutable,
{
let src_bytes = src.as_bytes();
// SAFETY: `src_bytes.as_ptr()` points to `src_bytes.len()` bytes of valid kernel memory.
unsafe {
arch_copy_to_user(
self.ptr as *mut core::ffi::c_void,
src_bytes.as_ptr() as *const core::ffi::c_void,
src_bytes.len(),
)
}
}
/// Writes a single copyable value to userspace.
pub fn write(&self, val: T) -> Result<(), Status>
where
T: IntoBytes + Immutable,
{
self.copy_to_user(&val)
}
/// Copies a slice of elements from `src` to userspace.
pub fn copy_slice_to_user(&self, src: &[T]) -> Result<(), Status>
where
T: IntoBytes + Immutable,
{
let src_bytes = src.as_bytes();
// SAFETY: `src_bytes.as_ptr()` points to `src_bytes.len()` bytes of valid kernel memory
// slice.
unsafe {
arch_copy_to_user(
self.ptr as *mut core::ffi::c_void,
src_bytes.as_ptr() as *const core::ffi::c_void,
src_bytes.len(),
)
}
}
}
/// A wrapper around a mutable pointer to user memory (read-write).
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Default)]
pub struct UserInOutPtr<T> {
ptr: *mut T,
}
impl<T> UserInOutPtr<T> {
/// Constructs a new `UserInOutPtr`.
pub const fn new(ptr: *mut T) -> Self {
Self { ptr }
}
/// Returns true if the underlying pointer is null.
pub fn is_null(&self) -> bool {
self.ptr.is_null()
}
/// Returns the underlying raw pointer.
pub fn as_ptr(&self) -> *mut T {
self.ptr
}
/// Returns a pointer offset by `count` bytes.
pub fn byte_offset(&self, count: isize) -> Self {
if self.ptr.is_null() {
return Self::new(core::ptr::null_mut());
}
Self::new(self.ptr.wrapping_byte_offset(count))
}
/// Returns a pointer offset by `index` elements.
pub fn element_offset(&self, index: usize) -> Self {
if self.ptr.is_null() {
return Self::new(core::ptr::null_mut());
}
Self::new(self.ptr.wrapping_add(index))
}
/// Reinterprets the pointer as a pointer to type `U`.
pub fn reinterpret<U>(&self) -> UserInOutPtr<U> {
UserInOutPtr::new(self.ptr.cast::<U>())
}
/// Returns a `UserInPtr` viewing the same user memory.
pub const fn as_in_ptr(&self) -> UserInPtr<T> {
UserInPtr::new(self.ptr)
}
/// Returns a `UserOutPtr` viewing the same user memory.
pub const fn as_out_ptr(&self) -> UserOutPtr<T> {
UserOutPtr::new(self.ptr)
}
/// Copies a single element from userspace into `dst`.
pub fn copy_from_user<'a>(&self, dst: &'a mut MaybeUninit<T>) -> Result<&'a mut T, Status>
where
T: FromBytes + IntoBytes,
{
self.as_in_ptr().copy_from_user(dst)
}
/// Reads and returns a single copyable element from userspace.
pub fn read(&self) -> Result<T, Status>
where
T: FromBytes + IntoBytes + Immutable,
{
self.as_in_ptr().read()
}
/// Copies a slice of elements from userspace into `dst`.
pub fn copy_slice_from_user<'a>(
&self,
dst: &'a mut [MaybeUninit<T>],
) -> Result<&'a mut [T], Status>
where
T: FromBytes + IntoBytes,
{
self.as_in_ptr().copy_slice_from_user(dst)
}
/// Copies a single element from `src` to userspace.
pub fn copy_to_user(&self, src: &T) -> Result<(), Status>
where
T: IntoBytes + Immutable,
{
self.as_out_ptr().copy_to_user(src)
}
/// Writes a single copyable value to userspace.
pub fn write(&self, val: T) -> Result<(), Status>
where
T: IntoBytes + Immutable,
{
self.as_out_ptr().write(val)
}
/// Copies a slice of elements from `src` to userspace.
pub fn copy_slice_to_user(&self, src: &[T]) -> Result<(), Status>
where
T: IntoBytes + Immutable,
{
self.as_out_ptr().copy_slice_to_user(src)
}
}