On 1/21/2026 6:53 PM, Timur Tabi wrote:
> The FRTS firmware in Turing and GA100 VBIOS has an older header
> format (v2 instead of v3). To support both v2 and v3 at runtime,
> add the FalconUCodeDescV2 struct, and update code that references
> the FalconUCodeDescV3 directly with a FalconUCodeDesc enum that
> encapsulates both.
>
> Signed-off-by: Timur Tabi <[email protected]>
> ---
> drivers/gpu/nova-core/firmware.rs | 203 ++++-
> drivers/gpu/nova-core/firmware/fwsec.rs | 46 +-
> drivers/gpu/nova-core/vbios.rs | 64 +-
> drivers/gpu/nova-core/vbios.rs.orig | 1105 +++++++++++++++++++++++
> 4 files changed, 1354 insertions(+), 64 deletions(-)
> create mode 100644 drivers/gpu/nova-core/vbios.rs.orig
>
> diff --git a/drivers/gpu/nova-core/firmware.rs
> b/drivers/gpu/nova-core/firmware.rs
> index 2d2008b33fb4..68779540aa28 100644
> --- a/drivers/gpu/nova-core/firmware.rs
> +++ b/drivers/gpu/nova-core/firmware.rs
> @@ -4,6 +4,7 @@
> //! to be loaded into a given execution unit.
>
> use core::marker::PhantomData;
> +use core::ops::Deref;
>
> use kernel::{
> device,
> @@ -15,7 +16,10 @@
>
> use crate::{
> dma::DmaObject,
> - falcon::FalconFirmware,
> + falcon::{
> + FalconFirmware,
> + FalconLoadTarget, //
> + },
> gpu,
> num::{
> FromSafeCast,
> @@ -43,6 +47,46 @@ fn request_firmware(
> .and_then(|path| firmware::Firmware::request(&path, dev))
> }
>
> +/// Structure used to describe some firmwares, notably FWSEC-FRTS.
> +#[repr(C)]
> +#[derive(Debug, Clone)]
> +pub(crate) struct FalconUCodeDescV2 {
> + /// Header defined by 'NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*' in OpenRM.
> + hdr: u32,
> + /// Stored size of the ucode after the header, compressed or uncompressed
> + stored_size: u32,
> + /// Uncompressed size of the ucode. If store_size == uncompressed_size,
> then the ucode
> + /// is not compressed.
> + pub(crate) uncompressed_size: u32,
> + /// Code entry point
> + pub(crate) virtual_entry: u32,
> + /// Offset after the code segment at which the Application Interface
> Table headers are located.
> + pub(crate) interface_offset: u32,
> + /// Base address at which to load the code segment into 'IMEM'.
> + pub(crate) imem_phys_base: u32,
> + /// Size in bytes of the code to copy into 'IMEM'.
> + pub(crate) imem_load_size: u32,
> + /// Virtual 'IMEM' address (i.e. 'tag') at which the code should start.
> + pub(crate) imem_virt_base: u32,
> + /// Virtual address of secure IMEM segment.
> + pub(crate) imem_sec_base: u32,
> + /// Size of secure IMEM segment.
> + pub(crate) imem_sec_size: u32,
> + /// Offset into stored (uncompressed) image at which DMEM begins.
> + pub(crate) dmem_offset: u32,
> + /// Base address at which to load the data segment into 'DMEM'.
> + pub(crate) dmem_phys_base: u32,
> + /// Size in bytes of the data to copy into 'DMEM'.
> + pub(crate) dmem_load_size: u32,
> + /// "Alternate" Size of data to load into IMEM.
> + pub(crate) alt_imem_load_size: u32,
> + /// "Alternate" Size of data to load into DMEM.
> + pub(crate) alt_dmem_load_size: u32,
> +}
> +
> +// SAFETY: all bit patterns are valid for this type, and it doesn't use
> interior mutability.
> +unsafe impl FromBytes for FalconUCodeDescV2 {}
> +
> /// Structure used to describe some firmwares, notably FWSEC-FRTS.
> #[repr(C)]
> #[derive(Debug, Clone)]
> @@ -76,13 +120,164 @@ pub(crate) struct FalconUCodeDescV3 {
> _reserved: u16,
> }
>
> -impl FalconUCodeDescV3 {
> +// 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
> +/// unified type, providing version-agnostic access to firmware metadata
> +/// via the [`FalconUCodeDescriptor`] trait.
> +#[derive(Debug, Clone)]
> +pub(crate) enum FalconUCodeDesc {
> + V2(FalconUCodeDescV2),
> + V3(FalconUCodeDescV3),
> +}
> +
> +impl Deref for FalconUCodeDesc {
> + type Target = dyn FalconUCodeDescriptor;
> +
> + fn deref(&self) -> &Self::Target {
> + match self {
> + FalconUCodeDesc::V2(v2) => v2,
> + FalconUCodeDesc::V3(v3) => v3,
> + }
> + }
I guess we still need to decide if we want to use 'dyn' for
FalconUCodeDescriptor or function wrappers with separate match statements. John
also recently felt, I think, that 'dyn' here is more complicated (I was the one
who suggested 'dyn' in v2 or so, but hey - in my defense, I am learning Rust too
;-)).
After seeing this patch, I think I realized 'dyn' *for this case* does add more
boilerplate, because now you have duplicated functions which almost do exactly
the same thing (seems much worse than duplicate match arms).
'dyn' would benefit a lot though if the match arms were large. But in this
patch, most of the match arms are just accessing fields.
For my -mm patches, where I am routing between version 2 and version 3 of the
page table/directory formats, I actually went with match arms instead of dyn
which was cleaner.
--
Joel Fernandes