xref: /linux/drivers/gpu/nova-core/gsp/hal/tu102.rs (revision bb2d007399d89bd900ec398bf5e29093f982ddcc)
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     /// Runs the FWSEC SB firmware.
60     fn run(
61         &self,
62         dev: &device::Device<device::Bound>,
63         bar: Bar0<'_>,
64         gsp_falcon: &Falcon<'_, GspEngine>,
65     ) -> Result {
66         match self {
67             Self::WithoutBl(fw) => fw.run(dev, gsp_falcon),
68             Self::WithBl(fw) => fw.run(dev, gsp_falcon, bar),
69         }
70     }
71 }
72 
73 // Contains the firmware required to fully reset GSP on chipsets where the GSP is started using
74 // FWSEC/Booter.
75 struct Sec2UnloadBundle {
76     fwsec_sb: FwsecUnloadFirmware,
77     booter_unloader: BooterFirmware,
78 }
79 
80 impl UnloadBundle for Sec2UnloadBundle {
81     fn run(&self, ctx: &GspBootContext<'_>) -> Result {
82         let dev = ctx.dev();
83         let bar = ctx.bar;
84 
85         // Run FWSEC-SB to reset the GSP falcon to its pre-libos state.
86         // Log errors but keep going if it fails.
87         let fwsec_sb_res = self
88             .fwsec_sb
89             .run(dev, bar, ctx.gsp_falcon)
90             .inspect_err(|e| dev_err!(dev, "FWSEC-SB failed to run: {:?}\n", e));
91 
92         // Remove WPR2 region if set.
93         let wpr2_hi = bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI);
94         let booter_unloader_res = (|| {
95             if !wpr2_hi.is_wpr2_set() {
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             let wpr2_hi = bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI);
114             if wpr2_hi.is_wpr2_set() {
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_layout: &FbLayout,
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 bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound() != 0 {
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_layout.frts.start,
164                 frts_size: fb_layout.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 (wpr2_lo, wpr2_hi) = (
193             bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO).lower_bound(),
194             bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound(),
195         );
196 
197         match (wpr2_lo, wpr2_hi) {
198             (_, 0) => {
199                 dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n");
200 
201                 Err(EIO)
202             }
203             (wpr2_lo, _) if wpr2_lo != fb_layout.frts.start => {
204                 dev_err!(
205                     dev,
206                     "WPR2 region created at unexpected address {:#x}; expected {:#x}\n",
207                     wpr2_lo,
208                     fb_layout.frts.start,
209                 );
210 
211                 Err(EIO)
212             }
213             (wpr2_lo, wpr2_hi) => {
214                 dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_lo, wpr2_hi);
215                 dev_dbg!(dev, "GPU instance built\n");
216 
217                 Ok(())
218             }
219         }
220     }
221 
222     /// Load and prepare the resources required to properly reset the GSP after it has been stopped.
223     fn build_unload_bundle(
224         &self,
225         dev: &device::Device<device::Bound>,
226         chipset: Chipset,
227         bios: &Vbios,
228         gsp_falcon: &Falcon<'_, GspEngine>,
229         sec2_falcon: &Falcon<'_, Sec2>,
230     ) -> Result<crate::gsp::UnloadBundle> {
231         // Load the FWSEC SB firmware, as well as its bootloader if required.
232         let fwsec_sb = FwsecFirmware::new(dev, gsp_falcon, bios, FwsecCommand::Sb)?;
233         let fwsec_sb = if self.needs_fwsec_bootloader {
234             FwsecUnloadFirmware::WithBl(FwsecFirmwareWithBl::new(fwsec_sb, dev, chipset)?)
235         } else {
236             FwsecUnloadFirmware::WithoutBl(fwsec_sb)
237         };
238 
239         KBox::new(
240             Sec2UnloadBundle {
241                 fwsec_sb,
242                 booter_unloader: BooterFirmware::new(
243                     dev,
244                     BooterKind::Unloader,
245                     chipset,
246                     FIRMWARE_VERSION,
247                     sec2_falcon,
248                 )?,
249             },
250             GFP_KERNEL,
251         )
252         .map(|b| crate::gsp::UnloadBundle(b))
253         .map_err(Into::into)
254     }
255 }
256 
257 impl GspHal for Tu102 {
258     fn boot(
259         &self,
260         gsp: &Gsp,
261         ctx: &GspBootContext<'_>,
262         fb_layout: &FbLayout,
263         wpr_meta: &Coherent<GspFwWprMeta>,
264     ) -> Result<Option<crate::gsp::UnloadBundle>> {
265         let dev = ctx.dev();
266         let bar = ctx.bar;
267         let chipset = ctx.chipset;
268         let gsp_falcon = ctx.gsp_falcon;
269         let sec2_falcon = ctx.sec2_falcon;
270 
271         let bios = Vbios::new(dev, bar)?;
272 
273         // Try and prepare the unload bundle.
274         //
275         // If the unload bundle creation fails, the GPU will need to be reset before the driver can
276         // be probed again.
277         let unload_bundle = self
278             .build_unload_bundle(dev, chipset, &bios, gsp_falcon, sec2_falcon)
279             .inspect_err(|e| dev_warn!(dev, "Failed to prepare unload firmware: {:?}\n", e))
280             .ok();
281 
282         // Run the unload bundle to try and recover the GSP if an error occurs.
283         let unload_guard = ScopeGuard::new_with_data(unload_bundle, |unload_bundle| {
284             if let Some(unload_bundle) = unload_bundle {
285                 let _ = unload_bundle.0.run(ctx);
286             }
287         });
288 
289         // FWSEC-FRTS is not executed on chips where the FRTS region size is 0 (e.g. GA100).
290         if !fb_layout.frts.is_empty() {
291             self.run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, fb_layout)?;
292         }
293 
294         gsp_falcon.reset()?;
295         let libos_handle = gsp.libos.dma_handle();
296         let (mbox0, mbox1) =
297             gsp_falcon.boot(Some(libos_handle as u32), Some((libos_handle >> 32) as u32))?;
298         dev_dbg!(dev, "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1);
299 
300         dev_dbg!(
301             dev,
302             "Using SEC2 to load and run the booter_load firmware...\n"
303         );
304 
305         BooterFirmware::new(
306             dev,
307             BooterKind::Loader,
308             chipset,
309             FIRMWARE_VERSION,
310             sec2_falcon,
311         )?
312         .run(dev, sec2_falcon, wpr_meta)?;
313 
314         Ok(unload_guard.dismiss())
315     }
316 
317     fn post_boot(&self, gsp: &Gsp, ctx: &GspBootContext<'_>, gsp_fw: &GspFirmware) -> Result {
318         GspSequencer::run(
319             &gsp.cmdq,
320             ctx,
321             gsp.libos.dma_handle(),
322             gsp_fw.bootloader.app_version,
323         )?;
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