Commit 69f5cd67 authored by Alexandre Courbot's avatar Alexandre Courbot Committed by Danilo Krummrich
Browse files

gpu: nova-core: add falcon register definitions and base code



Booting the GSP on Ampere requires an intricate dance between the GSP
and SEC2 falcons, where the GSP starts by running the FWSEC firmware to
create the WPR2 region , and then SEC2 loads the actual RISC-V firmware
into the GSP.

Add the common Falcon code and HAL for Ampere GPUs, and instantiate the
GSP and SEC2 Falcons that will be required to perform that dance and
boot the GSP.

Thanks to Ben Skeggs for pointing out an important bug in the memory
scrubbing code that could lead to a race condition and ultimately a
failure to boot the GSP!

Reviewed-by: default avatarLyude Paul <lyude@redhat.com>
Signed-off-by: default avatarAlexandre Courbot <acourbot@nvidia.com>
Link: https://lore.kernel.org/r/20250619-nova-frts-v6-15-ecf41ef99252@nvidia.com


Signed-off-by: default avatarDanilo Krummrich <dakr@kernel.org>
parent 6554ad65
Loading
Loading
Loading
Loading
+551 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

//! Falcon microprocessor base support

// To be removed when all code is used.
#![expect(dead_code)]

use core::ops::Deref;
use core::time::Duration;
use hal::FalconHal;
use kernel::bindings;
use kernel::device;
use kernel::prelude::*;
use kernel::types::ARef;

use crate::dma::DmaObject;
use crate::driver::Bar0;
use crate::gpu::Chipset;
use crate::regs;
use crate::util;

pub(crate) mod gsp;
mod hal;
pub(crate) mod sec2;

/// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
/// register.
#[repr(u8)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum FalconCoreRev {
    #[default]
    Rev1 = 1,
    Rev2 = 2,
    Rev3 = 3,
    Rev4 = 4,
    Rev5 = 5,
    Rev6 = 6,
    Rev7 = 7,
}

impl TryFrom<u8> for FalconCoreRev {
    type Error = Error;

    fn try_from(value: u8) -> Result<Self> {
        use FalconCoreRev::*;

        let rev = match value {
            1 => Rev1,
            2 => Rev2,
            3 => Rev3,
            4 => Rev4,
            5 => Rev5,
            6 => Rev6,
            7 => Rev7,
            _ => return Err(EINVAL),
        };

        Ok(rev)
    }
}

/// Revision subversion number of a falcon core, used in the
/// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register.
#[repr(u8)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum FalconCoreRevSubversion {
    #[default]
    Subversion0 = 0,
    Subversion1 = 1,
    Subversion2 = 2,
    Subversion3 = 3,
}

impl TryFrom<u8> for FalconCoreRevSubversion {
    type Error = Error;

    fn try_from(value: u8) -> Result<Self> {
        use FalconCoreRevSubversion::*;

        let sub_version = match value & 0b11 {
            0 => Subversion0,
            1 => Subversion1,
            2 => Subversion2,
            3 => Subversion3,
            _ => return Err(EINVAL),
        };

        Ok(sub_version)
    }
}

/// Security model of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
/// register.
#[repr(u8)]
#[derive(Debug, Default, Copy, Clone)]
pub(crate) enum FalconSecurityModel {
    /// Non-Secure: runs unsigned code without privileges.
    #[default]
    None = 0,
    /// Low-Secure: runs code with some privileges. Can only be entered from `Heavy` mode, which
    /// will typically validate the LS code through some signature.
    Light = 2,
    /// High-Secure: runs signed code with full privileges. Signature is validated by boot ROM.
    Heavy = 3,
}

impl TryFrom<u8> for FalconSecurityModel {
    type Error = Error;

    fn try_from(value: u8) -> Result<Self> {
        use FalconSecurityModel::*;

        let sec_model = match value {
            0 => None,
            2 => Light,
            3 => Heavy,
            _ => return Err(EINVAL),
        };

        Ok(sec_model)
    }
}

/// Signing algorithm for a given firmware, used in the [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`]
/// register.
#[repr(u8)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub(crate) enum FalconModSelAlgo {
    /// RSA3K.
    #[default]
    Rsa3k = 1,
}

impl TryFrom<u8> for FalconModSelAlgo {
    type Error = Error;

    fn try_from(value: u8) -> Result<Self> {
        match value {
            1 => Ok(FalconModSelAlgo::Rsa3k),
            _ => Err(EINVAL),
        }
    }
}

/// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] register.
#[repr(u8)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub(crate) enum DmaTrfCmdSize {
    /// 256 bytes transfer.
    #[default]
    Size256B = 0x6,
}

impl TryFrom<u8> for DmaTrfCmdSize {
    type Error = Error;

    fn try_from(value: u8) -> Result<Self> {
        match value {
            0x6 => Ok(Self::Size256B),
            _ => Err(EINVAL),
        }
    }
}

/// Currently active core on a dual falcon/riscv (Peregrine) controller.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) enum PeregrineCoreSelect {
    /// Falcon core is active.
    #[default]
    Falcon = 0,
    /// RISC-V core is active.
    Riscv = 1,
}

impl From<bool> for PeregrineCoreSelect {
    fn from(value: bool) -> Self {
        match value {
            false => PeregrineCoreSelect::Falcon,
            true => PeregrineCoreSelect::Riscv,
        }
    }
}

/// Different types of memory present in a falcon core.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum FalconMem {
    /// Instruction Memory.
    Imem,
    /// Data Memory.
    Dmem,
}

/// Target/source of a DMA transfer to/from falcon memory.
#[derive(Debug, Clone, Default)]
pub(crate) enum FalconFbifTarget {
    /// VRAM.
    #[default]
    LocalFb = 0,
    /// Coherent system memory.
    CoherentSysmem = 1,
    /// Non-coherent system memory.
    NoncoherentSysmem = 2,
}

impl TryFrom<u8> for FalconFbifTarget {
    type Error = Error;

    fn try_from(value: u8) -> Result<Self> {
        let res = match value {
            0 => Self::LocalFb,
            1 => Self::CoherentSysmem,
            2 => Self::NoncoherentSysmem,
            _ => return Err(EINVAL),
        };

        Ok(res)
    }
}

/// Type of memory addresses to use.
#[derive(Debug, Clone, Default)]
pub(crate) enum FalconFbifMemType {
    /// Virtual memory addresses.
    #[default]
    Virtual = 0,
    /// Physical memory addresses.
    Physical = 1,
}

/// Conversion from a single-bit register field.
impl From<bool> for FalconFbifMemType {
    fn from(value: bool) -> Self {
        match value {
            false => Self::Virtual,
            true => Self::Physical,
        }
    }
}

/// Trait defining the parameters of a given Falcon instance.
pub(crate) trait FalconEngine: Sync {
    /// Base I/O address for the falcon, relative from which its registers are accessed.
    const BASE: usize;
}

/// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM).
#[derive(Debug)]
pub(crate) struct FalconLoadTarget {
    /// Offset from the start of the source object to copy from.
    pub(crate) src_start: u32,
    /// Offset from the start of the destination memory to copy into.
    pub(crate) dst_start: u32,
    /// Number of bytes to copy.
    pub(crate) len: u32,
}

/// Parameters for the falcon boot ROM.
#[derive(Debug)]
pub(crate) struct FalconBromParams {
    /// Offset in `DMEM`` of the firmware's signature.
    pub(crate) pkc_data_offset: u32,
    /// Mask of engines valid for this firmware.
    pub(crate) engine_id_mask: u16,
    /// ID of the ucode used to infer a fuse register to validate the signature.
    pub(crate) ucode_id: u8,
}

/// Trait for providing load parameters of falcon firmwares.
pub(crate) trait FalconLoadParams {
    /// Returns the load parameters for `IMEM`.
    fn imem_load_params(&self) -> FalconLoadTarget;

    /// Returns the load parameters for `DMEM`.
    fn dmem_load_params(&self) -> FalconLoadTarget;

    /// Returns the parameters to write into the BROM registers.
    fn brom_params(&self) -> FalconBromParams;

    /// Returns the start address of the firmware.
    fn boot_addr(&self) -> u32;
}

/// Trait for a falcon firmware.
///
/// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
/// object.
pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> {
    /// Engine on which this firmware is to be loaded.
    type Target: FalconEngine;
}

/// Contains the base parameters common to all Falcon instances.
pub(crate) struct Falcon<E: FalconEngine> {
    hal: KBox<dyn FalconHal<E>>,
    dev: ARef<device::Device>,
}

