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