The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH 0/1] nova-core: Convert bindings to use zerocopy
@ 2026-06-29  2:52 Alistair Popple
  2026-06-29  2:52 ` [PATCH 1/1] nova-core: Update firmware bindings to use zerocopy traits Alistair Popple
  2026-06-29  5:55 ` [PATCH 0/1] nova-core: Convert bindings to use zerocopy SeungJong Ha
  0 siblings, 2 replies; 10+ messages in thread
From: Alistair Popple @ 2026-06-29  2:52 UTC (permalink / raw)
  To: acourbot
  Cc: Alistair Popple, Danilo Krummrich, Alice Ryhl,
	Nicolás Antinori, David Airlie, Shuah Khan, Simona Vetter,
	Gary Guo, Onur Özkan, Tamir Duberstein, Trevor Gross,
	Pedro Yudi Honda, SeungJong Ha, linux-kernel, dri-devel,
	rust-for-linux, nova-gpu

This patch converts all the bindings to use the new zerocopy library. Bindings
are currently generated using a fairly simple bindgen script[1]. This is a
temporary solution until the bindings have been stablisied but for now upstream
should be kept in sync with the resulting output.

Note that this does not completely remove all users of the transmute
From/AsBytes traits from nova-core, leaving some ambiguity with when referring
to FromBytes. However others have recently posted patches[2][3][4] to remove
the majority of other uses which should allow us to remove transmute::FromBytes
entirely from nova-core.

Given the inevitable conflicts that will arise from trying to merge several
different series doing bits of the same thing I'm happy if someone wants to take
this patch and the others and roll them into a single series. Alternatively I am
happy to take that on - hopefully Alex can provide some guidance here for what
would be preferred.

[1] - https://github.com/apopple-nvidia/nova-gsp-binding-generator/tree/zerocopy
[2] - https://lore.kernel.org/rust-for-linux/20260628-dma-zerocopy-bridge-v1-0-9a2895ebe30d@gmail.com/
[3] - https://lore.kernel.org/rust-for-linux/20260625205146.5047-1-niyudi.honda@usp.br/
[4] - https://lore.kernel.org/rust-for-linux/20260621143647.264770-1-nico.antinori.7@gmail.com/

Cc: Danilo Krummrich <dakr@kernel.org>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Miguel Ojeda <ojeda@kernel.org
Cc: "Nicolás Antinori" <nico.antinori.7@gmail.com>
Cc: "Alexandre Courbot" <acourbot@nvidia.com>
Cc: "David Airlie" <airlied@gmail.com>
Cc: "Shuah Khan" <skhan@linuxfoundation.org>
Cc: "Simona Vetter" <simona@ffwll.ch>
Cc: "Gary Guo" <gary@garyguo.net>
Cc: "Onur Özkan" <work@onurozkan.dev>
Cc: "Tamir Duberstein" <tamird@kernel.org>
Cc: "Trevor Gross" <tmgross@umich.edu>
Cc: Pedro Yudi Honda <niyudi.honda@usp.br>
Cc: SeungJong Ha <engineer.jjhama@gmail.com>
Cc: linux-kernel@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: rust-for-linux@vger.kernel.org
Cc: nova-gpu@lists.linux.de

Alistair Popple (1):
  nova-core: Update firmware bindings to use zerocopy traits

 drivers/gpu/nova-core/Makefile                |   4 +
 drivers/gpu/nova-core/gsp/cmdq.rs             |  21 +-
 .../gpu/nova-core/gsp/cmdq/continuation.rs    |  16 +-
 drivers/gpu/nova-core/gsp/commands.rs         |  19 +-
 drivers/gpu/nova-core/gsp/fw.rs               |  54 +----
 drivers/gpu/nova-core/gsp/fw/commands.rs      |  50 ++---
 drivers/gpu/nova-core/gsp/fw/r570_144.rs      |  41 ++++
 .../gpu/nova-core/gsp/fw/r570_144/bindings.rs | 185 ++++++++++++------
 drivers/gpu/nova-core/gsp/sequencer.rs        |   5 +-
 scripts/Makefile.build                        |   4 +-
 10 files changed, 223 insertions(+), 176 deletions(-)

-- 
2.54.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/1] nova-core: Update firmware bindings to use zerocopy traits
  2026-06-29  2:52 [PATCH 0/1] nova-core: Convert bindings to use zerocopy Alistair Popple
@ 2026-06-29  2:52 ` Alistair Popple
  2026-06-29  7:36   ` Alexandre Courbot
  2026-06-29  5:55 ` [PATCH 0/1] nova-core: Convert bindings to use zerocopy SeungJong Ha
  1 sibling, 1 reply; 10+ messages in thread
From: Alistair Popple @ 2026-06-29  2:52 UTC (permalink / raw)
  To: acourbot
  Cc: Alistair Popple, Danilo Krummrich, Alice Ryhl,
	Nicolás Antinori, David Airlie, Shuah Khan, Simona Vetter,
	Gary Guo, Onur Özkan, Tamir Duberstein, Trevor Gross,
	Pedro Yudi Honda, SeungJong Ha, linux-kernel, dri-devel,
	rust-for-linux, nova-gpu

Currently most of nova-core uses unsafe implementations of AsBytes and
FromBytes in order to read and write GSP commands using the generated
bindings. Whilst this is generally safe in practice there is nothing
that actually guarantees or checks this.

This is exactly what the zerocopy library was introduced to do - provide
compile-time checks ensuring From/AsBytes is safe. Make use of these
checks by converting all our generated bindings to derive the required
traits for the zerocopy checks, removing many unsafe implementations in
the process.

Note this does require the use of an unstable feature - trivial_bounds
- as some bindings end up needing to derive zerocopy::Unaligned.
A work-around is also required in the bindings to make some of the
__IncompleteArrayField<T> ZSTs monomorphic as the check macro can not
prove a generic type is aligned and thus forces T to derive
zerocopy::Unaligned, which is not satisfied by all types.

Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
 drivers/gpu/nova-core/Makefile                |   4 +
 drivers/gpu/nova-core/gsp/cmdq.rs             |  21 +-
 .../gpu/nova-core/gsp/cmdq/continuation.rs    |  16 +-
 drivers/gpu/nova-core/gsp/commands.rs         |  19 +-
 drivers/gpu/nova-core/gsp/fw.rs               |  54 +----
 drivers/gpu/nova-core/gsp/fw/commands.rs      |  50 ++---
 drivers/gpu/nova-core/gsp/fw/r570_144.rs      |  41 ++++
 .../gpu/nova-core/gsp/fw/r570_144/bindings.rs | 185 ++++++++++++------
 drivers/gpu/nova-core/gsp/sequencer.rs        |   5 +-
 scripts/Makefile.build                        |   4 +-
 10 files changed, 223 insertions(+), 176 deletions(-)

diff --git a/drivers/gpu/nova-core/Makefile b/drivers/gpu/nova-core/Makefile
index 4ae544f808f4..27a5752c4cf9 100644
--- a/drivers/gpu/nova-core/Makefile
+++ b/drivers/gpu/nova-core/Makefile
@@ -1,4 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 
+# The GSP firmware bindings derive zerocopy's `IntoBytes` on `#[repr(C)]`
+# unions, which is gated behind the `zerocopy_derive_union_into_bytes` cfg.
+rustflags-y += --cfg zerocopy_derive_union_into_bytes
+
 obj-$(CONFIG_NOVA_CORE) += nova-core.o
 nova-core-y := nova_core.o
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 0671ee8a9960..6e79ec688983 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -29,6 +29,14 @@
     },
 };
 
