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