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