Messages in this thread Patch in this message |  | | From | John Hubbard <> | | Subject | [PATCH v10 25/28] gpu: nova-core: Hopper/Blackwell: add FSP Chain of Trust boot | | Date | Fri, 10 Apr 2026 19:49:50 -0700 |
| |
Add boot_fmc() which builds and sends the Chain of Trust message to FSP. FmcBootArgs bundles the DMA-coherent boot parameters that FSP reads at boot time.
Signed-off-by: John Hubbard <jhubbard@nvidia.com> --- drivers/gpu/nova-core/fb.rs | 1 - drivers/gpu/nova-core/firmware/fsp.rs | 1 - drivers/gpu/nova-core/fsp.rs | 257 +++++++++++++++++++++++++- drivers/gpu/nova-core/gpu.rs | 1 - drivers/gpu/nova-core/gsp/boot.rs | 26 ++- drivers/gpu/nova-core/mctp.rs | 2 - 6 files changed, 275 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs index 756ff283a908..2b3a2bb2f1db 100644 --- a/drivers/gpu/nova-core/fb.rs +++ b/drivers/gpu/nova-core/fb.rs @@ -294,7 +294,6 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result< } /// Returns the PMU reserved memory size for `chipset`. -#[expect(dead_code)] pub(crate) fn calc_pmu_reserved_size(chipset: Chipset) -> u32 { match chipset.arch() { Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => PMU_RESERVED_SIZE, diff --git a/drivers/gpu/nova-core/firmware/fsp.rs b/drivers/gpu/nova-core/firmware/fsp.rs index 3968bacb7e61..301345aa491c 100644 --- a/drivers/gpu/nova-core/firmware/fsp.rs +++ b/drivers/gpu/nova-core/firmware/fsp.rs @@ -14,7 +14,6 @@ gpu::Chipset, // }; -#[expect(dead_code)] pub(crate) struct FspFirmware { /// FMC firmware image data (only the "image" ELF section). pub(crate) fmc_image: Coherent<[u8]>, diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs index bc98a5c3cfaa..75e06b2d5f06 100644 --- a/drivers/gpu/nova-core/fsp.rs +++ b/drivers/gpu/nova-core/fsp.rs @@ -8,9 +8,25 @@ use kernel::{ device, + dma::{ + Coherent, + CoherentBox, // + }, io::poll::read_poll_timeout, prelude::*, - time::Delta, // + ptr::{ + Alignable, + Alignment, // + }, + sizes::{ + SizeConstants, + SZ_2M, // + }, + time::Delta, + transmute::{ + AsBytes, + FromBytes, // + }, }; use crate::regs; @@ -34,7 +50,6 @@ pub(crate) const fn new(version: u16) -> Self { } /// Return the raw protocol version number for the wire format. - #[expect(dead_code)] pub(crate) const fn raw(self) -> u16 { self.0 } @@ -46,6 +61,89 @@ pub(crate) const fn raw(self) -> u16 { /// FSP secure boot completion timeout in milliseconds. const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 5000; +/// GSP FMC initialization parameters. +#[repr(C)] +#[derive(Debug, Clone, Copy, Default)] +struct GspFmcInitParams { + /// CC initialization "registry keys". + regkeys: u32, +} + +// SAFETY: GspFmcInitParams is a simple C struct with only primitive types. +unsafe impl AsBytes for GspFmcInitParams {} +// SAFETY: All bit patterns are valid for the primitive fields. +unsafe impl FromBytes for GspFmcInitParams {} + +/// GSP ACR (Authenticated Code RAM) boot parameters. +#[repr(C)] +#[derive(Debug, Clone, Copy, Default)] +struct GspAcrBootGspRmParams { + /// Physical memory aperture through which gspRmDescPa is accessed. + target: u32, + /// Size in bytes of the GSP-RM descriptor structure. + gsp_rm_desc_size: u32, + /// Physical offset in the target aperture of the GSP-RM descriptor structure. + gsp_rm_desc_offset: u64, + /// Physical offset in FB to set the start of the WPR containing GSP-RM. + wpr_carveout_offset: u64, + /// Size in bytes of the WPR containing GSP-RM. + wpr_carveout_size: u32, + /// Whether to boot GSP-RM or GSP-Proxy through ACR. + b_is_gsp_rm_boot: u32, +} + +// SAFETY: GspAcrBootGspRmParams is a simple C struct with only primitive types. +unsafe impl AsBytes for GspAcrBootGspRmParams {} +// SAFETY: All bit patterns are valid for the primitive fields. +unsafe impl FromBytes for GspAcrBootGspRmParams {} + +/// GSP RM boot parameters. +#[repr(C)] +#[derive(Debug, Clone, Copy, Default)] +struct GspRmParams { + /// Physical memory aperture through which bootArgsOffset is accessed. + target: u32, + /// Physical offset in the memory aperture that will be passed to GSP-RM. + boot_args_offset: u64, +} + +// SAFETY: GspRmParams is a simple C struct with only primitive types. +unsafe impl AsBytes for GspRmParams {} +// SAFETY: All bit patterns are valid for the primitive fields. +unsafe impl FromBytes for GspRmParams {} + +/// GSP SPDM (Security Protocol and Data Model) parameters. +#[repr(C)] +#[derive(Debug, Clone, Copy, Default)] +struct GspSpdmParams { + /// Physical memory aperture through which all addresses are accessed. + target: u32, + /// Physical offset in the memory aperture where SPDM payload buffer is stored. + payload_buffer_offset: u64, + /// Size of the above payload buffer. + payload_buffer_size: u32, +} + +// SAFETY: GspSpdmParams is a simple C struct with only primitive types. +unsafe impl AsBytes for GspSpdmParams {} +// SAFETY: All bit patterns are valid for the primitive fields. +unsafe impl FromBytes for GspSpdmParams {} + +/// Complete GSP FMC boot parameters passed to FSP. +#[repr(C)] +#[derive(Debug, Clone, Copy, Default)] +pub(crate) struct GspFmcBootParams { + init_params: GspFmcInitParams, + boot_gsp_rm_params: GspAcrBootGspRmParams, + gsp_rm_params: GspRmParams, + gsp_spdm_params: GspSpdmParams, +} + +// SAFETY: GspFmcBootParams is composed of C structs with only primitive types. +unsafe impl AsBytes for GspFmcBootParams {} +// SAFETY: All bit patterns are valid for the primitive fields. +unsafe impl FromBytes for GspFmcBootParams {} + /// Size constraints for FSP security signatures (Hopper/Blackwell). const FSP_HASH_SIZE: usize = 48; // SHA-384 hash const FSP_PKEY_SIZE: usize = 384; // RSA-3072 public key @@ -69,6 +167,35 @@ struct NvdmPayloadCommandResponse { error_code: u32, } +/// NVDM (NVIDIA Device Management) COT (Chain of Trust) payload structure. +/// This is the main message payload sent to FSP for Chain of Trust. +#[repr(C, packed)] +#[derive(Clone, Copy)] +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, + hash384: [u8; FSP_HASH_SIZE], + public_key: [u8; FSP_PKEY_SIZE], + signature: [u8; FSP_SIG_SIZE], + gsp_boot_args_sysmem_offset: u64, +} + +/// Complete FSP message structure with MCTP and NVDM headers. +#[repr(C, packed)] +#[derive(Clone, Copy)] +struct FspMessage { + mctp_header: u32, + nvdm_header: u32, + cot: NvdmPayloadCot, +} + +// SAFETY: FspMessage is a packed C struct with only integral fields. +unsafe impl AsBytes for FspMessage {} /// Complete FSP response structure with MCTP and NVDM headers. #[repr(C, packed)] #[derive(Clone, Copy)] @@ -89,6 +216,73 @@ pub(crate) trait MessageToFsp: AsBytes { /// NVDM type identifying this message to FSP. const NVDM_TYPE: u32; } + +impl MessageToFsp for FspMessage { + const NVDM_TYPE: u32 = NvdmType::Cot as u32; +} + +/// Bundled arguments for FMC boot via FSP Chain of Trust. +pub(crate) struct FmcBootArgs<'a> { + chipset: crate::gpu::Chipset, + fmc_image_fw: &'a Coherent<[u8]>, + fmc_boot_params: Coherent<GspFmcBootParams>, + resume: bool, + signatures: &'a FmcSignatures, +} + +impl<'a> FmcBootArgs<'a> { + /// Build FMC boot arguments, allocating the DMA-coherent boot parameter + /// structure that FSP will read. + #[allow(clippy::too_many_arguments)] + pub(crate) fn new( + dev: &device::Device<device::Bound>, + chipset: crate::gpu::Chipset, + fmc_image_fw: &'a Coherent<[u8]>, + wpr_meta_addr: u64, + wpr_meta_size: u32, + libos_addr: u64, + resume: bool, + signatures: &'a FmcSignatures, + ) -> Result<Self> { + // `GSP_DMA_TARGET_*` is not in the current Rust bindings yet. + const GSP_DMA_TARGET_COHERENT_SYSTEM: u32 = 1; + const GSP_DMA_TARGET_NONCOHERENT_SYSTEM: u32 = 2; + + let mut fmc_boot_params = CoherentBox::<GspFmcBootParams>::zeroed(dev, GFP_KERNEL)?; + + // Blackwell FSP expects wpr_carveout_offset and wpr_carveout_size to be zero; + // it obtains WPR info from other sources. + fmc_boot_params.boot_gsp_rm_params = GspAcrBootGspRmParams { + target: GSP_DMA_TARGET_COHERENT_SYSTEM, + gsp_rm_desc_size: wpr_meta_size, + gsp_rm_desc_offset: wpr_meta_addr, + b_is_gsp_rm_boot: 1, + ..Default::default() + }; + + fmc_boot_params.gsp_rm_params = GspRmParams { + target: GSP_DMA_TARGET_NONCOHERENT_SYSTEM, + boot_args_offset: libos_addr, + }; + + let fmc_boot_params: Coherent<GspFmcBootParams> = fmc_boot_params.into(); + + Ok(Self { + chipset, + fmc_image_fw, + fmc_boot_params, + resume, + signatures, + }) + } + + /// DMA address of the FMC boot parameters, needed after boot for lockdown + /// release polling. + #[expect(dead_code)] + pub(crate) fn boot_params_dma_handle(&self) -> u64 { + self.fmc_boot_params.dma_handle() + } +} /// FSP interface for Hopper/Blackwell GPUs. pub(crate) struct Fsp; @@ -188,8 +382,65 @@ pub(crate) fn extract_fmc_signatures( Ok(signatures) } + /// Boot GSP FMC via FSP Chain of Trust. + /// + /// Builds the COT message from the pre-configured [`FmcBootArgs`], sends it + /// to FSP, and waits for the response. + pub(crate) fn boot_fmc( + dev: &device::Device<device::Bound>, + bar: &crate::driver::Bar0, + fsp_falcon: &crate::falcon::Falcon<crate::falcon::fsp::Fsp>, + args: &FmcBootArgs<'_>, + ) -> Result { + dev_dbg!(dev, "Starting FSP boot sequence for {}\n", args.chipset); + + let fmc_addr = args.fmc_image_fw.dma_handle(); + let fmc_boot_params_addr = args.fmc_boot_params.dma_handle(); + + // frts_offset is relative to FB end: FRTS_location = FB_END - frts_offset + let frts_offset = if !args.resume { + let frts_reserved_size = crate::fb::calc_non_wpr_heap_size(args.chipset) + .checked_add(u64::from(crate::fb::calc_pmu_reserved_size(args.chipset))) + .ok_or(EINVAL)?; + + frts_reserved_size + .align_up(Alignment::new::<SZ_2M>()) + .ok_or(EINVAL)? + } else { + 0 + }; + let frts_size: u32 = if !args.resume { u32::SZ_1M } else { 0 }; + + let msg = KBox::new( + FspMessage { + mctp_header: MctpHeader::single_packet().raw(), + nvdm_header: NvdmHeader::new(NvdmType::Cot).raw(), + + cot: NvdmPayloadCot { + version: args.chipset.fsp_cot_version().ok_or(ENOTSUPP)?.raw(), + size: u16::try_from(core::mem::size_of::<NvdmPayloadCot>()) + .map_err(|_| EINVAL)?, + gsp_fmc_sysmem_offset: fmc_addr, + frts_sysmem_offset: 0, + frts_sysmem_size: 0, + frts_vidmem_offset: frts_offset, + frts_vidmem_size: frts_size, + hash384: args.signatures.hash384, + public_key: args.signatures.public_key, + signature: args.signatures.signature, + gsp_boot_args_sysmem_offset: fmc_boot_params_addr, + }, + }, + GFP_KERNEL, + )?; + + Self::send_sync_fsp(dev, bar, fsp_falcon, &*msg)?; + + dev_dbg!(dev, "FSP Chain of Trust completed successfully\n"); + Ok(()) + } + /// Send message to FSP and wait for response. - #[expect(dead_code)] fn send_sync_fsp<M>( dev: &device::Device<device::Bound>, bar: &crate::driver::Bar0, diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 5d7fd810687d..0a05d038ab76 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -141,7 +141,6 @@ pub(crate) const fn needs_fwsec_bootloader(self) -> bool { /// /// Hopper (GH100) uses version 1, Blackwell uses version 2. /// Returns `None` for architectures that do not use FSP. - #[expect(dead_code)] pub(crate) const fn fsp_cot_version(&self) -> Option<FspCotVersion> { match self.arch() { Architecture::Hopper => Some(FspCotVersion::new(1)), diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 739624af1cef..703c9ee48363 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -33,7 +33,10 @@ gsp::GspFirmware, FIRMWARE_VERSION, // }, - fsp::Fsp, + fsp::{ + FmcBootArgs, + Fsp, // + }, gpu::{ Architecture, Chipset, // @@ -203,16 +206,29 @@ fn boot_via_fsp( bar: &Bar0, chipset: Chipset, _gsp_falcon: &Falcon<Gsp>, - _wpr_meta: &Coherent<GspFwWprMeta>, - _libos: &Coherent<[LibosMemoryRegionInitArgument]>, + wpr_meta: &Coherent<GspFwWprMeta>, + libos: &Coherent<[LibosMemoryRegionInitArgument]>, ) -> Result { - let _fsp_falcon = Falcon::<FspEngine>::new(dev, chipset)?; + let fsp_falcon = Falcon::<FspEngine>::new(dev, chipset)?; let fsp_fw = FspFirmware::new(dev, chipset, FIRMWARE_VERSION)?; - let _signatures = Fsp::extract_fmc_signatures(dev, fsp_fw.fmc_elf.data())?; + let signatures = Fsp::extract_fmc_signatures(dev, fsp_fw.fmc_elf.data())?; Fsp::wait_secure_boot(dev, bar, chipset.arch())?; + let args = FmcBootArgs::new( + dev, + chipset, + &fsp_fw.fmc_image, + wpr_meta.dma_handle(), + core::mem::size_of::<GspFwWprMeta>() as u32, + libos.dma_handle(), + false, + &signatures, + )?; + + Fsp::boot_fmc(dev, bar, &fsp_falcon, &args)?; + Err(ENOTSUPP) } diff --git a/drivers/gpu/nova-core/mctp.rs b/drivers/gpu/nova-core/mctp.rs index 680ed19d196e..c23e8ec69636 100644 --- a/drivers/gpu/nova-core/mctp.rs +++ b/drivers/gpu/nova-core/mctp.rs @@ -6,8 +6,6 @@ //! Device Management) messages between the kernel driver and GPU firmware //! processors such as FSP and GSP. -#![expect(dead_code)] - /// NVDM message type identifiers carried over MCTP. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] -- 2.53.0
|  |