On 1/2/26 8:59 PM, Timur Tabi wrote:
...
> +impl Deref for FalconUCodeDesc {
> +    type Target = dyn FalconUCodeDescriptor;
> +
> +    fn deref(&self) -> &Self::Target {
> +        match self {
> +            FalconUCodeDesc::V2(v2) => v2,
> +            FalconUCodeDesc::V3(v3) => v3,
> +        }
> +    }
> +}

OK, I really don't want to do it this way, because this approach mixes
compile-time (explicit matching) and runtime (vtable) dispath, which is
odd and will surprise Rust readers.

I looked at Alex Courbot's feedback on the previous v4 patchset for this
patch, but he focused on things that I don't want to care about, which
is "what if we add more ucode versions". Because that won't happen--we
are done creating hardware/firmware for these older GPUs.

So what we want is a nice clear, directly readable implementation that
is *reasonably* Rust idiomatic. And I think that's available via:

1) Use direction enum methods, and

2) Use Option<> instead of zero sentinel values.

I've implemented these, and runtime tested the combined approach on my
TU117 board, and am including a diff below that can be applied on top of
your patchset.

It may have its own imperfections, but this is *approximately* what I'd
prefer (below). I believe that it strikes the right balance between
simplicity and Rust idiomatic code--and it's not any larger than what
you started with.

And it won't cost you any time to implement, since this already works:


diff --git a/drivers/gpu/nova-core/firmware.rs 
b/drivers/gpu/nova-core/firmware.rs
index 9f0ad02fbe22..c499491c25ab 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -4,7 +4,6 @@
 //! to be loaded into a given execution unit.

 use core::marker::PhantomData;
-use core::ops::Deref;

 use kernel::{
     device,
@@ -124,108 +123,92 @@ 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
-/// unified type, providing version-agnostic access to firmware metadata
-/// via the [`FalconUCodeDescriptor`] trait.
+/// unified type, providing version-agnostic access to common firmware
+/// metadata. Fields not present in a particular version return zero.
 #[derive(Debug, Clone)]
 pub(crate) enum FalconUCodeDesc {
     V2(FalconUCodeDescV2),
     V3(FalconUCodeDescV3),
 }

-impl Deref for FalconUCodeDesc {
-    type Target = dyn FalconUCodeDescriptor;
-
-    fn deref(&self) -> &Self::Target {
+impl FalconUCodeDesc {
+    /// Returns the header word.
+    pub(crate) fn hdr(&self) -> u32 {
         match self {
-            FalconUCodeDesc::V2(v2) => v2,
-            FalconUCodeDesc::V3(v3) => v3,
+            Self::V2(v2) => v2.hdr,
+            Self::V3(v3) => v3.hdr,
         }
     }
-}
-
-/// Trait providing a common interface for accessing Falcon microcode 
descriptor fields.
-///
-/// This trait abstracts over the different descriptor versions 
([`FalconUCodeDescV2`] and
-/// [`FalconUCodeDescV3`]), allowing code to work with firmware metadata 
without needing to
-/// know the specific descriptor version. Fields not present return zero.
-pub(crate) trait FalconUCodeDescriptor {
-    fn hdr(&self) -> u32;
-    fn imem_load_size(&self) -> u32;
-    fn interface_offset(&self) -> u32;
-    fn dmem_load_size(&self) -> u32;
-    fn pkc_data_offset(&self) -> u32;
-    fn engine_id_mask(&self) -> u16;
-    fn ucode_id(&self) -> u8;
-    fn signature_count(&self) -> u8;
-    fn signature_versions(&self) -> u16;
-
-    /// Returns the size in bytes of the header.
-    fn size(&self) -> usize {
-        let hdr = self.hdr();

-        const HDR_SIZE_SHIFT: u32 = 16;
-        const HDR_SIZE_MASK: u32 = 0xffff0000;
-        ((hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast()
+    /// Returns the IMEM load size in bytes.
+    pub(crate) fn imem_load_size(&self) -> u32 {
+        match self {
+            Self::V2(v2) => v2.imem_load_size,
+            Self::V3(v3) => v3.imem_load_size,
+        }
     }
-}

-impl FalconUCodeDescriptor for FalconUCodeDescV2 {
-    fn hdr(&self) -> u32 {
-        self.hdr
-    }
-    fn imem_load_size(&self) -> u32 {
-        self.imem_load_size
-    }
-    fn interface_offset(&self) -> u32 {
-        self.interface_offset
-    }
-    fn dmem_load_size(&self) -> u32 {
-        self.dmem_load_size
-    }
-    fn pkc_data_offset(&self) -> u32 {
-        0
-    }
-    fn engine_id_mask(&self) -> u16 {
-        0
-    }
-    fn ucode_id(&self) -> u8 {
-        0
-    }
-    fn signature_count(&self) -> u8 {
-        0
-    }
-    fn signature_versions(&self) -> u16 {
-        0
+    /// Returns the interface offset.
+    pub(crate) fn interface_offset(&self) -> u32 {
+        match self {
+            Self::V2(v2) => v2.interface_offset,
+            Self::V3(v3) => v3.interface_offset,
+        }
     }
-}

-impl FalconUCodeDescriptor for FalconUCodeDescV3 {
-    fn hdr(&self) -> u32 {
-        self.hdr
-    }
-    fn imem_load_size(&self) -> u32 {
-        self.imem_load_size
-    }
-    fn interface_offset(&self) -> u32 {
-        self.interface_offset
+    /// Returns the DMEM load size in bytes.
+    pub(crate) fn dmem_load_size(&self) -> u32 {
+        match self {
+            Self::V2(v2) => v2.dmem_load_size,
+            Self::V3(v3) => v3.dmem_load_size,
+        }
     }
-    fn dmem_load_size(&self) -> u32 {
-        self.dmem_load_size
+
+    /// Returns the PKC data offset (V3 only).
+    pub(crate) fn pkc_data_offset(&self) -> Option<u32> {
+        match self {
+            Self::V2(_) => None,
+            Self::V3(v3) => Some(v3.pkc_data_offset),
+        }
     }
-    fn pkc_data_offset(&self) -> u32 {
-        self.pkc_data_offset
+
+    /// Returns the engine ID mask (V3 only).
+    pub(crate) fn engine_id_mask(&self) -> Option<u16> {
+        match self {
+            Self::V2(_) => None,
+            Self::V3(v3) => Some(v3.engine_id_mask),
+        }
     }
-    fn engine_id_mask(&self) -> u16 {
-        self.engine_id_mask
+
+    /// Returns the ucode ID (V3 only).
+    pub(crate) fn ucode_id(&self) -> Option<u8> {
+        match self {
+            Self::V2(_) => None,
+            Self::V3(v3) => Some(v3.ucode_id),
+        }
     }
-    fn ucode_id(&self) -> u8 {
-        self.ucode_id
+
+    /// Returns the signature count (V3 only).
+    pub(crate) fn signature_count(&self) -> Option<u8> {
+        match self {
+            Self::V2(_) => None,
+            Self::V3(v3) => Some(v3.signature_count),
+        }
     }
-    fn signature_count(&self) -> u8 {
-        self.signature_count
+
+    /// Returns the signature versions bitmask (V3 only).
+    pub(crate) fn signature_versions(&self) -> Option<u16> {
+        match self {
+            Self::V2(_) => None,
+            Self::V3(v3) => Some(v3.signature_versions),
+        }
     }
-    fn signature_versions(&self) -> u16 {
-        self.signature_versions
+
+    /// Returns the size in bytes of the descriptor header.
+    pub(crate) fn size(&self) -> usize {
+        const HDR_SIZE_SHIFT: u32 = 16;
+        const HDR_SIZE_MASK: u32 = 0xffff0000;
+        ((self.hdr() & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast()
     }
 }

diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs 
b/drivers/gpu/nova-core/firmware/fwsec.rs
index f1f8f921aa43..28a52e251c3e 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -334,10 +334,12 @@ fn dmem_load_params(&self) -> FalconLoadTarget {
     }

     fn brom_params(&self) -> FalconBromParams {
+        // V2 descriptors don't have BROM parameters; use zero defaults.
+        // The Turing HAL's program_brom() is a no-op, so these values are 
unused.
         FalconBromParams {
-            pkc_data_offset: self.desc.pkc_data_offset(),
-            engine_id_mask: self.desc.engine_id_mask(),
-            ucode_id: self.desc.ucode_id(),
+            pkc_data_offset: self.desc.pkc_data_offset().unwrap_or(0),
+            engine_id_mask: self.desc.engine_id_mask().unwrap_or(0),
+            ucode_id: self.desc.ucode_id().unwrap_or(0),
         }
     }

@@ -463,52 +465,64 @@ pub(crate) fn new(
     ) -> Result<Self> {
         let ucode_dma = FirmwareDmaObject::<Self, _>::new_fwsec(dev, bios, 
cmd)?;

-        // Patch signature if needed.
+        // Patch signature if needed. V3 descriptors have signature metadata;
+        // V2 descriptors (Turing, GA100) don't require signature patching 
here.
         let desc = bios.fwsec_image().header()?;
-        let ucode_signed = if desc.signature_count() != 0 {
-            let sig_base_img =
-                usize::from_safe_cast(desc.imem_load_size() + 
desc.pkc_data_offset());
-            let desc_sig_versions = u32::from(desc.signature_versions());
-            let reg_fuse_version =
-                falcon.signature_reg_fuse_version(bar, desc.engine_id_mask(), 
desc.ucode_id())?;
-            dev_dbg!(
-                dev,
-                "desc_sig_versions: {:#x}, reg_fuse_version: {}\n",
-                desc_sig_versions,
-                reg_fuse_version
-            );
-            let signature_idx = {
-                let reg_fuse_version_bit = 1 << reg_fuse_version;
-
-                // Check if the fuse version is supported by the firmware.
-                if desc_sig_versions & reg_fuse_version_bit == 0 {
-                    dev_err!(
-                        dev,
-                        "no matching signature: {:#x} {:#x}\n",
-                        reg_fuse_version_bit,
-                        desc_sig_versions,
-                    );
-                    return Err(EINVAL);
-                }
-
-                // `desc_sig_versions` has one bit set per included signature. 
Thus, the index of
-                // the signature to patch is the number of bits in 
`desc_sig_versions` set to `1`
-                // before `reg_fuse_version_bit`.
-
-                // Mask of the bits of `desc_sig_versions` to preserve.
-                let reg_fuse_version_mask = 
reg_fuse_version_bit.wrapping_sub(1);
-
-                usize::from_safe_cast((desc_sig_versions & 
reg_fuse_version_mask).count_ones())
-            };
+        let ucode_signed = if let Some(sig_count) = desc.signature_count() {
+            if sig_count == 0 {
+                ucode_dma.no_patch_signature()
+            } else {
+                // These fields are guaranteed to be Some for V3 descriptors.
+                let pkc_data_offset = desc.pkc_data_offset().ok_or(EINVAL)?;
+                let engine_id_mask = desc.engine_id_mask().ok_or(EINVAL)?;
+                let ucode_id = desc.ucode_id().ok_or(EINVAL)?;
+                let sig_versions = desc.signature_versions().ok_or(EINVAL)?;
+
+                let sig_base_img =
+                    usize::from_safe_cast(desc.imem_load_size() + 
pkc_data_offset);
+                let desc_sig_versions = u32::from(sig_versions);
+                let reg_fuse_version =
+                    falcon.signature_reg_fuse_version(bar, engine_id_mask, 
ucode_id)?;
+                dev_dbg!(
+                    dev,
+                    "desc_sig_versions: {:#x}, reg_fuse_version: {}\n",
+                    desc_sig_versions,
+                    reg_fuse_version
+                );
+                let signature_idx = {
+                    let reg_fuse_version_bit = 1 << reg_fuse_version;
+
+                    // Check if the fuse version is supported by the firmware.
+                    if desc_sig_versions & reg_fuse_version_bit == 0 {
+                        dev_err!(
+                            dev,
+                            "no matching signature: {:#x} {:#x}\n",
+                            reg_fuse_version_bit,
+                            desc_sig_versions,
+                        );
+                        return Err(EINVAL);
+                    }
+
+                    // `desc_sig_versions` has one bit set per included 
signature. Thus, the index
+                    // of the signature to patch is the number of bits in 
`desc_sig_versions` set
+                    // to `1` before `reg_fuse_version_bit`.
+
+                    // Mask of the bits of `desc_sig_versions` to preserve.
+                    let reg_fuse_version_mask = 
reg_fuse_version_bit.wrapping_sub(1);
+
+                    usize::from_safe_cast((desc_sig_versions & 
reg_fuse_version_mask).count_ones())
+                };

-            dev_dbg!(dev, "patching signature with index {}\n", signature_idx);
-            let signature = bios
-                .fwsec_image()
-                .sigs(&desc)
-                .and_then(|sigs| sigs.get(signature_idx).ok_or(EINVAL))?;
+                dev_dbg!(dev, "patching signature with index {}\n", 
signature_idx);
+                let signature = bios
+                    .fwsec_image()
+                    .sigs(&desc)
+                    .and_then(|sigs| sigs.get(signature_idx).ok_or(EINVAL))?;

-            ucode_dma.patch_signature(signature, sig_base_img)?
+                ucode_dma.patch_signature(signature, sig_base_img)?
+            }
         } else {
+            // V2 descriptor - no signature patching needed
             ucode_dma.no_patch_signature()
         };

diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs
index 12256e5c8dc5..349b994fef92 100644
--- a/drivers/gpu/nova-core/vbios.rs
+++ b/drivers/gpu/nova-core/vbios.rs
@@ -1074,7 +1074,9 @@ pub(crate) fn ucode(&self, desc: &FalconUCodeDesc) -> 
Result<&[u8]> {
             })
     }

-    /// Get the signatures as a byte slice
+    /// Get the signatures as a byte slice.
+    ///
+    /// For V2 descriptors (which have no signature metadata), this returns an 
empty slice.
     pub(crate) fn sigs(&self, desc: &FalconUCodeDesc) -> 
Result<&[Bcrt30Rsa3kSignature]> {
         let hdr_size = match desc {
             FalconUCodeDesc::V2(_v2) => 
core::mem::size_of::<FalconUCodeDescV2>(),
@@ -1082,7 +1084,8 @@ pub(crate) fn sigs(&self, desc: &FalconUCodeDesc) -> 
Result<&[Bcrt30Rsa3kSignatu
         };
         // The signatures data follows the descriptor.
         let sigs_data_offset = self.falcon_ucode_offset + hdr_size;
-        let sigs_count = usize::from(desc.signature_count());
+        // V2 descriptors don't have signatures; unwrap_or(0) yields an empty 
slice.
+        let sigs_count = usize::from(desc.signature_count().unwrap_or(0));
         let sigs_size = sigs_count * 
core::mem::size_of::<Bcrt30Rsa3kSignature>();

         // Make sure the data is within bounds.

thanks,
-- 
John Hubbard

> +
> +/// Trait providing a common interface for accessing Falcon microcode 
> descriptor fields.
> +///
> +/// This trait abstracts over the different descriptor versions 
> ([`FalconUCodeDescV2`] and
> +/// [`FalconUCodeDescV3`]), allowing code to work with firmware metadata 
> without needing to
> +/// know the specific descriptor version. Fields not present return zero.
> +pub(crate) trait FalconUCodeDescriptor {
> +    fn hdr(&self) -> u32;
> +    fn imem_load_size(&self) -> u32;
> +    fn interface_offset(&self) -> u32;
> +    fn dmem_load_size(&self) -> u32;
> +    fn pkc_data_offset(&self) -> u32;
> +    fn engine_id_mask(&self) -> u16;
> +    fn ucode_id(&self) -> u8;
> +    fn signature_count(&self) -> u8;
> +    fn signature_versions(&self) -> u16;
> +
>      /// Returns the size in bytes of the header.
> -    pub(crate) fn size(&self) -> usize {
> +    fn size(&self) -> usize {
> +        let hdr = self.hdr();
> +
>          const HDR_SIZE_SHIFT: u32 = 16;
>          const HDR_SIZE_MASK: u32 = 0xffff0000;
> +        ((hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast()
> +    }
> +}
> +
> +impl FalconUCodeDescriptor for FalconUCodeDescV2 {
> +    fn hdr(&self) -> u32 {
> +        self.hdr
> +    }
> +    fn imem_load_size(&self) -> u32 {
> +        self.imem_load_size
> +    }
> +    fn interface_offset(&self) -> u32 {
> +        self.interface_offset
> +    }
> +    fn dmem_load_size(&self) -> u32 {
> +        self.dmem_load_size
> +    }
> +    fn pkc_data_offset(&self) -> u32 {
> +        0
> +    }
> +    fn engine_id_mask(&self) -> u16 {
> +        0
> +    }
> +    fn ucode_id(&self) -> u8 {
> +        0
> +    }
> +    fn signature_count(&self) -> u8 {
> +        0
> +    }
> +    fn signature_versions(&self) -> u16 {
> +        0
> +    }
> +}
>  
> -        ((self.hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast()
> +impl FalconUCodeDescriptor for FalconUCodeDescV3 {
> +    fn hdr(&self) -> u32 {
> +        self.hdr
> +    }
> +    fn imem_load_size(&self) -> u32 {
> +        self.imem_load_size
> +    }
> +    fn interface_offset(&self) -> u32 {
> +        self.interface_offset
> +    }
> +    fn dmem_load_size(&self) -> u32 {
> +        self.dmem_load_size
> +    }
> +    fn pkc_data_offset(&self) -> u32 {
> +        self.pkc_data_offset
> +    }
> +    fn engine_id_mask(&self) -> u16 {
> +        self.engine_id_mask
> +    }
> +    fn ucode_id(&self) -> u8 {
> +        self.ucode_id
> +    }
> +    fn signature_count(&self) -> u8 {
> +        self.signature_count
> +    }
> +    fn signature_versions(&self) -> u16 {
> +        self.signature_versions
>      }
>  }
>  
> diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs 
> b/drivers/gpu/nova-core/firmware/fwsec.rs
> index e4009faba6c5..1c1dcdacf5f5 100644
> --- a/drivers/gpu/nova-core/firmware/fwsec.rs
> +++ b/drivers/gpu/nova-core/firmware/fwsec.rs
> @@ -40,7 +40,7 @@
>          FalconLoadTarget, //
>      },
>      firmware::{
> -        FalconUCodeDescV3,
> +        FalconUCodeDesc,
>          FirmwareDmaObject,
>          FirmwareSignature,
>          Signed,
> @@ -218,38 +218,59 @@ unsafe fn transmute_mut<T: Sized + FromBytes + AsBytes>(
>  /// It is responsible for e.g. carving out the WPR2 region as the first step 
> of the GSP bootflow.
>  pub(crate) struct FwsecFirmware {
>      /// Descriptor of the firmware.
> -    desc: FalconUCodeDescV3,
> +    desc: FalconUCodeDesc,
>      /// GPU-accessible DMA object containing the firmware.
>      ucode: FirmwareDmaObject<Self, Signed>,
>  }
>  
>  impl FalconLoadParams for FwsecFirmware {
>      fn imem_sec_load_params(&self) -> FalconLoadTarget {
> -        FalconLoadTarget {
> -            src_start: 0,
> -            dst_start: self.desc.imem_phys_base,
> -            len: self.desc.imem_load_size,
> +        match &self.desc {
> +            FalconUCodeDesc::V2(v2) => FalconLoadTarget {
> +                src_start: 0,
> +                dst_start: v2.imem_sec_base,
> +                len: v2.imem_sec_size,
> +            },
> +            FalconUCodeDesc::V3(v3) => FalconLoadTarget {
> +                src_start: 0,
> +                dst_start: v3.imem_phys_base,
> +                len: v3.imem_load_size,
> +            },
>          }
>      }
>  
>      fn imem_ns_load_params(&self) -> Option<FalconLoadTarget> {
> -        // Only used on Turing and GA100, so return None for now
> -        None
> +        match &self.desc {
> +            FalconUCodeDesc::V2(v2) => Some(FalconLoadTarget {
> +                src_start: 0,
> +                dst_start: v2.imem_phys_base,
> +                len: v2.imem_load_size - v2.imem_sec_size,
> +            }),
> +            // Not used on V3 platforms
> +            FalconUCodeDesc::V3(_v3) => None,
> +        }
>      }
>  
>      fn dmem_load_params(&self) -> FalconLoadTarget {
> -        FalconLoadTarget {
> -            src_start: self.desc.imem_load_size,
> -            dst_start: self.desc.dmem_phys_base,
> -            len: self.desc.dmem_load_size,
> +        match &self.desc {
> +            FalconUCodeDesc::V2(v2) => FalconLoadTarget {
> +                src_start: v2.dmem_offset,
> +                dst_start: v2.dmem_phys_base,
> +                len: v2.dmem_load_size,
> +            },
> +            FalconUCodeDesc::V3(v3) => FalconLoadTarget {
> +                src_start: v3.imem_load_size,
> +                dst_start: v3.dmem_phys_base,
> +                len: v3.dmem_load_size,
> +            },
>          }
>      }
>  
>      fn brom_params(&self) -> FalconBromParams {
>          FalconBromParams {
> -            pkc_data_offset: self.desc.pkc_data_offset,
> -            engine_id_mask: self.desc.engine_id_mask,
> -            ucode_id: self.desc.ucode_id,
> +            pkc_data_offset: self.desc.pkc_data_offset(),
> +            engine_id_mask: self.desc.engine_id_mask(),
> +            ucode_id: self.desc.ucode_id(),
>          }
>      }
>  
> @@ -273,10 +294,10 @@ impl FalconFirmware for FwsecFirmware {
>  impl FirmwareDmaObject<FwsecFirmware, Unsigned> {
>      fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: 
> FwsecCommand) -> Result<Self> {
>          let desc = bios.fwsec_image().header()?;
> -        let ucode = bios.fwsec_image().ucode(desc)?;
> +        let ucode = bios.fwsec_image().ucode(&desc)?;
>          let mut dma_object = DmaObject::from_data(dev, ucode)?;
>  
> -        let hdr_offset = usize::from_safe_cast(desc.imem_load_size + 
> desc.interface_offset);
> +        let hdr_offset = usize::from_safe_cast(desc.imem_load_size() + 
> desc.interface_offset());
>          // SAFETY: we have exclusive access to `dma_object`.
>          let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, 
> hdr_offset) }?;
>  
> @@ -303,7 +324,7 @@ fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, 
> cmd: FwsecCommand) -> Re
>              let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe {
>                  transmute_mut(
>                      &mut dma_object,
> -                    (desc.imem_load_size + dmem_base).into_safe_cast(),
> +                    (desc.imem_load_size() + dmem_base).into_safe_cast(),
>                  )
>              }?;
>  
> @@ -317,7 +338,7 @@ fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, 
> cmd: FwsecCommand) -> Re
>              let frts_cmd: &mut FrtsCmd = unsafe {
>                  transmute_mut(
>                      &mut dma_object,
> -                    (desc.imem_load_size + 
> cmd_in_buffer_offset).into_safe_cast(),
> +                    (desc.imem_load_size() + 
> cmd_in_buffer_offset).into_safe_cast(),
>                  )
>              }?;
>  
> @@ -364,11 +385,12 @@ pub(crate) fn new(
>  
>          // Patch signature if needed.
>          let desc = bios.fwsec_image().header()?;
> -        let ucode_signed = if desc.signature_count != 0 {
> -            let sig_base_img = usize::from_safe_cast(desc.imem_load_size + 
> desc.pkc_data_offset);
> -            let desc_sig_versions = u32::from(desc.signature_versions);
> +        let ucode_signed = if desc.signature_count() != 0 {
> +            let sig_base_img =
> +                usize::from_safe_cast(desc.imem_load_size() + 
> desc.pkc_data_offset());
> +            let desc_sig_versions = u32::from(desc.signature_versions());
>              let reg_fuse_version =
> -                falcon.signature_reg_fuse_version(bar, desc.engine_id_mask, 
> desc.ucode_id)?;
> +                falcon.signature_reg_fuse_version(bar, 
> desc.engine_id_mask(), desc.ucode_id())?;
>              dev_dbg!(
>                  dev,
>                  "desc_sig_versions: {:#x}, reg_fuse_version: {}\n",
> @@ -402,7 +424,7 @@ pub(crate) fn new(
>              dev_dbg!(dev, "patching signature with index {}\n", 
> signature_idx);
>              let signature = bios
>                  .fwsec_image()
> -                .sigs(desc)
> +                .sigs(&desc)
>                  .and_then(|sigs| sigs.get(signature_idx).ok_or(EINVAL))?;
>  
>              ucode_dma.patch_signature(signature, sig_base_img)?
> @@ -411,7 +433,7 @@ pub(crate) fn new(
>          };
>  
>          Ok(FwsecFirmware {
> -            desc: desc.clone(),
> +            desc: desc,
>              ucode: ucode_signed,
>          })
>      }
> diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs
> index 7c26e4a2d61c..12256e5c8dc5 100644
> --- a/drivers/gpu/nova-core/vbios.rs
> +++ b/drivers/gpu/nova-core/vbios.rs
> @@ -19,6 +19,8 @@
>      driver::Bar0,
>      firmware::{
>          fwsec::Bcrt30Rsa3kSignature,
> +        FalconUCodeDesc,
> +        FalconUCodeDescV2,
>          FalconUCodeDescV3, //
>      },
>      num::FromSafeCast,
> @@ -1004,19 +1006,10 @@ fn build(self) -> Result<FwSecBiosImage> {
>  
>  impl FwSecBiosImage {
>      /// Get the FwSec header ([`FalconUCodeDescV3`]).
> -    pub(crate) fn header(&self) -> Result<&FalconUCodeDescV3> {
> +    pub(crate) fn header(&self) -> Result<FalconUCodeDesc> {
>          // Get the falcon ucode offset that was found in setup_falcon_data.
>          let falcon_ucode_offset = self.falcon_ucode_offset;
>  
> -        // Make sure the offset is within the data bounds.
> -        if falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>() > 
> self.base.data.len() {
> -            dev_err!(
> -                self.base.dev,
> -                "fwsec-frts header not contained within BIOS bounds\n"
> -            );
> -            return Err(ERANGE);
> -        }
> -
>          // Read the first 4 bytes to get the version.
>          let hdr_bytes: [u8; 4] = 
> self.base.data[falcon_ucode_offset..falcon_ucode_offset + 4]
>              .try_into()
> @@ -1024,33 +1017,49 @@ pub(crate) fn header(&self) -> 
> Result<&FalconUCodeDescV3> {
>          let hdr = u32::from_le_bytes(hdr_bytes);
>          let ver = (hdr & 0xff00) >> 8;
>  
> -        if ver != 3 {
> -            dev_err!(self.base.dev, "invalid fwsec firmware version: 
> {:?}\n", ver);
> -            return Err(EINVAL);
> +        let hdr_size = match ver {
> +            2 => core::mem::size_of::<FalconUCodeDescV2>(),
> +            3 => core::mem::size_of::<FalconUCodeDescV3>(),
> +            _ => {
> +                dev_err!(self.base.dev, "invalid fwsec firmware version: 
> {:?}\n", ver);
> +                return Err(EINVAL);
> +            }
> +        };
> +        // Make sure the offset is within the data bounds
> +        if falcon_ucode_offset + hdr_size > self.base.data.len() {
> +            dev_err!(
> +                self.base.dev,
> +                "fwsec-frts header not contained within BIOS bounds\n"
> +            );
> +            return Err(ERANGE);
>          }
>  
> -        // Return a reference to the FalconUCodeDescV3 structure.
> -        //
> -        // SAFETY: We have checked that `falcon_ucode_offset + 
> size_of::<FalconUCodeDescV3>` is
> -        // within the bounds of `data`. Also, this data vector is from ROM, 
> and the `data` field
> -        // in `BiosImageBase` is immutable after construction.
> -        Ok(unsafe {
> -            &*(self
> -                .base
> -                .data
> -                .as_ptr()
> -                .add(falcon_ucode_offset)
> -                .cast::<FalconUCodeDescV3>())
> -        })
> +        let data = self
> +            .base
> +            .data
> +            .get(falcon_ucode_offset..falcon_ucode_offset + hdr_size)
> +            .ok_or(EINVAL)?;
> +
> +        match ver {
> +            2 => {
> +                let v2 = FalconUCodeDescV2::from_bytes(data).ok_or(EINVAL)?;
> +                Ok(FalconUCodeDesc::V2(v2.clone()))
> +            }
> +            3 => {
> +                let v3 = FalconUCodeDescV3::from_bytes(data).ok_or(EINVAL)?;
> +                Ok(FalconUCodeDesc::V3(v3.clone()))
> +            }
> +            _ => Err(EINVAL),
> +        }
>      }
>  
>      /// Get the ucode data as a byte slice
> -    pub(crate) fn ucode(&self, desc: &FalconUCodeDescV3) -> Result<&[u8]> {
> +    pub(crate) fn ucode(&self, desc: &FalconUCodeDesc) -> Result<&[u8]> {
>          let falcon_ucode_offset = self.falcon_ucode_offset;
>  
>          // The ucode data follows the descriptor.
>          let ucode_data_offset = falcon_ucode_offset + desc.size();
> -        let size = usize::from_safe_cast(desc.imem_load_size + 
> desc.dmem_load_size);
> +        let size = usize::from_safe_cast(desc.imem_load_size() + 
> desc.dmem_load_size());
>  
>          // Get the data slice, checking bounds in a single operation.
>          self.base
> @@ -1066,10 +1075,14 @@ pub(crate) fn ucode(&self, desc: &FalconUCodeDescV3) 
> -> Result<&[u8]> {
>      }
>  
>      /// Get the signatures as a byte slice
> -    pub(crate) fn sigs(&self, desc: &FalconUCodeDescV3) -> 
> Result<&[Bcrt30Rsa3kSignature]> {
> +    pub(crate) fn sigs(&self, desc: &FalconUCodeDesc) -> 
> Result<&[Bcrt30Rsa3kSignature]> {
> +        let hdr_size = match desc {
> +            FalconUCodeDesc::V2(_v2) => 
> core::mem::size_of::<FalconUCodeDescV2>(),
> +            FalconUCodeDesc::V3(_v3) => 
> core::mem::size_of::<FalconUCodeDescV3>(),
> +        };
>          // The signatures data follows the descriptor.
> -        let sigs_data_offset = self.falcon_ucode_offset + 
> core::mem::size_of::<FalconUCodeDescV3>();
> -        let sigs_count = usize::from(desc.signature_count);
> +        let sigs_data_offset = self.falcon_ucode_offset + hdr_size;
> +        let sigs_count = usize::from(desc.signature_count());
>          let sigs_size = sigs_count * 
> core::mem::size_of::<Bcrt30Rsa3kSignature>();
>  
>          // Make sure the data is within bounds.



Reply via email to