// SPDX-License-Identifier: GPL-2.0 use kernel::{ device, dma::Coherent, io::poll::read_poll_timeout, io::Io, pci, prelude::*, time::Delta, // }; use crate::{ driver::Bar0, falcon::{ gsp::Gsp, sec2::Sec2, Falcon, // }, fb::FbLayout, firmware::{ booter::{ BooterFirmware, BooterKind, // }, fwsec::{ bootloader::FwsecFirmwareWithBl, FwsecCommand, FwsecFirmware, // }, gsp::GspFirmware, FIRMWARE_VERSION, // }, gpu::Chipset, gsp::{ commands, sequencer::{ GspSequencer, GspSequencerParams, // }, GspFwWprMeta, // }, regs, 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, chipset: Chipset, 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 bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound() != 0 { dev_err!( dev, "WPR2 region already exists - GPU needs to be reset to proceed\n" ); return Err(EBUSY); } // FWSEC-FRTS will create the WPR2 region. let fwsec_frts = FwsecFirmware::new( dev, falcon, bar, bios, FwsecCommand::Frts { frts_addr: fb_layout.frts.start, frts_size: fb_layout.frts.len(), }, )?; if chipset.needs_fwsec_bootloader() { let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?; // Load and run the bootloader, which will load FWSEC-FRTS and run it. fwsec_frts_bl.run(dev, falcon, bar)?; } else { // Load and run FWSEC-FRTS directly. fwsec_frts.run(dev, falcon, bar)?; } // SCRATCH_E contains the error code for FWSEC-FRTS. let frts_status = bar .read(regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR) .frts_err_code(); if frts_status != 0 { dev_err!( dev, "FWSEC-FRTS returned with error code {:#x}\n", frts_status ); return Err(EIO); } // Check that the WPR2 region has been created as we requested. let (wpr2_lo, wpr2_hi) = ( bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO).lower_bound(), bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).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, &gsp_fw)?; dev_dbg!(dev, "{:#x?}\n", fb_layout); Self::run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_layout)?; let booter_loader = BooterFirmware::new( dev, BooterKind::Loader, chipset, FIRMWARE_VERSION, sec2_falcon, bar, )?; let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?; self.cmdq .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev))?; self.cmdq .send_command_no_wait(bar, commands::SetRegistry::new())?; gsp_falcon.reset(bar)?; let libos_handle = self.libos.dma_handle(); let (mbox0, mbox1) = gsp_falcon.boot( bar, Some(libos_handle as u32), Some((libos_handle >> 32) as u32), )?; dev_dbg!(pdev, "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1); dev_dbg!( pdev, "Using SEC2 to load and run the booter_load firmware...\n" ); sec2_falcon.reset(bar)?; sec2_falcon.load(dev, bar, &booter_loader)?; let wpr_handle = wpr_meta.dma_handle(); let (mbox0, mbox1) = sec2_falcon.boot( bar, Some(wpr_handle as u32), Some((wpr_handle >> 32) as u32), )?; dev_dbg!(pdev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1); if mbox0 != 0 { dev_err!(pdev, "Booter-load failed with error {:#x}\n", mbox0); return Err(ENODEV); } gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version); // Poll for RISC-V to become active before running sequencer read_poll_timeout( || Ok(gsp_falcon.is_riscv_active(bar)), |val: &bool| *val, Delta::from_millis(10), Delta::from_secs(5), )?; dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(bar),); // Create and run the GSP sequencer. let seq_params = GspSequencerParams { bootloader_app_version: gsp_fw.bootloader.app_version, libos_dma_handle: libos_handle, gsp_falcon, sec2_falcon, dev: pdev.as_ref().into(), bar, }; GspSequencer::run(&self.cmdq, seq_params)?; // Wait until GSP is fully initialized. commands::wait_gsp_init_done(&self.cmdq)?; // Obtain and display basic GPU information. let info = commands::get_gsp_info(&self.cmdq, bar)?; match info.gpu_name() { Ok(name) => dev_info!(pdev, "GPU name: {}\n", name), Err(e) => dev_warn!(pdev, "GPU name unavailable: {:?}\n", e), } Ok(()) } }