impl<E: FalconEngine + 'static> Falcon<E> {
    /// Create a new falcon instance.
    ///
    /// `need_riscv` is set to `true` if the caller expects the falcon to be a dual falcon/riscv
    /// controller.
    pub(crate) fn new(
        dev: &device::Device,
        chipset: Chipset,
        bar: &Bar0,
        need_riscv: bool,
    ) -> Result<Self> {
        let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, E::BASE);
        // Check that the revision and security model contain valid values.
        let _ = hwcfg1.core_rev()?;
        let _ = hwcfg1.security_model()?;

        if need_riscv {
            let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
            if !hwcfg2.riscv() {
                dev_err!(
                    dev,
                    "riscv support requested on a controller that does not support it\n"
                );
                return Err(EINVAL);
            }
        }

        Ok(Self {
            hal: hal::falcon_hal(chipset)?,
            dev: dev.into(),
        })
    }

    /// Wait for memory scrubbing to complete.
    fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result {
        // TIMEOUT: memory scrubbing should complete in less than 20ms.
        util::wait_on(Duration::from_millis(20), || {
            if regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE).mem_scrubbing_done() {
                Some(())
            } else {
                None
            }
        })
    }

    /// Reset the falcon engine.
    fn reset_eng(&self, bar: &Bar0) -> Result {
        let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);

        // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set
        // RESET_READY so a non-failing timeout is used.
        let _ = util::wait_on(Duration::from_micros(150), || {
            let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
            if r.reset_ready() {
                Some(())
            } else {
                None
            }
        });

        regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v| v.set_reset(true));

        // TODO: replace with udelay() or equivalent once available.
        // TIMEOUT: falcon engine should not take more than 10us to reset.
        let _: Result = util::wait_on(Duration::from_micros(10), || None);

        regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v| v.set_reset(false));

        self.reset_wait_mem_scrubbing(bar)?;

        Ok(())
    }

    /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
    pub(crate) fn reset(&self, bar: &Bar0) -> Result {
        self.reset_eng(bar)?;
        self.hal.select_core(self, bar)?;
        self.reset_wait_mem_scrubbing(bar)?;

        regs::NV_PFALCON_FALCON_RM::default()
            .set_value(regs::NV_PMC_BOOT_0::read(bar).into())
            .write(bar, E::BASE);

        Ok(())
    }

    /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's
    /// `target_mem`.
    ///
    /// `sec` is set if the loaded firmware is expected to run in secure mode.
    fn dma_wr<F: FalconFirmware<Target = E>>(
        &self,
        bar: &Bar0,
        fw: &F,
        target_mem: FalconMem,
        load_offsets: FalconLoadTarget,
        sec: bool,
    ) -> Result {
        const DMA_LEN: u32 = 256;

        // For IMEM, we want to use the start offset as a virtual address tag for each page, since
        // code addresses in the firmware (and the boot vector) are virtual.
        //
        // For DMEM we can fold the start offset into the DMA handle.
        let (src_start, dma_start) = match target_mem {
            FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()),
            FalconMem::Dmem => (
                0,
                fw.dma_handle_with_offset(load_offsets.src_start as usize)?,
            ),
        };
        if dma_start % DMA_LEN as bindings::dma_addr_t > 0 {
            dev_err!(
                self.dev,
                "DMA transfer start addresses must be a multiple of {}",
                DMA_LEN
            );
            return Err(EINVAL);
        }
        if load_offsets.len % DMA_LEN > 0 {
            dev_err!(
                self.dev,
                "DMA transfer length must be a multiple of {}",
                DMA_LEN
            );
            return Err(EINVAL);
        }

        // Set up the base source DMA address.

        regs::NV_PFALCON_FALCON_DMATRFBASE::default()
            .set_base((dma_start >> 8) as u32)
            .write(bar, E::BASE);
        regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
            .set_base((dma_start >> 40) as u16)
            .write(bar, E::BASE);

        let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default()
            .set_size(DmaTrfCmdSize::Size256B)
            .set_imem(target_mem == FalconMem::Imem)
            .set_sec(if sec { 1 } else { 0 });

        for pos in (0..load_offsets.len).step_by(DMA_LEN as usize) {
            // Perform a transfer of size `DMA_LEN`.
            regs::NV_PFALCON_FALCON_DMATRFMOFFS::default()
                .set_offs(load_offsets.dst_start + pos)
                .write(bar, E::BASE);
            regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default()
                .set_offs(src_start + pos)
                .write(bar, E::BASE);
            cmd.write(bar, E::BASE);

            // Wait for the transfer to complete.
            // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
            // should ever take that long.
            util::wait_on(Duration::from_secs(2), || {
                let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, E::BASE);
                if r.idle() {
                    Some(())
                } else {
                    None
                }
            })?;
        }

        Ok(())
    }

    /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
    pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
        regs::NV_PFALCON_FBIF_CTL::alter(bar, E::BASE, |v| v.set_allow_phys_no_ctx(true));
        regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, E::BASE);
        regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, E::BASE, |v| {
            v.set_target(FalconFbifTarget::CoherentSysmem)
                .set_mem_type(FalconFbifMemType::Physical)
        });

        self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_load_params(), true)?;
        self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?;

        self.hal.program_brom(self, bar, &fw.brom_params())?;

        // Set `BootVec` to start of non-secure code.
        regs::NV_PFALCON_FALCON_BOOTVEC::default()
            .set_value(fw.boot_addr())
            .write(bar, E::BASE);

        Ok(())
    }

    /// Runs the loaded firmware and waits for its completion.
    ///
    /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
    /// prior to running.
    ///
    /// Wait up to two seconds for the firmware to complete, and return its exit status read from
    /// the `MBOX0` and `MBOX1` registers.
    pub(crate) fn boot(
        &self,
        bar: &Bar0,
        mbox0: Option<u32>,
        mbox1: Option<u32>,
    ) -> Result<(u32, u32)> {
        if let Some(mbox0) = mbox0 {
            regs::NV_PFALCON_FALCON_MAILBOX0::default()
                .set_value(mbox0)
                .write(bar, E::BASE);
        }

        if let Some(mbox1) = mbox1 {
            regs::NV_PFALCON_FALCON_MAILBOX1::default()
                .set_value(mbox1)
                .write(bar, E::BASE);
        }

        match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE).alias_en() {
            true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
                .set_startcpu(true)
                .write(bar, E::BASE),
            false => regs::NV_PFALCON_FALCON_CPUCTL::default()
                .set_startcpu(true)
                .write(bar, E::BASE),
        }

        // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
        util::wait_on(Duration::from_secs(2), || {
            let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE);
            if r.halted() {
                Some(())
            } else {
                None
            }
        })?;

        let (mbox0, mbox1) = (
            regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, E::BASE).value(),
            regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, E::BASE).value(),
        );

        Ok((mbox0, mbox1))
    }

    /// Returns the fused version of the signature to use in order to run a HS firmware on this
    /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
    pub(crate) fn signature_reg_fuse_version(
        &self,
        bar: &Bar0,
        engine_id_mask: u16,
        ucode_id: u8,
    ) -> Result<u32> {
        self.hal
            .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
    }
}
+24 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

