[PATCH v4 29/33] gpu: nova-core: Hopper/Blackwell: add GSP lockdown release polling

John Hubbard
2026-02-10T02:47:09Z

On Hopper and Blackwell, FSP boots GSP with hardware lockdown enabled.
After FSP Chain of Trust completes, the driver must poll for lockdown
release before proceeding with GSP initialization. Add the register
bit and helper functions needed for this polling.

Cc: Gary Guo <gary@garyguo.net>
Cc: Timur Tabi <ttabi@nvidia.com>
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 88 ++++++++++++++++++++++++++++++-
drivers/gpu/nova-core/regs.rs | 1 +
2 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 6191986fc6b5..1e8a7306e078 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -15,7 +15,8 @@
falcon::{
gsp::Gsp,
sec2::Sec2,
- Falcon, //
+ Falcon,
+ FalconEngine, //
},
fb::FbLayout,
firmware::{
@@ -154,6 +155,91 @@ fn run_booter(
Ok(())
}

+ /// Check if GSP lockdown has been released after FSP Chain of Trust
+ fn gsp_lockdown_released(
+ dev: &device::Device,
+ gsp_falcon: &Falcon<Gsp>,
+ bar: &Bar0,
+ fmc_boot_params_addr: u64,
+ mbox0: &mut u32,
+ ) -> bool {
+ // Read GSP falcon mailbox0
+ *mbox0 = gsp_falcon.read_mailbox0(bar);
+
+ // Check 1: If mbox0 has 0xbadf4100 pattern, GSP is still locked down
+ if *mbox0 != 0 && (*mbox0 & 0xffffff00) == 0xbadf4100 {
+ return false;
+ }
+
+ // Check 2: If mbox0 has a value, check if it's an error
+ if *mbox0 != 0 {
+ let mbox1 = gsp_falcon.read_mailbox1(bar);
+
+ let combined_addr = (u64::from(mbox1) << 32) | u64::from(*mbox0);
+ if combined_addr != fmc_boot_params_addr {
+ // Address doesn't match - GSP wrote an error code
+ // Return TRUE (lockdown released) with error
+ dev_dbg!(
+ dev,
+ "GSP lockdown error: mbox0={:#x}, combined_addr={:#x}, expected={:#x}\n",
+ *mbox0,
+ combined_addr,
+ fmc_boot_params_addr
+ );
+ return true;
+ }
+ }
+
+ // Check 3: Verify HWCFG2 RISCV_BR_PRIV_LOCKDOWN bit is clear
+ let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &crate::falcon::gsp::Gsp::ID);
+ !hwcfg2.riscv_br_priv_lockdown()
+ }
+
+ /// 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,
+ gsp_falcon: &Falcon<Gsp>,
+ fmc_boot_params_addr: u64,
+ ) -> Result<u32> {
+ dev_dbg!(dev, "Waiting for GSP lockdown release\n");
+
+ let mut mbox0: u32 = 0;
+
+ let (_, mbox0) = read_poll_timeout(
+ || {
+ let released = Self::gsp_lockdown_released(
+ dev,
+ gsp_falcon,
+ bar,
+ fmc_boot_params_addr,
+ &mut mbox0,
+ );
+
+ Ok((released, mbox0))
+ },
+ |(released, _)| *released,
+ Delta::ZERO,
+ Delta::from_millis(4000),
+ )
+ .inspect_err(|_| {
+ dev_err!(dev, "GSP lockdown release timeout\n");
+ })?;
+
+ // Check mbox0 for error after wait completion
+ if mbox0 != 0 {
+ dev_err!(dev, "GSP-FMC boot failed (mbox: {:#x})\n", mbox0);
+ return Err(EIO);
+ }
+
+ dev_dbg!(
+ dev,
+ "GSP hardware lockdown fully released, proceeding with initialization\n"
+ );
+ Ok(mbox0)
+ }
+
/// Attempt to boot the GSP.
///
/// This is a GPU-dependent and complex procedure that involves loading firmware files from
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index dfc30247bca6..01788aa8d1f1 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -321,6 +321,7 @@ pub(crate) fn vga_workspace_addr(self) -> Option<u64> {
register!(NV_PFALCON_FALCON_HWCFG2 @ PFalconBase[0x000000f4] {
10:10 riscv as bool;
12:12 mem_scrubbing as bool, "Set to 0 after memory scrubbing is completed";
+ 13:13 riscv_br_priv_lockdown as bool, "RISC-V branch privilege lockdown bit";
31:31 reset_ready as bool, "Signal indicating that reset is completed (GA102+)";
});

--
2.53.0