Re: [PATCH v4 17/33] gpu: nova-core: Hopper/Blackwell: add FSP falcon EMEM operations

Danilo Krummrich
2026-02-11T10:57:52Z

On Tue Feb 10, 2026 at 3:45 AM CET, John Hubbard wrote:
> 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 <gary@garyguo.net>
> Cc: Timur Tabi <ttabi@nvidia.com>
> Signed-off-by: John Hubbard <jhubbard@nvidia.com>
> ---
> 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(())
> + }
> +}

Technically, we could represent this as a separate I/O backend and use IoView /
IoSlice (once we have it).

So, you could have Falcon<Fsp>::emem(), which returns an &Emem that implements
Io [1].

This way we would get IoView and register!() for free on top of it. IoView will
allow you to modify fields of the FSP structures similar to what we have for DMA
with dma_read!() and dma_write!().

I just briefly glanced at the subsequent patches, but it looks like this could
save quite some code.

We may not get the full potential right away, as IoView is still WIP, but I
think it makes sense to consider it for a follow-up.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git/tree/rust/kernel/io.rs?h=driver-core-next#n303