use crate::{
    driver::Bar0,
    falcon::{Falcon, FalconEngine},
    regs,
};

/// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
pub(crate) struct Gsp(());

impl FalconEngine for Gsp {
    const BASE: usize = 0x00110000;
}

impl Falcon<Gsp> {
    /// Clears the SWGEN0 bit in the Falcon's IRQ status clear register to
    /// allow GSP to signal CPU for processing new messages in message queue.
    pub(crate) fn clear_swgen0_intr(&self, bar: &Bar0) {
        regs::NV_PFALCON_FALCON_IRQSCLR::default()
            .set_swgen0(true)
            .write(bar, Gsp::BASE);
    }
}
+54 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

use kernel::prelude::*;

use crate::driver::Bar0;
use crate::falcon::{Falcon, FalconBromParams, FalconEngine};
use crate::gpu::Chipset;

mod ga102;

/// Hardware Abstraction Layer for Falcon cores.
///
/// Implements chipset-specific low-level operations. The trait is generic against [`FalconEngine`]
/// so its `BASE` parameter can be used in order to avoid runtime bound checks when accessing
/// registers.
pub(crate) trait FalconHal<E: FalconEngine>: Sync {
    /// Activates the Falcon core if the engine is a risvc/falcon dual engine.
    fn select_core(&self, _falcon: &Falcon<E>, _bar: &Bar0) -> Result {
        Ok(())
    }

    /// Returns the fused version of the signature to use in order to run a HS firmware on this
    /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
    fn signature_reg_fuse_version(
        &self,
        falcon: &Falcon<E>,
        bar: &Bar0,
        engine_id_mask: u16,
        ucode_id: u8,
    ) -> Result<u32>;

    /// Program the boot ROM registers prior to starting a secure firmware.
    fn program_brom(&self, falcon: &Falcon<E>, bar: &Bar0, params: &FalconBromParams) -> Result;
}