+// TODO: Remove `as` once FromBytes is removed completely
+use zerocopy::{
+    FromBytes as ZCFromBytes,
+    Immutable,
+    IntoBytes,
+    KnownLayout, //
+};
+
 use continuation::{
     ContinuationRecord,
     SplitState, //
@@ -76,7 +84,7 @@ pub(crate) trait CommandToGsp {
     const FUNCTION: MsgFunction;
 
     /// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer.
-    type Command: FromBytes + AsBytes;
+    type Command: ZCFromBytes + KnownLayout + Immutable + IntoBytes;
 
     /// Type of the reply expected from the GSP, or [`NoReply`] for commands that don't
     /// have a reply.
@@ -133,7 +141,7 @@ pub(crate) trait MessageFromGsp: Sized {
     type InitError;
 
     /// Type containing the raw message to be read from the message queue.
-    type Message: FromBytes;
+    type Message: ZCFromBytes + KnownLayout + Immutable;
 
     /// Method reading the message from the message queue and returning it.
     ///
@@ -383,7 +391,7 @@ fn allocate_command(&mut self, size: usize, timeout: Delta) -> Result<GspCommand
         };
 
         // Extract area for the `GspMsgElement`.
-        let (header, slice_1) = GspMsgElement::from_bytes_mut_prefix(slice_1).ok_or(EIO)?;
+        let (header, slice_1) = GspMsgElement::mut_from_prefix(slice_1).map_err(|_| EIO)?;
 
         // Create the contents area.
         let (slice_1, slice_2) = if slice_1.len() > size {
@@ -639,7 +647,7 @@ fn send_single_command<M>(&mut self, bar: Bar0<'_>, command: M) -> Result
         // Extract area for the command itself. The GSP message header and the command header
         // together are guaranteed to fit entirely into a single page, so it's ok to only look
         // at `dst.contents.0` here.
-        let (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?;
+        let (cmd, payload_1) = M::Command::mut_from_prefix(dst.contents.0).map_err(|_| EIO)?;
 
         // Fill the header and command in-place.
         let msg_element = GspMsgElement::init(self.seq, size_in_bytes, M::FUNCTION);
@@ -744,7 +752,7 @@ fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> {
         .map(|(slice_1, slice_2)| (slice_1.as_flattened(), slice_2.as_flattened()))?;
 
         // Extract the `GspMsgElement`.
-        let (header, slice_1) = GspMsgElement::from_bytes_prefix(slice_1).ok_or(EIO)?;
+        let (header, slice_1) = GspMsgElement::ref_from_prefix(slice_1).map_err(|_| EIO)?;
 
         dev_dbg!(
             &self.dev,
@@ -822,7 +830,8 @@ fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M>
         // Extract the message. Store the result as we want to advance the read pointer even in
         // case of failure.
         let result = if function == M::FUNCTION {
-            let (cmd, contents_1) = M::Message::from_bytes_prefix(message.contents.0).ok_or(EIO)?;
+            let (cmd, contents_1) =
+                M::Message::ref_from_prefix(message.contents.0).map_err(|_| EIO)?;
             let mut sbuffer = SBufferIter::new_reader([contents_1, message.contents.1]);
 
             M::read(cmd, &mut sbuffer)
diff --git a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
index 05e904f18097..f0c69f0760a4 100644
--- a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
@@ -171,22 +171,18 @@ fn init_variable_payload(
 mod tests {
     use super::*;
 
-    use kernel::transmute::{
-        AsBytes,
-        FromBytes, //
+    use zerocopy_derive::{
+        FromBytes,
+        Immutable,
+        IntoBytes,
+        KnownLayout, //
     };
 
     /// Non-zero-sized command header for testing.
     #[repr(C)]
-    #[derive(Clone, Copy, Zeroable)]
+    #[derive(Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout, Zeroable)]
     struct TestHeader([u8; 64]);
 
-    // SAFETY: `TestHeader` is a plain array of bytes for which all bit patterns are valid.
-    unsafe impl FromBytes for TestHeader {}
-
-    // SAFETY: `TestHeader` is a plain array of bytes for which all bit patterns are valid.
-    unsafe impl AsBytes for TestHeader {}
-
     struct TestPayload {
         data: KVVec<u8>,
     }
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 86a3747cd31c..f3c5806f9d19 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -12,11 +12,7 @@
 use kernel::{
     device,
     pci,
-    prelude::*,
-    transmute::{
-        AsBytes,
-        FromBytes, //
-    }, //
+    prelude::*, //
 };
 
 use crate::{
@@ -130,13 +126,12 @@ fn init_variable_payload(
         let mut string_data = KVec::new();
 
         for entry in self.entries.iter().take(Self::NUM_ENTRIES) {
-            dst.write_all(
-                fw::commands::PackedRegistryEntry::new(
+            dst.write_all(zerocopy::IntoBytes::as_bytes(
+                &fw::commands::PackedRegistryEntry::new(
                     (string_data_start_offset + string_data.len()) as u32,
                     entry.value,
-                )
-                .as_bytes(),
-            )?;
+                ),
+            ))?;
 
             let key_bytes = entry.key.as_bytes();
             string_data.extend_from_slice(key_bytes, GFP_KERNEL)?;
@@ -150,10 +145,6 @@ fn init_variable_payload(
 /// Message type for GSP initialization done notification.
 struct GspInitDone;
 
-// SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it
-// trivially has no uninitialized bytes.
-unsafe impl FromBytes for GspInitDone {}
-
 impl MessageFromGsp for GspInitDone {
     const FUNCTION: MsgFunction = MsgFunction::GspInitDone;
     type InitError = Infallible;
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 4db0cfa4dc4d..be2b4b046b19 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -27,6 +27,8 @@
     },
 };
 
+use zerocopy_derive::{Immutable, KnownLayout};
+
 use crate::{
     fb::FbLayout,
     firmware::gsp::GspFirmware,
@@ -434,12 +436,6 @@ pub(crate) fn val(&self) -> u32 {
     }
 }
 
-// SAFETY: This struct only contains integer types for which all bit patterns are valid.
-unsafe impl FromBytes for RegWritePayload {}
-
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for RegWritePayload {}
-
 /// Wrapper for GSP sequencer register modify payload.
 #[repr(transparent)]
 #[derive(Copy, Clone, Debug)]
@@ -462,12 +458,6 @@ pub(crate) fn val(&self) -> u32 {
     }
 }
 
-// SAFETY: This struct only contains integer types for which all bit patterns are valid.
-unsafe impl FromBytes for RegModifyPayload {}
-
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for RegModifyPayload {}
-
 /// Wrapper for GSP sequencer register poll payload.
 #[repr(transparent)]
 #[derive(Copy, Clone, Debug)]
@@ -495,12 +485,6 @@ pub(crate) fn timeout(&self) -> u32 {
     }
 }
 
-// SAFETY: This struct only contains integer types for which all bit patterns are valid.
-unsafe impl FromBytes for RegPollPayload {}
-
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for RegPollPayload {}
-
 /// Wrapper for GSP sequencer delay payload.
 #[repr(transparent)]
 #[derive(Copy, Clone, Debug)]
@@ -513,12 +497,6 @@ pub(crate) fn val(&self) -> u32 {
     }
 }
 
-// SAFETY: This struct only contains integer types for which all bit patterns are valid.
-unsafe impl FromBytes for DelayUsPayload {}
-
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for DelayUsPayload {}
-
 /// Wrapper for GSP sequencer register store payload.
 #[repr(transparent)]
 #[derive(Copy, Clone, Debug)]
@@ -537,13 +515,8 @@ pub(crate) fn index(&self) -> u32 {
     }
 }
 
-// SAFETY: This struct only contains integer types for which all bit patterns are valid.
-unsafe impl FromBytes for RegStorePayload {}
-
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for RegStorePayload {}
-
 /// Wrapper for GSP sequencer buffer command.
+#[derive(FromBytes, Immutable, KnownLayout)]
 #[repr(transparent)]
 pub(crate) struct SequencerBufferCmd(bindings::GSP_SEQUENCER_BUFFER_CMD);
 
@@ -609,13 +582,8 @@ pub(crate) fn reg_store_payload(&self) -> Result<RegStorePayload> {
     }
 }
 
-// SAFETY: This struct only contains integer types for which all bit patterns are valid.
-unsafe impl FromBytes for SequencerBufferCmd {}
-
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for SequencerBufferCmd {}
-
 /// Wrapper for GSP run CPU sequencer RPC.
+#[derive(FromBytes, Immutable, KnownLayout)]
 #[repr(transparent)]
 pub(crate) struct RunCpuSequencer(bindings::rpc_run_cpu_sequencer_v17_00);
 
@@ -626,12 +594,6 @@ pub(crate) fn cmd_index(&self) -> u32 {
     }
 }
 
-// SAFETY: This struct only contains integer types for which all bit patterns are valid.
-unsafe impl FromBytes for RunCpuSequencer {}
-
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for RunCpuSequencer {}
-
 /// Struct containing the arguments required to pass a memory buffer to the GSP
 /// for use during initialisation.
 ///
@@ -780,6 +742,7 @@ fn init(cmd_size: usize, function: MsgFunction) -> impl Init<Self, Error> {
 /// GSP Message Element.
 ///
 /// This is essentially a message header expected to be followed by the message data.
+#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
 #[repr(transparent)]
 pub(crate) struct GspMsgElement {
     inner: bindings::GSP_MSG_QUEUE_ELEMENT,
@@ -859,13 +822,6 @@ pub(crate) fn element_count(&self) -> u32 {
     }
 }
 
-// SAFETY: Padding is explicit and does not contain uninitialized data.
-unsafe impl AsBytes for GspMsgElement {}
-
-// SAFETY: This struct only contains integer types for which all bit patterns
-// are valid.
-unsafe impl FromBytes for GspMsgElement {}
-
 /// Arguments for GSP startup.
 #[repr(transparent)]
 #[derive(Zeroable)]
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index ebdc12bcd4e3..18bb7ee2141a 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -6,11 +6,7 @@
 use kernel::{
     device,
     pci,
-    prelude::*,
-    transmute::{
-        AsBytes,
-        FromBytes, //
-    }, //
+    prelude::*, //
 };
 
 use crate::{
@@ -19,10 +15,16 @@
     num::IntoSafeCast, //
 };
 
+use zerocopy_derive::{
+    Immutable,
+    KnownLayout, //
+};
+
 use super::bindings;
 
 /// Payload of the `GspSetSystemInfo` command.
 #[repr(transparent)]
+#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
 pub(crate) struct GspSetSystemInfo {
     inner: bindings::GspSystemInfo,
 }
@@ -64,15 +66,8 @@ pub(crate) fn init<'a>(
     }
 }
 
-// SAFETY: These structs don't meet the no-padding requirements of AsBytes but
-//         that is not a problem because they are not used outside the kernel.
-unsafe impl AsBytes for GspSetSystemInfo {}
-
-// SAFETY: These structs don't meet the no-padding requirements of FromBytes but
-//         that is not a problem because they are not used outside the kernel.
-unsafe impl FromBytes for GspSetSystemInfo {}
-
 #[repr(transparent)]
+#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
 pub(crate) struct PackedRegistryEntry(bindings::PACKED_REGISTRY_ENTRY);
 
 impl PackedRegistryEntry {
@@ -92,11 +87,9 @@ pub(crate) fn new(offset: u32, value: u32) -> Self {
     }
 }
 
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for PackedRegistryEntry {}
-
 /// Payload of the `SetRegistry` command.
 #[repr(transparent)]
+#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
 pub(crate) struct PackedRegistryTable {
     inner: bindings::PACKED_REGISTRY_TABLE,
 }
@@ -115,16 +108,9 @@ pub(crate) fn init(num_entries: u32, size: u32) -> impl Init<Self> {
     }
 }
 
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for PackedRegistryTable {}
-
-// SAFETY: This struct only contains integer types for which all bit patterns
-// are valid.
-unsafe impl FromBytes for PackedRegistryTable {}
-
 /// Payload of the `GetGspStaticInfo` command and message.
 #[repr(transparent)]
-#[derive(Zeroable)]
+#[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Zeroable)]
 pub(crate) struct GspStaticConfigInfo(bindings::GspStaticConfigInfo_t);
 
 impl GspStaticConfigInfo {
@@ -169,13 +155,6 @@ pub(crate) fn usable_fb_regions(&self) -> impl Iterator<Item = Range<u64>> + '_
     }
 }
 
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for GspStaticConfigInfo {}
-
-// SAFETY: This struct only contains integer types for which all bit patterns
-// are valid.
-unsafe impl FromBytes for GspStaticConfigInfo {}
-
 /// Power level requested to the [`UnloadingGuestDriver`] command.
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 #[repr(u32)]
@@ -199,7 +178,7 @@ pub(crate) fn is_power_transition(self) -> bool {
 
 /// Payload of the `UnloadingGuestDriver` command and message.
 #[repr(transparent)]
-#[derive(Clone, Copy, Debug, Zeroable)]
+#[derive(Clone, Copy, Debug, FromBytes, Immutable, IntoBytes, KnownLayout, Zeroable)]
 pub(crate) struct UnloadingGuestDriver(bindings::rpc_unloading_guest_driver_v1F_07);
 
 impl UnloadingGuestDriver {
@@ -212,10 +191,3 @@ pub(crate) fn new(level: PowerStateLevel) -> Self {
         })
     }
 }
-
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for UnloadingGuestDriver {}
-
-// SAFETY: This struct only contains integer types for which all bit patterns
-// are valid.
-unsafe impl FromBytes for UnloadingGuestDriver {}
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144.rs b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
index 2e6f0d298756..75f5e4a19596 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
@@ -23,9 +23,50 @@
 )]
 use kernel::ffi;
 use pin_init::MaybeZeroable;
+use zerocopy_derive::{
+    FromBytes,
+    Immutable,
+    IntoBytes,
+    KnownLayout, //
+};
 
 include!("r570_144/bindings.rs");
 
 // SAFETY: This type has a size of zero, so its inclusion into another type should not affect their
 // ability to implement `Zeroable`.
 unsafe impl<T> kernel::prelude::Zeroable for __IncompleteArrayField<T> {}
+
+/// Monomorphic version of [`__IncompleteArrayField<PACKED_REGISTRY_ENTRY>`].
+///
+/// zerocopy's `IntoBytes` derive can only run its compile-time no-padding check
+/// on a concrete type; for the generic `__IncompleteArrayField<T>` it falls back
+/// to requiring every field be `Unaligned`, which `PACKED_REGISTRY_ENTRY`
+/// does not satisfy. Specializing to a concrete type lets the derive succeed.
+#[repr(C)]
+#[derive(Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout, MaybeZeroable)]
+pub struct __IncompletePackedRegistryEntry(
+    ::core::marker::PhantomData<PACKED_REGISTRY_ENTRY>,
+    [PACKED_REGISTRY_ENTRY; 0],
+);
+impl __IncompletePackedRegistryEntry {
+    #[inline]
+    pub const fn new() -> Self {
+        __IncompletePackedRegistryEntry(::core::marker::PhantomData, [])
+    }
+    #[inline]
+    pub fn as_ptr(&self) -> *const PACKED_REGISTRY_ENTRY {
+        self as *const _ as *const PACKED_REGISTRY_ENTRY
+    }
+    #[inline]
+    pub fn as_mut_ptr(&mut self) -> *mut PACKED_REGISTRY_ENTRY {
+        self as *mut _ as *mut PACKED_REGISTRY_ENTRY
+    }
+    #[inline]
+    pub unsafe fn as_slice(&self, len: usize) -> &[PACKED_REGISTRY_ENTRY] {
+        ::core::slice::from_raw_parts(self.as_ptr(), len)
+    }
+    #[inline]
+    pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [PACKED_REGISTRY_ENTRY] {
+        ::core::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+    }
+}
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index ea350f9b2cc4..9c85c93f6eee 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #[repr(C)]
-#[derive(Default)]
+#[derive(Default, FromBytes, IntoBytes, Immutable, KnownLayout)]
 pub struct __IncompleteArrayField<T>(::core::marker::PhantomData<T>, [T; 0]);
 impl<T> __IncompleteArrayField<T> {
     #[inline]
@@ -325,7 +325,9 @@ fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
 pub const NV_VGPU_MSG_EVENT_NUM_EVENTS: _bindgen_ty_3 = 4131;
 pub type _bindgen_ty_3 = ffi::c_uint;
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS {
     pub totalVFs: u32_,
     pub firstVfOffset: u32_,
@@ -349,7 +351,9 @@ pub struct NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS {
     pub __bindgen_padding_1: [u8; 7usize],
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS {
     pub BoardID: u32_,
     pub chipSKU: [ffi::c_char; 9usize],
@@ -365,7 +369,9 @@ pub struct NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS {
 }
 pub type NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG = [u8_; 17usize];
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO {
     pub base: u64_,
     pub limit: u64_,
@@ -377,14 +383,16 @@ pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO {
     pub blackList: NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS {
     pub numFBRegions: u32_,
     pub __bindgen_padding_0: [u8; 4usize],
     pub fbRegion: [NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO; 16usize],
 }
 #[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
+#[derive(Debug, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
     pub index: u32_,
     pub flags: u32_,
@@ -401,14 +409,18 @@ fn default() -> Self {
     }
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct DOD_METHOD_DATA {
     pub status: u32_,
     pub acpiIdListLen: u32_,
     pub acpiIdList: [u32_; 16usize],
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct JT_METHOD_DATA {
     pub status: u32_,
     pub jtCaps: u32_,
@@ -417,14 +429,18 @@ pub struct JT_METHOD_DATA {
     pub __bindgen_padding_0: u8,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct MUX_METHOD_DATA_ELEMENT {
     pub acpiId: u32_,
     pub mode: u32_,
     pub status: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct MUX_METHOD_DATA {
     pub tableLen: u32_,
     pub acpiIdMuxModeTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
@@ -432,13 +448,17 @@ pub struct MUX_METHOD_DATA {
     pub acpiIdMuxStateTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct CAPS_METHOD_DATA {
     pub status: u32_,
     pub optimusCaps: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct ACPI_METHOD_DATA {
     pub bValid: u8_,
     pub __bindgen_padding_0: [u8; 3usize],
@@ -447,21 +467,28 @@ pub struct ACPI_METHOD_DATA {
     pub muxMethodData: MUX_METHOD_DATA,
     pub capsMethodData: CAPS_METHOD_DATA,
 }
+pub type GspStaticConfigInfo = GspStaticConfigInfo_t;
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS {
     pub headIndex: u32_,
     pub maxHResolution: u32_,
     pub maxVResolution: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS {
     pub numHeads: u32_,
     pub maxNumHeads: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct BUSINFO {
     pub deviceID: u16_,
     pub vendorID: u16_,
@@ -471,7 +498,9 @@ pub struct BUSINFO {
     pub __bindgen_padding_0: u8,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_VF_INFO {
     pub totalVFs: u32_,
     pub firstVFOffset: u32_,
@@ -484,25 +513,31 @@ pub struct GSP_VF_INFO {
     pub __bindgen_padding_0: [u8; 5usize],
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_PCIE_CONFIG_REG {
     pub linkCap: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct EcidManufacturingInfo {
     pub ecidLow: u32_,
     pub ecidHigh: u32_,
     pub ecidExtended: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct FW_WPR_LAYOUT_OFFSET {
     pub nonWprHeapOffset: u64_,
     pub frtsOffset: u64_,
 }
 #[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
+#[derive(Debug, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct GspStaticConfigInfo_t {
     pub grCapsBits: [u8_; 23usize],
     pub __bindgen_padding_0: u8,
@@ -575,7 +610,9 @@ fn default() -> Self {
     }
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GspSystemInfo {
     pub gpuPhysAddr: u64_,
     pub gpuPhysFbAddr: u64_,
@@ -632,7 +669,9 @@ pub struct GspSystemInfo {
     pub hostPageSize: u64_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct MESSAGE_QUEUE_INIT_ARGUMENTS {
     pub sharedMemPhysAddr: u64_,
     pub pageTableEntryCount: u32_,
@@ -641,7 +680,9 @@ pub struct MESSAGE_QUEUE_INIT_ARGUMENTS {
     pub statQueueOffset: u64_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_SR_INIT_ARGUMENTS {
     pub oldLevel: u32_,
     pub flags: u32_,
@@ -649,7 +690,9 @@ pub struct GSP_SR_INIT_ARGUMENTS {
     pub __bindgen_padding_0: [u8; 3usize],
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_ARGUMENTS_CACHED {
     pub messageQueueInitArguments: MESSAGE_QUEUE_INIT_ARGUMENTS,
     pub srInitArguments: GSP_SR_INIT_ARGUMENTS,
@@ -659,13 +702,15 @@ pub struct GSP_ARGUMENTS_CACHED {
     pub profilerArgs: GSP_ARGUMENTS_CACHED__bindgen_ty_1,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_1 {
     pub pa: u64_,
     pub size: u64_,
 }
 #[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
+#[derive(Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub union rpc_message_rpc_union_field_v03_00 {
     pub spare: u32_,
     pub cpuRmGfid: u32_,
@@ -681,7 +726,7 @@ fn default() -> Self {
 }
 pub type rpc_message_rpc_union_field_v = rpc_message_rpc_union_field_v03_00;
 #[repr(C)]
-#[derive(MaybeZeroable)]
+#[derive(MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct rpc_message_header_v03_00 {
     pub header_version: u32_,
     pub signature: u32_,
@@ -704,7 +749,7 @@ fn default() -> Self {
 }
 pub type rpc_message_header_v = rpc_message_header_v03_00;
 #[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
+#[derive(Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct GspFwWprMeta {
     pub magic: u64_,
     pub revision: u64_,
@@ -739,19 +784,23 @@ pub struct GspFwWprMeta {
     pub verified: u64_,
 }
 #[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
+#[derive(Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub union GspFwWprMeta__bindgen_ty_1 {
     pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1__bindgen_ty_1,
     pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_1__bindgen_ty_2,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_1 {
     pub sysmemAddrOfSignature: u64_,
     pub sizeOfSignature: u64_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_2 {
     pub gspFwHeapFreeListWprOffset: u32_,
     pub unused0: u32_,
@@ -767,13 +816,15 @@ fn default() -> Self {
     }
 }
 #[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
+#[derive(Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub union GspFwWprMeta__bindgen_ty_2 {
     pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_2__bindgen_ty_1,
     pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2__bindgen_ty_2,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 {
     pub partitionRpcAddr: u64_,
     pub partitionRpcRequestOffset: u16_,
@@ -785,7 +836,9 @@ pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 {
     pub lsUcodeVersion: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_2 {
     pub partitionRpcPadding: [u32_; 4usize],
     pub sysmemAddrOfCrashReportQueue: u64_,
@@ -820,7 +873,9 @@ fn default() -> Self {
 pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_FB: LibosMemoryRegionLoc = 2;
 pub type LibosMemoryRegionLoc = ffi::c_uint;
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct LibosMemoryRegionInitArgument {
     pub id8: LibosAddress,
     pub pa: LibosAddress,
@@ -830,7 +885,9 @@ pub struct LibosMemoryRegionInitArgument {
     pub __bindgen_padding_0: [u8; 6usize],
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct PACKED_REGISTRY_ENTRY {
     pub nameOffset: u32_,
     pub type_: u8_,
@@ -839,14 +896,16 @@ pub struct PACKED_REGISTRY_ENTRY {
     pub length: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, MaybeZeroable)]
+#[derive(Debug, Default, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct PACKED_REGISTRY_TABLE {
     pub size: u32_,
     pub numEntries: u32_,
-    pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>,
+    pub entries: __IncompletePackedRegistryEntry,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct msgqTxHeader {
     pub version: u32_,
     pub size: u32_,
@@ -858,13 +917,15 @@ pub struct msgqTxHeader {
     pub entryOff: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct msgqRxHeader {
     pub readPtr: u32_,
 }
 #[repr(C)]
 #[repr(align(8))]
-#[derive(MaybeZeroable)]
+#[derive(MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct GSP_MSG_QUEUE_ELEMENT {
     pub authTagBuffer: [u8_; 16usize],
     pub aadBuffer: [u8_; 16usize],
@@ -889,12 +950,14 @@ fn default() -> Self {
 pub const GSP_DMA_TARGET_GSP_DMA_TARGET_COUNT: GSP_DMA_TARGET = 3;
 pub type GSP_DMA_TARGET = ffi::c_uint;
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_FMC_INIT_PARAMS {
     pub regkeys: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
+#[derive(Debug, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct GSP_ACR_BOOT_GSP_RM_PARAMS {
     pub target: GSP_DMA_TARGET,
     pub gspRmDescSize: u32_,
@@ -914,7 +977,7 @@ fn default() -> Self {
     }
 }
 #[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
+#[derive(Debug, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct GSP_RM_PARAMS {
     pub target: GSP_DMA_TARGET,
     pub __bindgen_padding_0: [u8; 4usize],
@@ -930,7 +993,7 @@ fn default() -> Self {
     }
 }
 #[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
+#[derive(Debug, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct GSP_SPDM_PARAMS {
     pub target: GSP_DMA_TARGET,
     pub __bindgen_padding_0: [u8; 4usize],
@@ -948,7 +1011,7 @@ fn default() -> Self {
     }
 }
 #[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
+#[derive(Debug, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct GSP_FMC_BOOT_PARAMS {
     pub initParams: GSP_FMC_INIT_PARAMS,
     pub __bindgen_padding_0: [u8; 4usize],
@@ -966,7 +1029,9 @@ fn default() -> Self {
     }
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct rpc_unloading_guest_driver_v1F_07 {
     pub bInPMTransition: u8_,
     pub bGc6Entering: u8_,
@@ -974,7 +1039,7 @@ pub struct rpc_unloading_guest_driver_v1F_07 {
     pub newLevel: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, MaybeZeroable)]
+#[derive(Debug, Default, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct rpc_run_cpu_sequencer_v17_00 {
     pub bufferSizeDWord: u32_,
     pub cmdIndex: u32_,
@@ -992,20 +1057,26 @@ pub struct rpc_run_cpu_sequencer_v17_00 {
 pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME: GSP_SEQ_BUF_OPCODE = 8;
 pub type GSP_SEQ_BUF_OPCODE = ffi::c_uint;
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_SEQ_BUF_PAYLOAD_REG_WRITE {
     pub addr: u32_,
     pub val: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_SEQ_BUF_PAYLOAD_REG_MODIFY {
     pub addr: u32_,
     pub mask: u32_,
     pub val: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_SEQ_BUF_PAYLOAD_REG_POLL {
     pub addr: u32_,
     pub mask: u32_,
@@ -1014,24 +1085,28 @@ pub struct GSP_SEQ_BUF_PAYLOAD_REG_POLL {
     pub error: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_SEQ_BUF_PAYLOAD_DELAY_US {
     pub val: u32_,
 }
 #[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+#[derive(
+    Debug, Default, Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes,
+)]
 pub struct GSP_SEQ_BUF_PAYLOAD_REG_STORE {
     pub addr: u32_,
     pub index: u32_,
 }
 #[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
+#[derive(Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub struct GSP_SEQUENCER_BUFFER_CMD {
     pub opCode: GSP_SEQ_BUF_OPCODE,
     pub payload: GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1,
 }
 #[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
+#[derive(Copy, Clone, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
 pub union GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1 {
     pub regWrite: GSP_SEQ_BUF_PAYLOAD_REG_WRITE,
     pub regModify: GSP_SEQ_BUF_PAYLOAD_REG_MODIFY,
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs
index e0850d21adca..99b05999c9b7 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -15,9 +15,10 @@
         delay::fsleep,
         Delta, //
     },
-    transmute::FromBytes, //
 };
 
+use zerocopy::FromBytes;
+
 use crate::{
     driver::Bar0,
     falcon::{
@@ -82,7 +83,7 @@ pub(crate) enum GspSeqCmd {
 impl GspSeqCmd {
     /// Creates a new `GspSeqCmd` from raw data returning the command and its size in bytes.
     pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> {
-        let fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?;
+        let fw_cmd = fw::SequencerBufferCmd::ref_from_bytes(data).map_err(|_| EINVAL)?;
         let opcode_size = core::mem::size_of::<u32>();
 
         let (cmd, size) = match fw_cmd.opcode()? {
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 911745743246..0d0f315e43d0 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -314,10 +314,12 @@ $(obj)/%.lst: $(obj)/%.c FORCE
 #   - Stable since Rust 1.89.0: `feature(generic_arg_infer)`.
 #   - Expected to become stable: `feature(arbitrary_self_types)`.
 #   - To be determined: `feature(used_with_arg)`.
+#   - Required by nova-core's zerocopy firmware bindings, whose derives emit
+#     trivial `where` bounds: `feature(trivial_bounds)`.
 #
 # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
 # the unstable features in use.
-rust_allowed_features := arbitrary_self_types,asm_goto,generic_arg_infer,used_with_arg
+rust_allowed_features := arbitrary_self_types,asm_goto,generic_arg_infer,used_with_arg,trivial_bounds
 
 # `--out-dir` is required to avoid temporaries being created by `rustc` in the
 # current working directory, which may be not accessible in the out-of-tree
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/1] nova-core: Convert bindings to use zerocopy
  2026-06-29  2:52 [PATCH 0/1] nova-core: Convert bindings to use zerocopy Alistair Popple
  2026-06-29  2:52 ` [PATCH 1/1] nova-core: Update firmware bindings to use zerocopy traits Alistair Popple
@ 2026-06-29  5:55 ` SeungJong Ha
  2026-06-29  7:09   ` Alexandre Courbot
  1 sibling, 1 reply; 10+ messages in thread
From: SeungJong Ha @ 2026-06-29  5:55 UTC (permalink / raw)
  To: Alistair Popple, acourbot
  Cc: Danilo Krummrich, Alice Ryhl, Nicolás Antinori, David Airlie,
	Shuah Khan, Simona Vetter, Gary Guo, Onur Özkan,
	Tamir Duberstein, Trevor Gross, Pedro Yudi Honda, SeungJong Ha,
	linux-kernel, dri-devel, rust-for-linux, nova-gpu

On Mon Jun 29, 2026 at 2:52 AM UTC, Alistair Popple wrote:
> Given the inevitable conflicts that will arise from trying to merge several
> different series doing bits of the same thing I'm happy if someone wants to take
> this patch and the others and roll them into a single series. Alternatively I am
> happy to take that on - hopefully Alex can provide some guidance here for what
> would be preferred.

Agreed it should be one series. Happy to help or fold in my part --
whatever Alex prefers.

Best regards,
SeungJong


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/1] nova-core: Convert bindings to use zerocopy
  2026-06-29  5:55 ` [PATCH 0/1] nova-core: Convert bindings to use zerocopy SeungJong Ha
@ 2026-06-29  7:09   ` Alexandre Courbot
  2026-06-29  8:05     ` Alistair Popple
  2026-06-29  8:46     ` Danilo Krummrich
  0 siblings, 2 replies; 10+ messages in thread
From: Alexandre Courbot @ 2026-06-29  7:09 UTC (permalink / raw)
  To: SeungJong Ha
  Cc: Alistair Popple, Danilo Krummrich, Alice Ryhl,
	Nicolás Antinori, David Airlie, Shuah Khan, Simona Vetter,
	Gary Guo, Onur Özkan, Tamir Duberstein, Trevor Gross,
	Pedro Yudi Honda, linux-kernel, dri-devel, rust-for-linux,
	nova-gpu

On Mon Jun 29, 2026 at 2:55 PM JST, SeungJong Ha wrote:
> On Mon Jun 29, 2026 at 2:52 AM UTC, Alistair Popple wrote:
>> Given the inevitable conflicts that will arise from trying to merge several
>> different series doing bits of the same thing I'm happy if someone wants to take
>> this patch and the others and roll them into a single series. Alternatively I am
>> happy to take that on - hopefully Alex can provide some guidance here for what
>> would be preferred.
>
> Agreed it should be one series. Happy to help or fold in my part --
> whatever Alex prefers.

Since there is no particular urgency to switch from transmute to
zerocopy, I think it makes sense to merge things in an order that limits
churn as much as possible.

This means the conversion should probably be done bottom-to-top,
starting with the bindings, then the types that embed them. For the
bindings, `#[derive(zerocopy_derive::most_traits)]` should be landing
soon [1] and I would like us to understand first whether that is
something we want to use with the bindings; if so, let's wait for it to
be available (unless there is a good reason to convert quickly) as each
change to `bindings.rs` is almost 200 LoCs.

Since Alistair maintains the tool generating the bindings [2], I think
it would make sense if he takes care of that part (bindings and anything
embedding them) as well.

There are also types in nova-core that do not depend on bindings at all;
for these I think it is fine to take the patches that have been already
submitted if they apply cleanly and build, as these are good
opportunities for first contributions. Nicolás was first to send
something for vbios/firmware [3] so I'd like to consider his v2 first
now that `-rc1` is tagged.

For the rest, I'm happy to merge anything that applies - small patches
limited to a single module should be easier to process and less likely
to conflict.

[1] https://lore.kernel.org/all/20260625231919.692444-1-ojeda@kernel.org/
[2] https://github.com/apopple-nvidia/nova-gsp-binding-generator/
[3] https://lore.kernel.org/all/20260621143647.264770-1-nico.antinori.7@gmail.com/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] nova-core: Update firmware bindings to use zerocopy traits
  2026-06-29  2:52 ` [PATCH 1/1] nova-core: Update firmware bindings to use zerocopy traits Alistair Popple
@ 2026-06-29  7:36   ` Alexandre Courbot
  2026-06-29  7:56     ` Alistair Popple
  2026-06-29  9:21     ` Miguel Ojeda
  0 siblings, 2 replies; 10+ messages in thread
From: Alexandre Courbot @ 2026-06-29  7:36 UTC (permalink / raw)
  To: Alistair Popple, Miguel Ojeda
  Cc: Danilo Krummrich, Alice Ryhl, Nicolás Antinori, David Airlie,
	Shuah Khan, Simona Vetter, Gary Guo, Onur Özkan,
	Tamir Duberstein, Trevor Gross, Pedro Yudi Honda, SeungJong Ha,
	linux-kernel, dri-devel, rust-for-linux, nova-gpu

On Mon Jun 29, 2026 at 11:52 AM JST, Alistair Popple wrote:
> Currently most of nova-core uses unsafe implementations of AsBytes and
> FromBytes in order to read and write GSP commands using the generated
> bindings. Whilst this is generally safe in practice there is nothing
> that actually guarantees or checks this.
>
> This is exactly what the zerocopy library was introduced to do - provide
> compile-time checks ensuring From/AsBytes is safe. Make use of these
> checks by converting all our generated bindings to derive the required
> traits for the zerocopy checks, removing many unsafe implementations in
> the process.
>
> Note this does require the use of an unstable feature - trivial_bounds
> - as some bindings end up needing to derive zerocopy::Unaligned.
> A work-around is also required in the bindings to make some of the
> __IncompleteArrayField<T> ZSTs monomorphic as the check macro can not
> prove a generic type is aligned and thus forces T to derive
> zerocopy::Unaligned, which is not satisfied by all types.
>
> Signed-off-by: Alistair Popple <apopple@nvidia.com>

I have counted and this removes 28 unsafe statements. :) (it also adds
2 tbf)

> ---
>  drivers/gpu/nova-core/Makefile                |   4 +
>  drivers/gpu/nova-core/gsp/cmdq.rs             |  21 +-
>  .../gpu/nova-core/gsp/cmdq/continuation.rs    |  16 +-
>  drivers/gpu/nova-core/gsp/commands.rs         |  19 +-
>  drivers/gpu/nova-core/gsp/fw.rs               |  54 +----
>  drivers/gpu/nova-core/gsp/fw/commands.rs      |  50 ++---
>  drivers/gpu/nova-core/gsp/fw/r570_144.rs      |  41 ++++
>  .../gpu/nova-core/gsp/fw/r570_144/bindings.rs | 185 ++++++++++++------
>  drivers/gpu/nova-core/gsp/sequencer.rs        |   5 +-
>  scripts/Makefile.build                        |   4 +-
>  10 files changed, 223 insertions(+), 176 deletions(-)
>
> diff --git a/drivers/gpu/nova-core/Makefile b/drivers/gpu/nova-core/Makefile
> index 4ae544f808f4..27a5752c4cf9 100644
> --- a/drivers/gpu/nova-core/Makefile
> +++ b/drivers/gpu/nova-core/Makefile
> @@ -1,4 +1,8 @@
>  # SPDX-License-Identifier: GPL-2.0
>  
> +# The GSP firmware bindings derive zerocopy's `IntoBytes` on `#[repr(C)]`
> +# unions, which is gated behind the `zerocopy_derive_union_into_bytes` cfg.
> +rustflags-y += --cfg zerocopy_derive_union_into_bytes
> +
>  obj-$(CONFIG_NOVA_CORE) += nova-core.o
>  nova-core-y := nova_core.o
> diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
> index 0671ee8a9960..6e79ec688983 100644
> --- a/drivers/gpu/nova-core/gsp/cmdq.rs
> +++ b/drivers/gpu/nova-core/gsp/cmdq.rs
> @@ -29,6 +29,14 @@
>      },
>  };
>  
> +// TODO: Remove `as` once FromBytes is removed completely
> +use zerocopy::{
> +    FromBytes as ZCFromBytes,

Since zerocopy's `FromBytes` is the one we will keep long-term, should
we rather rename the one from `transmute`?

<...>
> --- a/drivers/gpu/nova-core/gsp/fw/r570_144.rs
> +++ b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
> @@ -23,9 +23,50 @@
>  )]
>  use kernel::ffi;
>  use pin_init::MaybeZeroable;
> +use zerocopy_derive::{
> +    FromBytes,
> +    Immutable,
> +    IntoBytes,
> +    KnownLayout, //
> +};
>  
>  include!("r570_144/bindings.rs");
>  
>  // SAFETY: This type has a size of zero, so its inclusion into another type should not affect their
>  // ability to implement `Zeroable`.
>  unsafe impl<T> kernel::prelude::Zeroable for __IncompleteArrayField<T> {}
> +
> +/// Monomorphic version of [`__IncompleteArrayField<PACKED_REGISTRY_ENTRY>`].
> +///
> +/// zerocopy's `IntoBytes` derive can only run its compile-time no-padding check
> +/// on a concrete type; for the generic `__IncompleteArrayField<T>` it falls back
> +/// to requiring every field be `Unaligned`, which `PACKED_REGISTRY_ENTRY`
> +/// does not satisfy. Specializing to a concrete type lets the derive succeed.
> +#[repr(C)]
> +#[derive(Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout, MaybeZeroable)]
> +pub struct __IncompletePackedRegistryEntry(
> +    ::core::marker::PhantomData<PACKED_REGISTRY_ENTRY>,
> +    [PACKED_REGISTRY_ENTRY; 0],
> +);
> +impl __IncompletePackedRegistryEntry {
> +    #[inline]
> +    pub const fn new() -> Self {
> +        __IncompletePackedRegistryEntry(::core::marker::PhantomData, [])
> +    }
> +    #[inline]
> +    pub fn as_ptr(&self) -> *const PACKED_REGISTRY_ENTRY {
> +        self as *const _ as *const PACKED_REGISTRY_ENTRY
> +    }
> +    #[inline]
> +    pub fn as_mut_ptr(&mut self) -> *mut PACKED_REGISTRY_ENTRY {
> +        self as *mut _ as *mut PACKED_REGISTRY_ENTRY
> +    }
> +    #[inline]
> +    pub unsafe fn as_slice(&self, len: usize) -> &[PACKED_REGISTRY_ENTRY] {
> +        ::core::slice::from_raw_parts(self.as_ptr(), len)
> +    }
> +    #[inline]
> +    pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [PACKED_REGISTRY_ENTRY] {
> +        ::core::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
> +    }
> +}

Yikes, this is a one-shot so I guess we can live with that if neeeded.

> diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
> index ea350f9b2cc4..9c85c93f6eee 100644
> --- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
> +++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
> @@ -1,7 +1,7 @@
>  // SPDX-License-Identifier: GPL-2.0
>  
>  #[repr(C)]
> -#[derive(Default)]
> +#[derive(Default, FromBytes, IntoBytes, Immutable, KnownLayout)]

This is the part my other email [1] was about: are we going, long-term,
to replace this with a `#[derive(zerocopy_derive::most_traits)]`? If the
Nova's bindings generator program can take care of producing the correct
traits for each generated type then maybe that's even better, but at the
same time I am wondering whether the kernel's bindgen invocation isn't
going to automatically derive `most_traits` on all generated types. In
this case, we could end up with duplicate implementations.

I think this point needs to be clarified before we can decide when to
merge this patch.

[1] https://lore.kernel.org/all/DJLCF3LR0KMN.1TMKMZEZWKN8O@nvidia.com/

<...>
>  #[repr(C)]
> -#[derive(Debug, Default, MaybeZeroable)]
> +#[derive(Debug, Default, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
>  pub struct PACKED_REGISTRY_TABLE {
>      pub size: u32_,
>      pub numEntries: u32_,
> -    pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>,
> +    pub entries: __IncompletePackedRegistryEntry,

This is part of the generated bindings, right? How does the generator
program know it needs to use `__IncompletePackedRegistryEntry`?

<...>
> --- a/scripts/Makefile.build
> +++ b/scripts/Makefile.build
> @@ -314,10 +314,12 @@ $(obj)/%.lst: $(obj)/%.c FORCE
>  #   - Stable since Rust 1.89.0: `feature(generic_arg_infer)`.
>  #   - Expected to become stable: `feature(arbitrary_self_types)`.
>  #   - To be determined: `feature(used_with_arg)`.
> +#   - Required by nova-core's zerocopy firmware bindings, whose derives emit
> +#     trivial `where` bounds: `feature(trivial_bounds)`.
>  #
>  # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
>  # the unstable features in use.
> -rust_allowed_features := arbitrary_self_types,asm_goto,generic_arg_infer,used_with_arg
> +rust_allowed_features := arbitrary_self_types,asm_goto,generic_arg_infer,used_with_arg,trivial_bounds

This also might be something that will land soon through the Rust tree;
Miguel, do you know what the plan is by any chance?

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] nova-core: Update firmware bindings to use zerocopy traits
  2026-06-29  7:36   ` Alexandre Courbot
@ 2026-06-29  7:56     ` Alistair Popple
  2026-06-29  9:21     ` Miguel Ojeda
  1 sibling, 0 replies; 10+ messages in thread
From: Alistair Popple @ 2026-06-29  7:56 UTC (permalink / raw)
  To: Alexandre Courbot
  Cc: Miguel Ojeda, Danilo Krummrich, Alice Ryhl, Nicolás Antinori,
	David Airlie, Shuah Khan, Simona Vetter, Gary Guo,
	Onur Özkan, Tamir Duberstein, Trevor Gross, Pedro Yudi Honda,
	SeungJong Ha, linux-kernel, dri-devel, rust-for-linux, nova-gpu

On 2026-06-29 at 17:36 +1000, Alexandre Courbot <acourbot@nvidia.com> wrote...
> On Mon Jun 29, 2026 at 11:52 AM JST, Alistair Popple wrote:
> > Currently most of nova-core uses unsafe implementations of AsBytes and
> > FromBytes in order to read and write GSP commands using the generated
> > bindings. Whilst this is generally safe in practice there is nothing
> > that actually guarantees or checks this.
> >
> > This is exactly what the zerocopy library was introduced to do - provide
> > compile-time checks ensuring From/AsBytes is safe. Make use of these
> > checks by converting all our generated bindings to derive the required
> > traits for the zerocopy checks, removing many unsafe implementations in
> > the process.
> >
> > Note this does require the use of an unstable feature - trivial_bounds
> > - as some bindings end up needing to derive zerocopy::Unaligned.
> > A work-around is also required in the bindings to make some of the
> > __IncompleteArrayField<T> ZSTs monomorphic as the check macro can not
> > prove a generic type is aligned and thus forces T to derive
> > zerocopy::Unaligned, which is not satisfied by all types.
> >
> > Signed-off-by: Alistair Popple <apopple@nvidia.com>
> 
> I have counted and this removes 28 unsafe statements. :) (it also adds
> 2 tbf)
> 
> > ---
> >  drivers/gpu/nova-core/Makefile                |   4 +
> >  drivers/gpu/nova-core/gsp/cmdq.rs             |  21 +-
> >  .../gpu/nova-core/gsp/cmdq/continuation.rs    |  16 +-
> >  drivers/gpu/nova-core/gsp/commands.rs         |  19 +-
> >  drivers/gpu/nova-core/gsp/fw.rs               |  54 +----
> >  drivers/gpu/nova-core/gsp/fw/commands.rs      |  50 ++---
> >  drivers/gpu/nova-core/gsp/fw/r570_144.rs      |  41 ++++
> >  .../gpu/nova-core/gsp/fw/r570_144/bindings.rs | 185 ++++++++++++------
> >  drivers/gpu/nova-core/gsp/sequencer.rs        |   5 +-
> >  scripts/Makefile.build                        |   4 +-
> >  10 files changed, 223 insertions(+), 176 deletions(-)
> >
> > diff --git a/drivers/gpu/nova-core/Makefile b/drivers/gpu/nova-core/Makefile
> > index 4ae544f808f4..27a5752c4cf9 100644
> > --- a/drivers/gpu/nova-core/Makefile
> > +++ b/drivers/gpu/nova-core/Makefile
> > @@ -1,4 +1,8 @@
> >  # SPDX-License-Identifier: GPL-2.0
> >  
> > +# The GSP firmware bindings derive zerocopy's `IntoBytes` on `#[repr(C)]`
> > +# unions, which is gated behind the `zerocopy_derive_union_into_bytes` cfg.
> > +rustflags-y += --cfg zerocopy_derive_union_into_bytes
> > +
> >  obj-$(CONFIG_NOVA_CORE) += nova-core.o
> >  nova-core-y := nova_core.o
> > diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
> > index 0671ee8a9960..6e79ec688983 100644
> > --- a/drivers/gpu/nova-core/gsp/cmdq.rs
> > +++ b/drivers/gpu/nova-core/gsp/cmdq.rs
> > @@ -29,6 +29,14 @@
> >      },
> >  };
> >  
> > +// TODO: Remove `as` once FromBytes is removed completely
> > +use zerocopy::{
> > +    FromBytes as ZCFromBytes,
> 
> Since zerocopy's `FromBytes` is the one we will keep long-term, should
> we rather rename the one from `transmute`?

Good idea - it ended up this way because I was going to split the series up to
convert one type at a time but ended up squashing it into one big change given
the mechanical nature of the conversions.

> <...>
> > --- a/drivers/gpu/nova-core/gsp/fw/r570_144.rs
> > +++ b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
> > @@ -23,9 +23,50 @@
> >  )]
> >  use kernel::ffi;
> >  use pin_init::MaybeZeroable;
> > +use zerocopy_derive::{
> > +    FromBytes,
> > +    Immutable,
> > +    IntoBytes,
> > +    KnownLayout, //
> > +};
> >  
> >  include!("r570_144/bindings.rs");
> >  
> >  // SAFETY: This type has a size of zero, so its inclusion into another type should not affect their
> >  // ability to implement `Zeroable`.
> >  unsafe impl<T> kernel::prelude::Zeroable for __IncompleteArrayField<T> {}
> > +
> > +/// Monomorphic version of [`__IncompleteArrayField<PACKED_REGISTRY_ENTRY>`].
> > +///
> > +/// zerocopy's `IntoBytes` derive can only run its compile-time no-padding check
> > +/// on a concrete type; for the generic `__IncompleteArrayField<T>` it falls back
> > +/// to requiring every field be `Unaligned`, which `PACKED_REGISTRY_ENTRY`
> > +/// does not satisfy. Specializing to a concrete type lets the derive succeed.
> > +#[repr(C)]
> > +#[derive(Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout, MaybeZeroable)]
> > +pub struct __IncompletePackedRegistryEntry(
> > +    ::core::marker::PhantomData<PACKED_REGISTRY_ENTRY>,
> > +    [PACKED_REGISTRY_ENTRY; 0],
> > +);
> > +impl __IncompletePackedRegistryEntry {
> > +    #[inline]
> > +    pub const fn new() -> Self {
> > +        __IncompletePackedRegistryEntry(::core::marker::PhantomData, [])
> > +    }
> > +    #[inline]
> > +    pub fn as_ptr(&self) -> *const PACKED_REGISTRY_ENTRY {
> > +        self as *const _ as *const PACKED_REGISTRY_ENTRY
> > +    }
> > +    #[inline]
> > +    pub fn as_mut_ptr(&mut self) -> *mut PACKED_REGISTRY_ENTRY {
> > +        self as *mut _ as *mut PACKED_REGISTRY_ENTRY
> > +    }
> > +    #[inline]
> > +    pub unsafe fn as_slice(&self, len: usize) -> &[PACKED_REGISTRY_ENTRY] {
> > +        ::core::slice::from_raw_parts(self.as_ptr(), len)
> > +    }
> > +    #[inline]
> > +    pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [PACKED_REGISTRY_ENTRY] {
> > +        ::core::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
> > +    }
> > +}
> 
> Yikes, this is a one-shot so I guess we can live with that if neeeded.

Yes, this is a total hack but I couldn't figure out a better alternative.
Given this is the only type that has this problem I figured it was a bearable
trade-off to make, but am open to better suggestions if there are any.

> > diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
> > index ea350f9b2cc4..9c85c93f6eee 100644
> > --- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
> > +++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
> > @@ -1,7 +1,7 @@
> >  // SPDX-License-Identifier: GPL-2.0
> >  
> >  #[repr(C)]
> > -#[derive(Default)]
> > +#[derive(Default, FromBytes, IntoBytes, Immutable, KnownLayout)]
> 
> This is the part my other email [1] was about: are we going, long-term,
> to replace this with a `#[derive(zerocopy_derive::most_traits)]`? If the
> Nova's bindings generator program can take care of producing the correct
> traits for each generated type then maybe that's even better, but at the
> same time I am wondering whether the kernel's bindgen invocation isn't
> going to automatically derive `most_traits` on all generated types. In
> this case, we could end up with duplicate implementations.

If that's coming soon I think it makes sense to wait simply to avoid churn
on the generated bindings. I didn't do anything too smart for Nova's binding
generator - it basically just derives all these traits for all our bindings.
Being able to have it derive `most_traits` would be cleaner. So happy to rebase
once that is merged.

> I think this point needs to be clarified before we can decide when to
> merge this patch.
> 
> [1] https://lore.kernel.org/all/DJLCF3LR0KMN.1TMKMZEZWKN8O@nvidia.com/
> 
> <...>
> >  #[repr(C)]
> > -#[derive(Debug, Default, MaybeZeroable)]
> > +#[derive(Debug, Default, MaybeZeroable, FromBytes, Immutable, KnownLayout, IntoBytes)]
> >  pub struct PACKED_REGISTRY_TABLE {
> >      pub size: u32_,
> >      pub numEntries: u32_,
> > -    pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>,
> > +    pub entries: __IncompletePackedRegistryEntry,
> 
> This is part of the generated bindings, right? How does the generator
> program know it needs to use `__IncompletePackedRegistryEntry`?

Maigc[1] ... otherwise known as `sed` :-)

[1] - https://github.com/apopple-nvidia/nova-gsp-binding-generator/blob/zerocopy/Kbuild#L153

> <...>
> > --- a/scripts/Makefile.build
> > +++ b/scripts/Makefile.build
> > @@ -314,10 +314,12 @@ $(obj)/%.lst: $(obj)/%.c FORCE
> >  #   - Stable since Rust 1.89.0: `feature(generic_arg_infer)`.
> >  #   - Expected to become stable: `feature(arbitrary_self_types)`.
> >  #   - To be determined: `feature(used_with_arg)`.
> > +#   - Required by nova-core's zerocopy firmware bindings, whose derives emit
> > +#     trivial `where` bounds: `feature(trivial_bounds)`.
> >  #
> >  # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
> >  # the unstable features in use.
> > -rust_allowed_features := arbitrary_self_types,asm_goto,generic_arg_infer,used_with_arg
> > +rust_allowed_features := arbitrary_self_types,asm_goto,generic_arg_infer,used_with_arg,trivial_bounds
> 
> This also might be something that will land soon through the Rust tree;
> Miguel, do you know what the plan is by any chance?

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/1] nova-core: Convert bindings to use zerocopy
  2026-06-29  7:09   ` Alexandre Courbot
@ 2026-06-29  8:05     ` Alistair Popple
  2026-06-29  8:46     ` Danilo Krummrich
  1 sibling, 0 replies; 10+ messages in thread
From: Alistair Popple @ 2026-06-29  8:05 UTC (permalink / raw)
  To: Alexandre Courbot
  Cc: SeungJong Ha, Danilo Krummrich, Alice Ryhl, Nicolás Antinori,
	David Airlie, Shuah Khan, Simona Vetter, Gary Guo,
	Onur Özkan, Tamir Duberstein, Trevor Gross, Pedro Yudi Honda,
	linux-kernel, dri-devel, rust-for-linux, nova-gpu

On 2026-06-29 at 17:09 +1000, Alexandre Courbot <acourbot@nvidia.com> wrote...
> On Mon Jun 29, 2026 at 2:55 PM JST, SeungJong Ha wrote:
> > On Mon Jun 29, 2026 at 2:52 AM UTC, Alistair Popple wrote:
> >> Given the inevitable conflicts that will arise from trying to merge several
> >> different series doing bits of the same thing I'm happy if someone wants to take
> >> this patch and the others and roll them into a single series. Alternatively I am
> >> happy to take that on - hopefully Alex can provide some guidance here for what
> >> would be preferred.
> >
> > Agreed it should be one series. Happy to help or fold in my part --
> > whatever Alex prefers.
> 
> Since there is no particular urgency to switch from transmute to
> zerocopy, I think it makes sense to merge things in an order that limits
> churn as much as possible.
> 
> This means the conversion should probably be done bottom-to-top,
> starting with the bindings, then the types that embed them. For the
> bindings, `#[derive(zerocopy_derive::most_traits)]` should be landing
> soon [1] and I would like us to understand first whether that is
> something we want to use with the bindings; if so, let's wait for it to
> be available (unless there is a good reason to convert quickly) as each
> change to `bindings.rs` is almost 200 LoCs.

I think it makes sense to wait, if only to avoid churn on the generated
bindings.

> Since Alistair maintains the tool generating the bindings [2], I think
> it would make sense if he takes care of that part (bindings and anything
> embedding them) as well.

Agreed.

> There are also types in nova-core that do not depend on bindings at all;
> for these I think it is fine to take the patches that have been already
> submitted if they apply cleanly and build, as these are good
> opportunities for first contributions. Nicolás was first to send
> something for vbios/firmware [3] so I'd like to consider his v2 first
> now that `-rc1` is tagged.

Yep - given the interest from other contributors I'm going to leave all the
non-binding related conversions alone. In other words anything that does not
require changes to drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs would be
good for others to look at, and thanks for the contributions!

 - Alistair

> For the rest, I'm happy to merge anything that applies - small patches
> limited to a single module should be easier to process and less likely
> to conflict.
> 
> [1] https://lore.kernel.org/all/20260625231919.692444-1-ojeda@kernel.org/
> [2] https://github.com/apopple-nvidia/nova-gsp-binding-generator/
> [3] https://lore.kernel.org/all/20260621143647.264770-1-nico.antinori.7@gmail.com/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/1] nova-core: Convert bindings to use zerocopy
  2026-06-29  7:09   ` Alexandre Courbot
  2026-06-29  8:05     ` Alistair Popple
@ 2026-06-29  8:46     ` Danilo Krummrich
  1 sibling, 0 replies; 10+ messages in thread
From: Danilo Krummrich @ 2026-06-29  8:46 UTC (permalink / raw)
  To: Alexandre Courbot
  Cc: SeungJong Ha, Alistair Popple, Alice Ryhl, Nicolás Antinori,
	David Airlie, Shuah Khan, Simona Vetter, Gary Guo,
	Onur Özkan, Tamir Duberstein, Trevor Gross, Pedro Yudi Honda,
	linux-kernel, dri-devel, rust-for-linux, nova-gpu

On Mon Jun 29, 2026 at 9:09 AM CEST, Alexandre Courbot wrote:
> Since there is no particular urgency to switch from transmute to
> zerocopy, I think it makes sense to merge things in an order that limits
> churn as much as possible.
>
> This means the conversion should probably be done bottom-to-top,
> starting with the bindings, then the types that embed them. For the
> bindings, `#[derive(zerocopy_derive::most_traits)]` should be landing
> soon [1] and I would like us to understand first whether that is
> something we want to use with the bindings; if so, let's wait for it to
> be available (unless there is a good reason to convert quickly) as each
> change to `bindings.rs` is almost 200 LoCs.

Doing another subsequent conversion to most_traits seems trivial. The upside
would be that we could remove the transmute::* types from nova entirely, which
also allows us to switch the DMA code to zerocopy.

No need to go for it with might and main, but a minor inconvinience like the
above might be worth taking, as switching to zerocopy in core infrastructure can
simplify backports.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] nova-core: Update firmware bindings to use zerocopy traits
  2026-06-29  7:36   ` Alexandre Courbot
  2026-06-29  7:56     ` Alistair Popple
@ 2026-06-29  9:21     ` Miguel Ojeda
  2026-06-29  9:45       ` Alexandre Courbot
  1 sibling, 1 reply; 10+ messages in thread
From: Miguel Ojeda @ 2026-06-29  9:21 UTC (permalink / raw)
  To: Alexandre Courbot
  Cc: Alistair Popple, Miguel Ojeda, Danilo Krummrich, Alice Ryhl,
	Nicolás Antinori, David Airlie, Shuah Khan, Simona Vetter,
	Gary Guo, Onur Özkan, Tamir Duberstein, Trevor Gross,
	Pedro Yudi Honda, SeungJong Ha, linux-kernel, dri-devel,
	rust-for-linux, nova-gpu

On Mon, Jun 29, 2026 at 9:37 AM Alexandre Courbot <acourbot@nvidia.com> wrote:
>
> This is the part my other email [1] was about: are we going, long-term,
> to replace this with a `#[derive(zerocopy_derive::most_traits)]`? If the

> This also might be something that will land soon through the Rust tree;
> Miguel, do you know what the plan is by any chance?

If you mean:

  https://lore.kernel.org/rust-for-linux/20260625231919.692444-1-ojeda@kernel.org/

Then yeah :)

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] nova-core: Update firmware bindings to use zerocopy traits
  2026-06-29  9:21     ` Miguel Ojeda
@ 2026-06-29  9:45       ` Alexandre Courbot
  0 siblings, 0 replies; 10+ messages in thread
From: Alexandre Courbot @ 2026-06-29  9:45 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Alistair Popple, Miguel Ojeda, Danilo Krummrich, Alice Ryhl,
	Nicolás Antinori, David Airlie, Shuah Khan, Simona Vetter,
	Gary Guo, Onur Özkan, Tamir Duberstein, Trevor Gross,
	Pedro Yudi Honda, SeungJong Ha, linux-kernel, dri-devel,
	rust-for-linux, nova-gpu

On Mon Jun 29, 2026 at 6:21 PM JST, Miguel Ojeda wrote:
> On Mon, Jun 29, 2026 at 9:37 AM Alexandre Courbot <acourbot@nvidia.com> wrote:
>>
>> This is the part my other email [1] was about: are we going, long-term,
>> to replace this with a `#[derive(zerocopy_derive::most_traits)]`? If the
>
>> This also might be something that will land soon through the Rust tree;
>> Miguel, do you know what the plan is by any chance?
>
> If you mean:
>
>   https://lore.kernel.org/rust-for-linux/20260625231919.692444-1-ojeda@kernel.org/
>
> Then yeah :)

Thanks - also, is there a plan to automatically derive
`zerocopy_derive::most_traits` for generated bindings types, or is that
something that Nova should do by itself?

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-06-29  9:45 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-29  2:52 [PATCH 0/1] nova-core: Convert bindings to use zerocopy Alistair Popple
2026-06-29  2:52 ` [PATCH 1/1] nova-core: Update firmware bindings to use zerocopy traits Alistair Popple
2026-06-29  7:36   ` Alexandre Courbot
2026-06-29  7:56     ` Alistair Popple
2026-06-29  9:21     ` Miguel Ojeda
2026-06-29  9:45       ` Alexandre Courbot
2026-06-29  5:55 ` [PATCH 0/1] nova-core: Convert bindings to use zerocopy SeungJong Ha
2026-06-29  7:09   ` Alexandre Courbot
2026-06-29  8:05     ` Alistair Popple
2026-06-29  8:46     ` Danilo Krummrich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox