diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs index df6d5a382c7a..98a1c1dc8465 100644 --- a/drivers/gpu/nova-core/falcon/gsp.rs +++ b/drivers/gpu/nova-core/falcon/gsp.rs @@ -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::()) + .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::()) + .into_raw(); + + hwcfg2 != 0 && (hwcfg2 & GSP_TARGET_MASK_LOCKED_MASK) != GSP_TARGET_MASK_LOCKED_PATTERN + } } diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs index 6eb5c09b3352..2fe5a5e6dd8e 100644 --- a/drivers/gpu/nova-core/fsp.rs +++ b/drivers/gpu/nova-core/fsp.rs @@ -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. diff --git a/drivers/gpu/nova-core/gsp/hal/gh100.rs b/drivers/gpu/nova-core/gsp/hal/gh100.rs index f41f3fea15ff..57e31ef4819d 100644 --- a/drivers/gpu/nova-core/gsp/hal/gh100.rs +++ b/drivers/gpu/nova-core/gsp/hal/gh100.rs @@ -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, 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, + 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, + bar: &Bar0, + gsp_falcon: &Falcon, + 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, - _gsp_falcon: &'a Falcon, + gsp_falcon: &'a Falcon, _sec2_falcon: &'a Falcon, ) -> Result> { 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) } } diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index ce2392ef2f8b..cc24ab10b922 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -363,6 +363,8 @@ pub(crate) fn vga_workspace_addr(self) -> Option { 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;