/// Returns a boxed falcon HAL adequate for `chipset`.
///
/// We use a heap-allocated trait object instead of a statically defined one because the
/// generic `FalconEngine` argument makes it difficult to define all the combinations
/// statically.
pub(super) fn falcon_hal<E: FalconEngine + 'static>(
    chipset: Chipset,
) -> Result<KBox<dyn FalconHal<E>>> {
    use Chipset::*;

    let hal = match chipset {
        GA102 | GA103 | GA104 | GA106 | GA107 => {
            KBox::new(ga102::Ga102::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>>
        }
        _ => return Err(ENOTSUPP),
    };

    Ok(hal)
}
+119 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

use core::marker::PhantomData;
use core::time::Duration;

use kernel::device;
use kernel::prelude::*;

use crate::driver::Bar0;
use crate::falcon::{
    Falcon, FalconBromParams, FalconEngine, FalconModSelAlgo, PeregrineCoreSelect,
};
use crate::regs;
use crate::util;

use super::FalconHal;

fn select_core_ga102<E: FalconEngine>(bar: &Bar0) -> Result {
    let bcr_ctrl = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, E::BASE);
    if bcr_ctrl.core_select() != PeregrineCoreSelect::Falcon {
        regs::NV_PRISCV_RISCV_BCR_CTRL::default()
            .set_core_select(PeregrineCoreSelect::Falcon)
            .write(bar, E::BASE);

        // TIMEOUT: falcon core should take less than 10ms to report being enabled.
        util::wait_on(Duration::from_millis(10), || {
            let r = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, E::BASE);
            if r.valid() {
                Some(())
            } else {
                None
            }
        })?;
    }

    Ok(())
}

fn signature_reg_fuse_version_ga102(
    dev: &device::Device,
    bar: &Bar0,
    engine_id_mask: u16,
    ucode_id: u8,
) -> Result<u32> {
    // TODO: The ucode fuse versions are contained in the FUSE_OPT_FPF_<ENGINE>_UCODE<X>_VERSION
    // registers, which are an array. Our register definition macros do not allow us to manage them
    // properly, so we need to hardcode their addresses for now. Clean this up once we support
    // register arrays.

    // Each engine has 16 ucode version registers numbered from 1 to 16.
    if ucode_id == 0 || ucode_id > 16 {
        dev_err!(dev, "invalid ucode id {:#x}", ucode_id);
        return Err(EINVAL);
    }

    // Base address of the FUSE registers array corresponding to the engine.
    let reg_fuse_base = if engine_id_mask & 0x0001 != 0 {
        regs::NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION::OFFSET
    } else if engine_id_mask & 0x0004 != 0 {
        regs::NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION::OFFSET
    } else if engine_id_mask & 0x0400 != 0 {
        regs::NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION::OFFSET
    } else {
        dev_err!(dev, "unexpected engine_id_mask {:#x}", engine_id_mask);
        return Err(EINVAL);
    };

    // Read `reg_fuse_base[ucode_id - 1]`.
    let reg_fuse_version =
        bar.read32(reg_fuse_base + ((ucode_id - 1) as usize * core::mem::size_of::<u32>()));

    // TODO: replace with `last_set_bit` once it lands.
    Ok(u32::BITS - reg_fuse_version.leading_zeros())
}

fn program_brom_ga102<E: FalconEngine>(bar: &Bar0, params: &FalconBromParams) -> Result {
    regs::NV_PFALCON2_FALCON_BROM_PARAADDR::default()
        .set_value(params.pkc_data_offset)
        .write(bar, E::BASE);
    regs::NV_PFALCON2_FALCON_BROM_ENGIDMASK::default()
        .set_value(params.engine_id_mask as u32)
        .write(bar, E::BASE);
    regs::NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID::default()
        .set_ucode_id(params.ucode_id)
        .write(bar, E::BASE);
    regs::NV_PFALCON2_FALCON_MOD_SEL::default()
        .set_algo(FalconModSelAlgo::Rsa3k)
        .write(bar, E::BASE);

    Ok(())
}

pub(super) struct Ga102<E: FalconEngine>(PhantomData<E>);

impl<E: FalconEngine> Ga102<E> {
    pub(super) fn new() -> Self {
        Self(PhantomData)
    }
}

impl<E: FalconEngine> FalconHal<E> for Ga102<E> {
    fn select_core(&self, _falcon: &Falcon<E>, bar: &Bar0) -> Result {
        select_core_ga102::<E>(bar)
    }

    fn signature_reg_fuse_version(
        &self,
        falcon: &Falcon<E>,
        bar: &Bar0,
        engine_id_mask: u16,
        ucode_id: u8,
    ) -> Result<u32> {
        signature_reg_fuse_version_ga102(&falcon.dev, bar, engine_id_mask, ucode_id)
    }

    fn program_brom(&self, _falcon: &Falcon<E>, bar: &Bar0, params: &FalconBromParams) -> Result {
        program_brom_ga102::<E>(bar, params)
    }
}
+10 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

use crate::falcon::FalconEngine;

/// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
pub(crate) struct Sec2(());

impl FalconEngine for Sec2 {
    const BASE: usize = 0x00840000;
}
Loading