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