]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
gpu: nova-core: Hopper/Blackwell: add GSP lockdown release polling
authorJohn Hubbard <jhubbard@nvidia.com>
Wed, 3 Jun 2026 07:30:24 +0000 (16:30 +0900)
committerAlexandre Courbot <acourbot@nvidia.com>
Wed, 3 Jun 2026 14:52:42 +0000 (23:52 +0900)
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.

Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Link: https://patch.msgid.link/20260603-b4-blackwell-v13-7-d9f3a06939e0@nvidia.com
[acourbot: fix `lockdown_released` logic and add explanatory comments.]
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
drivers/gpu/nova-core/falcon/gsp.rs
drivers/gpu/nova-core/fsp.rs
drivers/gpu/nova-core/gsp/hal/gh100.rs
drivers/gpu/nova-core/regs.rs

index df6d5a382c7a54142635c0c7aa1532182c1dce08..98a1c1dc8465c08ce6ec08a8dfaa26b930a1a050 100644 (file)
@@ -24,6 +24,10 @@ use crate::{
     regs,
 };
 
+/// Pattern returned by GSP register reads while the PRIV target mask still blocks CPU access.
+const GSP_TARGET_MASK_LOCKED_PATTERN: u32 = 0xbadf_4100;
+const GSP_TARGET_MASK_LOCKED_MASK: u32 = 0xffff_ff00;
+
 /// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
 pub(crate) struct Gsp(());
 
@@ -57,4 +61,19 @@ impl Falcon<Gsp> {
         )
         .map(|_| true)
     }
+
+    /// Returns whether the RISC-V branch privilege lockdown bit is set.
+    pub(crate) fn riscv_branch_privilege_lockdown(&self, bar: &Bar0) -> bool {
+        bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<Gsp>())
+            .riscv_br_priv_lockdown()
+    }
+
+    /// Returns whether GSP registers can be read by the CPU.
+    pub(crate) fn priv_target_mask_released(&self, bar: &Bar0) -> bool {
+        let hwcfg2 = bar
+            .read(regs::NV_PFALCON_FALCON_HWCFG2::of::<Gsp>())
+            .into_raw();
+
+        hwcfg2 != 0 && (hwcfg2 & GSP_TARGET_MASK_LOCKED_MASK) != GSP_TARGET_MASK_LOCKED_PATTERN
+    }
 }
index 6eb5c09b335211215955995f751f2d55288f7e33..2fe5a5e6dd8e9999c62b3817524b9e513d45b328 100644 (file)
@@ -186,6 +186,12 @@ impl FmcBootArgs {
             resume,
         })
     }
+
+    /// DMA address of the FMC boot parameters, needed after boot for lockdown
+    /// release polling.
+    pub(crate) fn boot_params_dma_handle(&self) -> u64 {
+        self.fmc_boot_params.dma_handle()
+    }
 }
 
 /// FSP interface for Hopper/Blackwell GPUs.
index f41f3fea15ff220c647ace3f833e757b1c23e099..57e31ef4819db820148dd1c198e2ea9621e854aa 100644 (file)
@@ -5,7 +5,9 @@ use kernel::prelude::*;
 
 use kernel::{
     device,
-    dma::Coherent, //
+    dma::Coherent,
+    io::poll::read_poll_timeout,
+    time::Delta, //
 };
 
 use crate::{
@@ -33,6 +35,88 @@ use crate::{
     },
 };
 
