xref: /linux/drivers/gpu/nova-core/gsp/hal/tu102.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
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