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