+/// GSP falcon mailbox state, used to track lockdown release status.
+struct GspMbox {
+    mbox0: u32,
+    mbox1: u32,
+}
+
+impl GspMbox {
+    /// Reads both mailboxes from the GSP falcon.
+    fn read(gsp_falcon: &Falcon<GspEngine>, bar: &Bar0) -> Self {
+        Self {
+            mbox0: gsp_falcon.read_mailbox0(bar),
+            mbox1: gsp_falcon.read_mailbox1(bar),
+        }
+    }
+
+    /// Combines mailbox0 and mailbox1 into a 64-bit address.
+    fn combined_addr(&self) -> u64 {
+        (u64::from(self.mbox1) << 32) | u64::from(self.mbox0)
+    }
+
+    /// Returns `true` if GSP lockdown has been released or a GSP-FMC error happened.
+    ///
+    /// Returns `true` both on successful lockdown release and on GSP-FMC-reported errors, since
+    /// either condition should stop the poll loop.
+    fn lockdown_released_or_error(
+        &self,
+        gsp_falcon: &Falcon<GspEngine>,
+        bar: &Bar0,
+        fmc_boot_params_addr: u64,
+    ) -> bool {
+        // GSP-FMC normally clears the boot parameters address from the mailboxes early during
+        // boot. If the address is still there, keep polling rather than treating it as an error.
+        // Any other non-zero mailbox0 value is a GSP-FMC error code.
+        if self.mbox0 != 0 {
+            return self.combined_addr() != fmc_boot_params_addr;
+        }
+
+        !gsp_falcon.riscv_branch_privilege_lockdown(bar)
+    }
+}
+
+/// Waits for GSP lockdown to be released after FSP Chain of Trust.
+fn wait_for_gsp_lockdown_release(
+    dev: &device::Device<device::Bound>,
+    bar: &Bar0,
+    gsp_falcon: &Falcon<GspEngine>,
+    fmc_boot_params_addr: u64,
+) -> Result {
+    dev_dbg!(dev, "Waiting for GSP lockdown release\n");
+
+    let mbox = read_poll_timeout(
+        || {
+            // While the PRIV target mask is still locked to FSP, GSP register and mailbox reads
+            // are not meaningful. Wait until HWCFG2 says the CPU can read them.
+            Ok(match gsp_falcon.priv_target_mask_released(bar) {
+                false => None,
+                true => Some(GspMbox::read(gsp_falcon, bar)),
+            })
+        },
+        |mbox| match mbox {
+            None => false,
+            Some(mbox) => mbox.lockdown_released_or_error(gsp_falcon, bar, fmc_boot_params_addr),
+        },
+        Delta::from_millis(10),
+        Delta::from_secs(30),
+    )
+    .inspect_err(|_| {
+        dev_err!(dev, "GSP lockdown release timeout\n");
+    })?
+    .ok_or(EIO)?;
+
+    // If polling stopped with a non-zero mailbox0, it was not the boot parameters address
+    // anymore and therefore represents a GSP-FMC error code.
+    if mbox.mbox0 != 0 {
+        dev_err!(dev, "GSP-FMC boot failed (mbox: {:#x})\n", mbox.mbox0);
+        return Err(EIO);
+    }
+
+    dev_dbg!(dev, "GSP lockdown released\n");
+    Ok(())
+}
+
 struct Gh100;
 
 impl GspHal for Gh100 {
@@ -48,7 +132,7 @@ impl GspHal for Gh100 {
         chipset: Chipset,
         fb_layout: &FbLayout,
         wpr_meta: &Coherent<GspFwWprMeta>,
-        _gsp_falcon: &'a Falcon<GspEngine>,
+        gsp_falcon: &'a Falcon<GspEngine>,
         _sec2_falcon: &'a Falcon<Sec2>,
     ) -> Result<BootUnloadGuard<'a>> {
         let fsp_fw = FspFirmware::new(dev, chipset, FIRMWARE_VERSION)?;
@@ -64,6 +148,8 @@ impl GspHal for Gh100 {
 
         fsp.boot_fmc(dev, bar, fb_layout, &args)?;
 
+        wait_for_gsp_lockdown_release(dev, bar, gsp_falcon, args.boot_params_dma_handle())?;
+
         Err(ENOTSUPP)
     }
 }
index ce2392ef2f8b4e606c40705a0c2afa1504503c99..cc24ab10b922c6ea487f0c40e436ccfc9fa652fd 100644 (file)
@@ -363,6 +363,8 @@ register! {
     pub(crate) NV_PFALCON_FALCON_HWCFG2(u32) @ PFalconBase + 0x000000f4 {
         /// Signal indicating that reset is completed (GA102+).
         31:31   reset_ready => bool;
+        /// RISC-V branch privilege lockdown bit.
+        13:13   riscv_br_priv_lockdown => bool;
         /// Set to 0 after memory scrubbing is completed.
         12:12   mem_scrubbing => bool;
         10:10   riscv => bool;