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