xref: /linux/drivers/gpu/nova-core/gsp/boot.rs (revision 19b0a6e7c2be35f9b11c51c0c21c9a02c23252ff)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 use kernel::{
4     device,
5     dma::CoherentAllocation,
6     dma_write,
7     pci,
8     prelude::*, //
9 };
10 
11 use crate::{
12     driver::Bar0,
13     falcon::{
14         gsp::Gsp,
15         sec2::Sec2,
16         Falcon, //
17     },
18     fb::FbLayout,
19     firmware::{
20         booter::{
21             BooterFirmware,
22             BooterKind, //
23         },
24         fwsec::{
25             FwsecCommand,
26             FwsecFirmware, //
27         },
28         gsp::GspFirmware,
29         FIRMWARE_VERSION, //
30     },
31     gpu::Chipset,
32     gsp::{
33         commands,
34         GspFwWprMeta, //
35     },
36     regs,
37     vbios::Vbios,
38 };
39 
40 impl super::Gsp {
41     /// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly
42     /// created the WPR2 region.
43     fn run_fwsec_frts(
44         dev: &device::Device<device::Bound>,
45         falcon: &Falcon<Gsp>,
46         bar: &Bar0,
47         bios: &Vbios,
48         fb_layout: &FbLayout,
49     ) -> Result<()> {
50         // Check that the WPR2 region does not already exists - if it does, we cannot run
51         // FWSEC-FRTS until the GPU is reset.
52         if regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound() != 0 {
53             dev_err!(
54                 dev,
55                 "WPR2 region already exists - GPU needs to be reset to proceed\n"
56             );
57             return Err(EBUSY);
58         }
59 
60         let fwsec_frts = FwsecFirmware::new(
61             dev,
62             falcon,
63             bar,
64             bios,
65             FwsecCommand::Frts {
66                 frts_addr: fb_layout.frts.start,
67                 frts_size: fb_layout.frts.end - fb_layout.frts.start,
68             },
69         )?;
70 
71         // Run FWSEC-FRTS to create the WPR2 region.
72         fwsec_frts.run(dev, falcon, bar)?;
73 
74         // SCRATCH_E contains the error code for FWSEC-FRTS.
75         let frts_status = regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code();
76         if frts_status != 0 {
77             dev_err!(
78                 dev,
79                 "FWSEC-FRTS returned with error code {:#x}",
80                 frts_status
81             );
82 
83             return Err(EIO);
84         }
85 
86         // Check that the WPR2 region has been created as we requested.
87         let (wpr2_lo, wpr2_hi) = (
88             regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO::read(bar).lower_bound(),
89             regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound(),
90         );
91 
92         match (wpr2_lo, wpr2_hi) {
93             (_, 0) => {
94                 dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n");
95 
96                 Err(EIO)
97             }
98             (wpr2_lo, _) if wpr2_lo != fb_layout.frts.start => {
99                 dev_err!(
100                     dev,
101                     "WPR2 region created at unexpected address {:#x}; expected {:#x}\n",
102                     wpr2_lo,
103                     fb_layout.frts.start,
104                 );
105 
106                 Err(EIO)
107             }
108             (wpr2_lo, wpr2_hi) => {
109                 dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_lo, wpr2_hi);
110                 dev_dbg!(dev, "GPU instance built\n");
111 
112                 Ok(())
113             }
114         }
115     }
116 
117     /// Attempt to boot the GSP.
118     ///
119     /// This is a GPU-dependent and complex procedure that involves loading firmware files from
120     /// user-space, patching them with signatures, and building firmware-specific intricate data
121     /// structures that the GSP will use at runtime.
122     ///
123     /// Upon return, the GSP is up and running, and its runtime object given as return value.
124     pub(crate) fn boot(
125         mut self: Pin<&mut Self>,
126         pdev: &pci::Device<device::Bound>,
127         bar: &Bar0,
128         chipset: Chipset,
129         gsp_falcon: &Falcon<Gsp>,
130         sec2_falcon: &Falcon<Sec2>,
131     ) -> Result {
132         let dev = pdev.as_ref();
133 
134         let bios = Vbios::new(dev, bar)?;
135 
136         let gsp_fw = KBox::pin_init(
137             GspFirmware::new(dev, chipset, FIRMWARE_VERSION)?,
138             GFP_KERNEL,
139         )?;
140 
141         let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
142         dev_dbg!(dev, "{:#x?}\n", fb_layout);
143 
144         Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?;
145 
146         let _booter_loader = BooterFirmware::new(
147             dev,
148             BooterKind::Loader,
149             chipset,
150             FIRMWARE_VERSION,
151             sec2_falcon,
152             bar,
153         )?;
154 
155         let wpr_meta =
156             CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
157         dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
158 
159         self.cmdq
160             .send_command(bar, commands::SetSystemInfo::new(pdev))?;
161         self.cmdq.send_command(bar, commands::SetRegistry::new())?;
162 
163         Ok(())
164     }
165 }
166