rust: uaccess: add write_dma() for copying from DMA buffers to userspace

Add UserSliceWriter::write_dma() to copy data from a Coherent<[u8]> to
userspace. This provides a safe interface for copying DMA buffer
contents to userspace without requiring callers to work with raw
pointers.

Because write_dma() and write_slice() have common code, factor that code
out into a helper function, write_raw().

The method handles bounds checking and offset calculation internally,
wrapping the unsafe copy_to_user() call.

Signed-off-by: Timur Tabi <ttabi@nvidia.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Acked-by: Miguel Ojeda <ojeda@kernel.org>
Tested-by: John Hubbard <jhubbard@nvidia.com>
Tested-by: Eliot Courtney <ecourtney@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Link: https://patch.msgid.link/20260319212658.2541610-3-ttabi@nvidia.com
[ Rebase onto Coherent<T> changes; remove unnecessary turbofish from
  cast(). - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
Timur Tabi
2026-03-19 16:26:54 -05:00
committed by Danilo Krummrich
parent d35ae50c5f
commit 69bfce0f25

View File

@@ -7,6 +7,7 @@
use crate::{
alloc::{Allocator, Flags},
bindings,
dma::Coherent,
error::Result,
ffi::{c_char, c_void},
fs::file,
@@ -459,20 +460,19 @@ pub fn is_empty(&self) -> bool {
self.length == 0
}
/// Writes raw data to this user pointer from a kernel buffer.
/// Low-level write from a raw pointer.
///
/// Fails with [`EFAULT`] if the write happens on a bad address, or if the write goes out of
/// bounds of this [`UserSliceWriter`]. This call may modify the associated userspace slice even
/// if it returns an error.
pub fn write_slice(&mut self, data: &[u8]) -> Result {
let len = data.len();
let data_ptr = data.as_ptr().cast::<c_void>();
/// # Safety
///
/// The caller must ensure that `from` is valid for reads of `len` bytes.
unsafe fn write_raw(&mut self, from: *const u8, len: usize) -> Result {
if len > self.length {
return Err(EFAULT);
}
// SAFETY: `data_ptr` points into an immutable slice of length `len`, so we may read
// that many bytes from it.
let res = unsafe { bindings::copy_to_user(self.ptr.as_mut_ptr(), data_ptr, len) };
// SAFETY: Caller guarantees `from` is valid for `len` bytes (see this function's
// safety contract).
let res = unsafe { bindings::copy_to_user(self.ptr.as_mut_ptr(), from.cast(), len) };
if res != 0 {
return Err(EFAULT);
}
@@ -481,6 +481,71 @@ pub fn write_slice(&mut self, data: &[u8]) -> Result {
Ok(())
}
/// Writes raw data to this user pointer from a kernel buffer.
///
/// Fails with [`EFAULT`] if the write happens on a bad address, or if the write goes out of
/// bounds of this [`UserSliceWriter`]. This call may modify the associated userspace slice even
/// if it returns an error.
pub fn write_slice(&mut self, data: &[u8]) -> Result {
// SAFETY: `data` is a valid slice, so `data.as_ptr()` is valid for
// reading `data.len()` bytes.
unsafe { self.write_raw(data.as_ptr(), data.len()) }
}
/// Writes raw data to this user pointer from a DMA coherent allocation.
///
/// Copies `count` bytes from `alloc` starting from `offset` into this userspace slice.
///
/// # Errors
///
/// - [`EOVERFLOW`]: `offset + count` overflows.
/// - [`ERANGE`]: `offset + count` exceeds the size of `alloc`, or `count` exceeds the
/// size of the user-space buffer.
/// - [`EFAULT`]: the write hits a bad address or goes out of bounds of this
/// [`UserSliceWriter`].
///
/// This call may modify the associated userspace slice even if it returns an error.
///
/// Note: The memory may be concurrently modified by hardware (e.g., DMA). In such cases,
/// the copied data may be inconsistent, but this does not cause undefined behavior.
///
/// # Example
///
/// Copy the first 256 bytes of a DMA coherent allocation into a userspace buffer:
///
/// ```no_run
/// use kernel::uaccess::UserSliceWriter;
/// use kernel::dma::Coherent;
///
/// fn copy_dma_to_user(
/// mut writer: UserSliceWriter,
/// alloc: &Coherent<[u8]>,
/// ) -> Result {
/// writer.write_dma(alloc, 0, 256)
/// }
/// ```
pub fn write_dma(&mut self, alloc: &Coherent<[u8]>, offset: usize, count: usize) -> Result {
let len = alloc.size();
if offset.checked_add(count).ok_or(EOVERFLOW)? > len {
return Err(ERANGE);
}
if count > self.len() {
return Err(ERANGE);
}
// SAFETY: `as_ptr()` returns a valid pointer to a memory region of `count()` bytes, as
// guaranteed by the `Coherent` invariants. The check above ensures `offset + count <= len`.
let src_ptr = unsafe { alloc.as_ptr().cast::<u8>().add(offset) };
// Note: Use `write_raw` instead of `write_slice` because the allocation is coherent
// memory that hardware may modify (e.g., DMA); we cannot form a `&[u8]` slice over
// such volatile memory.
//
// SAFETY: `src_ptr` points into the allocation and is valid for `count` bytes (see above).
unsafe { self.write_raw(src_ptr, count) }
}
/// Writes raw data to this user pointer from a kernel buffer partially.
///
/// This is the same as [`Self::write_slice`] but considers the given `offset` into `data` and