Re: [PATCH v13 6/9] gpu: nova-core: Hopper/Blackwell: add FSP Chain of Trust boot
From: Alexandre Courbot
Date: Wed Jun 03 2026 - 09:43:51 EST
On Wed Jun 3, 2026 at 9:47 PM JST, Eliot Courtney wrote:
> On Wed Jun 3, 2026 at 4:30 PM JST, Alexandre Courbot wrote:
>> From: John Hubbard <jhubbard@xxxxxxxxxx>
>>
>> Build and send the Chain of Trust message to FSP, bundling the
>> DMA-coherent boot parameters that FSP reads at boot time.
>>
>> Signed-off-by: John Hubbard <jhubbard@xxxxxxxxxx>
>> Co-developed-by: Alexandre Courbot <acourbot@xxxxxxxxxx>
>> Signed-off-by: Alexandre Courbot <acourbot@xxxxxxxxxx>
>> ---
>> drivers/gpu/nova-core/firmware/fsp.rs | 2 -
>> drivers/gpu/nova-core/fsp.rs | 145 +++++++++++++++++++++-
>> drivers/gpu/nova-core/fsp/hal.rs | 1 -
>> drivers/gpu/nova-core/gsp.rs | 1 +
>> drivers/gpu/nova-core/gsp/fw.rs | 65 ++++++++++
>> drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 82 ++++++++++++
>> drivers/gpu/nova-core/gsp/hal/gh100.rs | 23 +++-
>> drivers/gpu/nova-core/mctp.rs | 2 -
>> 8 files changed, 308 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/gpu/nova-core/firmware/fsp.rs b/drivers/gpu/nova-core/firmware/fsp.rs
>> index 9b211426a75a..6eaf1c684b9d 100644
>> --- a/drivers/gpu/nova-core/firmware/fsp.rs
>> +++ b/drivers/gpu/nova-core/firmware/fsp.rs
>> @@ -39,10 +39,8 @@ pub(crate) struct FmcSignatures {
>>
>> pub(crate) struct FspFirmware {
>> /// FMC firmware image data (only the "image" ELF section).
>> - #[expect(dead_code)]
>> pub(crate) fmc_image: Coherent<[u8]>,
>> /// FMC firmware signatures.
>> - #[expect(dead_code)]
>> pub(crate) fmc_sigs: KBox<FmcSignatures>,
>> }
>>
>> diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
>> index cedfea173e50..883ac4f8b811 100644
>> --- a/drivers/gpu/nova-core/fsp.rs
>> +++ b/drivers/gpu/nova-core/fsp.rs
>> @@ -9,8 +9,14 @@
>>
>> use kernel::{
>> device,
>> + dma::Coherent,
>> io::poll::read_poll_timeout,
>> prelude::*,
>> + ptr::{
>> + Alignable,
>> + Alignment, //
>> + },
>> + sizes::SZ_2M,
>> time::Delta,
>> transmute::{
>> AsBytes,
>> @@ -24,13 +30,19 @@
>> fsp::Fsp as FspEngine,
>> Falcon, //
>> },
>> - firmware::fsp::FspFirmware,
>> + fb::FbLayout,
>> + firmware::fsp::{
>> + FmcSignatures,
>> + FspFirmware, //
>> + },
>> gpu::Chipset,
>> + gsp::GspFmcBootParams,
>> mctp::{
>> MctpHeader,
>> NvdmHeader,
>> NvdmType, //
>> },
>> + num,
>> regs, //
>> };
>>
>> @@ -66,6 +78,114 @@ pub(crate) trait MessageToFsp: AsBytes {
>> const NVDM_TYPE: NvdmType;
>> }
>>
>> +/// NVDM (NVIDIA Data Model) CoT (Chain of Trust) payload, the main
>> +/// message body sent to FSP for Chain of Trust boot.
>> +#[repr(C, packed)]
>> +#[derive(Clone, Copy, Zeroable)]
>> +struct NvdmPayloadCot {
>> + version: u16,
>> + size: u16,
>> + gsp_fmc_sysmem_offset: u64,
>> + frts_sysmem_offset: u64,
>> + frts_sysmem_size: u32,
>> + frts_vidmem_offset: u64,
>> + frts_vidmem_size: u32,
>> + sigs: FmcSignatures,
>> + gsp_boot_args_sysmem_offset: u64,
>> +}
>> +
>> +/// Complete FSP message structure with MCTP and NVDM headers.
>> +#[repr(C)]
>> +#[derive(Clone, Copy)]
>> +struct FspMessage {
>> + mctp_header: MctpHeader,
>> + nvdm_header: NvdmHeader,
>> + cot: NvdmPayloadCot,
>> +}
>> +
>> +impl FspMessage {
>> + /// Returns an in-place initializer for [`FspMessage`].
>> + fn new<'a>(
>> + fb_layout: &FbLayout,
>> + fsp_fw: &'a FspFirmware,
>> + args: &'a FmcBootArgs,
>> + ) -> Result<impl Init<Self> + 'a> {
>> + // frts_offset is relative to FB end: FRTS_location = FB_END - frts_offset
>> + let frts_offset = if !args.resume {
>> + let frts_reserved_size = fb_layout.heap.len() + u64::from(fb_layout.pmu_reserved_size);
>> +
>> + frts_reserved_size
>> + .align_up(Alignment::new::<SZ_2M>())
>> + .ok_or(EINVAL)?
>
> Why is `frts_offset` calculated using heap and pmu_reserved_size rather
> than looking at `fb_layout.frts`? If `fb_layout.frts` is the location we
> are meant to have the frts in then why use a (potentially) different
> location?
Conceptually the current code looks correct, but the naming is indeed
ambiguous. This seems to correspond to the CoT `frtsVidmemOffset`, not
the absolute FRTS address from `fb_layout.frts`. Will need to dig a bit
deeper but it looks like this requires a different name.
>
>> + } else {
>> + 0
>> + };
>> +
>> + let frts_size: u32 = if !args.resume {
>> + fb_layout.frts.len().try_into()?
>> + } else {
>> + 0
>> + };
>> +
>> + let version = hal::fsp_hal(args.chipset).ok_or(ENOTSUPP)?.cot_version();
>> + let size = num::usize_into_u16::<{ core::mem::size_of::<NvdmPayloadCot>() }>();
>> +
>> + Ok(init!(Self {
>> + mctp_header: MctpHeader::single_packet(),
>> + nvdm_header: NvdmHeader::new(NvdmType::Cot),
>> + // The payload is packed, so we cannot use `init!`. Initialize it member-by-member using
>> + // `chain`.
>> + cot <- pin_init::init_zeroed(),
>> + })
>> + .chain(move |msg| {
>> + msg.cot.version = version;
>> + msg.cot.size = size;
>> + msg.cot.gsp_fmc_sysmem_offset = fsp_fw.fmc_image.dma_handle();
>> + msg.cot.frts_vidmem_offset = frts_offset;
>> + msg.cot.frts_vidmem_size = frts_size;
>> + msg.cot.gsp_boot_args_sysmem_offset = args.fmc_boot_params.dma_handle();
>> + msg.cot.sigs = *fsp_fw.fmc_sigs;
>
> I guess we are leaving sysmem frts unset for now since we don't use it
> for anything hey
Yes, but let me add a note so we don't forget to come back to this as
they will likely become needed for systems without VRAM.