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