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.
next prev parent reply other threads:[~2026-07-06 6:31 UTC|newest]
Thread overview: 18+ 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-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-06 6:31 ` 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-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-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox