1 // SPDX-License-Identifier: GPL-2.0 2 3 use kernel::{ 4 device, 5 dma::CoherentAllocation, 6 dma_write, 7 io::poll::read_poll_timeout, 8 pci, 9 prelude::*, 10 time::Delta, // 11 }; 12 13 use crate::{ 14 driver::Bar0, 15 falcon::{ 16 gsp::Gsp, 17 sec2::Sec2, 18 Falcon, // 19 }, 20 fb::FbLayout, 21 firmware::{ 22 booter::{ 23 BooterFirmware, 24 BooterKind, // 25 }, 26 fwsec::{ 27 bootloader::FwsecFirmwareWithBl, 28 FwsecCommand, 29 FwsecFirmware, // 30 }, 31 gsp::GspFirmware, 32 FIRMWARE_VERSION, // 33 }, 34 gpu::Chipset, 35 gsp::{ 36 commands, 37 sequencer::{ 38 GspSequencer, 39 GspSequencerParams, // 40 }, 41 GspFwWprMeta, // 42 }, 43 regs, 44 vbios::Vbios, 45 }; 46 47 impl super::Gsp { 48 /// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly 49 /// created the WPR2 region. 50 fn run_fwsec_frts( 51 dev: &device::Device<device::Bound>, 52 chipset: Chipset, 53 falcon: &Falcon<Gsp>, 54 bar: &Bar0, 55 bios: &Vbios, 56 fb_layout: &FbLayout, 57 ) -> Result<()> { 58 // Check that the WPR2 region does not already exists - if it does, we cannot run 59 // FWSEC-FRTS until the GPU is reset. 60 if regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound() != 0 { 61 dev_err!( 62 dev, 63 "WPR2 region already exists - GPU needs to be reset to proceed\n" 64 ); 65 return Err(EBUSY); 66 } 67 68 // FWSEC-FRTS will create the WPR2 region. 69 let fwsec_frts = FwsecFirmware::new( 70 dev, 71 falcon, 72 bar, 73 bios, 74 FwsecCommand::Frts { 75 frts_addr: fb_layout.frts.start, 76 frts_size: fb_layout.frts.end - fb_layout.frts.start, 77 }, 78 )?; 79 80 if chipset.needs_fwsec_bootloader() { 81 let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?; 82 // Load and run the bootloader, which will load FWSEC-FRTS and run it. 83 fwsec_frts_bl.run(dev, falcon, bar)?; 84 } else { 85 // Load and run FWSEC-FRTS directly. 86 fwsec_frts.run(dev, falcon, bar)?; 87 } 88 89 // SCRATCH_E contains the error code for FWSEC-FRTS. 90 let frts_status = regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code(); 91 if frts_status != 0 { 92 dev_err!( 93 dev, 94 "FWSEC-FRTS returned with error code {:#x}\n", 95 frts_status 96 ); 97 98 return Err(EIO); 99 } 100 101 // Check that the WPR2 region has been created as we requested. 102 let (wpr2_lo, wpr2_hi) = ( 103 regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO::read(bar).lower_bound(), 104 regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound(), 105 ); 106 107 match (wpr2_lo, wpr2_hi) { 108 (_, 0) => { 109 dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n"); 110 111 Err(EIO) 112 } 113 (wpr2_lo, _) if wpr2_lo != fb_layout.frts.start => { 114 dev_err!( 115 dev, 116 "WPR2 region created at unexpected address {:#x}; expected {:#x}\n", 117 wpr2_lo, 118 fb_layout.frts.start, 119 ); 120 121 Err(EIO) 122 } 123 (wpr2_lo, wpr2_hi) => { 124 dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_lo, wpr2_hi); 125 dev_dbg!(dev, "GPU instance built\n"); 126 127 Ok(()) 128 } 129 } 130 } 131 132 /// Attempt to boot the GSP. 133 /// 134 /// This is a GPU-dependent and complex procedure that involves loading firmware files from 135 /// user-space, patching them with signatures, and building firmware-specific intricate data 136 /// structures that the GSP will use at runtime. 137 /// 138 /// Upon return, the GSP is up and running, and its runtime object given as return value. 139 pub(crate) fn boot( 140 mut self: Pin<&mut Self>, 141 pdev: &pci::Device<device::Bound>, 142 bar: &Bar0, 143 chipset: Chipset, 144 gsp_falcon: &Falcon<Gsp>, 145 sec2_falcon: &Falcon<Sec2>, 146 ) -> Result { 147 let dev = pdev.as_ref(); 148 149 let bios = Vbios::new(dev, bar)?; 150 151 let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?; 152 153 let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?; 154 dev_dbg!(dev, "{:#x?}\n", fb_layout); 155 156 Self::run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_layout)?; 157 158 let booter_loader = BooterFirmware::new( 159 dev, 160 BooterKind::Loader, 161 chipset, 162 FIRMWARE_VERSION, 163 sec2_falcon, 164 bar, 165 )?; 166 167 let wpr_meta = 168 CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?; 169 dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?; 170 171 self.cmdq 172 .send_command(bar, commands::SetSystemInfo::new(pdev))?; 173 self.cmdq.send_command(bar, commands::SetRegistry::new())?; 174 175 gsp_falcon.reset(bar)?; 176 let libos_handle = self.libos.dma_handle(); 177 let (mbox0, mbox1) = gsp_falcon.boot( 178 bar, 179 Some(libos_handle as u32), 180 Some((libos_handle >> 32) as u32), 181 )?; 182 dev_dbg!(pdev, "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1); 183 184 dev_dbg!( 185 pdev, 186 "Using SEC2 to load and run the booter_load firmware...\n" 187 ); 188 189 sec2_falcon.reset(bar)?; 190 sec2_falcon.load(dev, bar, &booter_loader)?; 191 let wpr_handle = wpr_meta.dma_handle(); 192 let (mbox0, mbox1) = sec2_falcon.boot( 193 bar, 194 Some(wpr_handle as u32), 195 Some((wpr_handle >> 32) as u32), 196 )?; 197 dev_dbg!(pdev, "SEC2 MBOX0: {:#x}, MBOX1{:#x}\n", mbox0, mbox1); 198 199 if mbox0 != 0 { 200 dev_err!(pdev, "Booter-load failed with error {:#x}\n", mbox0); 201 return Err(ENODEV); 202 } 203 204 gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version); 205 206 // Poll for RISC-V to become active before running sequencer 207 read_poll_timeout( 208 || Ok(gsp_falcon.is_riscv_active(bar)), 209 |val: &bool| *val, 210 Delta::from_millis(10), 211 Delta::from_secs(5), 212 )?; 213 214 dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(bar),); 215 216 // Create and run the GSP sequencer. 217 let seq_params = GspSequencerParams { 218 bootloader_app_version: gsp_fw.bootloader.app_version, 219 libos_dma_handle: libos_handle, 220 gsp_falcon, 221 sec2_falcon, 222 dev: pdev.as_ref().into(), 223 bar, 224 }; 225 GspSequencer::run(&mut self.cmdq, seq_params)?; 226 227 // Wait until GSP is fully initialized. 228 commands::wait_gsp_init_done(&mut self.cmdq)?; 229 230 // Obtain and display basic GPU information. 231 let info = commands::get_gsp_info(&mut self.cmdq, bar)?; 232 match info.gpu_name() { 233 Ok(name) => dev_info!(pdev, "GPU name: {}\n", name), 234 Err(e) => dev_warn!(pdev, "GPU name unavailable: {:?}\n", e), 235 } 236 237 Ok(()) 238 } 239 } 240