John Hubbard
2025-Dec-03 05:59 UTC
[PATCH 15/31] gpu: nova-core: Hopper/Blackwell: add FSP falcon EMEM operations
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.
Signed-off-by: John Hubbard <jhubbard at nvidia.com>
---
drivers/gpu/nova-core/falcon/fsp.rs | 60 ++++++++++++++++++++++++++++-
drivers/gpu/nova-core/regs.rs | 10 +++++
2 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/falcon/fsp.rs
b/drivers/gpu/nova-core/falcon/fsp.rs
index 7323ae2f2302..9e796e82e556 100644
--- a/drivers/gpu/nova-core/falcon/fsp.rs
+++ b/drivers/gpu/nova-core/falcon/fsp.rs
@@ -5,15 +5,27 @@
//! 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, //
+ },
};
+/// EMEM control register bit 24: write mode.
+const EMEM_CTL_WRITE: u32 = 1 << 24;
+/// EMEM control register bit 25: read mode.
+const EMEM_CTL_READ: u32 = 1 << 25;
+
/// Type specifying the `Fsp` falcon engine. Cannot be instantiated.
#[allow(dead_code)]
pub(crate) struct Fsp(());
@@ -30,3 +42,49 @@ 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.
+ #[allow(dead_code)]
+ pub(crate) fn write_emem(&self, bar: &Bar0, offset: u32, data:
&[u8]) -> Result {
+ if offset % 4 != 0 || data.len() % 4 != 0 {
+ return Err(EINVAL);
+ }
+
+ regs::NV_PFALCON_FALCON_EMEM_CTL::default()
+ .set_value(EMEM_CTL_WRITE | 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.
+ #[allow(dead_code)]
+ pub(crate) fn read_emem(&self, bar: &Bar0, offset: u32, data:
&mut [u8]) -> Result {
+ if offset % 4 != 0 || data.len() % 4 != 0 {
+ return Err(EINVAL);
+ }
+
+ regs::NV_PFALCON_FALCON_EMEM_CTL::default()
+ .set_value(EMEM_CTL_READ | 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 82cc6c0790e5..b642cee9611d 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -391,6 +391,16 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
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] {
+ 31:0 value as u32; // EMEM control register
+});
+
+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.52.0
Timur Tabi
2025-Dec-03 06:04 UTC
[PATCH 15/31] gpu: nova-core: Hopper/Blackwell: add FSP falcon EMEM operations
On Tue, 2025-12-02 at 21:59 -0800, John Hubbard wrote:> +??? /// Returns `EINVAL` if offset or data length is not 4-byte aligned. > +??? #[allow(dead_code)] > +??? pub(crate) fn write_emem(&self, bar: &Bar0, offset: u32, data: &[u8]) -> Result { > +??????? if offset % 4 != 0 || data.len() % 4 != 0 { > +??????????? return Err(EINVAL); > +??????? } > + > +??????? regs::NV_PFALCON_FALCON_EMEM_CTL::default() > +??????????? .set_value(EMEM_CTL_WRITE | 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(()) > +??? }So as you know, this is basically the same as my pio_wr_bytes function (which I should probably rename to pio_wr_slice since it writes a slice now). What do you think about extending FalconMem to include Emem and then update pio_wr_bytes/slice to handle Emem like it does Imem and Dmem?