From nobody Fri Oct 2 03:37:52 2026 Received: from mout-p-101.mailbox.org (mout-p-101.mailbox.org [80.241.56.151]) (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 BAA683F39D0; Wed, 5 Aug 2026 21:54:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.151 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785966902; cv=none; b=dRRbf+O+dR2UyzezhiRLhUdgEKNykI0zm7sdlmQLnZllVDczPHC/qejd5NqugEv60gFJjTe00BRRCEifAwWhaFUknWYmhoUGZ7o3l+heyJgzJiRMdLkfX2S9C3XBlb9UE91UOLXA2DIkLgffq2Wolmuw1FjLtkBbrZw/LWE67kY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785966902; c=relaxed/simple; bh=DBiAbkCS1lvRs9n49RlCrB61Ci8CG0hVQsEsnkBwnD0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=l1k/g54F8M9i5/I5pmPqhmnGh8LcYbqMFoRAnnNnLSxaoO//Jba7Z0uMkjUg/iWVN6mUxV0/+qtVRMKJ89O6/zRqFO+FLssu4vNVJbtfeg//WfDon7/vqckEeQI0wvwBKD341yJt7ZRBH3lmTOWjbMJM2NbLYNZcQcf335Nj1iw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mailbox.org; spf=pass smtp.mailfrom=mailbox.org; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b=U7Kr6nQp; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b=j7/k4Y0i; arc=none smtp.client-ip=80.241.56.151 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mailbox.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mailbox.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b="U7Kr6nQp"; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b="j7/k4Y0i" Received: from smtp202.mailbox.org (smtp202.mailbox.org [IPv6:2001:67c:2050:b231:465::202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-101.mailbox.org (Postfix) with ESMTPS id 4hFkjF20Y7z8v33; Wed, 05 Aug 2026 23:54:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785966897; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=TEFZse9b5nsPPhTe5byZ4ixDs16zcGmsf1GvqdvIIqk=; b=U7Kr6nQp6MUXGoJ5VenRUgbMxVqQdqN6qfj+Mh4yJzFuDCcL1waDM4dxj3Cw9oQNcZvn1i QXsiR7JeRR9r99eCs8E0xWdX66DH5pT96RXXdYN5nKBJuwMZzg7sfrA4FQVfSIgjW7xTAI 3hkmxGDB7IVucJizEO6ToHfQZZSYdGVBRzN2P89wMGQswKO9R2iF3Supe+YW5gbx/AtgNj BXsqDAZDUiHKKktNQqwvdnpcLQVgMFe46700o1o+WZxMyYRoNDDTB597X/CTkm5snmxfhJ 0MdluJyZqMHSoeJ94+9no1+YzfV0FfH+BrnVCPFXukexa/Oaf7EBhgFGcl2YBA== Authentication-Results: outgoing_mbo_mout; dkim=pass header.d=mailbox.org header.s=mail20150812 header.b="j7/k4Y0i"; spf=pass (outgoing_mbo_mout: domain of mhi@mailbox.org designates 2001:67c:2050:b231:465::202 as permitted sender) smtp.mailfrom=mhi@mailbox.org From: Maurice Hieronymus DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785966895; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=TEFZse9b5nsPPhTe5byZ4ixDs16zcGmsf1GvqdvIIqk=; b=j7/k4Y0iRIAh79gxNyCWhF6UyQ043cY2ZuHxssElx8gNj1OuUXLeqeBJ1IEGujf5DBoYgZ twxsx9z3cn8MsuE19t+dSQ1Scm/c7pvkFYVCJvnTTuQAJaT12CHkb2E2r8a0vGzBx6fVm0 ERNuKZx9iLXIsk1bff0jRqgM0KR23eBfoeAWgp/Jdiof/yUdVIvAcVw+tjLqz4hYET7c/z K5fZZZBobAsfB8Id/1NlWEk+2fdf8qCsmoBhehn8fg2Tp2pRQr28p7+ypbx3cQXdI5nHAE mzgO5JYjvsJMGxxDGoa6HarKcna8Ts/Q+O0S3884IbmOcIhcFQua3+CvFSSysQ== Date: Wed, 05 Aug 2026 23:54:41 +0200 Subject: [PATCH 1/3] rust: dma: add ContiguousBuffer trait for streaming DMA storage Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260805-dma-streaming-v1-1-03974c86b141@mailbox.org> References: <20260805-dma-streaming-v1-0-03974c86b141@mailbox.org> In-Reply-To: <20260805-dma-streaming-v1-0-03974c86b141@mailbox.org> To: Danilo Krummrich , Abdiel Janulgue , Daniel Almeida , Robin Murphy , Andreas Hindborg , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , David Airlie , Simona Vetter Cc: driver-core@lists.linux.dev, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, Maurice Hieronymus X-Developer-Signature: v=1; a=ed25519-sha256; t=1785966885; l=3482; i=mhi@mailbox.org; s=20260525; h=from:subject:message-id; bh=DBiAbkCS1lvRs9n49RlCrB61Ci8CG0hVQsEsnkBwnD0=; b=c9zTXb17WyO3ABUP8ekT+qxnVf2ERinmmJPjb+LT7SuLmPFnZU5D1pgWWQ2nnnxINsmO7sWOI dUqr+F05Kc6BV5HSAAYuKQwTi4k3jaRxPi8fRaaUX0OBQYyOxyHPmJ+ X-Developer-Key: i=mhi@mailbox.org; a=ed25519; pk=AHlEkGG3hpXZHntlEzF42Ip/LFyXWOgsNUvaHqAnV80= X-MBO-RS-ID: 835c32740e77a55c46e X-MBO-RS-META: 9o5gdoh6j1phbqdi1cdu48r85eusspup X-Rspamd-Queue-Id: 4hFkjF20Y7z8v33 The streaming DMA API (`dma_map_single()`) does not allocate, it maps a buffer the caller already owns. Not every allocation qualifies: the buffer must be a single physically contiguous region in the kernel's linear mapping, which rules out `vmalloc()`ed memory and the stack. Add `ContiguousBuffer`, an unsafe trait describing that requirement, and implement it for `KBox`, whose storage comes from `kmalloc()`. The trait hands out owned storage rather than a borrowed slice, so the mapping added in the next patch can take ownership and guarantee that no other CPU-side reference exists while the device owns the buffer. `Data` is bounded by `FromBytes` and `AsBytes` because the device may write an arbitrary byte pattern into the region and may read it, so it must not contain uninitialized padding. Signed-off-by: Maurice Hieronymus --- rust/kernel/dma.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 57 insertions(+) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 200def84fb69..8a8af5ab7feb 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -564,6 +564,63 @@ fn from(value: CoherentBox) -> Self { } } =20 +/// Backing storage that can be passed to the single-buffer streaming DMA = API. +/// +/// # Safety +/// +/// Implementers must guarantee that, for as long as `Self` is alive and n= ot mutated: +/// +/// * [`ptr`](Self::ptr) returns a pointer to the start of a single, physi= cally contiguous region +/// of [`size`](Self::size) bytes, and [`data`](Self::data) refers to ex= actly that region. +/// * The region lives in the kernel's linear mapping, i.e. it is neither = `vmalloc()`ed nor stack +/// memory, both of which `dma_map_single()` rejects. +/// * The region is DMA-safe in the sense of the [DMA API howto]. +/// +/// [DMA API howto]: srctree/Documentation/core-api/dma-api-howto.rst +pub unsafe trait ContiguousBuffer { + /// The CPU-side view of the region. + /// + /// [`FromBytes`] because the device may write an arbitrary byte patte= rn into the region, + /// [`AsBytes`] because it may read the region, which must therefore h= ave no uninitialized + /// padding. + type Data: ?Sized + FromBytes + AsBytes; + + /// Returns a pointer to the start of the region. + fn ptr(&mut self) -> *mut c_void; + + /// Returns the size of the region in bytes. + fn size(&self) -> usize; + + /// Returns a mutable reference to the region. + fn data(&mut self) -> &mut Self::Data; +} + +// SAFETY: `KBox` allocates via `kmalloc()`, which returns a single physic= ally contiguous, +// DMA-safe region in the kernel's linear mapping. All three methods descr= ibe that allocation. +unsafe impl ContiguousBuffer for KBox { + type Data =3D T; + + fn ptr(&mut self) -> *mut c_void { + let ptr =3D &raw mut **self; + ptr.cast() + } + + fn size(&self) -> usize { + const { + assert!( + core::mem::size_of::() > 0, + "It doesn't make sense to map a ZST for DMA" + ); + } + + core::mem::size_of_val(&**self) + } + + fn data(&mut self) -> &mut Self::Data { + self + } +} + /// An abstraction of the `dma_alloc_coherent` API. /// /// This is an abstraction around the `dma_alloc_coherent` API which is us= ed to allocate and map --=20 2.54.0 From nobody Fri Oct 2 03:37:52 2026 Received: from mout-p-201.mailbox.org (mout-p-201.mailbox.org [80.241.56.171]) (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 789083F6614; Wed, 5 Aug 2026 21:55:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785966905; cv=none; b=jjtxwyP0tiQ0xA8NpaWxlxSCxu3l0VWJLYCkpnu12x7dd16ieZfy4ZTm6Cik9Hc8qHdJVKM70rVgRxeH2IfNOz9y/F47hp4lKC9oS8x9SBeGRA+fDGqg8ulSF8CigrHE4lRMK76NMDZBRYwTPGFi9Fl5rP4NFd+JL29wOZ8fnTM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785966905; c=relaxed/simple; bh=sDbc5auW0d4Hk1Hdh5TZlTenU07Q/2X3PfhofpEw4fg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jDNW3bPPjVt7LFn9HMWpw1SMjr7/U0zPcxBJYXgvYLgU3ZAO+Y81KI+gKzbqxYs5x+RUmS0p2K8eXciBNPpdDco/xddR9q6wGGxpqn/mDolMiWm1Pi86XRZajDBF3FuRruRSoimlg9hT2jE66mK/3XxtPezBnTgPscsfE2vMrjA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mailbox.org; spf=pass smtp.mailfrom=mailbox.org; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b=py/Q3rlh; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b=xWoFAZuU; arc=none smtp.client-ip=80.241.56.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mailbox.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mailbox.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b="py/Q3rlh"; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b="xWoFAZuU" Received: from smtp202.mailbox.org (smtp202.mailbox.org [10.196.197.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-201.mailbox.org (Postfix) with ESMTPS id 4hFkjK353gzMl1l; Wed, 05 Aug 2026 23:55:01 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785966901; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hgSStPoxTspKtg2zosEO89i5YfxrHI3A8bs/UDZL7t4=; b=py/Q3rlhV1EqLz9alYRrztS1zQo+taBJD4lTA3UhHwy4vNFf6MiDJFLQlavWHMUwyWSv9K feYXMLLhy5oLaAIQK5VDvbL7hK8vIobvTN6gvJRzxIDixW9wUyV7stQLQ6EOShanqnHI2A FkFkY+w7VsZB6EOJknwejOcJAtWEFCvtcGBpmjEenb8LwDWWaoNWv5R8gVaAkjvw66Znti Fcpm09pjQD5QtB6LQAxYCvN9j2gx2rZYqgwTSYc6iHjT12T8DI4EAlGU4in4U+ec4zKu50 eeFgrrkSoZTKuzvilNCVSyBjyLJDoMRlbkHPltg46AxhSc9RB6wEarXPkIjyeQ== From: Maurice Hieronymus DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785966899; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hgSStPoxTspKtg2zosEO89i5YfxrHI3A8bs/UDZL7t4=; b=xWoFAZuUYDJhdE/y7OZ41tpKOjhCQ9cDe4/nPdobS7GNhFpAioUGMEWyuCic9+kDxqxkvT eXHdVr8jkxBzaGcterazU/rc/DG+9grYXnH+2q6s0Qn0t5uO7se+hgel+jdpiVhL2Ngyxv WQJO6dsdKNZbrVRsiKQVWVL3/bRANzTrwQvrhL5esFVFSI8aw39Ns9e9U0ycrxTkDVHT4M 9sBPeT5v9RaW5kKpt0lnCY1CIB8RH42Idlso/pfm3GlSsLAklY1oCzIv3PH4Yy59x7Hx65 0AAT5muMW8vSVot+R72xvhgLRVmCVoenqgchE5HJtbGhu4mIE/TCfibwecb29g== Date: Wed, 05 Aug 2026 23:54:42 +0200 Subject: [PATCH 2/3] rust: dma: add abstraction for the single-buffer streaming DMA API Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260805-dma-streaming-v1-2-03974c86b141@mailbox.org> References: <20260805-dma-streaming-v1-0-03974c86b141@mailbox.org> In-Reply-To: <20260805-dma-streaming-v1-0-03974c86b141@mailbox.org> To: Danilo Krummrich , Abdiel Janulgue , Daniel Almeida , Robin Murphy , Andreas Hindborg , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , David Airlie , Simona Vetter Cc: driver-core@lists.linux.dev, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, Maurice Hieronymus X-Developer-Signature: v=1; a=ed25519-sha256; t=1785966885; l=19366; i=mhi@mailbox.org; s=20260525; h=from:subject:message-id; bh=sDbc5auW0d4Hk1Hdh5TZlTenU07Q/2X3PfhofpEw4fg=; b=2GFyMmOuYIFgfvkeZTJJinQdmrZcdbpY9J0OeWAMlIph0SkTM042dggJ1drsT9vgAxeeVtN+J RPvsHvaXianBmfvCMfETazuSFz2MWNIWe1TdF5V1lCo5y5biFw5mMW4 X-Developer-Key: i=mhi@mailbox.org; a=ed25519; pk=AHlEkGG3hpXZHntlEzF42Ip/LFyXWOgsNUvaHqAnV80= X-MBO-RS-ID: e7ccac53ed79f7c25b5 X-MBO-RS-META: a51ghs1uho576hdcs5widi8ngzra9b4y Add `Streaming`, a safe abstraction around `dma_map_single_attrs()`. Between map and unmap the buffer belongs to the device, and the CPU may only access it in between a `dma_sync_single_for_cpu()` / `dma_sync_single_for_device()` pair. The types encode that protocol: - `Streaming` owns the backing storage, so no other CPU-side reference to the region exists. - `submit()` consumes it and returns a `StreamingInFlight`, the only source of the `DmaAddress`. It owns the buffer, so the contents stay unreachable while a transfer may be in flight, no matter where the driver stores the address. - `for_cpu()` is therefore safe: no transfer can have been started from a `Streaming`. It syncs for the CPU, returns a guard dereferencing to the contents, and syncs for the device again on drop. - `complete()` turns a `StreamingInFlight` back into a `Streaming`. Whether the device has finished cannot be checked by any abstraction, so this is the one `unsafe` operation. Dropping a `StreamingInFlight` leaks the mapping and the backing storage, with a warning. A transfer may still be in flight: freeing the storage would leave the device writing through a dangling handle, and even unmapping could recycle a SWIOTLB bounce slot mid-transfer. Reclaiming either takes the assertion only `complete()` can make, so an early `?` return between `submit()` and `complete()` costs a leak instead of a device-side use-after-free. The mapping is torn down on drop of a `Streaming`, or by `into_inner()`, which returns the backing storage. Unmapping already hands the buffer back to the CPU, so `into_inner()` skips a synchronization nothing would consume. The `'a` lifetime binds the mapping to a `Device`: the DMA API may only be called while a driver is bound, and `Drop` unmaps. `DataDirection::None` (a `BUG_ON()` in the DMA core) and empty buffers (not representable by `dma_map_single()`) are rejected with `EINVAL`. So are `DMA_ATTR_SKIP_CPU_SYNC`, which disables the implicit CPU cache maintenance the type invariants are built on with no way to compensate through this API, and `DMA_ATTR_MMIO`, which describes memory a `ContiguousBuffer` cannot represent. Signed-off-by: Maurice Hieronymus --- rust/helpers/dma.c | 35 +++++ rust/kernel/dma.rs | 392 +++++++++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 427 insertions(+) diff --git a/rust/helpers/dma.c b/rust/helpers/dma.c index 9fbeb507b08c..7e1e5c67431a 100644 --- a/rust/helpers/dma.c +++ b/rust/helpers/dma.c @@ -49,3 +49,38 @@ __rust_helper void rust_helper_dma_set_max_seg_size(stru= ct device *dev, { dma_set_max_seg_size(dev, size); } + +__rust_helper dma_addr_t rust_helper_dma_map_single_attrs(struct device *d= ev, + void *ptr, size_t size, + enum dma_data_direction dir, + unsigned long attrs) +{ + return dma_map_single_attrs(dev, ptr, size, dir, attrs); +} + +__rust_helper void rust_helper_dma_unmap_single_attrs(struct device *dev, + dma_addr_t addr, size_t size, + enum dma_data_direction dir, + unsigned long attrs) +{ + dma_unmap_single_attrs(dev, addr, size, dir, attrs); +} + +__rust_helper int rust_helper_dma_mapping_error(struct device *dev, dma_ad= dr_t addr) +{ + return dma_mapping_error(dev, addr); +} + +__rust_helper void rust_helper_dma_sync_single_for_cpu(struct device *dev, + dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + dma_sync_single_for_cpu(dev, addr, size, dir); +} + +__rust_helper void rust_helper_dma_sync_single_for_device(struct device *d= ev, + dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + dma_sync_single_for_device(dev, addr, size, dir); +} diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 8a8af5ab7feb..cbaf30a2de86 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -24,6 +24,7 @@ uaccess::UserSliceWriter, }; use core::{ + mem::ManuallyDrop, ops::{ Deref, DerefMut, // @@ -345,6 +346,14 @@ const fn const_cast(val: bindings::dma_data_direction)= -> u32 { // is within the representable range of `u32`. wide_val as u32 } + + /// Returns whether this direction may be passed to a mapping or synch= ronization primitive. + /// + /// Equivalent to `valid_dma_direction()`; [`Self::None`] is a debuggi= ng aid the DMA core + /// rejects with a `BUG_ON()`. + const fn is_valid(self) -> bool { + !matches!(self, Self::None) + } } =20 impl From for bindings::dma_data_direction { @@ -621,6 +630,389 @@ fn data(&mut self) -> &mut Self::Data { } } =20 +/// An abstraction of the `dma_map_single` API. +/// +/// Unlike [`Coherent`], a streaming mapping is a temporary lease on memor= y the caller already +/// owns: between mapping and unmapping the buffer belongs to the device, = and the CPU may only +/// access it in between a `dma_sync_single_for_cpu()` / `dma_sync_single_= for_device()` pair. +/// +/// [`Streaming`] owns the backing storage and is one of two states: [`sub= mit`](Self::submit) +/// consumes it and returns a [`StreamingInFlight`], the only source of th= e [`DmaAddress`]; +/// [`complete`](StreamingInFlight::complete) turns that back into a [`Str= eaming`], whose +/// [`for_cpu`](Self::for_cpu) yields a [`StreamingCpuGuard`] dereferencin= g to the contents. +/// +/// The mapping is torn down on drop of a [`Streaming`], or by [`into_inne= r`](Self::into_inner), +/// which returns the backing storage. Dropping a [`StreamingInFlight`] in= stead leaks the mapping +/// and the storage: safe code cannot prove the device is done, so it cann= ot be allowed to reclaim +/// either. +/// +/// The `'a` lifetime keeps the device bound for the life of the mapping. +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::{Bound, Device}; +/// use kernel::dma::{ +/// DataDirection, +/// Streaming, // +/// }; +/// +/// # fn test(dev: &Device) -> Result { +/// let buf =3D KBox::new(0u64, GFP_KERNEL)?; +/// let mut dma =3D Streaming::new(dev, buf, DataDirection::Bidirectional)= ?; +/// +/// // The CPU prepares the buffer, and hands it back to the device by dro= pping the guard. +/// *dma.for_cpu() =3D 42; +/// +/// // Hand the buffer to the device; `dma` is consumed, so its contents a= re now unreachable. +/// let dma =3D dma.submit(); +/// +/// // Program `dma.dma_handle()` and `dma.size()` into the device. +/// +/// // SAFETY: For the sake of the example, assume the transfer has been w= aited for. +/// let mut dma =3D unsafe { dma.complete() }; +/// +/// assert_eq!(*dma.for_cpu(), 42); +/// # Ok::<(), Error>(()) } +/// ``` +/// +/// # Invariants +/// +/// * `dma_addr` denotes a live mapping of `container` for the lifetime of= the instance, and +/// `container`, `direction` and `dma_attrs` are unchanged since it was = established. +/// * `direction` is not [`DataDirection::None`]. +/// * The buffer is synchronized for the device whenever no [`StreamingCpu= Guard`] borrowed from +/// this instance is alive. +pub struct Streaming<'a, C: ContiguousBuffer> { + container: C, + direction: DataDirection, + dma_addr: DmaAddress, + dma_attrs: Attrs, + dev: &'a device::Device, +} + +impl<'a, C: ContiguousBuffer> Streaming<'a, C> { + /// Maps `container` for streaming DMA in `direction`. + /// + /// Ownership of `container` is moved into the returned [`Streaming`];= the buffer belongs to + /// the device until [`for_cpu`](Self::for_cpu) is called. + /// + /// Returns [`EINVAL`] for an empty buffer, for [`DataDirection::None`= ], or if `dma_attrs` + /// contains [`DMA_ATTR_SKIP_CPU_SYNC`](attrs::DMA_ATTR_SKIP_CPU_SYNC)= or + /// [`DMA_ATTR_MMIO`](attrs::DMA_ATTR_MMIO). + /// + /// # Examples + /// + /// ``` + /// # use kernel::device::{Bound, Device}; + /// use kernel::dma::{ + /// attrs::*, + /// DataDirection, + /// Streaming, // + /// }; + /// + /// # fn test(dev: &Device) -> Result { + /// let buf =3D KBox::new(0u64, GFP_KERNEL)?; + /// let dma =3D Streaming::new_with_attrs( + /// dev, + /// buf, + /// DataDirection::ToDevice, + /// DMA_ATTR_WEAK_ORDERING, + /// )?; + /// # Ok::<(), Error>(()) } + /// ``` + pub fn new_with_attrs( + dev: &'a device::Device, + mut container: C, + direction: DataDirection, + dma_attrs: Attrs, + ) -> Result { + // The DMA core `BUG_ON()`s on `DMA_NONE`, bail early. + if !direction.is_valid() { + return Err(EINVAL); + } + + // The type invariants are built on the implicit CPU cache mainten= ance performed by + // `dma_map_single_attrs()` and `dma_unmap_single_attrs()`; `DMA_A= TTR_SKIP_CPU_SYNC` + // disables it, with no way to compensate through this API. `DMA_A= TTR_MMIO` describes + // memory a `ContiguousBuffer` cannot represent. + if dma_attrs.contains(attrs::DMA_ATTR_SKIP_CPU_SYNC) + || dma_attrs.contains(attrs::DMA_ATTR_MMIO) + { + return Err(EINVAL); + } + + let size =3D container.size(); + + // `dma_map_single_attrs` cannot handle zero-length mappings, bail= early. + if size =3D=3D 0 { + return Err(EINVAL); + } + + // SAFETY: + // - Device pointer is guaranteed as valid by the type invariant o= n `Device`. + // - By the safety requirements of `ContiguousBuffer`, `container.= ptr()` points to a single + // physically contiguous region of `size` bytes in the kernel's = linear mapping. + // - `container` is moved into `Self` below, so the region stays a= live and at a stable + // address until the mapping is torn down in `Drop`. + let dma_addr =3D unsafe { + bindings::dma_map_single_attrs( + dev.as_raw(), + container.ptr(), + size, + direction.into(), + dma_attrs.as_raw(), + ) + }; + + // SAFETY: Device pointer is valid per the above, and `dma_addr` w= as just returned by + // `dma_map_single_attrs()` for this device. + to_result(unsafe { bindings::dma_mapping_error(dev.as_raw(), dma_a= ddr) })?; + + // INVARIANT: + // - The mapping was just established with these exact parameters,= none of which is + // mutated afterwards. + // - `direction` was checked above. + Ok(Streaming { + container, + direction, + dma_addr, + dma_attrs, + dev, + }) + } + + /// Performs the same functionality as [`Streaming::new_with_attrs`], = except the `dma_attrs` + /// is 0 by default. + #[inline] + pub fn new( + dev: &'a device::Device, + container: C, + direction: DataDirection, + ) -> Result { + Self::new_with_attrs(dev, container, direction, Attrs(0)) + } + + /// Returns the size of the mapping in bytes. + #[inline] + pub fn size(&self) -> usize { + self.container.size() + } + + /// Returns the direction this buffer was mapped with. + #[inline] + pub fn direction(&self) -> DataDirection { + self.direction + } + + /// Hands the buffer to the device. + /// + /// This performs no synchronization: by the type invariants the buffe= r is already + /// synchronized for the device. + #[inline] + pub fn submit(self) -> StreamingInFlight<'a, C> { + StreamingInFlight(ManuallyDrop::new(self)) + } + + /// Transfers ownership of the buffer back to the CPU and returns a gu= ard granting access to + /// its contents. + /// + /// Dropping the guard transfers ownership back to the device. If the = buffer is not handed to + /// the device again, prefer [`into_inner`](Self::into_inner), which u= nmaps instead. + pub fn for_cpu(&mut self) -> StreamingCpuGuard<'_, C::Data> { + let dev =3D self.dev; + let dma_addr =3D self.dma_addr; + let direction =3D self.direction; + let size =3D self.container.size(); + + // SAFETY: By the type invariants, `dev` is bound and `dma_addr` d= enotes a live mapping of + // `size` bytes established with `direction`, which is the range s= ynced here. + unsafe { + bindings::dma_sync_single_for_cpu(dev.as_raw(), dma_addr, size= , direction.into()) + }; + + // INVARIANT: The buffer is now owned by the CPU, and dropping the= guard hands it back. + StreamingCpuGuard { + data: self.container.data(), + dev, + dma_addr, + size, + direction, + } + } + + /// Tears the mapping down and returns the backing storage. + /// + /// Unmapping transfers ownership of the buffer back to the CPU, so no= separate + /// [`for_cpu`](Self::for_cpu) is needed. + /// + /// # Examples + /// + /// ``` + /// # use kernel::device::{Bound, Device}; + /// use kernel::dma::{ + /// DataDirection, + /// Streaming, // + /// }; + /// + /// # fn test(dev: &Device) -> Result { + /// let dma =3D Streaming::new( + /// dev, + /// KBox::new(0u64, GFP_KERNEL)?, + /// DataDirection::FromDevice, + /// )? + /// .submit(); + /// + /// // Program `dma.dma_handle()` into the device. + /// + /// // SAFETY: For the sake of the example, assume the transfer has be= en waited for. + /// let dma =3D unsafe { dma.complete() }; + /// + /// // Take the buffer back; the mapping is gone once this returns. + /// let buf: KBox =3D dma.into_inner(); + /// # Ok::<(), Error>(()) } + /// ``` + pub fn into_inner(self) -> C { + let mut this =3D ManuallyDrop::new(self); + + this.unmap(); + + // SAFETY: `this` is wrapped in a `ManuallyDrop`, so `Streaming::d= rop()` never runs and + // `this.container` is never read again. The remaining fields are = all `Copy`. + unsafe { core::ptr::read(&this.container) } + } + + /// Tears the mapping down. + /// + /// Shared by [`Drop`] and [`into_inner`](Self::into_inner), both of w= hich run it exactly once. + fn unmap(&mut self) { + // SAFETY: By the type invariants, `self.dev` is bound and the map= ping is still live, with + // exactly the address, size, direction and attributes it was crea= ted with. Both callers + // run this at most once, so the mapping cannot be torn down twice. + unsafe { + bindings::dma_unmap_single_attrs( + self.dev.as_raw(), + self.dma_addr, + self.container.size(), + self.direction.into(), + self.dma_attrs.as_raw(), + ) + }; + } +} + +impl Drop for Streaming<'_, C> { + fn drop(&mut self) { + self.unmap(); + } +} + +/// A [`Streaming`] mapping whose [`DmaAddress`] has been handed out. +/// +/// Returned by [`Streaming::submit`]. It owns the buffer, so the contents= are unreachable while +/// it exists. [`complete`](Self::complete) is the only way back: it is th= e caller's assertion +/// that the device has finished, which nothing else can establish. Droppi= ng this instead leaks +/// the mapping and the backing storage, since reclaiming either while the= device may still +/// access the buffer would be a use-after-free. +pub struct StreamingInFlight<'a, C: ContiguousBuffer>(ManuallyDrop>); + +impl<'a, C: ContiguousBuffer> StreamingInFlight<'a, C> { + /// Returns the DMA address to program into the device. + #[inline] + pub fn dma_handle(&self) -> DmaAddress { + self.0.dma_addr + } + + /// Returns the size of the mapping in bytes. + #[inline] + pub fn size(&self) -> usize { + self.0.size() + } + + /// Returns the direction this buffer was mapped with. + #[inline] + pub fn direction(&self) -> DataDirection { + self.0.direction() + } + + /// Takes the buffer back from the device. + /// + /// This performs no synchronization; [`Streaming::for_cpu`] does that. + /// + /// # Safety + /// + /// The device must have finished accessing the buffer. + #[inline] + pub unsafe fn complete(self) -> Streaming<'a, C> { + let mut this =3D ManuallyDrop::new(self); + + // SAFETY: `this` is wrapped in a `ManuallyDrop`, so `StreamingInF= light::drop()` never + // runs and `this.0` is never touched again. + unsafe { ManuallyDrop::take(&mut this.0) } + } +} + +impl Drop for StreamingInFlight<'_, C> { + fn drop(&mut self) { + // A transfer may still be in flight: freeing the storage would le= ave the device writing + // through a dangling handle, and unmapping could recycle a SWIOTL= B bounce slot + // mid-transfer. Reclaiming either requires the assertion only `co= mplete()` can make, so + // leak both. + dev_warn!( + self.0.dev, + "StreamingInFlight dropped without complete(); leaking the map= ping and its storage\n" + ); + } +} + +/// A guard granting the CPU access to the contents of a [`Streaming`] buf= fer. +/// +/// Returned by [`Streaming::for_cpu`]. Dropping it issues a `dma_sync_sin= gle_for_device()`, which +/// hands the buffer back to the device. +/// +/// # Invariants +/// +/// * `dev`, `dma_addr`, `size` and `direction` describe the live mapping = of the [`Streaming`] this +/// guard borrows, and are unchanged for the lifetime of the guard. +/// * `data` refers to exactly the mapped region. +pub struct StreamingCpuGuard<'a, T: ?Sized> { + data: &'a mut T, + dev: &'a device::Device, + dma_addr: DmaAddress, + size: usize, + direction: DataDirection, +} + +impl Drop for StreamingCpuGuard<'_, T> { + fn drop(&mut self) { + // SAFETY: By the type invariants, `self.dev` is bound and `self.d= ma_addr` denotes a live + // mapping of `self.size` bytes established with `self.direction`,= which is the range + // synced here. + unsafe { + bindings::dma_sync_single_for_device( + self.dev.as_raw(), + self.dma_addr, + self.size, + self.direction.into(), + ) + }; + } +} + +impl Deref for StreamingCpuGuard<'_, T> { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + self.data + } +} + +impl DerefMut for StreamingCpuGuard<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.data + } +} + /// An abstraction of the `dma_alloc_coherent` API. /// /// This is an abstraction around the `dma_alloc_coherent` API which is us= ed to allocate and map --=20 2.54.0 From nobody Fri Oct 2 03:37:52 2026 Received: from mout-p-101.mailbox.org (mout-p-101.mailbox.org [80.241.56.151]) (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 2F2BD3F7AB7; Wed, 5 Aug 2026 21:55:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.151 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785966913; cv=none; b=fn9112ouD1oC54chqxyTo93NTDbTARY/+mMZ8ksQPEtHK5WMvnCWTlLEz9JEMA2saKvSoXJCJLiVy2LiC3TO6sHH7qj49EdxH5ApCDbLyw8+ceaInYZYlCf3aZe+2MAO4dpzK3WyHjWj8YxMWAa6LtRB3H+VIvu+12mBfvnj9kA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785966913; c=relaxed/simple; bh=ThOABvvFsour3ijRiEVxhr05cModfN4gyoRRJwxQlzA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=lq7hfKjgN44M579KHSeuQe5eGA8BlcA18sDS2wGExALEk1DFqzpsN6hPJhiMfzknYUw4wvxot9h+NoP1CeCB3CsluucSjN88npAaw7kZ+C/XrsiwLktag4DcBbMAG3VxNkf8FXLgw+GMeZXs8KMZPDsRnnRVN+pae0rf+82R7rQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mailbox.org; spf=pass smtp.mailfrom=mailbox.org; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b=vmamja+D; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b=AiUAnXHo; arc=none smtp.client-ip=80.241.56.151 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mailbox.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mailbox.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b="vmamja+D"; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b="AiUAnXHo" Received: from smtp202.mailbox.org (smtp202.mailbox.org [IPv6:2001:67c:2050:b231:465::202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-101.mailbox.org (Postfix) with ESMTPS id 4hFkjQ0Dqtz8v33; Wed, 05 Aug 2026 23:55:06 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785966906; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Bezt+Zsz+6o7rpt8SOOEgp0ND8Yn/JD38WcjGDxzRL4=; b=vmamja+DdIEECiONfwcQwwmGAqdESGQvdJJEyXcVuNJIWH39Iy4PiuFAjVq3nyEwQS2zER qD4tplYjakFBa1So/bGmfi6QaBXnXrCXa42WgzNkI7JBYa1enwjZZ/nuBvZqLwjBstsk9A /sg+/TfnoY++svgUTxHT9+IJwPhfu1Yz2AusJyVEDFHpYR0AtFvCdHdzjxqA8w+664FGuN 0OpYIby89693ly/CgqIqoKUyYJQX5kEvxQ6tUFXaRwjdW+C6hKaorSKowb4o0MR+ED2lzk ZVfG5bEFrdgIy1CVhzr2TxACAcxidL/4xGlZTI81yHJ6L13Mees3W3VhqJ7JLg== Authentication-Results: outgoing_mbo_mout; dkim=pass header.d=mailbox.org header.s=mail20150812 header.b=AiUAnXHo; spf=pass (outgoing_mbo_mout: domain of mhi@mailbox.org designates 2001:67c:2050:b231:465::202 as permitted sender) smtp.mailfrom=mhi@mailbox.org From: Maurice Hieronymus DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785966904; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Bezt+Zsz+6o7rpt8SOOEgp0ND8Yn/JD38WcjGDxzRL4=; b=AiUAnXHoT70uU+hHU9Jew+KvkY+Bg1LMftCr/Uuvy5shqaLHOE2SFxrGMlK4w/H63vwhAf LXhSiS8HlC+Jb9Da6NvVk0+nvQQqhwMFWUU9P2mNxPu5s/DtyV17y8eOiTV66UZLdWZ+8g UrKkzzIvbdGXmEv6lbd40pHGP1THwG/5emlNfMzyJWjlImmYqcHGd7HqEzFZsUWxztwyf4 +I1LicAlDH5RY+4vezCTKtUPxkeAWsnLa8F1UHn4MD1CX4UdhdMsoEWELc4Go2+iYjpvwQ +LezW7QL4o9pBYE9btvcdBOGf+z8UhgVzAxZkhoDjn5yyGYHzy7LX0jyVWLAEw== Date: Wed, 05 Aug 2026 23:54:43 +0200 Subject: [PATCH 3/3] gpu: nova-core: gsp: map the WPR meta for streaming DMA Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260805-dma-streaming-v1-3-03974c86b141@mailbox.org> References: <20260805-dma-streaming-v1-0-03974c86b141@mailbox.org> In-Reply-To: <20260805-dma-streaming-v1-0-03974c86b141@mailbox.org> To: Danilo Krummrich , Abdiel Janulgue , Daniel Almeida , Robin Murphy , Andreas Hindborg , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , David Airlie , Simona Vetter Cc: driver-core@lists.linux.dev, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, Maurice Hieronymus X-Developer-Signature: v=1; a=ed25519-sha256; t=1785966885; l=6274; i=mhi@mailbox.org; s=20260525; h=from:subject:message-id; bh=ThOABvvFsour3ijRiEVxhr05cModfN4gyoRRJwxQlzA=; b=YCrrpuj6GlMj5rQmtZsCgCUvG2BV9HN9tuj8qLzbCj/GsTtpyV/FqSzoZ9WK1FKYzRj6MTnll zLFjHi80ZDTBvjIL4EftCQnjieHerAFcPXWVdIyZilWpVio2I0/aD72 X-Developer-Key: i=mhi@mailbox.org; a=ed25519; pk=AHlEkGG3hpXZHntlEzF42Ip/LFyXWOgsNUvaHqAnV80= X-MBO-RS-META: jyegkrsu9kunrco4ypfdr8s61uhas51p X-MBO-RS-ID: 4c71b2105cb0efc4092 X-Rspamd-Queue-Id: 4hFkjQ0Dqtz8v33 `GspFwWprMeta` is filled in once during boot, read out of system memory by the booter, and dropped when `Gsp::boot()` returns. That is a streaming transfer, so a coherent allocation buys nothing. Map a `KBox` instead. The mapping is submitted right after initialization, which makes the contents unreachable for the duration of the chipset-specific boot sequence and lets `dma_handle()` be read through a shared reference. `complete()` is called as soon as `hal.boot()` succeeds, the earliest point the device is provably done with the metadata: on Tu102 the Booter-load falcon has halted, on GH100 GSP-FMC has released the lockdown. If `hal.boot()` fails instead, no such proof exists -- `Gsp::unload()` deliberately carries on past failed steps, so the falcon may still be reading the buffer -- and `wpr_meta` drops in flight, trading a one-off leak for a device-side use-after-free. Signed-off-by: Maurice Hieronymus --- drivers/gpu/nova-core/firmware/booter.rs | 11 +++++++---- drivers/gpu/nova-core/gsp/boot.rs | 20 ++++++++++++++++++-- drivers/gpu/nova-core/gsp/hal.rs | 4 ++-- drivers/gpu/nova-core/gsp/hal/gh100.rs | 4 ++-- drivers/gpu/nova-core/gsp/hal/tu102.rs | 4 ++-- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-co= re/firmware/booter.rs index d9313ac361af..780c7702a777 100644 --- a/drivers/gpu/nova-core/firmware/booter.rs +++ b/drivers/gpu/nova-core/firmware/booter.rs @@ -9,9 +9,12 @@ =20 use kernel::{ device, - dma::Coherent, + dma::StreamingInFlight, prelude::*, - transmute::FromBytes, // + transmute::{ + AsBytes, + FromBytes, // + }, }; =20 use crate::{ @@ -402,12 +405,12 @@ pub(crate) fn new( /// /// Resets SEC2, loads this firmware image, then boots with the WPR me= tadata /// address passed via the SEC2 mailboxes. - pub(crate) fn run( + pub(crate) fn run( &self, dev: &device::Device, bar: Bar0<'_>, sec2_falcon: &Falcon, - wpr_meta: &Coherent, + wpr_meta: &StreamingInFlight<'_, KBox>, ) -> Result { sec2_falcon.reset(bar)?; sec2_falcon.load(dev, bar, self)?; diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/= boot.rs index 8afb62d689cb..300ebf4e843d 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -4,7 +4,10 @@ use kernel::{ bits, device, - dma::Coherent, + dma::{ + DataDirection, + Streaming, // + }, io::poll::read_poll_timeout, pci, prelude::*, @@ -117,7 +120,12 @@ pub(crate) fn boot( let fb_layout =3D FbLayout::new(chipset, bar, &gsp_fw)?; dev_dbg!(dev, "{:#x?}\n", fb_layout); =20 - let wpr_meta =3D Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new= (&gsp_fw, &fb_layout))?; + let wpr_meta =3D Streaming::new( + dev, + KBox::init(GspFwWprMeta::new(&gsp_fw, &fb_layout), GFP_KERNEL)= ?, + DataDirection::ToDevice, + )? + .submit(); =20 // Perform the chipset-specific boot sequence, and retrieve the un= load bundle. let unload_guard =3D hal.boot( @@ -131,6 +139,14 @@ pub(crate) fn boot( sec2_falcon, )?; =20 + // The chipset-specific boot sequence only succeeds once the devic= e is done reading the + // WPR metadata: on Tu102 the Booter-load falcon has halted, on GH= 100 GSP-FMC has released + // the lockdown. If it fails instead, `wpr_meta` drops in flight a= nd leaks, as the falcon + // may still be reading the buffer. + // + // SAFETY: Per the above, the device has finished accessing the bu= ffer. + let _ =3D unsafe { wpr_meta.complete() }; + gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version); =20 // Poll for RISC-V to become active before continuing. diff --git a/drivers/gpu/nova-core/gsp/hal.rs b/drivers/gpu/nova-core/gsp/h= al.rs index 04f004856c60..09a523e3a180 100644 --- a/drivers/gpu/nova-core/gsp/hal.rs +++ b/drivers/gpu/nova-core/gsp/hal.rs @@ -8,7 +8,7 @@ =20 use kernel::{ device, - dma::Coherent, // + dma::StreamingInFlight, // }; =20 use crate::{ @@ -61,7 +61,7 @@ fn boot<'a>( bar: Bar0<'a>, chipset: Chipset, fb_layout: &FbLayout, - wpr_meta: &Coherent, + wpr_meta: &StreamingInFlight<'_, KBox>, gsp_falcon: &'a Falcon, sec2_falcon: &'a Falcon, ) -> Result>; diff --git a/drivers/gpu/nova-core/gsp/hal/gh100.rs b/drivers/gpu/nova-core= /gsp/hal/gh100.rs index 98f5ce197d13..67a79c54a739 100644 --- a/drivers/gpu/nova-core/gsp/hal/gh100.rs +++ b/drivers/gpu/nova-core/gsp/hal/gh100.rs @@ -5,7 +5,7 @@ =20 use kernel::{ device, - dma::Coherent, + dma::StreamingInFlight, io::poll::read_poll_timeout, time::Delta, // }; @@ -156,7 +156,7 @@ fn boot<'a>( bar: Bar0<'a>, chipset: Chipset, fb_layout: &FbLayout, - wpr_meta: &Coherent, + wpr_meta: &StreamingInFlight<'_, KBox>, gsp_falcon: &'a Falcon, sec2_falcon: &'a Falcon, ) -> Result> { diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core= /gsp/hal/tu102.rs index 2f6301af7113..7d906de6d8bf 100644 --- a/drivers/gpu/nova-core/gsp/hal/tu102.rs +++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs @@ -5,7 +5,7 @@ =20 use kernel::{ device, - dma::Coherent, + dma::StreamingInFlight, io::Io, // }; =20 @@ -262,7 +262,7 @@ fn boot<'a>( bar: Bar0<'a>, chipset: Chipset, fb_layout: &FbLayout, - wpr_meta: &Coherent, + wpr_meta: &StreamingInFlight<'_, KBox>, gsp_falcon: &'a Falcon, sec2_falcon: &'a Falcon, ) -> Result> { --=20 2.54.0