All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Alexandre Courbot" <acourbot@nvidia.com>
To: "Timur Tabi" <ttabi@nvidia.com>
Cc: <driver-core@lists.linux.dev>, <nova-gpu@lists.linux.dev>,
	<rust-for-linux@vger.kernel.org>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Eliot Courtney" <ecourtney@nvidia.com>,
	"Zhi Wang" <zhiw@nvidia.com>,
	"John Hubbard" <jhubbard@nvidia.com>,
	"Luis Chamberlain" <mcgrof@kernel.org>,
	"Russ Weight" <russ.weight@linux.dev>,
	"Miguel Ojeda" <ojeda@kernel.org>, "Gary Guo" <gary@garyguo.net>
Subject: Re: [PATCH v3 3/7] gpu: nova-core: transition booter_load to TLV images
Date: Mon, 06 Jul 2026 15:31:29 +0900	[thread overview]
Message-ID: <DJR9ZZLA72BK.G14HXKV5E8LP@nvidia.com> (raw)
In-Reply-To: <20260702192712.3450652-4-ttabi@nvidia.com>

On Fri Jul 3, 2026 at 4:27 AM JST, Timur Tabi wrote:
<...>
>  /// Signature for Booter firmware. Their size is encoded into the header and not known a compile
>  /// time, so we just wrap a byte slices on which we can implement [`FirmwareSignature`].
>  struct BooterSignature<'a>(&'a [u8]);
> @@ -291,85 +85,91 @@ pub(crate) fn new(
>          dev: &device::Device<device::Bound>,
>          kind: BooterKind,
>          chipset: Chipset,
> -        ver: &str,
>          falcon: &Falcon<'_, <Self as FalconFirmware>::Target>,
>      ) -> Result<Self> {
>          let fw_name = match kind {
>              BooterKind::Loader => "booter_load",
>              BooterKind::Unloader => "booter_unload",
>          };
> -        let fw = super::request_firmware(dev, chipset, fw_name, ver)?;
> -        let bin_fw = BinFirmware::new(&fw)?;
> -
> -        // The binary firmware embeds a Heavy-Secured firmware.
> -        let hs_fw = HsFirmwareV2::new(&bin_fw)?;
> -
> -        // The Heavy-Secured firmware embeds a firmware load descriptor.
> -        let load_hdr = HsLoadHeaderV2::new(&hs_fw)?;
> -
> -        // Offset in `ucode` where to patch the signature.
> -        let patch_loc = hs_fw.patch_location()?;
> +        let fw = request_tlv(dev, chipset, fw_name)?;
> +        let tlv = Tlv::new(fw.data())?;
> +        dev_dbg!(
> +            dev,
> +            "loaded {} firmware v{}\n",
> +            fw_name,
> +            tlv.get_string(b"VERS")?
> +        );
> +
> +        let os_data_offset = tlv.get_u32(b"DAOF")?;
> +        let os_data_size = tlv.get_u32(b"DASZ")?;
> +        let os_code_offset = tlv.get_u32(b"CDOF")?;
> +        let os_code_size = tlv.get_u32(b"CDSZ")?;
> +        let patch_loc = tlv.get_u32(b"PLOC")?;
> +        let fuse_version = tlv.get_u32(b"FUSE")?;
> +        let engine_id = tlv.get_u32(b"ENID")?;
> +        let ucode_id = tlv.get_u32(b"UCID")?;
> +        let app0_code_offset = tlv.get_u32(b"A0CO")?;
> +        let app0_code_size = tlv.get_u32(b"A0CS")?;
> +        let num_sigs = tlv.get_u32(b"NSIG")?;
> +        let sig_bytes = tlv.get_bytes(b"SIGN")?;
> +
> +        // Booter is always signed

The `.rst` file mentions the non-signed case though - we should
reconcile the spec and the code one way or the other.

> +        if !(1..=15).contains(&num_sigs) || sig_bytes.len() % num_sigs as usize != 0 {

s/as usize/into_safe_cast.

> +            dev_err!(dev, "invalid signature count {}\n", num_sigs);
> +            return Err(EINVAL);
> +        }
>  
> -        let sig_params = HsSignatureParams::new(&hs_fw)?;
>          let brom_params = FalconBromParams {
> -            // `load_hdr.os_data_offset` is an absolute index, but `pkc_data_offset` is from the
> +            // `os_data_offset` is an absolute index, but `pkc_data_offset` is from the
>              // signature patch location.
> -            pkc_data_offset: patch_loc
> -                .checked_sub(load_hdr.os_data_offset)
> -                .ok_or(EINVAL)?,
> -            engine_id_mask: u16::try_from(sig_params.engine_id_mask).map_err(|_| EINVAL)?,
> -            ucode_id: u8::try_from(sig_params.ucode_id).map_err(|_| EINVAL)?,
> +            pkc_data_offset: patch_loc.checked_sub(os_data_offset).ok_or(EINVAL)?,
> +            engine_id_mask: u16::try_from(engine_id).map_err(|_| EINVAL)?,
> +            ucode_id: u8::try_from(ucode_id).map_err(|_| EINVAL)?,
>          };
> -        let app0 = HsLoadHeaderV2App::new(&hs_fw, 0)?;
>  
> -        // Object containing the firmware microcode to be signature-patched.
> -        let ucode = bin_fw
> -            .data()
> -            .ok_or(EINVAL)
> +        let ucode = tlv
> +            .get_bytes(b"BLOB")
>              .and_then(FirmwareObject::<Self, _>::new_booter)?;
>  
> -        let ucode_signed = {
> -            let mut signatures = hs_fw.signatures_iter()?.peekable();
> -
> -            if signatures.peek().is_none() {
> -                // If there are no signatures, then the firmware is unsigned.
> -                ucode.no_patch_signature()
> -            } else {
> -                // Obtain the version from the fuse register, and extract the corresponding
> -                // signature.
> -                let reg_fuse_version = falcon
> -                    .signature_reg_fuse_version(brom_params.engine_id_mask, brom_params.ucode_id)?;
> -
> -                // `0` means the last signature should be used.
> -                const FUSE_VERSION_USE_LAST_SIG: u32 = 0;
> -                let signature = match reg_fuse_version {
> -                    FUSE_VERSION_USE_LAST_SIG => signatures.last(),
> -                    // Otherwise hardware fuse version needs to be subtracted to obtain the index.
> -                    reg_fuse_version => {
> -                        let Some(idx) = sig_params.fuse_ver.checked_sub(reg_fuse_version) else {
> -                            dev_err!(dev, "invalid fuse version for Booter firmware\n");
> -                            return Err(EINVAL);
> -                        };
> -                        signatures.nth(idx.into_safe_cast())
> -                    }
> -                }
> -                .ok_or(EINVAL)?;
> -
> -                ucode.patch_signature(&signature, patch_loc.into_safe_cast())?
> -            }
> +        // Obtain the version from the fuse register, and extract the corresponding
> +        // signature.
> +        let reg_fuse_version =
> +            falcon.signature_reg_fuse_version(brom_params.engine_id_mask, brom_params.ucode_id)?;
> +
> +        const FUSE_VERSION_USE_LAST_SIG: u32 = 0;
> +        let index = match reg_fuse_version {
> +            // `0` means the last signature should be used.
> +            FUSE_VERSION_USE_LAST_SIG => num_sigs - 1,
> +            // Otherwise, hardware fuse version needs to be subtracted to obtain the index.
> +            _ => fuse_version.checked_sub(reg_fuse_version).ok_or(EINVAL)?,
>          };
>  
> +        // The size of one signature
> +        let sig_size = sig_bytes
> +            .len()
> +            .checked_div(num_sigs.into_safe_cast())
> +            .ok_or(EINVAL)?;
> +
> +        // Extract the nth signature
> +        let sig_chunk = sig_bytes
> +            .chunks_exact(sig_size)

As Sashiko pointed out, this will panic if `sig_size == 0`, so we need
to check that - and whether an unsigned Booter is valid at all.

> +            .nth(index as usize)

s/as_usize/into_safe_cast.

> +            .ok_or(EINVAL)?;
> +
> +        let signature = BooterSignature(sig_chunk);
> +        let ucode_signed = ucode.patch_signature(&signature, patch_loc.into_safe_cast())?;
> +
>          // There are two versions of Booter, one for Turing/GA100, and another for
>          // GA102+.  The extraction of the IMEM sections differs between the two
>          // versions.  Unfortunately, the file names are the same, and the headers
>          // don't indicate the versions.  The only way to differentiate is by the Chipset.
>          let (imem_sec_dst_start, imem_ns_load_target) = if chipset <= Chipset::GA100 {
>              (
> -                app0.offset,
> +                app0_code_offset,
>                  Some(FalconDmaLoadTarget {
>                      src_start: 0,
> -                    dst_start: load_hdr.os_code_offset,
> -                    len: load_hdr.os_code_size,
> +                    dst_start: os_code_offset,
> +                    len: os_code_size,
>                  }),
>              )
>          } else {
> @@ -378,15 +178,15 @@ pub(crate) fn new(
>  
>          Ok(Self {
>              imem_sec_load_target: FalconDmaLoadTarget {
> -                src_start: app0.offset,
> +                src_start: app0_code_offset,
>                  dst_start: imem_sec_dst_start,
> -                len: app0.len,
> +                len: app0_code_size,
>              },
>              imem_ns_load_target,
>              dmem_load_target: FalconDmaLoadTarget {
> -                src_start: load_hdr.os_data_offset,
> +                src_start: os_data_offset,
>                  dst_start: 0,
> -                len: load_hdr.os_data_size,
> +                len: os_data_size,
>              },
>              brom_params,
>              ucode: ucode_signed,
> diff --git a/drivers/gpu/nova-core/firmware/tlv.rs b/drivers/gpu/nova-core/firmware/tlv.rs
> index 56e0d5cab580..68b12637ff2c 100644
> --- a/drivers/gpu/nova-core/firmware/tlv.rs
> +++ b/drivers/gpu/nova-core/firmware/tlv.rs
> @@ -11,7 +11,6 @@
>  use crate::gpu;
>  
>  /// Requests the GPU firmware TLV `name` suitable for `chipset`.
> -#[expect(dead_code)]
>  pub(crate) fn request_tlv(
>      dev: &device::Device,
>      chipset: gpu::Chipset,
> @@ -117,6 +116,9 @@ fn next(&mut self) -> Option<Self::Item> {
>  /// be exactly partitionable into blocks (no trailing partial header or slack). After
>  /// that, [`TlvIter`] only signals end-of-stream via [`None`], not parse failure.
>  ///
> +/// Although the spec forbids duplicate tags, neither the constructor nor the iterator
> +/// enforces this restriction.  Instead, duplicate tags are simply ignored.
> +///

This chunk should probably be in patch 2.

>  /// # Invariants
>  ///
>  /// `data` is a validated TLV payload (the bytes *after* the `NVFW` magic): it is the exact
> @@ -129,7 +131,6 @@ pub(crate) struct Tlv<'a> {
>      data: &'a [u8],
>  }
>  
> -#[expect(dead_code)]
>  impl<'a> Tlv<'a> {
>      const MAGIC: &'static [u8; 4] = b"NVFW";
>  
> @@ -161,6 +162,7 @@ pub(crate) fn new(data: &'a [u8]) -> Result<Self> {
>              else {
>                  return Err(EINVAL);
>              };
> +

Same here.

  reply	other threads:[~2026-07-06  6:31 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-02 19:27 [PATCH v3 0/7] Transition Nova Core to TLV firmare images Timur Tabi
2026-07-02 19:27 ` [PATCH v3 1/7] rust: firmware: add request_into_buf() Timur Tabi
2026-07-03  2:51   ` Alvin Sun
2026-07-03  3:06     ` Timur Tabi
2026-07-03 13:51       ` Danilo Krummrich
2026-07-06  6:30   ` Alexandre Courbot
2026-07-06 10:50     ` Danilo Krummrich
2026-07-07  2:54       ` Timur Tabi
2026-07-07  5:10         ` Alexandre Courbot
2026-07-07 13:32           ` Danilo Krummrich
2026-07-02 19:27 ` [PATCH v3 2/7] gpu: nova-core: add TLV parser for firmware files Timur Tabi
2026-07-02 19:45   ` Timur Tabi
2026-07-06  6:31     ` Alexandre Courbot
2026-07-07  2:56       ` Timur Tabi
2026-07-07  5:13         ` Alexandre Courbot
2026-07-08 22:48           ` Timur Tabi
2026-07-09  3:43             ` Alexandre Courbot
2026-07-09 21:29               ` Timur Tabi
2026-07-06  6:31   ` Alexandre Courbot
2026-07-08 20:12     ` Timur Tabi
2026-07-09  1:55       ` Alexandre Courbot
2026-07-02 19:27 ` [PATCH v3 3/7] gpu: nova-core: transition booter_load to TLV images Timur Tabi
2026-07-06  6:31   ` Alexandre Courbot [this message]
2026-07-08 20:46     ` Timur Tabi
2026-07-09  3:51       ` Alexandre Courbot
2026-07-09 21:31         ` Timur Tabi
2026-07-02 19:27 ` [PATCH v3 4/7] gpu: nova-core: transition gsp " Timur Tabi
2026-07-02 19:27 ` [PATCH v3 5/7] gpu: nova-core: transition gen_bootloader " Timur Tabi
2026-07-06  6:31   ` Alexandre Courbot
2026-07-08 22:26     ` Timur Tabi
2026-07-09  2:24       ` Alexandre Courbot
2026-07-09 21:27         ` Timur Tabi
2026-07-09 21:49           ` John Hubbard
2026-07-09 22:13             ` Timur Tabi
2026-07-09 22:29               ` John Hubbard
2026-07-09 22:39                 ` Timur Tabi
2026-07-09 22:41                   ` John Hubbard
2026-07-09 22:44                     ` Timur Tabi
2026-07-02 19:27 ` [PATCH v3 6/7] gpu: nova-core: transition fsp " Timur Tabi
2026-07-02 19:27 ` [PATCH v3 7/7] gpu: nova-core: update firmware module info for " Timur Tabi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DJR9ZZLA72BK.G14HXKV5E8LP@nvidia.com \
    --to=acourbot@nvidia.com \
    --cc=dakr@kernel.org \
    --cc=driver-core@lists.linux.dev \
    --cc=ecourtney@nvidia.com \
    --cc=gary@garyguo.net \
    --cc=jhubbard@nvidia.com \
    --cc=mcgrof@kernel.org \
    --cc=nova-gpu@lists.linux.dev \
    --cc=ojeda@kernel.org \
    --cc=russ.weight@linux.dev \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=ttabi@nvidia.com \
    --cc=zhiw@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.