Add the FSP-based boot path for Hopper and Blackwell GPUs. Unlike Turing/Ampere/Ada which use SEC2 to load the booter firmware, Hopper and Blackwell use FSP (Firmware System Processor) with FMC firmware to establish a Chain of Trust and boot GSP directly.
The boot() function now dispatches to either run_booter() (SEC2 path) or run_fsp() (FSP path) based on the GPU architecture. The cmdq commands are moved to after GSP boot, and the GSP sequencer is only run for SEC2-based architectures. Cc: Gary Guo <[email protected]> Signed-off-by: John Hubbard <[email protected]> --- drivers/gpu/nova-core/fb.rs | 1 - drivers/gpu/nova-core/firmware/fsp.rs | 2 - drivers/gpu/nova-core/fsp.rs | 6 +- drivers/gpu/nova-core/gsp/boot.rs | 158 ++++++++++++++++++++------ 4 files changed, 123 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs index 7c502f15622c..a860f43ec5af 100644 --- a/drivers/gpu/nova-core/fb.rs +++ b/drivers/gpu/nova-core/fb.rs @@ -180,7 +180,6 @@ pub(crate) struct FbLayout { pub(crate) heap: FbRange, pub(crate) vf_partition_count: u8, /// Total reserved size (heap + PMU reserved), aligned to 2MB. - #[expect(unused)] pub(crate) total_reserved_size: u32, } diff --git a/drivers/gpu/nova-core/firmware/fsp.rs b/drivers/gpu/nova-core/firmware/fsp.rs index edcc173c2fa6..e10954aa146a 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 section) - submitted to hardware pub(crate) fmc_image: DmaObject, @@ -23,7 +22,6 @@ pub(crate) struct FspFirmware { } impl FspFirmware { - #[expect(dead_code)] pub(crate) fn new( dev: &device::Device<device::Bound>, chipset: Chipset, diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs index 6a0bc800abb0..c97d298fd6c8 100644 --- a/drivers/gpu/nova-core/fsp.rs +++ b/drivers/gpu/nova-core/fsp.rs @@ -1,8 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// TODO: remove this once the code is fully functional -#![expect(dead_code)] - //! FSP (Firmware System Processor) interface for Hopper/Blackwell GPUs. //! //! Hopper/Blackwell use a simplified firmware boot sequence: FMC --> FSP --> GSP. @@ -11,6 +8,7 @@ use kernel::{ device, + dma::CoherentAllocation, io::poll::read_poll_timeout, prelude::*, ptr::{ @@ -355,8 +353,6 @@ pub(crate) fn create_fmc_boot_params( wpr_meta_size: u32, libos_addr: u64, ) -> Result<kernel::dma::CoherentAllocation<GspFmcBootParams>> { - use kernel::dma::CoherentAllocation; - const GSP_DMA_TARGET_COHERENT_SYSTEM: u32 = 1; const GSP_DMA_TARGET_NONCOHERENT_SYSTEM: u32 = 2; diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 178e319d48e1..5284a35dcc2c 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -13,6 +13,7 @@ use crate::{ driver::Bar0, falcon::{ + fsp::Fsp as FspEngine, gsp::Gsp, sec2::Sec2, Falcon, @@ -24,6 +25,7 @@ BooterFirmware, BooterKind, // }, + fsp::FspFirmware, fwsec::{ FwsecCommand, FwsecFirmware, // @@ -31,9 +33,14 @@ gsp::GspFirmware, FIRMWARE_VERSION, // }, - gpu::Chipset, + fsp::Fsp, + gpu::{ + Architecture, + Chipset, // + }, gsp::{ commands, + fw::LibosMemoryRegionInitArgument, sequencer::{ GspSequencer, GspSequencerParams, // @@ -155,6 +162,55 @@ fn run_booter( Ok(()) } + fn run_fsp( + dev: &device::Device<device::Bound>, + bar: &Bar0, + chipset: Chipset, + gsp_falcon: &Falcon<Gsp>, + wpr_meta: &CoherentAllocation<GspFwWprMeta>, + libos: &CoherentAllocation<LibosMemoryRegionInitArgument>, + fb_layout: &FbLayout, + ) -> Result { + let fsp_falcon = Falcon::<FspEngine>::new(dev, chipset)?; + + Fsp::wait_secure_boot(dev, bar, chipset.arch())?; + + let fsp_fw = FspFirmware::new(dev, chipset, FIRMWARE_VERSION)?; + + // fmc_full is a KVec<u8> for CPU-side signature extraction only. + // A separate buffer, fsp_fw.fmc_image, is what gets submitted to the hardware. + let signatures = Fsp::extract_fmc_signatures_static(dev, &fsp_fw.fmc_full)?; + + // Create FMC boot parameters + let fmc_boot_params = Fsp::create_fmc_boot_params( + dev, + wpr_meta.dma_handle(), + core::mem::size_of::<GspFwWprMeta>() as u32, + libos.dma_handle(), + )?; + + // Execute FSP Chain of Trust + // NOTE: FSP Chain of Trust handles GSP boot internally - we do NOT reset or boot GSP + Fsp::boot_gsp_fmc_with_signatures( + dev, + bar, + chipset, + &fsp_fw.fmc_image, + &fmc_boot_params, + u64::from(fb_layout.total_reserved_size), + false, // not resuming + &fsp_falcon, + &signatures, + )?; + + // Wait for GSP lockdown to be released + let fmc_boot_params_addr = fmc_boot_params.dma_handle(); + let _mbox0 = + Self::wait_for_gsp_lockdown_release(dev, bar, gsp_falcon, fmc_boot_params_addr)?; + + Ok(()) + } + /// Check if GSP lockdown has been released after FSP Chain of Trust fn gsp_lockdown_released( dev: &device::Device, @@ -196,7 +252,6 @@ fn gsp_lockdown_released( } /// Wait for GSP lockdown to be released after FSP Chain of Trust - #[expect(dead_code)] fn wait_for_gsp_lockdown_release( dev: &device::Device, bar: &Bar0, @@ -257,43 +312,63 @@ pub(crate) fn boot( ) -> Result { let dev = pdev.as_ref(); - let bios = Vbios::new(dev, bar)?; - let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?; let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?; dev_dbg!(dev, "{:#x?}\n", fb_layout); - Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?; + if matches!( + chipset.arch(), + Architecture::Turing | Architecture::Ampere | Architecture::Ada + ) { + let bios = Vbios::new(dev, bar)?; + Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?; + } let wpr_meta = CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?; dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?; - self.cmdq - .send_command(bar, commands::SetSystemInfo::new(pdev))?; - self.cmdq.send_command(bar, commands::SetRegistry::new())?; + // For SEC2-based architectures, reset GSP and boot it before SEC2 + if matches!( + chipset.arch(), + Architecture::Turing | Architecture::Ampere | Architecture::Ada + ) { + gsp_falcon.reset(bar)?; + let libos_handle = self.libos.dma_handle(); + let (mbox0, mbox1) = gsp_falcon.boot( + bar, + Some(libos_handle as u32), + Some((libos_handle >> 32) as u32), + )?; + dev_dbg!( + pdev.as_ref(), + "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", + mbox0, + mbox1 + ); - gsp_falcon.reset(bar)?; - let libos_handle = self.libos.dma_handle(); - let (mbox0, mbox1) = gsp_falcon.boot( - bar, - Some(libos_handle as u32), - Some((libos_handle >> 32) as u32), - )?; - dev_dbg!( - pdev.as_ref(), - "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", - mbox0, - mbox1 - ); + dev_dbg!( + pdev.as_ref(), + "Using SEC2 to load and run the booter_load firmware...\n" + ); + } - dev_dbg!( - pdev.as_ref(), - "Using SEC2 to load and run the booter_load firmware...\n" - ); + match chipset.arch() { + Architecture::Turing | Architecture::Ampere | Architecture::Ada => { + Self::run_booter(dev, bar, chipset, sec2_falcon, &wpr_meta)? + } - Self::run_booter(dev, bar, chipset, sec2_falcon, &wpr_meta)?; + Architecture::Hopper | Architecture::Blackwell => Self::run_fsp( + dev, + bar, + chipset, + gsp_falcon, + &wpr_meta, + &self.libos, + &fb_layout, + )?, + } gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version); @@ -311,16 +386,27 @@ pub(crate) fn boot( gsp_falcon.is_riscv_active(bar), ); - // Create and run the GSP sequencer. - let seq_params = GspSequencerParams { - bootloader_app_version: gsp_fw.bootloader.app_version, - libos_dma_handle: libos_handle, - gsp_falcon, - sec2_falcon, - dev: pdev.as_ref().into(), - bar, - }; - GspSequencer::run(&mut self.cmdq, seq_params)?; + // Now that GSP is active, send system info and registry + self.cmdq + .send_command(bar, commands::SetSystemInfo::new(pdev))?; + self.cmdq.send_command(bar, commands::SetRegistry::new())?; + + if matches!( + chipset.arch(), + Architecture::Turing | Architecture::Ampere | Architecture::Ada + ) { + let libos_handle = self.libos.dma_handle(); + // Create and run the GSP sequencer. + let seq_params = GspSequencerParams { + bootloader_app_version: gsp_fw.bootloader.app_version, + libos_dma_handle: libos_handle, + gsp_falcon, + sec2_falcon, + dev: pdev.as_ref().into(), + bar, + }; + GspSequencer::run(&mut self.cmdq, seq_params)?; + } // Wait until GSP is fully initialized. commands::wait_gsp_init_done(&mut self.cmdq)?; -- 2.52.0
