From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from fout-b4-smtp.messagingengine.com (fout-b4-smtp.messagingengine.com [202.12.124.147]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BE92B2F4A14; Sat, 6 Dec 2025 15:58:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.147 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765036714; cv=none; b=Alqz1sVV3kOJJFA/Lvm2wYBZr+XlNdcNR4OvuQyrcwxdvTN2wK+9AhS+dox1TTwjZtE5w2bKxOSlLnnx+tERs4/Ro6MjFAVjgGp+R6caogG7uHdoYDPPS2depyVdaBvAjHW7hT6i1+P7GDAaQp/SWtDxAlhhaKzCi8Buj+WMHC4= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765036714; c=relaxed/simple; bh=EAjun57d1PLd5wm/xc+83kjnPQ8c176/0Mdtse6UU/c=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=LHguo+nz8hoXHlK+cytnMg/kUdS+l4v9Bj6PWazeo3hOC93nbzhFgsxzZc+XXvC/6jkEsIzM9qgh3hgkOYuaxaJc59jCfyGdMIiQEfwX7jebcgr2A28HBe7jgWzdKYov2oOntqMNDOqpAnRO0G9f7xWBJJhFxLSssnrl8ycOTUU= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=jannau.net; spf=pass smtp.mailfrom=jannau.net; dkim=pass (2048-bit key) header.d=jannau.net header.i=@jannau.net header.b=jLK97Nr+; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=cSamX0lg; arc=none smtp.client-ip=202.12.124.147 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=jannau.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=jannau.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=jannau.net header.i=@jannau.net header.b="jLK97Nr+"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="cSamX0lg" Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailfout.stl.internal (Postfix) with ESMTP id 6D4951D000E5; Sat, 6 Dec 2025 10:58:30 -0500 (EST) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-04.internal (MEProxy); Sat, 06 Dec 2025 10:58:30 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jannau.net; h=cc :cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm3; t=1765036710; x=1765123110; bh=OePPmB7WDX 9Fvk0i2giuguzFWfLKOHpXQRh3h1WMM90=; b=jLK97Nr+7BSP1OWm6KuTwLeDuz 3KQbN3GmP4W72b+IHdvLqLVE0qCj1K8xUFKCdRHMlvcdNfBoNTXwietndeJrqZxJ /vNoFuP1EF32x+0LjjylFs3s+lIzzr05ktxFxdu3kTgH8iI94Yu3/VKaAfD0O9gI oSR7yP6dOMOvB+/bUZ+c0YW1gRWhGokcg9vHgZr4ifcmHwh8y1zEKWaeUR/wHLGs ueNwhJTPvBT332PlI4Nv5lhhHQXghjdDSlrtlb8fhBwFwVlExyt5W2pBFy7hM2Kf CJMpxau+fOssnWbsRHOFAWGtJL7/aT55Fj3HTdDiuFqWgrWAJihxSsirdlxQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1765036710; x=1765123110; bh=OePPmB7WDX9Fvk0i2giuguzFWfLKOHpXQRh 3h1WMM90=; b=cSamX0lgfzesMpLPeKldxKX1xU1L7MBCEbrmdrZ0DoO2MDVQhym iD9f0z4hXskSTjcYZswNZjlYFa21vkz0uNN4qxZqKWkrR5eZSiieGzYxB866af+E C1ITRTS+3e2uajJ3XmaPa+QHw3TSMpYHYX7xOWuf7lZwb9jKpcnC13pdn61MK2yX rIuAkScs8kdPwajV8pu+NEBL6vt4GUA+HtsXRbT+ztwuEVWwdZGRbFGqdgPekngx d0fJYbzMRFwAFF6CkPBbyxEQwFylMcQMmDHX+yhwr2O/DlHV7cj9QfWD9UqWvs9d fvyCIqjmnkFNajALPz+qbvtzHmXl2E2UzgA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefgedrtddtgdduudefhecutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpuffrtefokffrpgfnqfghnecuuegr ihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjug hrpeffhffvvefukfhfgggtuggjsehttdertddttdejnecuhfhrohhmpeflrghnnhgvucfi rhhunhgruhcuoehjsehjrghnnhgruhdrnhgvtheqnecuggftrfgrthhtvghrnhepgfdvff evleegudejfeefheehkeehleehfefgjefffeetudegtefhuedufeehfeetnecuvehluhhs thgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepjhesjhgrnhhnrghurd hnvghtpdhnsggprhgtphhtthhopedvuddpmhhouggvpehsmhhtphhouhhtpdhrtghpthht oheplhihuhguvgesrhgvughhrghtrdgtohhmpdhrtghpthhtohepughrihdquggvvhgvlh eslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrghdprhgtphhtthhopehruhhsthdq fhhorhdqlhhinhhugiesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopegrlh hitggvrhihhhhlsehgohhoghhlvgdrtghomhdprhgtphhtthhopegurghnihgvlhdrrghl mhgvihgurgestgholhhlrggsohhrrgdrtghomhdprhgtphhtthhopegurghkrheskhgvrh hnvghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqkhgvrhhnvghlsehvghgvrhdrkhgv rhhnvghlrdhorhhgpdhrtghpthhtohepohhjvggurgeskhgvrhhnvghlrdhorhhgpdhrtg hpthhtoheprghlvgigrdhgrgihnhhorhesghhmrghilhdrtghomh X-ME-Proxy: Feedback-ID: i47b949f6:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Sat, 6 Dec 2025 10:58:29 -0500 (EST) Date: Sat, 6 Dec 2025 16:58:27 +0100 From: Janne Grunau To: Lyude Paul Cc: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Alice Ryhl , Daniel Almeida , Danilo Krummrich , linux-kernel@vger.kernel.org, Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?B?QmrDtnJu?= Roy Baron , Benno Lossin , Andreas Hindborg , Trevor Gross , Greg Kroah-Hartman , Viresh Kumar , FUJITA Tomonori , Krishna Ketan Rai , Tamir Duberstein , Xiangfei Ding Subject: Re: [PATCH v6 7/8] rust: Introduce iosys_map bindings Message-ID: <20251206155827.GC1097212@robin.jannau.net> References: <20251202220924.520644-1-lyude@redhat.com> <20251202220924.520644-8-lyude@redhat.com> Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: <20251202220924.520644-8-lyude@redhat.com> On Tue, Dec 02, 2025 at 05:03:33PM -0500, Lyude Paul wrote: > This introduces a set of bindings for working with iosys_map in rust code. > The design of this is heavily based off the design for both the io and > dma_map bindings for Rust. > > Signed-off-by: Lyude Paul > > --- > V5: > - Fix incorrect field size being passed to iosys_map_memcpy_to() > - Add an additional unit test, basic_macro(), which can successfully catch > the above issue so it doesn't happen again in the future. > V6: > - Drop as_slice/as_mut_slice (Alice Rhyl) > > rust/helpers/helpers.c | 1 + > rust/helpers/iosys_map.c | 15 + > rust/kernel/iosys_map.rs | 614 +++++++++++++++++++++++++++++++++++++++ > rust/kernel/lib.rs | 1 + > 4 files changed, 631 insertions(+) > create mode 100644 rust/helpers/iosys_map.c > create mode 100644 rust/kernel/iosys_map.rs > > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c > index 36d40f911345c..d549af697bd60 100644 > --- a/rust/helpers/helpers.c > +++ b/rust/helpers/helpers.c > @@ -31,6 +31,7 @@ > #include "irq.c" > #include "fs.c" > #include "io.c" > +#include "iosys_map.c" > #include "jump_label.c" > #include "kunit.c" > #include "maple_tree.c" > diff --git a/rust/helpers/iosys_map.c b/rust/helpers/iosys_map.c > new file mode 100644 > index 0000000000000..b105261c3cf8a > --- /dev/null > +++ b/rust/helpers/iosys_map.c > @@ -0,0 +1,15 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include > + > +void rust_helper_iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset, > + const void *src, size_t len) > +{ > + iosys_map_memcpy_to(dst, dst_offset, src, len); > +} > + > +void rust_helper_iosys_map_memcpy_from(void *dst, const struct iosys_map *src, > + size_t src_offset, size_t len) > +{ > + iosys_map_memcpy_from(dst, src, src_offset, len); > +} > diff --git a/rust/kernel/iosys_map.rs b/rust/kernel/iosys_map.rs > new file mode 100644 > index 0000000000000..884a3d2be3348 > --- /dev/null > +++ b/rust/kernel/iosys_map.rs > @@ -0,0 +1,614 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! IO-agnostic memory mapping interfaces. > +//! > +//! This crate provides bindings for the `struct iosys_map` type, which provides a common interface > +//! for memory mappings which can reside within coherent memory, or within IO memory. > +//! > +//! C header: [`include/linux/iosys-map.h`](srctree/include/linux/pci.h) > + > +use crate::{ > + prelude::*, > + transmute::{AsBytes, FromBytes}, > +}; > +use bindings; > +use core::{ > + marker::PhantomData, > + mem::{self, MaybeUninit}, > + ops::{Deref, DerefMut, Range}, > +}; > + > +/// Raw unsized representation of a `struct iosys_map`. > +/// > +/// This struct is a transparent wrapper around `struct iosys_map`. The C API does not provide the > +/// size of the mapping by default, and thus this type also does not include the size of the > +/// mapping. As such, it cannot be used for actually accessing the underlying data pointed to by the > +/// mapping. > +/// > +/// With the exception of kernel crates which may provide their own wrappers around `RawIoSysMap`, > +/// users will typically not interact with this type directly. > +pub struct RawIoSysMap(bindings::iosys_map, PhantomData); > + > +impl RawIoSysMap { > + /// Convert from a raw `bindings::iosys_map`. > + #[expect(unused)] > + #[inline] > + pub(crate) fn from_raw(val: bindings::iosys_map) -> Self { > + Self(val, PhantomData) > + } > + > + /// Convert from a `RawIoSysMap` to a raw `bindings::iosys_map` ref. > + #[inline] > + pub(crate) fn as_raw(&self) -> &bindings::iosys_map { > + &self.0 > + } > + > + /// Convert from a `RawIoSysMap` to a raw mutable `bindings::iosys_map` ref. > + #[inline] > + pub(crate) fn as_raw_mut(&mut self) -> &mut bindings::iosys_map { > + &mut self.0 > + } > + > + /// Returns whether the mapping is within IO memory space or not. > + #[inline] > + pub fn is_iomem(&self) -> bool { > + self.0.is_iomem > + } > + > + /// Returns the size of a single item in this mapping. > + pub const fn item_size(&self) -> usize { > + mem::size_of::() > + } > + > + /// Returns a mutable address to the memory pointed to by this iosys map. > + /// > + /// Note that this address is not guaranteed to reside in system memory, and may reside in IO > + /// memory. > + #[inline] > + pub fn as_mut_ptr(&self) -> *mut T { > + if self.is_iomem() { > + // SAFETY: We confirmed above that this iosys map is contained within iomem, so it's > + // safe to read vaddr_iomem > + unsafe { self.0.__bindgen_anon_1.vaddr_iomem } > + } else { > + // SAFETY: We confirmed above that this iosys map is not contaned within iomem, so it's > + // safe to read vaddr. > + unsafe { self.0.__bindgen_anon_1.vaddr } > + } > + .cast() > + } > + > + /// Returns an immutable address to the memory pointed to by this iosys map. > + /// > + /// Note that this address is not guaranteed to reside in system memory, and may reside in IO > + /// memory. > + #[inline] > + pub fn as_ptr(&self) -> *const T { > + self.as_mut_ptr().cast_const() > + } > +} > + > +// SAFETY: As we make no guarantees about the validity of the mapping, there's no issue with sending > +// this type between threads. > +unsafe impl Send for RawIoSysMap {} > + > +impl Clone for RawIoSysMap { > + fn clone(&self) -> Self { > + Self(self.0, PhantomData) > + } > +} > + > +/// A sized version of a [`RawIoSysMap`]. > +/// > +/// Since this type includes the size of the [`RawIoSysMap`], it can be used for accessing the > +/// underlying data pointed to by it. > +/// > +/// # Invariants > +/// > +/// - The iosys mapping referenced by this type is guaranteed to be of at least `size` bytes in > +/// size > +/// - The iosys mapping referenced by this type is valid for the lifetime `'a`. > +#[derive(Clone)] > +pub struct IoSysMapRef<'a, T: AsBytes + FromBytes> { > + map: RawIoSysMap, > + size: usize, > + _p: PhantomData<&'a T>, > +} > + > +impl<'a, T: AsBytes + FromBytes> IoSysMapRef<'a, T> { > + /// Create a new [`IoSysMapRef`] from a [`RawIoSysMap`]. > + /// > + /// # Safety > + /// > + /// - The caller guarantees that the mapping referenced by `map` is of at least `size` bytes in > + /// size. > + /// - The caller guarantees that the mapping referenced by `map` remains valid for the lifetime > + /// of `'a`. > + #[allow(unused)] > + pub(crate) unsafe fn new(map: RawIoSysMap, size: usize) -> IoSysMapRef<'a, T> { > + // INVARIANT: Our safety contract fulfills the type invariants of `IoSysMapRef`. > + IoSysMapRef { > + map, > + size, > + _p: PhantomData, > + } > + } > + > + /// Return the size of the `IoSysMapRef`. > + #[inline] > + pub fn size(&self) -> usize { > + self.size > + } > + > + /// Writes `src` to the region starting from `offset`. > + /// > + /// `offset` is in units of `T`, not the number of bytes. > + /// > + /// This function can return the following errors: > + /// > + /// * [`EOVERFLOW`] if calculating the length of the slice results in an overflow. > + /// * [`EINVAL`] if the slice would go out of bounds of the memory region. > + /// > + /// # Examples > + /// > + /// ``` > + /// use kernel::iosys_map::*; > + /// > + /// # fn test() -> Result { > + /// # let mut map = tests::VecIoSysMap::new(&[0; 3])?; > + /// # { > + /// # let mut map = map.get(); > + /// map.write(&[1, 2, 3], 0)?; // (now [1, 2, 3]) > + /// map.write(&[4], 2)?; // (now [1, 2, 4]) > + /// # } > + /// # > + /// # map.assert_eq(&[1, 2, 4]); > + /// # > + /// # Ok::<(), Error>(()) } > + /// # assert!(test().is_ok()); > + /// ``` > + pub fn write(&mut self, src: &[T], offset: usize) -> Result { > + let range = self.compute_range(offset, src.len())?; > + > + // SAFETY: > + // - The address pointed to by this iosys_map is guaranteed to be valid via IoSysMapRef's > + // type invariants. > + // - We checked that this range of memory is within bounds above > + unsafe { > + bindings::iosys_map_memcpy_to( > + self.as_raw_mut(), > + range.start, > + src.as_ptr().cast(), > + range.len(), > + ) > + }; > + > + Ok(()) > + } > + > + /// Attempt to compute the offset of an item within the iosys map using its index. > + /// > + /// Returns an error if an overflow occurs. > + /// > + /// # Safety > + /// > + /// This function checks for overflows, but it explicitly does not check if the offset goes out > + /// of bounds. It is the caller's responsibility to check for this before using the returned > + /// offset with the iosys_map API. > + unsafe fn item_from_index(&self, idx: usize) -> Result { > + self.item_size().checked_mul(idx).ok_or(EOVERFLOW) > + } > + > + /// Compute the range within this mapping a specific data type at a given offset would occupy. > + /// > + /// This function returns the computed range if it doesn't overflow, but does not check whether > + /// or not the range is within the bounds of the allocated region pointed to by this iosys > + /// mapping. The part of the comment starting with "but does not check ..." doesn't appear to be accurate anymore. It checks `range_end > self.size`. The guarantees on successful return below contradict it as well. > + /// > + /// On success, the range returned by this function is guaranteed: > + /// > + /// * To be a valid range of memory within the virtual mapping for this gem object. This isn't a gem object anymore > + /// * To be properly aligned to [`RawIoSysMap::item_size()`]. > + fn compute_range(&self, offset: usize, count: usize) -> Result> { > + // SAFETY: If the offset is out of bounds, we'll catch this via overflow checks or when > + // checking range_end. > + let offset = unsafe { self.item_from_index(offset)? }; > + let range_size = count.checked_mul(self.item_size()).ok_or(EOVERFLOW)?; > + let range_end = offset.checked_add(range_size).ok_or(EOVERFLOW)?; > + > + if range_end > self.size { > + return Err(EINVAL); > + } > + > + // INVARIANT: Since `offset` and `count` are both in units of `T`, we're guaranteed that the The use of offset is a little confusing since the function parameter is shadowed by the computed byte offset. Janne