Add external memory (EMEM) read/write operations to the GPU's FSP falcon engine. These operations use Falcon PIO (Programmed I/O) to communicate with the FSP through indirect memory access.
Cc: Gary Guo <[email protected]> Cc: Timur Tabi <[email protected]> Signed-off-by: John Hubbard <[email protected]> --- drivers/gpu/nova-core/falcon/fsp.rs | 59 ++++++++++++++++++++++++++++- drivers/gpu/nova-core/regs.rs | 13 +++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/falcon/fsp.rs b/drivers/gpu/nova-core/falcon/fsp.rs index cc3fc3cf2f6a..fb1c8c89d2ff 100644 --- a/drivers/gpu/nova-core/falcon/fsp.rs +++ b/drivers/gpu/nova-core/falcon/fsp.rs @@ -5,13 +5,20 @@ //! The FSP falcon handles secure boot and Chain of Trust operations //! on Hopper and Blackwell architectures, replacing SEC2's role. +use kernel::prelude::*; + use crate::{ + driver::Bar0, falcon::{ + Falcon, FalconEngine, PFalcon2Base, PFalconBase, // }, - regs::macros::RegisterBase, + regs::{ + self, + macros::RegisterBase, // + }, }; /// Type specifying the `Fsp` falcon engine. Cannot be instantiated. @@ -29,3 +36,53 @@ impl RegisterBase<PFalcon2Base> for Fsp { impl FalconEngine for Fsp { const ID: Self = Fsp(()); } + +impl Falcon<Fsp> { + /// Writes `data` to FSP external memory at byte `offset` using Falcon PIO. + /// + /// Returns `EINVAL` if offset or data length is not 4-byte aligned. + #[expect(unused)] + pub(crate) fn write_emem(&self, bar: &Bar0, offset: u32, data: &[u8]) -> Result { + // TODO: replace with `is_multiple_of` once the MSRV is >= 1.82. + if offset % 4 != 0 || data.len() % 4 != 0 { + return Err(EINVAL); + } + + regs::NV_PFALCON_FALCON_EMEM_CTL::default() + .set_wr_mode(true) + .set_offset(offset) + .write(bar, &Fsp::ID); + + for chunk in data.chunks_exact(4) { + let word = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + regs::NV_PFALCON_FALCON_EMEM_DATA::default() + .set_data(word) + .write(bar, &Fsp::ID); + } + + Ok(()) + } + + /// Reads FSP external memory at byte `offset` into `data` using Falcon PIO. + /// + /// Returns `EINVAL` if offset or data length is not 4-byte aligned. + #[expect(unused)] + pub(crate) fn read_emem(&self, bar: &Bar0, offset: u32, data: &mut [u8]) -> Result { + // TODO: replace with `is_multiple_of` once the MSRV is >= 1.82. + if offset % 4 != 0 || data.len() % 4 != 0 { + return Err(EINVAL); + } + + regs::NV_PFALCON_FALCON_EMEM_CTL::default() + .set_rd_mode(true) + .set_offset(offset) + .write(bar, &Fsp::ID); + + for chunk in data.chunks_exact_mut(4) { + let word = regs::NV_PFALCON_FALCON_EMEM_DATA::read(bar, &Fsp::ID).data(); + chunk.copy_from_slice(&word.to_le_bytes()); + } + + Ok(()) + } +} diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index ea0d32f5396c..1ae57cc42a9f 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -8,6 +8,7 @@ pub(crate) mod macros; use kernel::{ + io::Io, prelude::*, time, // }; @@ -431,6 +432,18 @@ pub(crate) fn reset_engine<E: FalconEngine>(bar: &Bar0) { 8:8 br_fetch as bool; }); +// GP102 EMEM PIO registers (used by FSP for Hopper/Blackwell) +// These registers provide falcon external memory communication interface +register!(NV_PFALCON_FALCON_EMEM_CTL @ PFalconBase[0x00000ac0] { + 23:0 offset as u32; // EMEM byte offset (must be 4-byte aligned) + 24:24 wr_mode as bool; // Write mode + 25:25 rd_mode as bool; // Read mode +}); + +register!(NV_PFALCON_FALCON_EMEM_DATA @ PFalconBase[0x00000ac4] { + 31:0 data as u32; // EMEM data register +}); + // The modules below provide registers that are not identical on all supported chips. They should // only be used in HAL modules. -- 2.53.0
