| // SPDX-License-Identifier: GPL-2.0 |
| |
| use kernel::{ |
| device, |
| dma::CoherentAllocation, |
| dma_write, |
| io::poll::read_poll_timeout, |
| pci, |
| prelude::*, |
| time::Delta, // |
| }; |
| |
| use crate::{ |
| driver::Bar0, |
| falcon::{ |
| gsp::Gsp, |
| sec2::Sec2, |
| Falcon, // |
| }, |
| fb::FbLayout, |
| firmware::{ |
| booter::{ |
| BooterFirmware, |
| BooterKind, // |
| }, |
| fwsec::{ |
| 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<device::Bound>, |
| falcon: &Falcon<Gsp>, |
| 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( |
| mut self: Pin<&mut Self>, |
| pdev: &pci::Device<device::Bound>, |
| bar: &Bar0, |
| chipset: Chipset, |
| gsp_falcon: &Falcon<Gsp>, |
| sec2_falcon: &Falcon<Sec2>, |
| ) -> 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, gsp_falcon, bar, &bios, &fb_layout)?; |
| |
| let booter_loader = BooterFirmware::new( |
| dev, |
| BooterKind::Loader, |
| chipset, |
| FIRMWARE_VERSION, |
| sec2_falcon, |
| bar, |
| )?; |
| |
| let wpr_meta = |
| CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?; |
| dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?; |
| |
| self.cmdq |
| .send_command(bar, commands::SetSystemInfo::new(pdev))?; |
| self.cmdq.send_command(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.as_ref(), |
| "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", |
| mbox0, |
| mbox1 |
| ); |
| |
| dev_dbg!( |
| pdev.as_ref(), |
| "Using SEC2 to load and run the booter_load firmware...\n" |
| ); |
| |
| sec2_falcon.reset(bar)?; |
| sec2_falcon.dma_load(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.as_ref(), |
| "SEC2 MBOX0: {:#x}, MBOX1{:#x}\n", |
| mbox0, |
| mbox1 |
| ); |
| |
| if mbox0 != 0 { |
| dev_err!( |
| pdev.as_ref(), |
| "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.as_ref(), |
| "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(&mut self.cmdq, seq_params)?; |
| |
| // Wait until GSP is fully initialized. |
| commands::wait_gsp_init_done(&mut self.cmdq)?; |
| |
| // Obtain and display basic GPU information. |
| let info = commands::get_gsp_info(&mut self.cmdq, bar)?; |
| dev_info!( |
| pdev.as_ref(), |
| "GPU name: {}\n", |
| info.gpu_name().unwrap_or("invalid GPU name") |
| ); |
| |
| Ok(()) |
| } |
| } |