* [PATCH 1/7] drm/nova: use `zerocopy` in firmware.rs
2026-06-25 19:05 [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
@ 2026-06-25 19:05 ` Pedro Yudi Honda
2026-06-25 19:05 ` [PATCH 2/7] drm/nova: use `zerocopy` in vbios.rs Pedro Yudi Honda
` (6 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 19:05 UTC (permalink / raw)
To: dakr, acourbot, nova-gpu
Cc: aliceryhl, airlied, simona, dri-devel, ojeda, rust-for-linux,
Pedro Yudi Honda
From: Pedro Yudi Honda <niyudi.honda@usp.br>
In firmware.rs, replace the following `transmute` traits with their
`zerocopy` equivalents:
- `transmute::FromBytes` -> `zerocopy::FromBytes`
Update call sites accordingly.
Note that the module `elf` is untouched, as the bindings generated by
bindgen do not implement `FromBytes` for the structs.
Signed-off-by: Pedro Yudi Honda <niyudi.honda@usp.br>
---
drivers/gpu/nova-core/firmware.rs | 16 ++++------------
.../gpu/nova-core/firmware/fwsec/bootloader.rs | 4 +++-
drivers/gpu/nova-core/vbios.rs | 4 ++--
3 files changed, 9 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 1e89390209f5..1eae03344a61 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -11,8 +11,7 @@
device,
firmware,
prelude::*,
- str::CString,
- transmute::FromBytes, //
+ str::CString, //
};
use crate::{
@@ -88,7 +87,7 @@ pub(crate) struct FalconUCodeDescV2 {
/// Structure used to describe some firmwares, notably FWSEC-FRTS.
#[repr(C)]
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, FromBytes)]
pub(crate) struct FalconUCodeDescV3 {
/// Header defined by `NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*` in OpenRM.
hdr: u32,
@@ -119,10 +118,6 @@ pub(crate) struct FalconUCodeDescV3 {
_reserved: u16,
}
-// SAFETY: all bit patterns are valid for this type, and it doesn't use
-// interior mutability.
-unsafe impl FromBytes for FalconUCodeDescV3 {}
-
/// Enum wrapping the different versions of Falcon microcode descriptors.
///
/// This allows handling both V2 and V3 descriptor formats through a
@@ -351,7 +346,7 @@ fn no_patch_signature(self) -> FirmwareObject<F, Signed> {
/// Header common to most firmware files.
#[repr(C)]
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, FromBytes)]
struct BinHdr {
/// Magic number, must be `0x10de`.
bin_magic: u32,
@@ -367,9 +362,6 @@ struct BinHdr {
data_size: u32,
}
-// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
-unsafe impl FromBytes for BinHdr {}
-
// A firmware blob starting with a `BinHdr`.
struct BinFirmware<'a> {
hdr: BinHdr,
@@ -385,7 +377,7 @@ fn new(fw: &'a firmware::Firmware) -> Result<Self> {
fw.get(0..size_of::<BinHdr>())
// Extract header.
- .and_then(BinHdr::from_bytes_copy)
+ .and_then(|b| BinHdr::read_from_bytes(b).ok())
// Validate header.
.filter(|hdr| hdr.bin_magic == BIN_MAGIC)
.map(|hdr| Self { hdr, fw })
diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
index 039920dc340b..13af1c381df1 100644
--- a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
@@ -29,6 +29,8 @@
},
};
+use zerocopy::FromBytes as _;
+
use crate::{
driver::Bar0,
falcon::{
@@ -154,7 +156,7 @@ pub(crate) fn new(
let hdr = fw
.data()
.get(0..size_of::<BinHdr>())
- .and_then(BinHdr::from_bytes_copy)
+ .and_then(|b| BinHdr::read_from_bytes(b).ok())
.ok_or(EINVAL)?;
let desc = {
diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs
index c6e6bfcd6a1f..e6d144587c4e 100644
--- a/drivers/gpu/nova-core/vbios.rs
+++ b/drivers/gpu/nova-core/vbios.rs
@@ -935,8 +935,8 @@ pub(crate) fn header(&self) -> Result<FalconUCodeDesc> {
Ok(FalconUCodeDesc::V2(v2))
}
3 => {
- let v3 = FalconUCodeDescV3::from_bytes_copy_prefix(data)
- .ok_or(EINVAL)?
+ let v3 = FalconUCodeDescV3::read_from_prefix(data)
+ .map_err(|_| EINVAL)?
.0;
Ok(FalconUCodeDesc::V3(v3))
}
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 2/7] drm/nova: use `zerocopy` in vbios.rs
2026-06-25 19:05 [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
2026-06-25 19:05 ` [PATCH 1/7] drm/nova: use `zerocopy` in firmware.rs Pedro Yudi Honda
@ 2026-06-25 19:05 ` Pedro Yudi Honda
2026-06-25 19:05 ` [PATCH 3/7] drm/nova: use `zerocopy` in booter.rs Pedro Yudi Honda
` (5 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 19:05 UTC (permalink / raw)
To: dakr, acourbot, nova-gpu
Cc: aliceryhl, airlied, simona, dri-devel, ojeda, rust-for-linux,
Pedro Yudi Honda
From: Pedro Yudi Honda <niyudi.honda@usp.br>
In vbios.rs, replave the following `transmute` traits with their
`zerocopy` equivalents:
- `transmute::FromBytes` -> `zerocopy::FromBytes`
Update call sites accordingly.
Signed-off-by: Pedro Yudi Honda <niyudi.honda@usp.br>
---
drivers/gpu/nova-core/vbios.rs | 56 +++++++++++-----------------------
1 file changed, 17 insertions(+), 39 deletions(-)
diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs
index e6d144587c4e..a3c0c06f6818 100644
--- a/drivers/gpu/nova-core/vbios.rs
+++ b/drivers/gpu/nova-core/vbios.rs
@@ -13,11 +13,8 @@
register,
sizes::SZ_4K,
sync::aref::ARef,
- transmute::FromBytes,
};
-use zerocopy::FromBytes as _;
-
use crate::{
driver::Bar0,
firmware::{
@@ -359,7 +356,7 @@ pub(crate) fn fwsec_image(&self) -> &FwSecBiosImage {
}
/// PCI Data Structure as defined in PCI Firmware Specification
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, FromBytes)]
#[repr(C)]
struct PcirStruct {
/// PCI Data Structure signature ("PCIR" or "NPDS")
@@ -388,15 +385,12 @@ struct PcirStruct {
max_runtime_image_len: u16,
}
-// SAFETY: all bit patterns are valid for `PcirStruct`.
-unsafe impl FromBytes for PcirStruct {}
-
impl PcirStruct {
/// The bit in `last_image` that indicates the last image.
const LAST_IMAGE_BIT_MASK: u8 = 0x80;
fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
- let (pcir, _) = PcirStruct::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
+ let (pcir, _) = PcirStruct::read_from_prefix(data).map_err(|_| EINVAL)?;
// Signature should be "PCIR" (0x52494350) or "NPDS" (0x5344504e).
if &pcir.signature != b"PCIR" && &pcir.signature != b"NPDS" {
@@ -432,7 +426,7 @@ fn image_size_bytes(&self) -> usize {
/// This is the head of the BIT table, that is used to locate the Falcon data. The BIT table (with
/// its header) is in the [`PciAtBiosImage`] and the falcon data it is pointing to is in the
/// [`FwSecBiosImage`].
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, FromBytes)]
#[repr(C)]
struct BitHeader {
/// 0h: BIT Header Identifier (BMP=0x7FFF/BIT=0xB8FF)
@@ -451,12 +445,9 @@ struct BitHeader {
checksum: u8,
}
-// SAFETY: all bit patterns are valid for `BitHeader`.
-unsafe impl FromBytes for BitHeader {}
-
impl BitHeader {
fn new(data: &[u8]) -> Result<Self> {
- let (header, _) = BitHeader::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
+ let (header, _) = BitHeader::read_from_prefix(data).map_err(|_| EINVAL)?;
// Check header ID and signature
if header.id != 0xB8FF || &header.signature != b"BIT\0" {
@@ -468,7 +459,7 @@ fn new(data: &[u8]) -> Result<Self> {
}
/// BIT Token Entry: Records in the BIT table followed by the BIT header.
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, FromBytes)]
#[repr(C)]
struct BitToken {
/// 00h: Token identifier
@@ -481,9 +472,6 @@ struct BitToken {
data_offset: u16,
}
-// SAFETY: all bit patterns are valid for `BitToken`.
-unsafe impl FromBytes for BitToken {}
-
impl BitToken {
/// BIT token ID for Falcon data.
const ID_FALCON_DATA: u8 = 0x70;
@@ -508,7 +496,7 @@ fn from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self> {
.and_then(|data| data.get(..entry_size))
.ok_or(EINVAL)?;
- let (token, _) = BitToken::from_bytes_copy_prefix(entry).ok_or(EINVAL)?;
+ let (token, _) = BitToken::read_from_prefix(entry).map_err(|_| EINVAL)?;
// Check if this token has the requested ID
if token.id == token_id {
@@ -525,7 +513,7 @@ fn from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self> {
///
/// This header is at the beginning of every image in the set of images in the ROM. It contains a
/// pointer to the PCI Data Structure which describes the image.
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, FromBytes)]
#[repr(C)]
struct PciRomHeader {
/// 00h: Signature (0xAA55)
@@ -536,13 +524,10 @@ struct PciRomHeader {
pci_data_struct_offset: u16,
}
-// SAFETY: all bit patterns are valid for `PciRomHeader`.
-unsafe impl FromBytes for PciRomHeader {}
-
impl PciRomHeader {
fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
- let (rom_header, _) = PciRomHeader::from_bytes_copy_prefix(data)
- .ok_or(EINVAL)
+ let (rom_header, _) = PciRomHeader::read_from_prefix(data)
+ .map_err(|_| EINVAL)
.inspect_err(|_| dev_err!(dev, "Not enough data for ROM header\n"))?;
// Check for valid ROM signatures.
@@ -564,7 +549,7 @@ fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
/// PCI Data Structure. It contains some fields that are redundant with the PCI Data Structure, but
/// are needed for traversing the BIOS images. It is expected to be present in all BIOS images
/// except for NBSI images.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, FromBytes)]
#[repr(C)]
struct NpdeStruct {
/// 00h: Signature ("NPDE")
@@ -579,15 +564,12 @@ struct NpdeStruct {
last_image: u8,
}
-// SAFETY: all bit patterns are valid for `NpdeStruct`.
-unsafe impl FromBytes for NpdeStruct {}
-
impl NpdeStruct {
/// The bit in `last_image` that indicates the last image.
const LAST_IMAGE_BIT_MASK: u8 = 0x80;
fn new(dev: &device::Device, data: &[u8]) -> Option<Self> {
- let (npde, _) = NpdeStruct::from_bytes_copy_prefix(data)?;
+ let (npde, _) = NpdeStruct::read_from_prefix(data).ok()?;
// Signature should be "NPDE" (0x4544504E).
if &npde.signature != b"NPDE" {
@@ -784,7 +766,7 @@ fn falcon_data_offset(&self, dev: &device::Device) -> Result<usize> {
let data = &self.base.data;
let (ptr, _) = data
.get(offset..)
- .and_then(u32::from_bytes_copy_prefix)
+ .and_then(|b| u32::read_from_prefix(b).ok())
.ok_or(EINVAL)?;
usize::from_safe_cast(ptr)
@@ -814,6 +796,7 @@ fn try_from(base: BiosImage) -> Result<Self> {
/// The [`PmuLookupTableEntry`] structure is a single entry in the [`PmuLookupTable`].
///
/// See the [`PmuLookupTable`] description for more information.
+#[derive(FromBytes)]
#[repr(C, packed)]
struct PmuLookupTableEntry {
application_id: u8,
@@ -821,9 +804,6 @@ struct PmuLookupTableEntry {
data: u32,
}
-// SAFETY: all bit patterns are valid for `PmuLookupTableEntry`.
-unsafe impl FromBytes for PmuLookupTableEntry {}
-
impl PmuLookupTableEntry {
/// PMU lookup table application ID for firmware security license ucode.
#[expect(dead_code)]
@@ -836,6 +816,7 @@ impl PmuLookupTableEntry {
}
#[repr(C)]
+#[derive(FromBytes)]
struct PmuLookupTableHeader {
version: u8,
header_len: u8,
@@ -843,9 +824,6 @@ struct PmuLookupTableHeader {
entry_count: u8,
}
-// SAFETY: all bit patterns are valid for `PmuLookupTableHeader`.
-unsafe impl FromBytes for PmuLookupTableHeader {}
-
/// The [`PmuLookupTableEntry`] structure is used to find the [`PmuLookupTableEntry`] for a given
/// application ID.
///
@@ -857,7 +835,7 @@ struct PmuLookupTable {
impl PmuLookupTable {
fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
- let (header, _) = PmuLookupTableHeader::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
+ let (header, _) = PmuLookupTableHeader::read_from_prefix(data).map_err(|_| EINVAL)?;
let header_len = usize::from(header.header_len);
let entry_len = usize::from(header.entry_len);
@@ -872,8 +850,8 @@ fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
let mut entries = KVVec::with_capacity(entry_count, GFP_KERNEL)?;
for i in 0..entry_count {
- let (entry, _) = PmuLookupTableEntry::from_bytes_copy_prefix(&data[i * entry_len..])
- .ok_or(EINVAL)?;
+ let (entry, _) = PmuLookupTableEntry::read_from_prefix(&data[i * entry_len..])
+ .map_err(|_| EINVAL)?;
entries.push(entry, GFP_KERNEL)?;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 3/7] drm/nova: use `zerocopy` in booter.rs
2026-06-25 19:05 [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
2026-06-25 19:05 ` [PATCH 1/7] drm/nova: use `zerocopy` in firmware.rs Pedro Yudi Honda
2026-06-25 19:05 ` [PATCH 2/7] drm/nova: use `zerocopy` in vbios.rs Pedro Yudi Honda
@ 2026-06-25 19:05 ` Pedro Yudi Honda
2026-06-25 19:05 ` [PATCH 4/7] drm/nova: use `zerocopy` in fwsec.rs Pedro Yudi Honda
` (4 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 19:05 UTC (permalink / raw)
To: dakr, acourbot, nova-gpu
Cc: aliceryhl, airlied, simona, dri-devel, ojeda, rust-for-linux,
Pedro Yudi Honda
From: Pedro Yudi Honda <niyudi.honda@usp.br>
In firmware/booter.rs, replace the following `transmute` traits with
their `zerocopy` equivalents:
- `transmute::FromBytes` -> `zerocopy::FromBytes`
Update call sites accordingly.
Signed-off-by: Pedro Yudi Honda <niyudi.honda@usp.br>
---
drivers/gpu/nova-core/firmware/booter.rs | 26 +++++++-----------------
1 file changed, 7 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs
index d9313ac361af..0cda2c50fbbe 100644
--- a/drivers/gpu/nova-core/firmware/booter.rs
+++ b/drivers/gpu/nova-core/firmware/booter.rs
@@ -10,8 +10,7 @@
use kernel::{
device,
dma::Coherent,
- prelude::*,
- transmute::FromBytes, //
+ prelude::*, //
};
use crate::{
@@ -44,7 +43,7 @@ fn frombytes_at<S: FromBytes + Sized>(slice: &[u8], offset: usize) -> Result<S>
let end = offset.checked_add(size_of::<S>()).ok_or(EINVAL)?;
slice
.get(offset..end)
- .and_then(S::from_bytes_copy)
+ .and_then(|b| S::read_from_bytes(b).ok())
.ok_or(EINVAL)
}
@@ -53,7 +52,7 @@ fn frombytes_at<S: FromBytes + Sized>(slice: &[u8], offset: usize) -> Result<S>
/// Such firmwares have an application-specific payload that needs to be patched with a given
/// signature.
#[repr(C)]
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, FromBytes)]
struct HsHeaderV2 {
/// Offset to the start of the signatures.
sig_prod_offset: u32,
@@ -76,9 +75,6 @@ struct HsHeaderV2 {
header_size: u32,
}
-// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
-unsafe impl FromBytes for HsHeaderV2 {}
-
/// Heavy-Secured Firmware image container.
///
/// This provides convenient access to the fields of [`HsHeaderV2`] that are actually indices to
@@ -145,6 +141,7 @@ fn signatures_iter(&'a self) -> Result<impl Iterator<Item = BooterSignature<'a>>
/// Signature parameters, as defined in the firmware.
#[repr(C)]
+#[derive(FromBytes)]
struct HsSignatureParams {
/// Fuse version to use.
fuse_ver: u32,
@@ -154,9 +151,6 @@ struct HsSignatureParams {
ucode_id: u32,
}
-// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
-unsafe impl FromBytes for HsSignatureParams {}
-
impl HsSignatureParams {
/// Returns the signature parameters contained in `hs_fw`.
///
@@ -171,14 +165,14 @@ fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {
hs_fw
.fw
.get(start..end)
- .and_then(Self::from_bytes_copy)
+ .and_then(|b| Self::read_from_bytes(b).ok())
.ok_or(EINVAL)
}
}
/// Header for code and data load offsets.
#[repr(C)]
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, FromBytes)]
struct HsLoadHeaderV2 {
// Offset at which the code starts.
os_code_offset: u32,
@@ -192,9 +186,6 @@ struct HsLoadHeaderV2 {
num_apps: u32,
}
-// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
-unsafe impl FromBytes for HsLoadHeaderV2 {}
-
impl HsLoadHeaderV2 {
/// Returns the load header contained in `hs_fw`.
///
@@ -206,7 +197,7 @@ fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {
/// Header for app code loader.
#[repr(C)]
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, FromBytes)]
struct HsLoadHeaderV2App {
/// Offset at which to load the app code.
offset: u32,
@@ -214,9 +205,6 @@ struct HsLoadHeaderV2App {
len: u32,
}
-// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
-unsafe impl FromBytes for HsLoadHeaderV2App {}
-
impl HsLoadHeaderV2App {
/// Returns the [`HsLoadHeaderV2App`] for app `idx` of `hs_fw`.
///
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 4/7] drm/nova: use `zerocopy` in fwsec.rs
2026-06-25 19:05 [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
` (2 preceding siblings ...)
2026-06-25 19:05 ` [PATCH 3/7] drm/nova: use `zerocopy` in booter.rs Pedro Yudi Honda
@ 2026-06-25 19:05 ` Pedro Yudi Honda
2026-06-25 19:05 ` [PATCH 5/7] drm/nova: use `zerocopy` in bootloader.rs Pedro Yudi Honda
` (3 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 19:05 UTC (permalink / raw)
To: dakr, acourbot, nova-gpu
Cc: aliceryhl, airlied, simona, dri-devel, ojeda, rust-for-linux,
Pedro Yudi Honda
From: Pedro Yudi Honda <niyudi.honda@usp.br>
In firmware/fwsec.rs, replace the following `transmute` traits with
their `zerocopy` equivalents:
- `transmute::FromBytes` -> `zerocopy::FromBytes`
- `transmute::AsBytes` -> `zerocopy::IntoBytes`
- add `zerocopy::KnownLayout` where necessary
Update call sites accordingly.
Signed-off-by: Pedro Yudi Honda <niyudi.honda@usp.br>
---
drivers/gpu/nova-core/firmware/fwsec.rs | 47 ++++++-------------------
1 file changed, 11 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs
index 199ae2adb664..aafa481d805f 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -20,10 +20,6 @@
Device, //
},
prelude::*,
- transmute::{
- AsBytes,
- FromBytes, //
- },
};
use crate::{
@@ -50,26 +46,22 @@
const NVFW_FALCON_APPIF_ID_DMEMMAPPER: u32 = 0x4;
#[repr(C)]
-#[derive(Debug)]
+#[derive(Debug, FromBytes)]
struct FalconAppifHdrV1 {
version: u8,
header_size: u8,
entry_size: u8,
entry_count: u8,
}
-// SAFETY: Any byte sequence is valid for this struct.
-unsafe impl FromBytes for FalconAppifHdrV1 {}
#[repr(C, packed)]
-#[derive(Debug)]
+#[derive(Debug, FromBytes)]
struct FalconAppifV1 {
id: u32,
dmem_base: u32,
}
-// SAFETY: Any byte sequence is valid for this struct.
-unsafe impl FromBytes for FalconAppifV1 {}
-#[derive(Debug)]
+#[derive(Debug, FromBytes, IntoBytes, zerocopy_derive::KnownLayout)]
#[repr(C, packed)]
struct FalconAppifDmemmapperV3 {
signature: u32,
@@ -90,12 +82,8 @@ struct FalconAppifDmemmapperV3 {
ucode_cmd_mask1: u32,
multi_tgt_tbl: u32,
}
-// SAFETY: Any byte sequence is valid for this struct.
-unsafe impl FromBytes for FalconAppifDmemmapperV3 {}
-// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
-unsafe impl AsBytes for FalconAppifDmemmapperV3 {}
-#[derive(Debug)]
+#[derive(Debug, FromBytes, IntoBytes, zerocopy_derive::KnownLayout)]
#[repr(C, packed)]
struct ReadVbios {
ver: u32,
@@ -104,12 +92,8 @@ struct ReadVbios {
size: u32,
flags: u32,
}
-// SAFETY: Any byte sequence is valid for this struct.
-unsafe impl FromBytes for ReadVbios {}
-// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
-unsafe impl AsBytes for ReadVbios {}
-#[derive(Debug)]
+#[derive(Debug, FromBytes, IntoBytes, zerocopy_derive::KnownLayout)]
#[repr(C, packed)]
struct FrtsRegion {
ver: u32,
@@ -118,22 +102,15 @@ struct FrtsRegion {
size: u32,
ftype: u32,
}
-// SAFETY: Any byte sequence is valid for this struct.
-unsafe impl FromBytes for FrtsRegion {}
-// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
-unsafe impl AsBytes for FrtsRegion {}
const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2;
#[repr(C, packed)]
+#[derive(FromBytes, IntoBytes, zerocopy_derive::KnownLayout)]
struct FrtsCmd {
read_vbios: ReadVbios,
frts_region: FrtsRegion,
}
-// SAFETY: Any byte sequence is valid for this struct.
-unsafe impl FromBytes for FrtsCmd {}
-// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
-unsafe impl AsBytes for FrtsCmd {}
const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS: u32 = 0x15;
const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB: u32 = 0x19;
@@ -152,11 +129,9 @@ pub(crate) enum FwsecCommand {
/// A single signature that can be patched into a FWSEC image.
#[repr(transparent)]
+#[derive(FromBytes)]
pub(crate) struct Bcrt30Rsa3kSignature([u8; BCRT30_RSA3K_SIG_SIZE]);
-/// SAFETY: A signature is just an array of bytes.
-unsafe impl FromBytes for Bcrt30Rsa3kSignature {}
-
impl From<[u8; BCRT30_RSA3K_SIG_SIZE]> for Bcrt30Rsa3kSignature {
fn from(sig: [u8; BCRT30_RSA3K_SIG_SIZE]) -> Self {
Self(sig)
@@ -229,7 +204,7 @@ fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
let hdr = ucode
.get(hdr_offset..)
- .and_then(FalconAppifHdrV1::from_bytes_prefix)
+ .and_then(|b| FalconAppifHdrV1::read_from_prefix(b).ok())
.ok_or(EINVAL)?
.0;
@@ -247,7 +222,7 @@ fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
let app = ucode
.get(entry_offset..)
- .and_then(FalconAppifV1::from_bytes_prefix)
+ .and_then(|b| FalconAppifV1::read_from_prefix(b).ok())
.ok_or(EINVAL)?
.0;
@@ -264,7 +239,7 @@ fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
let dmem_mapper = ucode
.get_mut(dmem_mapper_offset..)
- .and_then(FalconAppifDmemmapperV3::from_bytes_mut_prefix)
+ .and_then(|b| FalconAppifDmemmapperV3::mut_from_prefix(b).ok())
.ok_or(EINVAL)?
.0;
@@ -282,7 +257,7 @@ fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
let frts_cmd = ucode
.get_mut(frts_cmd_offset..)
- .and_then(FrtsCmd::from_bytes_mut_prefix)
+ .and_then(|b| FrtsCmd::mut_from_prefix(b).ok())
.ok_or(EINVAL)?
.0;
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 5/7] drm/nova: use `zerocopy` in bootloader.rs
2026-06-25 19:05 [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
` (3 preceding siblings ...)
2026-06-25 19:05 ` [PATCH 4/7] drm/nova: use `zerocopy` in fwsec.rs Pedro Yudi Honda
@ 2026-06-25 19:05 ` Pedro Yudi Honda
2026-06-25 19:05 ` [PATCH 6/7] drm/nova: use `zerocopy` in riscv.rs Pedro Yudi Honda
` (2 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 19:05 UTC (permalink / raw)
To: dakr, acourbot, nova-gpu
Cc: aliceryhl, airlied, simona, dri-devel, ojeda, rust-for-linux,
Pedro Yudi Honda
From: Pedro Yudi Honda <niyudi.honda@usp.br>
In firmware/fwsec/bootloader.rs, replace the following `transmute`
traits with their `zerocopy` equivalents:
- `transmute::FromBytes` -> `zerocopy::FromBytes`
- `transmute::AsBytes` -> `zerocopy::IntoBytes`
- add `zerocopy::Immutable` where necessary
Update call sites accordingly.
Signed-off-by: Pedro Yudi Honda <niyudi.honda@usp.br>
---
.../gpu/nova-core/firmware/fwsec/bootloader.rs | 16 +++-------------
1 file changed, 3 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
index 13af1c381df1..6f1c8daad7d3 100644
--- a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
@@ -23,14 +23,8 @@
Alignment, //
},
sizes,
- transmute::{
- AsBytes,
- FromBytes, //
- },
};
-use zerocopy::FromBytes as _;
-
use crate::{
driver::Bar0,
falcon::{
@@ -61,7 +55,7 @@
///
/// Most of its fields appear to be legacy and carry incorrect values, so they are left unused.
#[repr(C)]
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, FromBytes)]
struct BootloaderDesc {
/// Starting tag of bootloader.
start_tag: u32,
@@ -77,15 +71,13 @@ struct BootloaderDesc {
/// Size of data section in the image. Unused as we build the data section ourselves.
_data_size: u32,
}
-// SAFETY: any byte sequence is valid for this struct.
-unsafe impl FromBytes for BootloaderDesc {}
/// Structure used by the boot-loader to load the rest of the code.
///
/// This has to be filled by the GPU driver and copied into DMEM at offset
/// [`BootloaderDesc.dmem_load_off`].
#[repr(C, packed)]
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, IntoBytes, zerocopy_derive::Immutable)]
struct BootloaderDmemDescV2 {
/// Reserved, should always be first element.
reserved: [u32; 4],
@@ -124,8 +116,6 @@ struct BootloaderDmemDescV2 {
/// Arguments to be passed to the target firmware being loaded.
argv: u32,
}
-// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
-unsafe impl AsBytes for BootloaderDmemDescV2 {}
/// Wrapper for [`FwsecFirmware`] that includes the bootloader performing the actual load
/// operation.
@@ -164,7 +154,7 @@ pub(crate) fn new(
fw.data()
.get(desc_offset..)
- .and_then(BootloaderDesc::from_bytes_copy_prefix)
+ .and_then(|b| BootloaderDesc::read_from_prefix(b).ok())
.ok_or(EINVAL)?
.0
};
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 6/7] drm/nova: use `zerocopy` in riscv.rs
2026-06-25 19:05 [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
` (4 preceding siblings ...)
2026-06-25 19:05 ` [PATCH 5/7] drm/nova: use `zerocopy` in bootloader.rs Pedro Yudi Honda
@ 2026-06-25 19:05 ` Pedro Yudi Honda
2026-06-25 19:05 ` [PATCH 7/7] drm/nova: remove unused trait in commands.rs Pedro Yudi Honda
2026-06-25 21:38 ` [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Danilo Krummrich
7 siblings, 0 replies; 10+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 19:05 UTC (permalink / raw)
To: dakr, acourbot, nova-gpu
Cc: aliceryhl, airlied, simona, dri-devel, ojeda, rust-for-linux,
Pedro Yudi Honda
From: Pedro Yudi Honda <niyudi.honda@usp.br>
In riscv.rs, replave the following `transmute` traits with their
`zerocopy` equivalents:
- `transmute::FromBytes` -> `zerocopy::FromBytes`
Update call sites accordingly.
Signed-off-by: Pedro Yudi Honda <niyudi.honda@usp.br>
---
drivers/gpu/nova-core/firmware/riscv.rs | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs
index 2afa7f36404e..ba56bc7d4188 100644
--- a/drivers/gpu/nova-core/firmware/riscv.rs
+++ b/drivers/gpu/nova-core/firmware/riscv.rs
@@ -7,8 +7,7 @@
device,
dma::Coherent,
firmware::Firmware,
- prelude::*,
- transmute::FromBytes, //
+ prelude::*, //
};
use crate::{
@@ -18,7 +17,7 @@
/// Descriptor for microcode running on a RISC-V core.
#[repr(C)]
-#[derive(Debug)]
+#[derive(Debug, FromBytes)]
struct RmRiscvUCodeDesc {
version: u32,
bootloader_offset: u32,
@@ -36,9 +35,6 @@ struct RmRiscvUCodeDesc {
monitor_code_size: u32,
}
-// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
-unsafe impl FromBytes for RmRiscvUCodeDesc {}
-
impl RmRiscvUCodeDesc {
/// Interprets the header of `bin_fw` as a [`RmRiscvUCodeDesc`] and returns it.
///
@@ -50,7 +46,7 @@ fn new(bin_fw: &BinFirmware<'_>) -> Result<Self> {
bin_fw
.fw
.get(offset..end)
- .and_then(Self::from_bytes_copy)
+ .and_then(|b| Self::read_from_bytes(b).ok())
.ok_or(EINVAL)
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 7/7] drm/nova: remove unused trait in commands.rs
2026-06-25 19:05 [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
` (5 preceding siblings ...)
2026-06-25 19:05 ` [PATCH 6/7] drm/nova: use `zerocopy` in riscv.rs Pedro Yudi Honda
@ 2026-06-25 19:05 ` Pedro Yudi Honda
2026-06-25 21:38 ` [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Danilo Krummrich
7 siblings, 0 replies; 10+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 19:05 UTC (permalink / raw)
To: dakr, acourbot, nova-gpu
Cc: aliceryhl, airlied, simona, dri-devel, ojeda, rust-for-linux,
Pedro Yudi Honda
From: Pedro Yudi Honda <niyudi.honda@usp.br>
In gsp/commands.rs, remove unused `transmute::FromBytes` implementation.
Signed-off-by: Pedro Yudi Honda <niyudi.honda@usp.br>
---
drivers/gpu/nova-core/gsp/commands.rs | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index f84de9f4f045..a3c0b7fa3b9e 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -12,10 +12,7 @@
device,
pci,
prelude::*,
- transmute::{
- AsBytes,
- FromBytes, //
- }, //
+ transmute::AsBytes//
};
use crate::{
@@ -149,10 +146,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;
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy`
2026-06-25 19:05 [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
` (6 preceding siblings ...)
2026-06-25 19:05 ` [PATCH 7/7] drm/nova: remove unused trait in commands.rs Pedro Yudi Honda
@ 2026-06-25 21:38 ` Danilo Krummrich
2026-06-26 13:15 ` Nicolás Antinori
7 siblings, 1 reply; 10+ messages in thread
From: Danilo Krummrich @ 2026-06-25 21:38 UTC (permalink / raw)
To: Pedro Yudi Honda
Cc: acourbot, nova-gpu, aliceryhl, airlied, simona, dri-devel, ojeda,
rust-for-linux, Nicolás Antinori
(Cc: Nicolás)
On Thu Jun 25, 2026 at 9:05 PM CEST, Pedro Yudi Honda wrote:
> This series follows the introduction of `zerocopy` to rust-next and
> replaces `transmute` traits with `zerocopy` traits. These changes are
> mechanical and don't alter functionality.
Thanks for your contribution!
Please note that this patch series does not apply to the development tree of the
Nova driver; patches for the Nova driver must be based on the drm-rust tree [1].
Please see this related explanation [2].
Also note that there's already a submission implementing a subset of this
series. Nicolás mentioned to resend once the backmerge of Linus's tree into
drm-rust-next has happened, please align with Nicolás and base your changes on
top of his changes.
Thanks,
Danilo
[1] https://gitlab.freedesktop.org/drm/rust/kernel/-/tree/drm-rust-next
[2] https://lore.kernel.org/nova-gpu/DJFVRMR67B0W.2AJBHU1PWDPFT@kernel.org/
[3] https://lore.kernel.org/nova-gpu/20260621143647.264770-1-nico.antinori.7@gmail.com/
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy`
2026-06-25 21:38 ` [PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Danilo Krummrich
@ 2026-06-26 13:15 ` Nicolás Antinori
0 siblings, 0 replies; 10+ messages in thread
From: Nicolás Antinori @ 2026-06-26 13:15 UTC (permalink / raw)
To: Danilo Krummrich, Pedro Yudi Honda
Cc: acourbot, nova-gpu, aliceryhl, airlied, simona, dri-devel, ojeda,
rust-for-linux
On Thu Jun 25, 2026 at 6:38 PM -03, Danilo Krummrich wrote:
> (Cc: Nicolás)
>
> On Thu Jun 25, 2026 at 9:05 PM CEST, Pedro Yudi Honda wrote:
>> This series follows the introduction of `zerocopy` to rust-next and
>> replaces `transmute` traits with `zerocopy` traits. These changes are
>> mechanical and don't alter functionality.
>
> Thanks for your contribution!
>
> Please note that this patch series does not apply to the development tree of the
> Nova driver; patches for the Nova driver must be based on the drm-rust tree [1].
> Please see this related explanation [2].
>
> Also note that there's already a submission implementing a subset of this
> series. Nicolás mentioned to resend once the backmerge of Linus's tree into
> drm-rust-next has happened, please align with Nicolás and base your changes on
> top of his changes.
Hello,
I'll add Pedro to CC when I send the v2.
Thank you both!
Nicolás
>
> Thanks,
> Danilo
>
> [1] https://gitlab.freedesktop.org/drm/rust/kernel/-/tree/drm-rust-next
> [2] https://lore.kernel.org/nova-gpu/DJFVRMR67B0W.2AJBHU1PWDPFT@kernel.org/
> [3] https://lore.kernel.org/nova-gpu/20260621143647.264770-1-nico.antinori.7@gmail.com/
^ permalink raw reply [flat|nested] 10+ messages in thread