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