Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Timur Tabi and committed by
Danilo Krummrich
69bfce0f d35ae50c

+75 -10
+75 -10
rust/kernel/uaccess.rs
··· 7 7 use crate::{ 8 8 alloc::{Allocator, Flags}, 9 9 bindings, 10 + dma::Coherent, 10 11 error::Result, 11 12 ffi::{c_char, c_void}, 12 13 fs::file, ··· 460 459 self.length == 0 461 460 } 462 461 463 - /// Writes raw data to this user pointer from a kernel buffer. 462 + /// Low-level write from a raw pointer. 464 463 /// 465 - /// Fails with [`EFAULT`] if the write happens on a bad address, or if the write goes out of 466 - /// bounds of this [`UserSliceWriter`]. This call may modify the associated userspace slice even 467 - /// if it returns an error. 468 - pub fn write_slice(&mut self, data: &[u8]) -> Result { 469 - let len = data.len(); 470 - let data_ptr = data.as_ptr().cast::<c_void>(); 464 + /// # Safety 465 + /// 466 + /// The caller must ensure that `from` is valid for reads of `len` bytes. 467 + unsafe fn write_raw(&mut self, from: *const u8, len: usize) -> Result { 471 468 if len > self.length { 472 469 return Err(EFAULT); 473 470 } 474 - // SAFETY: `data_ptr` points into an immutable slice of length `len`, so we may read 475 - // that many bytes from it. 476 - let res = unsafe { bindings::copy_to_user(self.ptr.as_mut_ptr(), data_ptr, len) }; 471 + 472 + // SAFETY: Caller guarantees `from` is valid for `len` bytes (see this function's 473 + // safety contract). 474 + let res = unsafe { bindings::copy_to_user(self.ptr.as_mut_ptr(), from.cast(), len) }; 477 475 if res != 0 { 478 476 return Err(EFAULT); 479 477 } 480 478 self.ptr = self.ptr.wrapping_byte_add(len); 481 479 self.length -= len; 482 480 Ok(()) 481 + } 482 + 483 + /// Writes raw data to this user pointer from a kernel buffer. 484 + /// 485 + /// Fails with [`EFAULT`] if the write happens on a bad address, or if the write goes out of 486 + /// bounds of this [`UserSliceWriter`]. This call may modify the associated userspace slice even 487 + /// if it returns an error. 488 + pub fn write_slice(&mut self, data: &[u8]) -> Result { 489 + // SAFETY: `data` is a valid slice, so `data.as_ptr()` is valid for 490 + // reading `data.len()` bytes. 491 + unsafe { self.write_raw(data.as_ptr(), data.len()) } 492 + } 493 + 494 + /// Writes raw data to this user pointer from a DMA coherent allocation. 495 + /// 496 + /// Copies `count` bytes from `alloc` starting from `offset` into this userspace slice. 497 + /// 498 + /// # Errors 499 + /// 500 + /// - [`EOVERFLOW`]: `offset + count` overflows. 501 + /// - [`ERANGE`]: `offset + count` exceeds the size of `alloc`, or `count` exceeds the 502 + /// size of the user-space buffer. 503 + /// - [`EFAULT`]: the write hits a bad address or goes out of bounds of this 504 + /// [`UserSliceWriter`]. 505 + /// 506 + /// This call may modify the associated userspace slice even if it returns an error. 507 + /// 508 + /// Note: The memory may be concurrently modified by hardware (e.g., DMA). In such cases, 509 + /// the copied data may be inconsistent, but this does not cause undefined behavior. 510 + /// 511 + /// # Example 512 + /// 513 + /// Copy the first 256 bytes of a DMA coherent allocation into a userspace buffer: 514 + /// 515 + /// ```no_run 516 + /// use kernel::uaccess::UserSliceWriter; 517 + /// use kernel::dma::Coherent; 518 + /// 519 + /// fn copy_dma_to_user( 520 + /// mut writer: UserSliceWriter, 521 + /// alloc: &Coherent<[u8]>, 522 + /// ) -> Result { 523 + /// writer.write_dma(alloc, 0, 256) 524 + /// } 525 + /// ``` 526 + pub fn write_dma(&mut self, alloc: &Coherent<[u8]>, offset: usize, count: usize) -> Result { 527 + let len = alloc.size(); 528 + if offset.checked_add(count).ok_or(EOVERFLOW)? > len { 529 + return Err(ERANGE); 530 + } 531 + 532 + if count > self.len() { 533 + return Err(ERANGE); 534 + } 535 + 536 + // SAFETY: `as_ptr()` returns a valid pointer to a memory region of `count()` bytes, as 537 + // guaranteed by the `Coherent` invariants. The check above ensures `offset + count <= len`. 538 + let src_ptr = unsafe { alloc.as_ptr().cast::<u8>().add(offset) }; 539 + 540 + // Note: Use `write_raw` instead of `write_slice` because the allocation is coherent 541 + // memory that hardware may modify (e.g., DMA); we cannot form a `&[u8]` slice over 542 + // such volatile memory. 543 + // 544 + // SAFETY: `src_ptr` points into the allocation and is valid for `count` bytes (see above). 545 + unsafe { self.write_raw(src_ptr, count) } 483 546 } 484 547 485 548 /// Writes raw data to this user pointer from a kernel buffer partially.