// SPDX-License-Identifier: GPL-2.0 use kernel::device; use kernel::pci; use kernel::prelude::*; use crate::driver::Bar0; use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon}; use crate::fb::FbLayout; use crate::firmware::{ booter::{BooterFirmware, BooterKind}, fwsec::{FwsecCommand, FwsecFirmware}, gsp::GspFirmware, FIRMWARE_VERSION, }; use crate::gpu::Chipset; use crate::regs; use crate::vbios::Vbios; impl super::Gsp { /// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly /// created the WPR2 region. fn run_fwsec_frts( dev: &device::Device, falcon: &Falcon, bar: &Bar0, bios: &Vbios, fb_layout: &FbLayout, ) -> Result<()> { // Check that the WPR2 region does not already exists - if it does, we cannot run // FWSEC-FRTS until the GPU is reset. if regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound() != 0 { dev_err!( dev, "WPR2 region already exists - GPU needs to be reset to proceed\n" ); return Err(EBUSY); } let fwsec_frts = FwsecFirmware::new( dev, falcon, bar, bios, FwsecCommand::Frts { frts_addr: fb_layout.frts.start, frts_size: fb_layout.frts.end - fb_layout.frts.start, }, )?; // Run FWSEC-FRTS to create the WPR2 region. fwsec_frts.run(dev, falcon, bar)?; // SCRATCH_E contains the error code for FWSEC-FRTS. let frts_status = regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code(); if frts_status != 0 { dev_err!( dev, "FWSEC-FRTS returned with error code {:#x}", frts_status ); return Err(EIO); } // Check that the WPR2 region has been created as we requested. let (wpr2_lo, wpr2_hi) = ( regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO::read(bar).lower_bound(), regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound(), ); match (wpr2_lo, wpr2_hi) { (_, 0) => { dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n"); Err(EIO) } (wpr2_lo, _) if wpr2_lo != fb_layout.frts.start => { dev_err!( dev, "WPR2 region created at unexpected address {:#x}; expected {:#x}\n", wpr2_lo, fb_layout.frts.start, ); Err(EIO) } (wpr2_lo, wpr2_hi) => { dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_lo, wpr2_hi); dev_dbg!(dev, "GPU instance built\n"); Ok(()) } } } /// Attempt to boot the GSP. /// /// This is a GPU-dependent and complex procedure that involves loading firmware files from /// user-space, patching them with signatures, and building firmware-specific intricate data /// structures that the GSP will use at runtime. /// /// Upon return, the GSP is up and running, and its runtime object given as return value. pub(crate) fn boot( self: Pin<&mut Self>, pdev: &pci::Device, bar: &Bar0, chipset: Chipset, gsp_falcon: &Falcon, sec2_falcon: &Falcon, ) -> 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)?; dev_dbg!(dev, "{:#x?}\n", fb_layout); Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?; let _booter_loader = BooterFirmware::new( dev, BooterKind::Loader, chipset, FIRMWARE_VERSION, sec2_falcon, bar, )?; Ok(()) } }