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