Commit 015b1d36 authored by Alexandre Courbot's avatar Alexandre Courbot
Browse files

gpu: nova-core: firmware: process the GSP bootloader



The GSP bootloader is a small RISC-V firmware that is loaded by Booter
onto the GSP core and is in charge of loading, validating, and starting
the actual GSP firmware.

It is a regular binary firmware file containing a specific header.
Create a type holding the DMA-mapped firmware as well as useful
information extracted from the header, and hook it into our firmware
structure for later use.

The GSP bootloader is stored into the `GspFirmware` structure, since it
is part of the GSP firmware package. This makes the `Firmware` structure
empty, so remove it.

Reviewed-by: default avatarJohn Hubbard <jhubbard@nvidia.com>
Acked-by: default avatarDanilo Krummrich <dakr@kernel.org>
Link: https://lore.kernel.org/r/20250913-nova_firmware-v6-8-9007079548b0@nvidia.com


Signed-off-by: default avatarAlexandre Courbot <acourbot@nvidia.com>
parent a841614e
Loading
Loading
Loading
Loading
+1 −17
Original line number Diff line number Diff line
@@ -15,11 +15,11 @@
use crate::dma::DmaObject;
use crate::falcon::FalconFirmware;
use crate::gpu;
use crate::gpu::Chipset;

pub(crate) mod booter;
pub(crate) mod fwsec;
pub(crate) mod gsp;
pub(crate) mod riscv;

pub(crate) const FIRMWARE_VERSION: &str = "535.113.01";

@@ -36,22 +36,6 @@ fn request_firmware(
        .and_then(|path| firmware::Firmware::request(&path, dev))
}

/// Structure encapsulating the firmware blobs required for the GPU to operate.
#[expect(dead_code)]
pub(crate) struct Firmware {
    bootloader: firmware::Firmware,
}

impl Firmware {
    pub(crate) fn new(dev: &device::Device, chipset: Chipset, ver: &str) -> Result<Firmware> {
        let request = |name| request_firmware(dev, chipset, name, ver);

        Ok(Firmware {
            bootloader: request("bootloader")?,
        })
    }
}

/// Structure used to describe some firmwares, notably FWSEC-FRTS.
#[repr(C)]
#[derive(Debug, Clone)]
+7 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
use kernel::scatterlist::{Owned, SGTable};

use crate::dma::DmaObject;
use crate::firmware::riscv::RiscvFirmware;
use crate::gpu::{Architecture, Chipset};
use crate::gsp::GSP_PAGE_SIZE;

@@ -131,6 +132,8 @@ pub(crate) struct GspFirmware {
    size: usize,
    /// Device-mapped GSP signatures matching the GPU's [`Chipset`].
    signatures: DmaObject,
    /// GSP bootloader, verifies the GSP firmware before loading and running it.
    bootloader: RiscvFirmware,
}

impl GspFirmware {
@@ -164,6 +167,9 @@ pub(crate) fn new<'a, 'b>(
            })
            .map_err(|_| ENOMEM)?;

        let bl = super::request_firmware(dev, chipset, "bootloader", ver)?;
        let bootloader = RiscvFirmware::new(dev, &bl)?;

        Ok(try_pin_init!(Self {
            fw <- SGTable::new(dev, fw_vvec, DataDirection::ToDevice, GFP_KERNEL),
            level2 <- {
@@ -206,6 +212,7 @@ pub(crate) fn new<'a, 'b>(
            },
            size,
            signatures,
            bootloader,
        }))
    }

+91 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

//! Support for firmware binaries designed to run on a RISC-V core. Such firmwares files have a
//! dedicated header.

use core::mem::size_of;

use kernel::device;
use kernel::firmware::Firmware;
use kernel::prelude::*;
use kernel::transmute::FromBytes;

use crate::dma::DmaObject;
use crate::firmware::BinFirmware;

/// Descriptor for microcode running on a RISC-V core.
#[repr(C)]
#[derive(Debug)]
struct RmRiscvUCodeDesc {
    version: u32,
    bootloader_offset: u32,
    bootloader_size: u32,
    bootloader_param_offset: u32,
    bootloader_param_size: u32,
    riscv_elf_offset: u32,
    riscv_elf_size: u32,
    app_version: u32,
    manifest_offset: u32,
    manifest_size: u32,
    monitor_data_offset: u32,
    monitor_data_size: u32,
    monitor_code_offset: u32,
    monitor_code_size: u32,
}

// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
unsafe impl FromBytes for RmRiscvUCodeDesc {}

impl RmRiscvUCodeDesc {
    /// Interprets the header of `bin_fw` as a [`RmRiscvUCodeDesc`] and returns it.
    ///
    /// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image.
    fn new(bin_fw: &BinFirmware<'_>) -> Result<Self> {
        let offset = bin_fw.hdr.header_offset as usize;

        bin_fw
            .fw
            .get(offset..offset + size_of::<Self>())
            .and_then(Self::from_bytes_copy)
            .ok_or(EINVAL)
    }
}

/// A parsed firmware for a RISC-V core, ready to be loaded and run.
#[expect(unused)]
pub(crate) struct RiscvFirmware {
    /// Offset at which the code starts in the firmware image.
    code_offset: u32,
    /// Offset at which the data starts in the firmware image.
    data_offset: u32,
    /// Offset at which the manifest starts in the firmware image.
    manifest_offset: u32,
    /// Application version.
    app_version: u32,
    /// Device-mapped firmware image.
    ucode: DmaObject,
}

impl RiscvFirmware {
    /// Parses the RISC-V firmware image contained in `fw`.
    pub(crate) fn new(dev: &device::Device<device::Bound>, fw: &Firmware) -> Result<Self> {
        let bin_fw = BinFirmware::new(fw)?;

        let riscv_desc = RmRiscvUCodeDesc::new(&bin_fw)?;

        let ucode = {
            let start = bin_fw.hdr.data_offset as usize;
            let len = bin_fw.hdr.data_size as usize;

            DmaObject::from_data(dev, fw.data().get(start..start + len).ok_or(EINVAL)?)?
        };

        Ok(Self {
            ucode,
            code_offset: riscv_desc.monitor_code_offset,
            data_offset: riscv_desc.monitor_data_offset,
            manifest_offset: riscv_desc.manifest_offset,
            app_version: riscv_desc.app_version,
        })
    }
}
+0 −4
Original line number Diff line number Diff line
@@ -5,7 +5,6 @@
use crate::driver::Bar0;
use crate::falcon::{gsp::Gsp as GspFalcon, sec2::Sec2 as Sec2Falcon, Falcon};
use crate::fb::SysmemFlush;
use crate::firmware::{Firmware, FIRMWARE_VERSION};
use crate::gfw;
use crate::gsp::Gsp;
use crate::regs;
@@ -175,7 +174,6 @@ pub(crate) struct Gpu {
    spec: Spec,
    /// MMIO mapping of PCI BAR 0
    bar: Arc<Devres<Bar0>>,
    fw: Firmware,
    /// System memory page required for flushing all pending GPU-side memory writes done through
    /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
    sysmem_flush: SysmemFlush,
@@ -211,8 +209,6 @@ pub(crate) fn new<'a>(
                    .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?;
            },

            fw <- Firmware::new(pdev.as_ref(), spec.chipset, FIRMWARE_VERSION)?,

            sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,

            gsp_falcon: Falcon::new(