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