1 // SPDX-License-Identifier: GPL-2.0 2 // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 4 use kernel::prelude::*; 5 6 use kernel::{ 7 device, 8 dma::Coherent, 9 io::Io, 10 types::ScopeGuard, // 11 }; 12 13 use crate::{ 14 driver::Bar0, 15 falcon::{ 16 gsp::Gsp as GspEngine, 17 sec2::Sec2, 18 Falcon, // 19 }, 20 fb::{ 21 wpr2_range, 22 FbRanges, // 23 }, 24 firmware::{ 25 booter::{ 26 BooterFirmware, 27 BooterKind, // 28 }, 29 fwsec::{ 30 bootloader::FwsecFirmwareWithBl, 31 FwsecCommand, 32 FwsecFirmware, // 33 }, 34 gsp::GspFirmware, // 35 }, 36 gpu::Chipset, 37 gsp::{ 38 hal::{ 39 GspHal, 40 UnloadBundle, // 41 }, 42 regs, 43 sequencer::GspSequencer, 44 Gsp, 45 GspBootContext, 46 GspFwWprMeta, // 47 }, 48 vbios::Vbios, // 49 }; 50 51 // A ready-to-run FWSEC unload firmware. 52 // 53 // Since there are two variants of the prepared firmware (with and without a bootloader), this type 54 // abstracts the difference. 55 enum FwsecUnloadFirmware { 56 WithoutBl(FwsecFirmware), 57 WithBl(FwsecFirmwareWithBl), 58 } 59 60 impl FwsecUnloadFirmware { 61 /// Runs the FWSEC SB firmware. 62 fn run( 63 &self, 64 dev: &device::Device<device::Bound>, 65 bar: Bar0<'_>, 66 gsp_falcon: &Falcon<'_, GspEngine>, 67 ) -> Result { 68 match self { 69 Self::WithoutBl(fw) => fw.run(dev, gsp_falcon), 70 Self::WithBl(fw) => fw.run(dev, gsp_falcon, bar), 71 } 72 } 73 } 74 75 // Contains the firmware required to fully reset GSP on chipsets where the GSP is started using 76 // FWSEC/Booter. 77 struct Sec2UnloadBundle { 78 fwsec_sb: FwsecUnloadFirmware, 79 booter_unloader: BooterFirmware, 80 } 81 82 impl UnloadBundle for Sec2UnloadBundle { 83 fn run(&self, ctx: &mut GspBootContext<'_, '_>) -> Result { 84 let dev = ctx.dev(); 85 let bar = ctx.bar; 86 87 // Run FWSEC-SB to reset the GSP falcon to its pre-libos state. 88 // Log errors but keep going if it fails. 89 let fwsec_sb_res = self 90 .fwsec_sb 91 .run(dev, bar, ctx.gsp_falcon) 92 .inspect_err(|e| dev_err!(dev, "FWSEC-SB failed to run: {:?}\n", e)); 93 94 // Remove WPR2 region if set. 95 let booter_unloader_res = (|| { 96 if wpr2_range(bar).is_none() { 97 return Ok(()); 98 } 99 100 ctx.sec2_falcon.reset()?; 101 ctx.sec2_falcon.load(&self.booter_unloader)?; 102 103 // Sentinel value to confirm that Booter Unloader has run. 104 const MAILBOX_SENTINEL: u32 = 0xff; 105 let (mbox0, _) = ctx 106 .sec2_falcon 107 .boot(Some(MAILBOX_SENTINEL), Some(MAILBOX_SENTINEL))?; 108 if mbox0 != 0 { 109 dev_err!(dev, "Booter Unloader returned error 0x{:x}\n", mbox0); 110 return Err(EINVAL); 111 } 112 113 // Confirm that the WPR2 region has been removed. 114 if wpr2_range(bar).is_some() { 115 dev_err!( 116 dev, 117 "WPR2 region still set after Booter Unloader returned\n" 118 ); 119 return Err(EBUSY); 120 } 121 122 Ok(()) 123 })() 124 .inspect_err(|e| dev_err!(dev, "Booter Unloader failed to run: {:?}\n", e)); 125 126 fwsec_sb_res.and(booter_unloader_res) 127 } 128 } 129 130 pub(super) struct Tu102 { 131 /// If `true`, then the FWSEC-FRTS bootloader will be used to load the actual firmware. 132 pub(super) needs_fwsec_bootloader: bool, 133 } 134 135 impl Tu102 { 136 /// Helper method to load and run the FWSEC-FRTS firmware and confirm that it has properly 137 /// created the WPR2 region. 138 fn run_fwsec_frts( 139 &self, 140 dev: &device::Device<device::Bound>, 141 chipset: Chipset, 142 falcon: &Falcon<'_, GspEngine>, 143 bar: Bar0<'_>, 144 bios: &Vbios, 145 fb_ranges: &FbRanges, 146 ) -> Result { 147 // Check that the WPR2 region does not already exist - if it does, we cannot run 148 // FWSEC-FRTS until the GPU is reset. 149 if wpr2_range(bar).is_some() { 150 dev_err!( 151 dev, 152 "WPR2 region already exists - GPU needs to be reset to proceed\n" 153 ); 154 return Err(EBUSY); 155 } 156 157 // FWSEC-FRTS will create the WPR2 region. 158 let fwsec_frts = FwsecFirmware::new( 159 dev, 160 falcon, 161 bios, 162 FwsecCommand::Frts { 163 frts_addr: fb_ranges.frts.start, 164 frts_size: fb_ranges.frts.len(), 165 }, 166 )?; 167 168 if self.needs_fwsec_bootloader { 169 let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?; 170 // Load and run the bootloader, which will load FWSEC-FRTS and run it. 171 fwsec_frts_bl.run(dev, falcon, bar)?; 172 } else { 173 // Load and run FWSEC-FRTS directly. 174 fwsec_frts.run(dev, falcon)?; 175 } 176 177 // SCRATCH_E contains the error code for FWSEC-FRTS. 178 let frts_status = bar 179 .read(regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR) 180 .frts_err_code(); 181 if frts_status != 0 { 182 dev_err!( 183 dev, 184 "FWSEC-FRTS returned with error code {:#x}\n", 185 frts_status 186 ); 187 188 return Err(EIO); 189 } 190 191 // Check that the WPR2 region has been created as we requested. 192 let Some(wpr2_range) = wpr2_range(bar) else { 193 dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n"); 194 195 return Err(EIO); 196 }; 197 198 if wpr2_range.start != fb_ranges.frts.start { 199 dev_err!( 200 dev, 201 "WPR2 region created at unexpected address {:#x}; expected {:#x}\n", 202 wpr2_range.start, 203 fb_ranges.frts.start, 204 ); 205 206 return Err(EIO); 207 } 208 209 dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_range.start, wpr2_range.end); 210 dev_dbg!(dev, "GPU instance built\n"); 211 212 Ok(()) 213 } 214 215 /// Load and prepare the resources required to properly reset the GSP after it has been stopped. 216 fn build_unload_bundle( 217 &self, 218 dev: &device::Device<device::Bound>, 219 chipset: Chipset, 220 bios: &Vbios, 221 gsp_falcon: &Falcon<'_, GspEngine>, 222 sec2_falcon: &Falcon<'_, Sec2>, 223 ) -> Result<crate::gsp::UnloadBundle> { 224 // Load the FWSEC SB firmware, as well as its bootloader if required. 225 let fwsec_sb = FwsecFirmware::new(dev, gsp_falcon, bios, FwsecCommand::Sb)?; 226 let fwsec_sb = if self.needs_fwsec_bootloader { 227 FwsecUnloadFirmware::WithBl(FwsecFirmwareWithBl::new(fwsec_sb, dev, chipset)?) 228 } else { 229 FwsecUnloadFirmware::WithoutBl(fwsec_sb) 230 }; 231 232 KBox::new( 233 Sec2UnloadBundle { 234 fwsec_sb, 235 booter_unloader: BooterFirmware::new( 236 dev, 237 BooterKind::Unloader, 238 chipset, 239 sec2_falcon, 240 )?, 241 }, 242 GFP_KERNEL, 243 ) 244 .map(|b| crate::gsp::UnloadBundle(b)) 245 .map_err(Into::into) 246 } 247 } 248 249 impl GspHal for Tu102 { 250 fn boot( 251 &self, 252 gsp: &Gsp, 253 ctx: &mut GspBootContext<'_, '_>, 254 gsp_fw: &GspFirmware, 255 ) -> Result<Option<crate::gsp::UnloadBundle>> { 256 let dev = ctx.dev(); 257 let bar = ctx.bar; 258 let chipset = ctx.chipset; 259 let gsp_falcon = ctx.gsp_falcon; 260 let sec2_falcon = ctx.sec2_falcon; 261 262 let fb_ranges = FbRanges::new(chipset, bar, gsp_fw, ctx.vgpu.state())?; 263 dev_dbg!(dev, "{:#x?}\n", fb_ranges); 264 265 // Declared before the unload guard so that if Booter fails while running, SEC2 is reset 266 // by the guard before this allocation is freed. 267 let wpr_meta = Coherent::init( 268 dev, 269 GFP_KERNEL, 270 GspFwWprMeta::from_ranges(gsp_fw, &fb_ranges), 271 )?; 272 273 let bios = Vbios::new(dev, bar)?; 274 275 // Try and prepare the unload bundle. 276 // 277 // If the unload bundle creation fails, the GPU will need to be reset before the driver can 278 // be probed again. 279 let unload_bundle = self 280 .build_unload_bundle(dev, chipset, &bios, gsp_falcon, sec2_falcon) 281 .inspect_err(|e| dev_warn!(dev, "Failed to prepare unload firmware: {:?}\n", e)) 282 .ok(); 283 284 // Run the unload bundle to try and recover the GSP if an error occurs. 285 let unload_guard = ScopeGuard::new_with_data(unload_bundle, |unload_bundle| { 286 if let Some(unload_bundle) = unload_bundle { 287 let _ = unload_bundle.0.run(ctx); 288 } 289 }); 290 291 // FWSEC-FRTS is not executed on chips where the FRTS region size is 0 (e.g. GA100). 292 if !fb_ranges.frts.is_empty() { 293 self.run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_ranges)?; 294 } 295 296 gsp_falcon.reset()?; 297 let libos_dma_address = gsp.libos.dma_address(); 298 let (mbox0, mbox1) = gsp_falcon.boot( 299 Some(libos_dma_address as u32), 300 Some((libos_dma_address >> 32) as u32), 301 )?; 302 dev_dbg!(dev, "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1); 303 304 dev_dbg!( 305 dev, 306 "Using SEC2 to load and run the booter_load firmware...\n" 307 ); 308 309 BooterFirmware::new(dev, BooterKind::Loader, chipset, sec2_falcon)?.run( 310 dev, 311 sec2_falcon, 312 &wpr_meta, 313 )?; 314 315 Ok(unload_guard.dismiss()) 316 } 317 318 fn post_boot( 319 &self, 320 gsp: &Gsp, 321 ctx: &mut GspBootContext<'_, '_>, 322 gsp_fw: &GspFirmware, 323 ) -> Result { 324 GspSequencer::run(&gsp.cmdq, ctx, &gsp.libos, gsp_fw.bootloader.app_version)?; 325 326 Ok(()) 327 } 328 } 329 330 /// The TU102 HAL requires the use of the FWSEC bootloader. 331 const TU102: Tu102 = Tu102 { 332 needs_fwsec_bootloader: true, 333 }; 334 335 pub(super) const TU102_HAL: &dyn GspHal = &TU102; 336