xref: /linux/drivers/gpu/nova-core/gsp/boot.rs (revision 82b78182eacf82c1847c6f1fd93d91c15efb69cf)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 use kernel::{
4     device,
5     dma::Coherent,
6     io::poll::read_poll_timeout,
7     io::Io,
8     pci,
9     prelude::*,
10     time::Delta, //
11 };
12 
13 use crate::{
14     driver::Bar0,
15     falcon::{
16         gsp::Gsp,
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::{
35         Architecture,
36         Chipset, //
37     },
38     gsp::{
39         commands,
40         sequencer::{
41             GspSequencer,
42             GspSequencerParams, //
43         },
44         GspFwWprMeta, //
45     },
46     regs,
47     vbios::Vbios,
48 };
49 
50 impl super::Gsp {
51     /// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly
52     /// created the WPR2 region.
53     fn run_fwsec_frts(
54         dev: &device::Device<device::Bound>,
55         chipset: Chipset,
56         falcon: &Falcon<Gsp>,
57         bar: &Bar0,
58         bios: &Vbios,
59         fb_layout: &FbLayout,
60     ) -> Result<()> {
61         // Check that the WPR2 region does not already exists - if it does, we cannot run
62         // FWSEC-FRTS until the GPU is reset.
63         if bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound() != 0 {
64             dev_err!(
65                 dev,
66                 "WPR2 region already exists - GPU needs to be reset to proceed\n"
67             );
68             return Err(EBUSY);
69         }
70 
71         // FWSEC-FRTS will create the WPR2 region.
72         let fwsec_frts = FwsecFirmware::new(
73             dev,
74             falcon,
75             bar,
76             bios,
77             FwsecCommand::Frts {
78                 frts_addr: fb_layout.frts.start,
79                 frts_size: fb_layout.frts.len(),
80             },
81         )?;
82 
83         if chipset.needs_fwsec_bootloader() {
84             let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?;
85             // Load and run the bootloader, which will load FWSEC-FRTS and run it.
86             fwsec_frts_bl.run(dev, falcon, bar)?;
87         } else {
88             // Load and run FWSEC-FRTS directly.
89             fwsec_frts.run(dev, falcon, bar)?;
90         }
91 
92         // SCRATCH_E contains the error code for FWSEC-FRTS.
93         let frts_status = bar
94             .read(regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR)
95             .frts_err_code();
96         if frts_status != 0 {
97             dev_err!(
98                 dev,
99                 "FWSEC-FRTS returned with error code {:#x}\n",
100                 frts_status
101             );
102 
103             return Err(EIO);
104         }
105 
106         // Check that the WPR2 region has been created as we requested.
107         let (wpr2_lo, wpr2_hi) = (
108             bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO).lower_bound(),
109             bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound(),
110         );
111 
112         match (wpr2_lo, wpr2_hi) {
113             (_, 0) => {
114                 dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n");
115 
116                 Err(EIO)
117             }
118             (wpr2_lo, _) if wpr2_lo != fb_layout.frts.start => {
119                 dev_err!(
120                     dev,
121                     "WPR2 region created at unexpected address {:#x}; expected {:#x}\n",
122                     wpr2_lo,
123                     fb_layout.frts.start,
124                 );
125 
126                 Err(EIO)
127             }
128             (wpr2_lo, wpr2_hi) => {
129                 dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_lo, wpr2_hi);
130                 dev_dbg!(dev, "GPU instance built\n");
131 
132                 Ok(())
133             }
134         }
135     }
136 
137     /// Attempt to boot the GSP.
138     ///
139     /// This is a GPU-dependent and complex procedure that involves loading firmware files from
140     /// user-space, patching them with signatures, and building firmware-specific intricate data
141     /// structures that the GSP will use at runtime.
142     ///
143     /// Upon return, the GSP is up and running, and its runtime object given as return value.
144     pub(crate) fn boot(
145         self: Pin<&mut Self>,
146         pdev: &pci::Device<device::Bound>,
147         bar: &Bar0,
148         chipset: Chipset,
149         gsp_falcon: &Falcon<Gsp>,
150         sec2_falcon: &Falcon<Sec2>,
151     ) -> Result {
152         // The FSP boot process of Hopper+ is not supported for now.
153         if matches!(
154             chipset.arch(),
155             Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x
156         ) {
157             return Err(ENOTSUPP);
158         }
159 
160         let dev = pdev.as_ref();
161 
162         let bios = Vbios::new(dev, bar)?;
163 
164         let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?;
165 
166         let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
167         dev_dbg!(dev, "{:#x?}\n", fb_layout);
168 
169         // FWSEC-FRTS is not executed on chips where the FRTS region size is 0 (e.g. GA100).
170         if !fb_layout.frts.is_empty() {
171             Self::run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_layout)?;
172         }
173 
174         let booter_loader = BooterFirmware::new(
175             dev,
176             BooterKind::Loader,
177             chipset,
178             FIRMWARE_VERSION,
179             sec2_falcon,
180             bar,
181         )?;
182 
183         let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
184 
185         self.cmdq
186             .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev))?;
187         self.cmdq
188             .send_command_no_wait(bar, commands::SetRegistry::new())?;
189 
190         gsp_falcon.reset(bar)?;
191         let libos_handle = self.libos.dma_handle();
192         let (mbox0, mbox1) = gsp_falcon.boot(
193             bar,
194             Some(libos_handle as u32),
195             Some((libos_handle >> 32) as u32),
196         )?;
197         dev_dbg!(pdev, "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1);
198 
199         dev_dbg!(
200             pdev,
201             "Using SEC2 to load and run the booter_load firmware...\n"
202         );
203 
204         sec2_falcon.reset(bar)?;
205         sec2_falcon.load(dev, bar, &booter_loader)?;
206         let wpr_handle = wpr_meta.dma_handle();
207         let (mbox0, mbox1) = sec2_falcon.boot(
208             bar,
209             Some(wpr_handle as u32),
210             Some((wpr_handle >> 32) as u32),
211         )?;
212         dev_dbg!(pdev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1);
213 
214         if mbox0 != 0 {
215             dev_err!(pdev, "Booter-load failed with error {:#x}\n", mbox0);
216             return Err(ENODEV);
217         }
218 
219         gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
220 
221         // Poll for RISC-V to become active before running sequencer
222         read_poll_timeout(
223             || Ok(gsp_falcon.is_riscv_active(bar)),
224             |val: &bool| *val,
225             Delta::from_millis(10),
226             Delta::from_secs(5),
227         )?;
228 
229         dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(bar),);
230 
231         // Create and run the GSP sequencer.
232         let seq_params = GspSequencerParams {
233             bootloader_app_version: gsp_fw.bootloader.app_version,
234             libos_dma_handle: libos_handle,
235             gsp_falcon,
236             sec2_falcon,
237             dev: pdev.as_ref().into(),
238             bar,
239         };
240         GspSequencer::run(&self.cmdq, seq_params)?;
241 
242         // Wait until GSP is fully initialized.
243         commands::wait_gsp_init_done(&self.cmdq)?;
244 
245         // Obtain and display basic GPU information.
246         let info = commands::get_gsp_info(&self.cmdq, bar)?;
247         match info.gpu_name() {
248             Ok(name) => dev_info!(pdev, "GPU name: {}\n", name),
249             Err(e) => dev_warn!(pdev, "GPU name unavailable: {:?}\n", e),
250         }
251 
252         Ok(())
253     }
254 }
255