File Index
Files and directories
Sign in
FILE

boot.rs

File preview with syntax highlighting.

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