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