* [PATCH 1/7] drm/nova: use `zerocopy` in firmware.rs
2026-06-25 20:51 [RESEND PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
@ 2026-06-25 20:51 ` Pedro Yudi Honda
2026-06-25 20:51 ` [PATCH 2/7] drm/nova: use `zerocopy` in vbios.rs Pedro Yudi Honda
` (5 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 20:51 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] 13+ messages in thread* [PATCH 2/7] drm/nova: use `zerocopy` in vbios.rs
2026-06-25 20:51 [RESEND PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
2026-06-25 20:51 ` [PATCH 1/7] drm/nova: use `zerocopy` in firmware.rs Pedro Yudi Honda
@ 2026-06-25 20:51 ` Pedro Yudi Honda
2026-06-25 21:14 ` sashiko-bot
2026-06-25 20:51 ` [PATCH 3/7] drm/nova: use `zerocopy` in booter.rs Pedro Yudi Honda
` (4 subsequent siblings)
6 siblings, 1 reply; 13+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 20:51 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] 13+ messages in thread* Re: [PATCH 2/7] drm/nova: use `zerocopy` in vbios.rs
2026-06-25 20:51 ` [PATCH 2/7] drm/nova: use `zerocopy` in vbios.rs Pedro Yudi Honda
@ 2026-06-25 21:14 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-06-25 21:14 UTC (permalink / raw)
To: Pedro Yudi Honda; +Cc: Danilo Krummrich, dri-devel, ojeda, Alexandre Courbot
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] VBIOS structures are parsed using native endianness, yielding incorrect values and validation failures on Big Endian architectures.
--
commit dfd6016814a03a8ecf36ea1b92019e446ec39712
Author: Pedro Yudi Honda <niyudi.honda@usp.br>
drm/nova: use zerocopy in vbios.rs
This commit replaces the transmute traits with their zerocopy equivalents
for parsing VBIOS structures. It updates the derive macros and changes the
initialization calls to use zerocopy's read_from_prefix().
> diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs
> index e6d144587c4ee..a3c0c06f6818d 100644
> --- a/drivers/gpu/nova-core/vbios.rs
> +++ b/drivers/gpu/nova-core/vbios.rs
[ ... ]
> @@ -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)
> signature: u16,
[Severity: High]
This is a pre-existing issue, but does mapping this byte stream directly into
native integer types break on Big Endian architectures?
Since VBIOS data is typically little-endian, parsing it into native integer
types (like the u16 signature here, or the u16 id in BitHeader) means the
bytes will be interpreted backwards on Big Endian systems. For example, the
signature 0xAA55 would be read as 0x55AA, causing validation to fail entirely.
Additionally, in VbiosIterator::read_more(), the data is read via try_read32()
and converted to a byte stream using .to_ne_bytes(), which scrambles the byte
order in 4-byte chunks on Big Endian platforms.
Should these structures be using explicit little-endian types (like __le16)
to ensure correct VBIOS parsing across all architectures?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260625205146.5047-1-niyudi.honda@usp.br?part=2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 3/7] drm/nova: use `zerocopy` in booter.rs
2026-06-25 20:51 [RESEND PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
2026-06-25 20:51 ` [PATCH 1/7] drm/nova: use `zerocopy` in firmware.rs Pedro Yudi Honda
2026-06-25 20:51 ` [PATCH 2/7] drm/nova: use `zerocopy` in vbios.rs Pedro Yudi Honda
@ 2026-06-25 20:51 ` Pedro Yudi Honda
2026-06-25 21:06 ` sashiko-bot
2026-06-25 20:51 ` [PATCH 4/7] drm/nova: use `zerocopy` in fwsec.rs Pedro Yudi Honda
` (3 subsequent siblings)
6 siblings, 1 reply; 13+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 20:51 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] 13+ messages in thread* Re: [PATCH 3/7] drm/nova: use `zerocopy` in booter.rs
2026-06-25 20:51 ` [PATCH 3/7] drm/nova: use `zerocopy` in booter.rs Pedro Yudi Honda
@ 2026-06-25 21:06 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-06-25 21:06 UTC (permalink / raw)
To: Pedro Yudi Honda; +Cc: Danilo Krummrich, dri-devel, ojeda, Alexandre Courbot
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Firmware structures are parsed using native integer types, causing endianness mismatches on big-endian architectures.
--
commit f0a0d5143a0bf185e80fec9d804b5bc8f1e3c0b1
Author: Pedro Yudi Honda <niyudi.honda@usp.br>
drm/nova: use zerocopy in booter.rs
This commit replaces the transmute::FromBytes traits with their
zerocopy::FromBytes equivalents in the firmware/booter.rs file. It updates the
call sites and struct derivations to match the new library usage.
> diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs
> index d9313ac361af3..0cda2c50fbbe9 100644
> --- a/drivers/gpu/nova-core/firmware/booter.rs
> +++ b/drivers/gpu/nova-core/firmware/booter.rs
[ ... ]
> @@ -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,
> /// Size of the signatures.
> sig_prod_size: u32,
[Severity: High]
This is a pre-existing issue, but does using native integer types like u32
in firmware structures cause endianness mismatches on big-endian architectures?
When frombytes_at() reads the firmware headers directly from fixed-endian
firmware blobs into host-endian structures using S::read_from_bytes(b), the
driver will parse incorrect offset and size values on big-endian systems.
Should these structures be updated to use explicitly endian-aware types (like
kernel::types::Le32) for fields such as sig_prod_offset and sig_prod_size to
ensure correct parsing across all architectures and avoid potential
out-of-bounds memory accesses?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260625205146.5047-1-niyudi.honda@usp.br?part=3
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 4/7] drm/nova: use `zerocopy` in fwsec.rs
2026-06-25 20:51 [RESEND PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
` (2 preceding siblings ...)
2026-06-25 20:51 ` [PATCH 3/7] drm/nova: use `zerocopy` in booter.rs Pedro Yudi Honda
@ 2026-06-25 20:51 ` Pedro Yudi Honda
2026-06-25 20:51 ` [PATCH 5/7] drm/nova: use `zerocopy` in bootloader.rs Pedro Yudi Honda
` (2 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 20:51 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] 13+ messages in thread* [PATCH 5/7] drm/nova: use `zerocopy` in bootloader.rs
2026-06-25 20:51 [RESEND PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
` (3 preceding siblings ...)
2026-06-25 20:51 ` [PATCH 4/7] drm/nova: use `zerocopy` in fwsec.rs Pedro Yudi Honda
@ 2026-06-25 20:51 ` Pedro Yudi Honda
2026-06-25 20:51 ` [PATCH 6/7] drm/nova: use `zerocopy` in riscv.rs Pedro Yudi Honda
2026-06-25 20:51 ` [PATCH 7/7] drm/nova: remove unused trait in commands.rs Pedro Yudi Honda
6 siblings, 0 replies; 13+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 20:51 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] 13+ messages in thread* [PATCH 6/7] drm/nova: use `zerocopy` in riscv.rs
2026-06-25 20:51 [RESEND PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
` (4 preceding siblings ...)
2026-06-25 20:51 ` [PATCH 5/7] drm/nova: use `zerocopy` in bootloader.rs Pedro Yudi Honda
@ 2026-06-25 20:51 ` Pedro Yudi Honda
2026-06-25 20:51 ` [PATCH 7/7] drm/nova: remove unused trait in commands.rs Pedro Yudi Honda
6 siblings, 0 replies; 13+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 20:51 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] 13+ messages in thread* [PATCH 7/7] drm/nova: remove unused trait in commands.rs
2026-06-25 20:51 [RESEND PATCH 0/7] drm/nova: replace `transmute` with `zerocopy` Pedro Yudi Honda
` (5 preceding siblings ...)
2026-06-25 20:51 ` [PATCH 6/7] drm/nova: use `zerocopy` in riscv.rs Pedro Yudi Honda
@ 2026-06-25 20:51 ` Pedro Yudi Honda
2026-06-25 21:04 ` sashiko-bot
6 siblings, 1 reply; 13+ messages in thread
From: Pedro Yudi Honda @ 2026-06-25 20:51 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] 13+ messages in thread* Re: [PATCH 7/7] drm/nova: remove unused trait in commands.rs
2026-06-25 20:51 ` [PATCH 7/7] drm/nova: remove unused trait in commands.rs Pedro Yudi Honda
@ 2026-06-25 21:04 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-06-25 21:04 UTC (permalink / raw)
To: Pedro Yudi Honda; +Cc: Danilo Krummrich, ojeda, Alexandre Courbot, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Low] The import modification violates the Rust vertical import style guideline by omitting a trailing comma.
Pre-existing issues:
- [High] The size field of the GSP registry table is incorrectly initialized, missing the base structure's size.
--
commit 6004d87646aa24143dd922fea1595d59bd5bbcb7
Author: Pedro Yudi Honda <niyudi.honda@usp.br>
drm/nova: remove unused trait in commands.rs
This commit removes the unused transmute::FromBytes implementation and its
import in drivers/gpu/nova-core/gsp/commands.rs.
> diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
> index f84de9f4f0450..a3c0b7fa3b9ed 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//
[Severity: Low]
Does this modification violate the Rust vertical import style guideline by
omitting a trailing comma?
[ ... ]
This is a pre-existing issue, but I noticed a potential problem in
SetRegistry::init() in this same file:
drivers/gpu/nova-core/gsp/commands.rs:SetRegistry::init() {
...
fn init(&self) -> impl Init<Self::Command, Self::InitError> {
Self::Command::init(Self::NUM_ENTRIES as u32, self.variable_payload_len() as u32)
}
...
}
[Severity: High]
Is the size field of the GSP registry table incorrectly initialized here by
omitting the base structure's size?
Since the table size provided to the GSP firmware only accounts for the
variable payload and omits the table's header size, the string offsets
computed for the registry keys will mathematically exceed the declared table
size.
Will the firmware reject the command due to an apparent out-of-bounds access,
potentially causing driver bring-up to fail entirely?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260625205146.5047-1-niyudi.honda@usp.br?part=7
^ permalink raw reply [flat|nested] 13+ messages in thread