gpu: nova-core: Hopper/Blackwell: add GSP lockdown release polling

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>
This commit is contained in:
John Hubbard 2026-06-03 16:30:24 +09:00 committed by Alexandre Courbot
parent d317e4585f
commit a69a9e23dc
4 changed files with 115 additions and 2 deletions

View File

@ -24,6 +24,10 @@
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 @@ pub(crate) fn check_reload_completed(&self, bar: &Bar0, timeout: Delta) -> Resul
)
.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
}
}

View File

@ -186,6 +186,12 @@ pub(crate) fn new(
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.

View File

@ -5,7 +5,9 @@
use kernel::{
device,
dma::Coherent, //
dma::Coherent,
io::poll::read_poll_timeout,
time::Delta, //
};
use crate::{
@ -33,6 +35,88 @@
},
};
/// 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 @@ fn boot<'a>(
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 @@ fn boot<'a>(
fsp.boot_fmc(dev, bar, fb_layout, &args)?;
wait_for_gsp_lockdown_release(dev, bar, gsp_falcon, args.boot_params_dma_handle())?;
Err(ENOTSUPP)
}
}

View File

@ -363,6 +363,8 @@ pub(crate) fn vga_workspace_addr(self) -> Option<u64> {
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;