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