Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 00/14] nova-core: Complete GSP boot and begin RPC communication
Hello!
These patches a refresh of the series adding support for final stages of the
GSP boot process where a sequencer which inteprets firmware instructions needs
to run to boot the GSP processor, followed by waiting for an INIT_DONE message
from the GSP and finally retrieving GPU information from the GSP.
The patches are based on Alex's github branch which have several
prerequisites:
Repo: https://github.com/Gnurou/linux.git Branch: b4/gsp_boot
The entire tree (these patches + patches from Alex, Alistair, John) can be
found at the git repository:
git: git://git.kernel.org/pub/scm/linux/kernel/git/jfern/linux.git
tag: nova-sequencer-init-done-v3
I also dropped several patches (mainly from John that have already been
applied). Tested on my Ampere GA102 and I see in dmesg:
NovaCore 0000:00:07.0: GPU name: NVIDIA GeForce RTX 3090 Ti
Changes from v2 to v3:
- Added several tags.
- Fixed commit messages, style errors.
- Added GspStaticInfo patch.
- Fixed bug found by Timur in the sequencer code (related to ignoring messages).
- Removed excessive dev_dbg prints in sequencer code (John Hubbard).
Previous series:
v1: https://lore.kernel.org/all/20250829173254.2068763-1-joelagnelf at
nvidia.com/
v2: https://lore.kernel.org/all/20251102235920.3784592-1-joelagnelf at
nvidia.com/
Alistair Popple (2):
gpu: nova-core: gsp: Wait for gsp initialization to complete
gpu: nova-core: gsp: Retrieve GSP static info to gather GPU
information
Joel Fernandes (12):
gpu: nova-core: falcon: Move waiting until halted to a helper
gpu: nova-core: falcon: Move start functionality into separate helper
gpu: nova-core: falcon: Move mbox functionalities into helper
gpu: nova-core: falcon: Move dma_reset functionality into helper
gpu: nova-core: gsp: Add support for checking if GSP reloaded
gpu: nova-core: Add bindings required by GSP sequencer
gpu: nova-core: Implement the GSP sequencer
gpu: nova-core: sequencer: Add register opcodes
gpu: nova-core: sequencer: Add delay opcode support
gpu: nova-core: sequencer: Implement basic core operations
gpu: nova-core: sequencer: Implement core resume operation
gpu: nova-core: sequencer: Refactor run() to handle unknown messages
drivers/gpu/nova-core/falcon.rs | 101 +++--
drivers/gpu/nova-core/falcon/gsp.rs | 17 +
drivers/gpu/nova-core/gsp.rs | 1 +
drivers/gpu/nova-core/gsp/boot.rs | 35 +-
drivers/gpu/nova-core/gsp/cmdq.rs | 1 -
drivers/gpu/nova-core/gsp/commands.rs | 102 ++++-
drivers/gpu/nova-core/gsp/fw.rs | 47 ++
.../gpu/nova-core/gsp/fw/r570_144/bindings.rs | 248 +++++++++++
drivers/gpu/nova-core/gsp/sequencer.rs | 404 ++++++++++++++++++
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/regs.rs | 6 +
drivers/gpu/nova-core/sbuffer.rs | 1 -
drivers/gpu/nova-core/util.rs | 16 +
13 files changed, 943 insertions(+), 37 deletions(-)
create mode 100644 drivers/gpu/nova-core/gsp/sequencer.rs
create mode 100644 drivers/gpu/nova-core/util.rs
base-commit: 8adec6f12a71641402fdc77e7d514ceee019312b
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 01/14] gpu: nova-core: falcon: Move waiting until halted to a helper
Move the "waiting until halted" functionality into a helper so that we
can use it in the sequencer, which is a separate sequencer operation.
Reviewed-by: Lyude Paul <lyude at redhat.com>
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index dc8c2179935e..dc883ce5f28b 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -540,6 +540,19 @@ pub(crate) fn dma_load<F: FalconFirmware<Target =
E>>(&self, bar: &Bar0, fw: &F)
Ok(())
}
+ /// Wait until the falcon CPU is halted.
+ pub(crate) fn wait_till_halted(&self, bar: &Bar0) ->
Result<()> {
+ // TIMEOUT: arbitrarily large value, firmwares should complete in less
than 2 seconds.
+ read_poll_timeout(
+ || Ok(regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID)),
+ |r| r.halted(),
+ Delta::ZERO,
+ Delta::from_secs(2),
+ )?;
+
+ Ok(())
+ }
+
/// Runs the loaded firmware and waits for its completion.
///
/// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0`
and `MBOX1` registers
@@ -574,13 +587,7 @@ pub(crate) fn boot(
.write(bar, &E::ID),
}
- // TIMEOUT: arbitrarily large value, firmwares should complete in less
than 2 seconds.
- read_poll_timeout(
- || Ok(regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID)),
- |r| r.halted(),
- Delta::ZERO,
- Delta::from_secs(2),
- )?;
+ self.wait_till_halted(bar)?;
let (mbox0, mbox1) = (
regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value(),
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 02/14] gpu: nova-core: falcon: Move start functionality into separate helper
Move start functionality into a separate helper so we can use it from
the sequencer.
Reviewed-by: Lyude Paul <lyude at redhat.com>
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index dc883ce5f28b..1bcee06fdec2 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -553,7 +553,21 @@ pub(crate) fn wait_till_halted(&self, bar: &Bar0)
-> Result<()> {
Ok(())
}
- /// Runs the loaded firmware and waits for its completion.
+ /// Start the falcon CPU.
+ pub(crate) fn start(&self, bar: &Bar0) -> Result<()> {
+ match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en()
{
+ true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
+ .set_startcpu(true)
+ .write(bar, &E::ID),
+ false => regs::NV_PFALCON_FALCON_CPUCTL::default()
+ .set_startcpu(true)
+ .write(bar, &E::ID),
+ }
+
+ Ok(())
+ }
+
+ /// Start running the loaded firmware.
///
/// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0`
and `MBOX1` registers
/// prior to running.
@@ -578,15 +592,7 @@ pub(crate) fn boot(
.write(bar, &E::ID);
}
- match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en()
{
- true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
- .set_startcpu(true)
- .write(bar, &E::ID),
- false => regs::NV_PFALCON_FALCON_CPUCTL::default()
- .set_startcpu(true)
- .write(bar, &E::ID),
- }
-
+ self.start(bar)?;
self.wait_till_halted(bar)?;
let (mbox0, mbox1) = (
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 03/14] gpu: nova-core: falcon: Move mbox functionalities into helper
Move falcon reading/writing to mbox functionality into helper so we can
use it from the sequencer resume flow.
Reviewed-by: Lyude Paul <lyude at redhat.com>
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 51 +++++++++++++++++++++++----------
1 file changed, 36 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 1bcee06fdec2..181347feb3ca 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -567,19 +567,13 @@ pub(crate) fn start(&self, bar: &Bar0) ->
Result<()> {
Ok(())
}
- /// Start running the loaded firmware.
- ///
- /// `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(
+ /// Writes values to the mailbox registers if provided.
+ pub(crate) fn write_mailboxes(
&self,
bar: &Bar0,
mbox0: Option<u32>,
mbox1: Option<u32>,
- ) -> Result<(u32, u32)> {
+ ) -> Result<()> {
if let Some(mbox0) = mbox0 {
regs::NV_PFALCON_FALCON_MAILBOX0::default()
.set_value(mbox0)
@@ -591,18 +585,45 @@ pub(crate) fn boot(
.set_value(mbox1)
.write(bar, &E::ID);
}
+ Ok(())
+ }
- self.start(bar)?;
- self.wait_till_halted(bar)?;
+ /// Reads the value from mbox0 register.
+ pub(crate) fn read_mailbox0(&self, bar: &Bar0) ->
Result<u32> {
+ Ok(regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value())
+ }
- let (mbox0, mbox1) = (
- regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value(),
- regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value(),
- );
+ /// Reads the value from mbox1 register.
+ pub(crate) fn read_mailbox1(&self, bar: &Bar0) ->
Result<u32> {
+ Ok(regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value())
+ }
+ /// Reads values from both mailbox registers.
+ pub(crate) fn read_mailboxes(&self, bar: &Bar0) ->
Result<(u32, u32)> {
+ let mbox0 = self.read_mailbox0(bar)?;
+ let mbox1 = self.read_mailbox1(bar)?;
Ok((mbox0, mbox1))
}
+ /// Start running the loaded firmware.
+ ///
+ /// `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)> {
+ self.write_mailboxes(bar, mbox0, mbox1)?;
+ self.start(bar)?;
+ self.wait_till_halted(bar)?;
+ self.read_mailboxes(bar)
+ }
+
/// 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(
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 04/14] gpu: nova-core: falcon: Move dma_reset functionality into helper
Move dma_reset so we can use it for the upcoming sequencer
functionality.
Reviewed-by: Lyude Paul <lyude at redhat.com>
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 181347feb3ca..964033ded3f2 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -371,6 +371,12 @@ pub(crate) fn new(dev: &device::Device, chipset:
Chipset) -> Result<Self> {
})
}
+ /// Resets DMA-related registers.
+ pub(crate) fn dma_reset(&self, bar: &Bar0) {
+ regs::NV_PFALCON_FBIF_CTL::update(bar, &E::ID, |v|
v.set_allow_phys_no_ctx(true));
+ regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID);
+ }
+
/// Wait for memory scrubbing to complete.
fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result {
// TIMEOUT: memory scrubbing should complete in less than 20ms.
@@ -520,8 +526,7 @@ fn dma_wr<F: FalconFirmware<Target = E>>(
/// 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::update(bar, &E::ID, |v|
v.set_allow_phys_no_ctx(true));
- regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID);
+ self.dma_reset(bar);
regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| {
v.set_target(FalconFbifTarget::CoherentSysmem)
.set_mem_type(FalconFbifMemType::Physical)
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 05/14] gpu: nova-core: gsp: Add support for checking if GSP reloaded
During the sequencer process, we need to check if GSP was successfully
reloaded. Add functionality to check for the same.
Reviewed-by: Lyude Paul <lyude at redhat.com>
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/falcon/gsp.rs | 18 ++++++++++++++++++
drivers/gpu/nova-core/regs.rs | 6 ++++++
2 files changed, 24 insertions(+)
diff --git a/drivers/gpu/nova-core/falcon/gsp.rs
b/drivers/gpu/nova-core/falcon/gsp.rs
index f17599cb49fa..e0c0b18ec5bf 100644
--- a/drivers/gpu/nova-core/falcon/gsp.rs
+++ b/drivers/gpu/nova-core/falcon/gsp.rs
@@ -1,5 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
+use kernel::{
+ io::poll::read_poll_timeout,
+ prelude::*,
+ time::Delta, //
+};
+
use crate::{
driver::Bar0,
falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
@@ -29,4 +35,16 @@ pub(crate) fn clear_swgen0_intr(&self, bar: &Bar0) {
.set_swgen0(true)
.write(bar, &Gsp::ID);
}
+
+ /// Checks if GSP reload/resume has completed during the boot process.
+ #[expect(dead_code)]
+ pub(crate) fn check_reload_completed(&self, bar: &Bar0, timeout:
Delta) -> Result<bool> {
+ read_poll_timeout(
+ || Ok(regs::NV_PGC6_BSI_SECURE_SCRATCH_14::read(bar)),
+ |val| val.boot_stage_3_handoff(),
+ Delta::ZERO,
+ timeout,
+ )
+ .map(|_| true)
+ }
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index c945adf63b9e..cb7f60a6b911 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -124,6 +124,12 @@ pub(crate) fn higher_bound(self) -> u64 {
// These scratch registers remain powered on even in a low-power state and have
a designated group
// number.
+// Boot Sequence Interface (BSI) register used to determine
+// if GSP reload/resume has completed during the boot process.
+register!(NV_PGC6_BSI_SECURE_SCRATCH_14 @ 0x001180f8 {
+ 26:26 boot_stage_3_handoff as bool;
+});
+
// Privilege level mask register. It dictates whether the host CPU has
privilege to access the
// `PGC6_AON_SECURE_SCRATCH_GROUP_05` register (which it needs to read
GFW_BOOT).
register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128,
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 06/14] gpu: nova-core: Add bindings required by GSP sequencer
Add several firmware bindings required by GSP sequencer code.
Co-developed-by: Alistair Popple <apopple at nvidia.com>
Signed-off-by: Alistair Popple <apopple at nvidia.com>
Reviewed-by: Lyude Paul <lyude at redhat.com>
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/gsp/fw.rs | 45 ++++++++++
.../gpu/nova-core/gsp/fw/r570_144/bindings.rs | 85 +++++++++++++++++++
2 files changed, 130 insertions(+)
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 687749bdbb45..53e28458cd7d 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -543,6 +543,51 @@ pub(crate) fn element_count(&self) -> u32 {
}
}
+#[expect(unused)]
+pub(crate) use r570_144::{
+ // GSP sequencer run structure with information on how to run the
sequencer.
+ rpc_run_cpu_sequencer_v17_00,
+
+ // GSP sequencer structures.
+ GSP_SEQUENCER_BUFFER_CMD,
+ GSP_SEQ_BUF_OPCODE,
+
+ // GSP sequencer core operation opcodes.
+ GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET,
+ GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME,
+ GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START,
+ GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT,
+
+ // GSP sequencer delay opcode and payload.
+ GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US,
+
+ // GSP sequencer register opcodes.
+ GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY,
+ GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL,
+ GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE,
+ GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE,
+
+ // GSP sequencer delay payload structure.
+ GSP_SEQ_BUF_PAYLOAD_DELAY_US,
+
+ // GSP sequencer register payload structures.
+ GSP_SEQ_BUF_PAYLOAD_REG_MODIFY,
+ GSP_SEQ_BUF_PAYLOAD_REG_POLL,
+ GSP_SEQ_BUF_PAYLOAD_REG_STORE,
+ GSP_SEQ_BUF_PAYLOAD_REG_WRITE, //
+};
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for GSP_SEQUENCER_BUFFER_CMD {}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for rpc_run_cpu_sequencer_v17_00 {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for rpc_run_cpu_sequencer_v17_00 {}
+
// SAFETY: Padding is explicit and will not contain uninitialized data.
unsafe impl AsBytes for GspMsgElement {}
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index 32933874ff97..c5c589c1e2ac 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -664,6 +664,7 @@ pub struct PACKED_REGISTRY_TABLE {
pub numEntries: u32_,
pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>,
}
+
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, Zeroable)]
pub struct msgqTxHeader {
@@ -702,3 +703,87 @@ fn default() -> Self {
}
}
}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct rpc_run_cpu_sequencer_v17_00 {
+ pub bufferSizeDWord: u32_,
+ pub cmdIndex: u32_,
+ pub regSaveArea: [u32_; 8usize],
+ pub commandBuffer: __IncompleteArrayField<u32_>,
+}
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE: GSP_SEQ_BUF_OPCODE =
0;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY: GSP_SEQ_BUF_OPCODE
= 1;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL: GSP_SEQ_BUF_OPCODE =
2;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US: GSP_SEQ_BUF_OPCODE =
3;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE: GSP_SEQ_BUF_OPCODE =
4;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET: GSP_SEQ_BUF_OPCODE
= 5;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START: GSP_SEQ_BUF_OPCODE
= 6;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT:
GSP_SEQ_BUF_OPCODE = 7;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME: GSP_SEQ_BUF_OPCODE
= 8;
+pub type GSP_SEQ_BUF_OPCODE = ffi::c_uint;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GSP_SEQ_BUF_PAYLOAD_REG_WRITE {
+ pub addr: u32_,
+ pub val: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GSP_SEQ_BUF_PAYLOAD_REG_MODIFY {
+ pub addr: u32_,
+ pub mask: u32_,
+ pub val: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GSP_SEQ_BUF_PAYLOAD_REG_POLL {
+ pub addr: u32_,
+ pub mask: u32_,
+ pub val: u32_,
+ pub timeout: u32_,
+ pub error: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GSP_SEQ_BUF_PAYLOAD_DELAY_US {
+ pub val: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GSP_SEQ_BUF_PAYLOAD_REG_STORE {
+ pub addr: u32_,
+ pub index: u32_,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct GSP_SEQUENCER_BUFFER_CMD {
+ pub opCode: GSP_SEQ_BUF_OPCODE,
+ pub payload: GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1 {
+ pub regWrite: GSP_SEQ_BUF_PAYLOAD_REG_WRITE,
+ pub regModify: GSP_SEQ_BUF_PAYLOAD_REG_MODIFY,
+ pub regPoll: GSP_SEQ_BUF_PAYLOAD_REG_POLL,
+ pub delayUs: GSP_SEQ_BUF_PAYLOAD_DELAY_US,
+ pub regStore: GSP_SEQ_BUF_PAYLOAD_REG_STORE,
+}
+impl Default for GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+impl Default for GSP_SEQUENCER_BUFFER_CMD {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 07/14] gpu: nova-core: Implement the GSP sequencer
Implement the GSP sequencer which culminates in INIT_DONE message being
received from the GSP indicating that the GSP has successfully booted.
This is just initial sequencer support, the actual commands will be
added in the next patches.
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/gsp.rs | 1 +
drivers/gpu/nova-core/gsp/boot.rs | 19 ++-
drivers/gpu/nova-core/gsp/cmdq.rs | 1 -
drivers/gpu/nova-core/gsp/sequencer.rs | 205 +++++++++++++++++++++++++
drivers/gpu/nova-core/sbuffer.rs | 1 -
5 files changed, 224 insertions(+), 3 deletions(-)
create mode 100644 drivers/gpu/nova-core/gsp/sequencer.rs
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 36175eafaf2e..9d62aea3c782 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -16,6 +16,7 @@
pub(crate) mod cmdq;
pub(crate) mod commands;
mod fw;
+mod sequencer;
use fw::GspArgumentsCached;
use fw::LibosMemoryRegionInitArgument;
diff --git a/drivers/gpu/nova-core/gsp/boot.rs
b/drivers/gpu/nova-core/gsp/boot.rs
index 649c758eda70..761020a11153 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -19,7 +19,13 @@
};
use crate::gpu::Chipset;
use crate::gsp::commands::{build_registry, set_system_info};
-use crate::gsp::GspFwWprMeta;
+use crate::gsp::{
+ sequencer::{
+ GspSequencer,
+ GspSequencerParams, //
+ },
+ GspFwWprMeta, //
+};
use crate::regs;
use crate::vbios::Vbios;
@@ -204,6 +210,17 @@ pub(crate) fn boot(
gsp_falcon.is_riscv_active(bar),
);
+ // Create and run the GSP sequencer.
+ let seq_params = GspSequencerParams {
+ gsp_fw: &gsp_fw,
+ libos_dma_handle: libos_handle,
+ gsp_falcon,
+ sec2_falcon,
+ dev: pdev.as_ref(),
+ bar,
+ };
+ GspSequencer::run(&mut self.cmdq, seq_params,
Delta::from_secs(10))?;
+
Ok(())
}
}
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs
b/drivers/gpu/nova-core/gsp/cmdq.rs
index 0fb8ff26ba2f..0185629a3b5c 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -418,7 +418,6 @@ struct FullCommand<M> {
Ok(())
}
- #[expect(unused)]
pub(crate) fn receive_msg_from_gsp<M: MessageFromGsp, R>(
&mut self,
timeout: Delta,
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs
b/drivers/gpu/nova-core/gsp/sequencer.rs
new file mode 100644
index 000000000000..ee096c04d9eb
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! GSP Sequencer implementation for Pre-hopper GSP boot sequence.
+
+use core::mem::size_of;
+use kernel::alloc::flags::GFP_KERNEL;
+use kernel::device;
+use kernel::prelude::*;
+use kernel::time::Delta;
+use kernel::transmute::FromBytes;
+
+use crate::driver::Bar0;
+use crate::falcon::{
+ gsp::Gsp,
+ sec2::Sec2,
+ Falcon, //
+};
+use crate::firmware::gsp::GspFirmware;
+use crate::gsp::cmdq::{
+ Cmdq,
+ MessageFromGsp, //
+};
+use crate::gsp::fw;
+
+use kernel::{
+ dev_dbg,
+ dev_err, //
+};
+
+impl MessageFromGsp for fw::rpc_run_cpu_sequencer_v17_00 {
+ const FUNCTION: fw::MsgFunction = fw::MsgFunction::GspRunCpuSequencer;
+}
+
+const CMD_SIZE: usize = size_of::<fw::GSP_SEQUENCER_BUFFER_CMD>();
+
+struct GspSequencerInfo<'a> {
+ info: &'a fw::rpc_run_cpu_sequencer_v17_00,
+ cmd_data: KVec<u8>,
+}
+
+/// GSP Sequencer Command types with payload data.
+/// Commands have an opcode and a opcode-dependent struct.
+#[allow(dead_code)]
+pub(crate) enum GspSeqCmd {}
+
+impl GspSeqCmd {
+ /// Creates a new GspSeqCmd from a firmware GSP_SEQUENCER_BUFFER_CMD.
+ pub(crate) fn from_fw_cmd(_cmd: &fw::GSP_SEQUENCER_BUFFER_CMD) ->
Result<Self> {
+ Err(EINVAL)
+ }
+
+ pub(crate) fn new(data: &[u8], dev:
&device::Device<device::Bound>) -> Result<Self> {
+ let fw_cmd =
fw::GSP_SEQUENCER_BUFFER_CMD::from_bytes(data).ok_or(EINVAL)?;
+ let cmd = Self::from_fw_cmd(fw_cmd)?;
+
+ if data.len() < cmd.size_bytes() {
+ dev_err!(dev, "data is not enough for command");
+ return Err(EINVAL);
+ }
+
+ Ok(cmd)
+ }
+
+ /// Get the size of this command in bytes, the command consists of
+ /// a 4-byte opcode, and a variable-sized payload.
+ pub(crate) fn size_bytes(&self) -> usize {
+ 0
+ }
+}
+
+#[expect(dead_code)]
+pub(crate) struct GspSequencer<'a> {
+ seq_info: GspSequencerInfo<'a>,
+ bar: &'a Bar0,
+ sec2_falcon: &'a Falcon<Sec2>,
+ gsp_falcon: &'a Falcon<Gsp>,
+ libos_dma_handle: u64,
+ gsp_fw: &'a GspFirmware,
+ dev: &'a device::Device<device::Bound>,
+}
+
+pub(crate) trait GspSeqCmdRunner {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result;
+}
+
+impl GspSeqCmdRunner for GspSeqCmd {
+ fn run(&self, _seq: &GspSequencer<'_>) -> Result {
+ Ok(())
+ }
+}
+
+pub(crate) struct GspSeqIter<'a> {
+ cmd_data: &'a [u8],
+ current_offset: usize, // Tracking the current position.
+ total_cmds: u32,
+ cmds_processed: u32,
+ dev: &'a device::Device<device::Bound>,
+}
+
+impl<'a> Iterator for GspSeqIter<'a> {
+ type Item = Result<GspSeqCmd>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ // Stop if we've processed all commands or reached the end of data.
+ if self.cmds_processed >= self.total_cmds || self.current_offset
>= self.cmd_data.len() {
+ return None;
+ }
+
+ // Check if we have enough data for opcode.
+ let opcode_size = size_of::<fw::GSP_SEQ_BUF_OPCODE>();
+ if self.current_offset + opcode_size > self.cmd_data.len() {
+ return Some(Err(EINVAL));
+ }
+
+ let offset = self.current_offset;
+
+ // Handle command creation based on available data,
+ // zero-pad if necessary (since last command may not be full size).
+ let mut buffer = [0u8; CMD_SIZE];
+ let copy_len = if offset + CMD_SIZE <= self.cmd_data.len() {
+ CMD_SIZE
+ } else {
+ self.cmd_data.len() - offset
+ };
+ buffer[..copy_len].copy_from_slice(&self.cmd_data[offset..offset +
copy_len]);
+ let cmd_result = GspSeqCmd::new(&buffer, self.dev);
+
+ cmd_result.map_or_else(
+ |_err| {
+ dev_err!(self.dev, "Error parsing command at offset
{}", offset);
+ None
+ },
+ |cmd| {
+ self.current_offset += cmd.size_bytes();
+ self.cmds_processed += 1;
+ Some(Ok(cmd))
+ },
+ )
+ }
+}
+
+impl<'a, 'b> IntoIterator for &'b
GspSequencer<'a> {
+ type Item = Result<GspSeqCmd>;
+ type IntoIter = GspSeqIter<'b>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ let cmd_data = &self.seq_info.cmd_data[..];
+
+ GspSeqIter {
+ cmd_data,
+ current_offset: 0,
+ total_cmds: self.seq_info.info.cmdIndex,
+ cmds_processed: 0,
+ dev: self.dev,
+ }
+ }
+}
+
+/// Parameters for running the GSP sequencer.
+pub(crate) struct GspSequencerParams<'a> {
+ pub(crate) gsp_fw: &'a GspFirmware,
+ pub(crate) libos_dma_handle: u64,
+ pub(crate) gsp_falcon: &'a Falcon<Gsp>,
+ pub(crate) sec2_falcon: &'a Falcon<Sec2>,
+ pub(crate) dev: &'a device::Device<device::Bound>,
+ pub(crate) bar: &'a Bar0,
+}
+
+impl<'a> GspSequencer<'a> {
+ pub(crate) fn run(cmdq: &mut Cmdq, params:
GspSequencerParams<'a>, timeout: Delta) -> Result {
+ cmdq.receive_msg_from_gsp(timeout, |info, mut sbuf| {
+ let cmd_data = sbuf.flush_into_kvec(GFP_KERNEL)?;
+ let seq_info = GspSequencerInfo { info, cmd_data };
+
+ let sequencer = GspSequencer {
+ seq_info,
+ bar: params.bar,
+ sec2_falcon: params.sec2_falcon,
+ gsp_falcon: params.gsp_falcon,
+ libos_dma_handle: params.libos_dma_handle,
+ gsp_fw: params.gsp_fw,
+ dev: params.dev,
+ };
+
+ dev_dbg!(params.dev, "Running CPU Sequencer commands");
+
+ for cmd_result in &sequencer {
+ match cmd_result {
+ Ok(cmd) => cmd.run(&sequencer)?,
+ Err(e) => {
+ dev_err!(
+ params.dev,
+ "Error running command at index {}",
+ sequencer.seq_info.info.cmdIndex
+ );
+ return Err(e);
+ }
+ }
+ }
+
+ dev_dbg!(params.dev, "CPU Sequencer commands completed
successfully");
+ Ok(())
+ })
+ }
+}
diff --git a/drivers/gpu/nova-core/sbuffer.rs b/drivers/gpu/nova-core/sbuffer.rs
index 4d7cbc4bd060..36890c8610c2 100644
--- a/drivers/gpu/nova-core/sbuffer.rs
+++ b/drivers/gpu/nova-core/sbuffer.rs
@@ -162,7 +162,6 @@ pub(crate) fn read_exact(&mut self, mut dst: &mut
[u8]) -> Result {
/// Read all the remaining data into a [`KVec`].
///
/// `self` will be empty after this operation.
- #[expect(unused)]
pub(crate) fn flush_into_kvec(&mut self, flags: kernel::alloc::Flags)
-> Result<KVec<u8>> {
let mut buf = KVec::<u8>::new();
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 08/14] gpu: nova-core: sequencer: Add register opcodes
These opcodes are used for register write, modify, poll and store (save)
sequencer operations.
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/gsp/sequencer.rs | 106 +++++++++++++++++++++++--
1 file changed, 99 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs
b/drivers/gpu/nova-core/gsp/sequencer.rs
index ee096c04d9eb..32a0446b8c75 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -5,6 +5,7 @@
use core::mem::size_of;
use kernel::alloc::flags::GFP_KERNEL;
use kernel::device;
+use kernel::io::poll::read_poll_timeout;
use kernel::prelude::*;
use kernel::time::Delta;
use kernel::transmute::FromBytes;
@@ -40,13 +41,36 @@ struct GspSequencerInfo<'a> {
/// GSP Sequencer Command types with payload data.
/// Commands have an opcode and a opcode-dependent struct.
-#[allow(dead_code)]
-pub(crate) enum GspSeqCmd {}
+#[allow(clippy::enum_variant_names)]
+pub(crate) enum GspSeqCmd {
+ RegWrite(fw::GSP_SEQ_BUF_PAYLOAD_REG_WRITE),
+ RegModify(fw::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY),
+ RegPoll(fw::GSP_SEQ_BUF_PAYLOAD_REG_POLL),
+ RegStore(fw::GSP_SEQ_BUF_PAYLOAD_REG_STORE),
+}
impl GspSeqCmd {
/// Creates a new GspSeqCmd from a firmware GSP_SEQUENCER_BUFFER_CMD.
- pub(crate) fn from_fw_cmd(_cmd: &fw::GSP_SEQUENCER_BUFFER_CMD) ->
Result<Self> {
- Err(EINVAL)
+ pub(crate) fn from_fw_cmd(cmd: &fw::GSP_SEQUENCER_BUFFER_CMD) ->
Result<Self> {
+ match cmd.opCode {
+ fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE => {
+ // SAFETY: We're using the union field that corresponds to
the opCode.
+ Ok(GspSeqCmd::RegWrite(unsafe { cmd.payload.regWrite }))
+ }
+ fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY => {
+ // SAFETY: We're using the union field that corresponds to
the opCode.
+ Ok(GspSeqCmd::RegModify(unsafe { cmd.payload.regModify }))
+ }
+ fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL => {
+ // SAFETY: We're using the union field that corresponds to
the opCode.
+ Ok(GspSeqCmd::RegPoll(unsafe { cmd.payload.regPoll }))
+ }
+ fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE => {
+ // SAFETY: We're using the union field that corresponds to
the opCode.
+ Ok(GspSeqCmd::RegStore(unsafe { cmd.payload.regStore }))
+ }
+ _ => Err(EINVAL),
+ }
}
pub(crate) fn new(data: &[u8], dev:
&device::Device<device::Bound>) -> Result<Self> {
@@ -64,7 +88,16 @@ pub(crate) fn new(data: &[u8], dev:
&device::Device<device::Bound>) -> Result<Se
/// Get the size of this command in bytes, the command consists of
/// a 4-byte opcode, and a variable-sized payload.
pub(crate) fn size_bytes(&self) -> usize {
- 0
+ let opcode_size = size_of::<fw::GSP_SEQ_BUF_OPCODE>();
+ match self {
+ // For commands with payloads, add the payload size in bytes.
+ GspSeqCmd::RegWrite(_) => opcode_size +
size_of::<fw::GSP_SEQ_BUF_PAYLOAD_REG_WRITE>(),
+ GspSeqCmd::RegModify(_) => {
+ opcode_size +
size_of::<fw::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY>()
+ }
+ GspSeqCmd::RegPoll(_) => opcode_size +
size_of::<fw::GSP_SEQ_BUF_PAYLOAD_REG_POLL>(),
+ GspSeqCmd::RegStore(_) => opcode_size +
size_of::<fw::GSP_SEQ_BUF_PAYLOAD_REG_STORE>(),
+ }
}
}
@@ -83,12 +116,71 @@ pub(crate) trait GspSeqCmdRunner {
fn run(&self, sequencer: &GspSequencer<'_>) -> Result;
}
-impl GspSeqCmdRunner for GspSeqCmd {
- fn run(&self, _seq: &GspSequencer<'_>) -> Result {
+impl GspSeqCmdRunner for fw::GSP_SEQ_BUF_PAYLOAD_REG_WRITE {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result
{
+ let addr = self.addr as usize;
+ let val = self.val;
+ let _ = sequencer.bar.try_write32(val, addr);
+ Ok(())
+ }
+}
+
+impl GspSeqCmdRunner for fw::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result
{
+ let addr = self.addr as usize;
+ if let Ok(temp) = sequencer.bar.try_read32(addr) {
+ let _ = sequencer
+ .bar
+ .try_write32((temp & !self.mask) | self.val, addr);
+ }
Ok(())
}
}
+impl GspSeqCmdRunner for fw::GSP_SEQ_BUF_PAYLOAD_REG_POLL {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result
{
+ let addr = self.addr as usize;
+ let mut timeout_us = i64::from(self.timeout);
+
+ // Default timeout to 4 seconds.
+ timeout_us = if timeout_us == 0 { 4000000 } else { timeout_us };
+
+ // First read.
+ sequencer.bar.try_read32(addr)?;
+
+ // Poll the requested register with requested timeout.
+ read_poll_timeout(
+ || sequencer.bar.try_read32(addr),
+ |current| (current & self.mask) == self.val,
+ Delta::ZERO,
+ Delta::from_micros(timeout_us),
+ )
+ .map(|_| ())
+ }
+}
+
+impl GspSeqCmdRunner for fw::GSP_SEQ_BUF_PAYLOAD_REG_STORE {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result
{
+ let addr = self.addr as usize;
+ let _index = self.index;
+
+ let _val = sequencer.bar.try_read32(addr)?;
+
+ Ok(())
+ }
+}
+
+impl GspSeqCmdRunner for GspSeqCmd {
+ fn run(&self, seq: &GspSequencer<'_>) -> Result {
+ match self {
+ GspSeqCmd::RegWrite(cmd) => cmd.run(seq),
+ GspSeqCmd::RegModify(cmd) => cmd.run(seq),
+ GspSeqCmd::RegPoll(cmd) => cmd.run(seq),
+ GspSeqCmd::RegStore(cmd) => cmd.run(seq),
+ }
+ }
+}
+
pub(crate) struct GspSeqIter<'a> {
cmd_data: &'a [u8],
current_offset: usize, // Tracking the current position.
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 09/14] gpu: nova-core: sequencer: Add delay opcode support
Implement a sequencer opcode for delay operations.
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/gsp/sequencer.rs | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs
b/drivers/gpu/nova-core/gsp/sequencer.rs
index 32a0446b8c75..17118967a8d4 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -4,6 +4,7 @@
use core::mem::size_of;
use kernel::alloc::flags::GFP_KERNEL;
+use kernel::bindings;
use kernel::device;
use kernel::io::poll::read_poll_timeout;
use kernel::prelude::*;
@@ -46,6 +47,7 @@ pub(crate) enum GspSeqCmd {
RegWrite(fw::GSP_SEQ_BUF_PAYLOAD_REG_WRITE),
RegModify(fw::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY),
RegPoll(fw::GSP_SEQ_BUF_PAYLOAD_REG_POLL),
+ DelayUs(fw::GSP_SEQ_BUF_PAYLOAD_DELAY_US),
RegStore(fw::GSP_SEQ_BUF_PAYLOAD_REG_STORE),
}
@@ -65,6 +67,10 @@ pub(crate) fn from_fw_cmd(cmd:
&fw::GSP_SEQUENCER_BUFFER_CMD) -> Result<Self> {
// SAFETY: We're using the union field that corresponds to
the opCode.
Ok(GspSeqCmd::RegPoll(unsafe { cmd.payload.regPoll }))
}
+ fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US => {
+ // SAFETY: We're using the union field that corresponds to
the opCode.
+ Ok(GspSeqCmd::DelayUs(unsafe { cmd.payload.delayUs }))
+ }
fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE => {
// SAFETY: We're using the union field that corresponds to
the opCode.
Ok(GspSeqCmd::RegStore(unsafe { cmd.payload.regStore }))
@@ -96,6 +102,7 @@ pub(crate) fn size_bytes(&self) -> usize {
opcode_size +
size_of::<fw::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY>()
}
GspSeqCmd::RegPoll(_) => opcode_size +
size_of::<fw::GSP_SEQ_BUF_PAYLOAD_REG_POLL>(),
+ GspSeqCmd::DelayUs(_) => opcode_size +
size_of::<fw::GSP_SEQ_BUF_PAYLOAD_DELAY_US>(),
GspSeqCmd::RegStore(_) => opcode_size +
size_of::<fw::GSP_SEQ_BUF_PAYLOAD_REG_STORE>(),
}
}
@@ -159,6 +166,21 @@ fn run(&self, sequencer:
&GspSequencer<'_>) -> Result {
}
}
+impl GspSeqCmdRunner for fw::GSP_SEQ_BUF_PAYLOAD_DELAY_US {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result
{
+ dev_dbg!(sequencer.dev, "DelayUs: val=0x{:x}\n", self.val);
+ // SAFETY: `usleep_range_state` is safe to call with any parameter.
+ unsafe {
+ bindings::usleep_range_state(
+ self.val as usize,
+ self.val as usize,
+ bindings::TASK_UNINTERRUPTIBLE,
+ )
+ };
+ Ok(())
+ }
+}
+
impl GspSeqCmdRunner for fw::GSP_SEQ_BUF_PAYLOAD_REG_STORE {
fn run(&self, sequencer: &GspSequencer<'_>) -> Result
{
let addr = self.addr as usize;
@@ -176,6 +198,7 @@ fn run(&self, seq: &GspSequencer<'_>)
-> Result {
GspSeqCmd::RegWrite(cmd) => cmd.run(seq),
GspSeqCmd::RegModify(cmd) => cmd.run(seq),
GspSeqCmd::RegPoll(cmd) => cmd.run(seq),
+ GspSeqCmd::DelayUs(cmd) => cmd.run(seq),
GspSeqCmd::RegStore(cmd) => cmd.run(seq),
}
}
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 10/14] gpu: nova-core: sequencer: Implement basic core operations
These opcodes implement various falcon-related boot operations: reset,
start, wait-for-halt.
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/gsp/sequencer.rs | 27 ++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs
b/drivers/gpu/nova-core/gsp/sequencer.rs
index 17118967a8d4..0192ac61df4c 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -49,6 +49,9 @@ pub(crate) enum GspSeqCmd {
RegPoll(fw::GSP_SEQ_BUF_PAYLOAD_REG_POLL),
DelayUs(fw::GSP_SEQ_BUF_PAYLOAD_DELAY_US),
RegStore(fw::GSP_SEQ_BUF_PAYLOAD_REG_STORE),
+ CoreReset,
+ CoreStart,
+ CoreWaitForHalt,
}
impl GspSeqCmd {
@@ -75,6 +78,11 @@ pub(crate) fn from_fw_cmd(cmd:
&fw::GSP_SEQUENCER_BUFFER_CMD) -> Result<Self> {
// SAFETY: We're using the union field that corresponds to
the opCode.
Ok(GspSeqCmd::RegStore(unsafe { cmd.payload.regStore }))
}
+ fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET =>
Ok(GspSeqCmd::CoreReset),
+ fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START =>
Ok(GspSeqCmd::CoreStart),
+ fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT =>
{
+ Ok(GspSeqCmd::CoreWaitForHalt)
+ }
_ => Err(EINVAL),
}
}
@@ -96,6 +104,9 @@ pub(crate) fn new(data: &[u8], dev:
&device::Device<device::Bound>) -> Result<Se
pub(crate) fn size_bytes(&self) -> usize {
let opcode_size = size_of::<fw::GSP_SEQ_BUF_OPCODE>();
match self {
+ // Each simple command type just adds 4 bytes (opcode_size) for the
header.
+ GspSeqCmd::CoreReset | GspSeqCmd::CoreStart |
GspSeqCmd::CoreWaitForHalt => opcode_size,
+
// For commands with payloads, add the payload size in bytes.
GspSeqCmd::RegWrite(_) => opcode_size +
size_of::<fw::GSP_SEQ_BUF_PAYLOAD_REG_WRITE>(),
GspSeqCmd::RegModify(_) => {
@@ -200,6 +211,22 @@ fn run(&self, seq: &GspSequencer<'_>)
-> Result {
GspSeqCmd::RegPoll(cmd) => cmd.run(seq),
GspSeqCmd::DelayUs(cmd) => cmd.run(seq),
GspSeqCmd::RegStore(cmd) => cmd.run(seq),
+ GspSeqCmd::CoreReset => {
+ dev_dbg!(seq.dev, "CoreReset\n");
+ seq.gsp_falcon.reset(seq.bar)?;
+ seq.gsp_falcon.dma_reset(seq.bar);
+ Ok(())
+ }
+ GspSeqCmd::CoreStart => {
+ dev_dbg!(seq.dev, "CoreStart\n");
+ seq.gsp_falcon.start(seq.bar)?;
+ Ok(())
+ }
+ GspSeqCmd::CoreWaitForHalt => {
+ dev_dbg!(seq.dev, "CoreWaitForHalt\n");
+ seq.gsp_falcon.wait_till_halted(seq.bar)?;
+ Ok(())
+ }
}
}
}
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 11/14] gpu: nova-core: sequencer: Implement core resume operation
Implement core resume operation. This is the last step of the sequencer
resulting in resume of the GSP and proceeding to INIT_DONE stage of GSP
boot.
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/falcon/gsp.rs | 1 -
drivers/gpu/nova-core/gsp/fw.rs | 1 -
drivers/gpu/nova-core/gsp/sequencer.rs | 49 ++++++++++++++++++++++++--
3 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon/gsp.rs
b/drivers/gpu/nova-core/falcon/gsp.rs
index e0c0b18ec5bf..391699dc3a8c 100644
--- a/drivers/gpu/nova-core/falcon/gsp.rs
+++ b/drivers/gpu/nova-core/falcon/gsp.rs
@@ -37,7 +37,6 @@ pub(crate) fn clear_swgen0_intr(&self, bar: &Bar0) {
}
/// Checks if GSP reload/resume has completed during the boot process.
- #[expect(dead_code)]
pub(crate) fn check_reload_completed(&self, bar: &Bar0, timeout:
Delta) -> Result<bool> {
read_poll_timeout(
|| Ok(regs::NV_PGC6_BSI_SECURE_SCRATCH_14::read(bar)),
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 53e28458cd7d..bb79f92432aa 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -543,7 +543,6 @@ pub(crate) fn element_count(&self) -> u32 {
}
}
-#[expect(unused)]
pub(crate) use r570_144::{
// GSP sequencer run structure with information on how to run the
sequencer.
rpc_run_cpu_sequencer_v17_00,
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs
b/drivers/gpu/nova-core/gsp/sequencer.rs
index 0192ac61df4c..3b4796425d0b 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -52,6 +52,7 @@ pub(crate) enum GspSeqCmd {
CoreReset,
CoreStart,
CoreWaitForHalt,
+ CoreResume,
}
impl GspSeqCmd {
@@ -83,6 +84,7 @@ pub(crate) fn from_fw_cmd(cmd:
&fw::GSP_SEQUENCER_BUFFER_CMD) -> Result<Self> {
fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT =>
{
Ok(GspSeqCmd::CoreWaitForHalt)
}
+ fw::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME =>
Ok(GspSeqCmd::CoreResume),
_ => Err(EINVAL),
}
}
@@ -105,7 +107,10 @@ pub(crate) fn size_bytes(&self) -> usize {
let opcode_size = size_of::<fw::GSP_SEQ_BUF_OPCODE>();
match self {
// Each simple command type just adds 4 bytes (opcode_size) for the
header.
- GspSeqCmd::CoreReset | GspSeqCmd::CoreStart |
GspSeqCmd::CoreWaitForHalt => opcode_size,
+ GspSeqCmd::CoreReset
+ | GspSeqCmd::CoreStart
+ | GspSeqCmd::CoreWaitForHalt
+ | GspSeqCmd::CoreResume => opcode_size,
// For commands with payloads, add the payload size in bytes.
GspSeqCmd::RegWrite(_) => opcode_size +
size_of::<fw::GSP_SEQ_BUF_PAYLOAD_REG_WRITE>(),
@@ -119,7 +124,6 @@ pub(crate) fn size_bytes(&self) -> usize {
}
}
-#[expect(dead_code)]
pub(crate) struct GspSequencer<'a> {
seq_info: GspSequencerInfo<'a>,
bar: &'a Bar0,
@@ -227,6 +231,47 @@ fn run(&self, seq: &GspSequencer<'_>)
-> Result {
seq.gsp_falcon.wait_till_halted(seq.bar)?;
Ok(())
}
+ GspSeqCmd::CoreResume => {
+ dev_dbg!(seq.dev, "CoreResume\n");
+ // At this point, 'SEC2-RTOS' has been loaded into SEC2
by the sequencer
+ // but neither SEC2-RTOS nor GSP-RM is running yet. This part
of the
+ // sequencer will start both.
+
+ // Reset the GSP to prepare it for resuming.
+ seq.gsp_falcon.reset(seq.bar)?;
+
+ // Write the libOS DMA handle to GSP mailboxes.
+ seq.gsp_falcon.write_mailboxes(
+ seq.bar,
+ Some(seq.libos_dma_handle as u32),
+ Some((seq.libos_dma_handle >> 32) as u32),
+ )?;
+
+ // Start the SEC2 falcon which will trigger GSP-RM to resume on
the GSP.
+ seq.sec2_falcon.start(seq.bar)?;
+
+ // Poll until GSP-RM reload/resume has completed (up to 2
seconds).
+ seq.gsp_falcon
+ .check_reload_completed(seq.bar, Delta::from_secs(2))?;
+
+ // Verify SEC2 completed successfully by checking its mailbox
for errors.
+ let mbox0 = seq.sec2_falcon.read_mailbox0(seq.bar)?;
+ if mbox0 != 0 {
+ dev_err!(seq.dev, "Sequencer: sec2 errors:
{:?}\n", mbox0);
+ return Err(EIO);
+ }
+
+ // Configure GSP with the bootloader version.
+ seq.gsp_falcon
+ .write_os_version(seq.bar,
seq.gsp_fw.bootloader.app_version);
+
+ // Verify the GSP's RISC-V core is active indicating
successful GSP boot.
+ if !seq.gsp_falcon.is_riscv_active(seq.bar) {
+ dev_err!(seq.dev, "Sequencer: RISC-V core is not
active\n");
+ return Err(EIO);
+ }
+ Ok(())
+ }
}
}
}
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 12/14] gpu: nova-core: gsp: Wait for gsp initialization to complete
From: Alistair Popple <apopple at nvidia.com>
This adds the GSP init done command to wait for GSP initialization
to complete. Once this command has been received the GSP is fully
operational and will respond properly to normal RPC commands.
Signed-off-by: Alistair Popple <apopple at nvidia.com>
Co-developed-by: Joel Fernandes <joelagnelf at nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 8 +++++-
drivers/gpu/nova-core/gsp/commands.rs | 39 +++++++++++++++++++++++++--
2 files changed, 44 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs
b/drivers/gpu/nova-core/gsp/boot.rs
index 761020a11153..0dd8099f5f8c 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -18,7 +18,11 @@
FIRMWARE_VERSION,
};
use crate::gpu::Chipset;
-use crate::gsp::commands::{build_registry, set_system_info};
+use crate::gsp::commands::{
+ build_registry,
+ gsp_init_done,
+ set_system_info, //
+};
use crate::gsp::{
sequencer::{
GspSequencer,
@@ -221,6 +225,8 @@ pub(crate) fn boot(
};
GspSequencer::run(&mut self.cmdq, seq_params,
Delta::from_secs(10))?;
+ gsp_init_done(&mut self.cmdq, Delta::from_secs(10))?;
+
Ok(())
}
}
diff --git a/drivers/gpu/nova-core/gsp/commands.rs
b/drivers/gpu/nova-core/gsp/commands.rs
index 338d1695027f..521e252c2805 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -4,16 +4,51 @@
use kernel::device;
use kernel::pci;
use kernel::prelude::*;
-use kernel::transmute::AsBytes;
+use kernel::time::Delta;
+use kernel::transmute::{
+ AsBytes,
+ FromBytes, //
+};
use super::fw::commands::*;
use super::fw::MsgFunction;
use crate::driver::Bar0;
use crate::gsp::cmdq::Cmdq;
-use crate::gsp::cmdq::{CommandToGsp, CommandToGspBase,
CommandToGspWithPayload};
+use crate::gsp::cmdq::{
+ CommandToGsp,
+ CommandToGspBase,
+ CommandToGspWithPayload,
+ MessageFromGsp, //
+};
use crate::gsp::GSP_PAGE_SIZE;
use crate::sbuffer::SBufferIter;
+/// Message type for GSP initialization done notification.
+struct GspInitDone {}
+
+// SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it
+// trivially has no uninitialized bytes.
+unsafe impl AsBytes for GspInitDone {}
+
+// SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it
+// trivially has no uninitialized bytes.
+unsafe impl FromBytes for GspInitDone {}
+
+impl MessageFromGsp for GspInitDone {
+ const FUNCTION: MsgFunction = MsgFunction::GspInitDone;
+}
+
+/// Waits for GSP initialization to complete.
+pub(crate) fn gsp_init_done(cmdq: &mut Cmdq, timeout: Delta) -> Result {
+ loop {
+ match cmdq.receive_msg_from_gsp::<GspInitDone, ()>(timeout, |_,
_| Ok(())) {
+ Ok(()) => break Ok(()),
+ Err(ERANGE) => continue,
+ Err(e) => break Err(e),
+ }
+ }
+}
+
// For now we hard-code the registry entries. Future work will allow others to
// be added as module parameters.
const GSP_REGISTRY_NUM_ENTRIES: usize = 3;
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 13/14] gpu: nova-core: sequencer: Refactor run() to handle unknown messages
Refactor GspSequencer::run() to follow the same pattern as gsp_init_done()
by wrapping message reception in a loop that ignores unknown messages
(ERANGE errors).
Suggested-by: Timur Tabi <ttabi at nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/gsp/sequencer.rs | 80 +++++++++++++++-----------
1 file changed, 46 insertions(+), 34 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs
b/drivers/gpu/nova-core/gsp/sequencer.rs
index 3b4796425d0b..a96a4fa74f29 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -35,8 +35,8 @@ impl MessageFromGsp for fw::rpc_run_cpu_sequencer_v17_00 {
const CMD_SIZE: usize = size_of::<fw::GSP_SEQUENCER_BUFFER_CMD>();
-struct GspSequencerInfo<'a> {
- info: &'a fw::rpc_run_cpu_sequencer_v17_00,
+struct GspSequencerInfo {
+ cmd_index: u32,
cmd_data: KVec<u8>,
}
@@ -125,7 +125,7 @@ pub(crate) fn size_bytes(&self) -> usize {
}
pub(crate) struct GspSequencer<'a> {
- seq_info: GspSequencerInfo<'a>,
+ seq_info: GspSequencerInfo,
bar: &'a Bar0,
sec2_falcon: &'a Falcon<Sec2>,
gsp_falcon: &'a Falcon<Gsp>,
@@ -336,7 +336,7 @@ fn into_iter(self) -> Self::IntoIter {
GspSeqIter {
cmd_data,
current_offset: 0,
- total_cmds: self.seq_info.info.cmdIndex,
+ total_cmds: self.seq_info.cmd_index,
cmds_processed: 0,
dev: self.dev,
}
@@ -355,38 +355,50 @@ pub(crate) struct GspSequencerParams<'a> {
impl<'a> GspSequencer<'a> {
pub(crate) fn run(cmdq: &mut Cmdq, params:
GspSequencerParams<'a>, timeout: Delta) -> Result {
- cmdq.receive_msg_from_gsp(timeout, |info, mut sbuf| {
- let cmd_data = sbuf.flush_into_kvec(GFP_KERNEL)?;
- let seq_info = GspSequencerInfo { info, cmd_data };
-
- let sequencer = GspSequencer {
- seq_info,
- bar: params.bar,
- sec2_falcon: params.sec2_falcon,
- gsp_falcon: params.gsp_falcon,
- libos_dma_handle: params.libos_dma_handle,
- gsp_fw: params.gsp_fw,
- dev: params.dev,
- };
-
- dev_dbg!(params.dev, "Running CPU Sequencer commands");
-
- for cmd_result in &sequencer {
- match cmd_result {
- Ok(cmd) => cmd.run(&sequencer)?,
- Err(e) => {
- dev_err!(
- params.dev,
- "Error running command at index {}",
- sequencer.seq_info.info.cmdIndex
- );
- return Err(e);
- }
+ let seq_info = loop {
+ match cmdq.receive_msg_from_gsp(
+ timeout,
+ |info: &fw::rpc_run_cpu_sequencer_v17_00, mut sbuf| {
+ let cmd_data = sbuf.flush_into_kvec(GFP_KERNEL)?;
+ Ok(GspSequencerInfo {
+ cmd_index: info.cmdIndex,
+ cmd_data,
+ })
+ },
+ ) {
+ Ok(seq_info) => break seq_info,
+ Err(ERANGE) => continue,
+ Err(e) => return Err(e),
+ }
+ };
+
+ let sequencer = GspSequencer {
+ seq_info,
+ bar: params.bar,
+ sec2_falcon: params.sec2_falcon,
+ gsp_falcon: params.gsp_falcon,
+ libos_dma_handle: params.libos_dma_handle,
+ gsp_fw: params.gsp_fw,
+ dev: params.dev,
+ };
+
+ dev_dbg!(params.dev, "Running CPU Sequencer commands");
+
+ for cmd_result in &sequencer {
+ match cmd_result {
+ Ok(cmd) => cmd.run(&sequencer)?,
+ Err(e) => {
+ dev_err!(
+ params.dev,
+ "Error running command at index {}",
+ sequencer.seq_info.cmd_index
+ );
+ return Err(e);
}
}
+ }
- dev_dbg!(params.dev, "CPU Sequencer commands completed
successfully");
- Ok(())
- })
+ dev_dbg!(params.dev, "CPU Sequencer commands completed
successfully");
+ Ok(())
}
}
--
2.34.1
Joel Fernandes
2025-Nov-06 23:11 UTC
[PATCH v3 14/14] gpu: nova-core: gsp: Retrieve GSP static info to gather GPU information
From: Alistair Popple <apopple at nvidia.com>
After GSP initialization is complete, retrieve the static configuration
information from GSP-RM. This information includes GPU name, capabilities,
memory configuration, and other properties. On some GPU variants, it is
also required to do this for initialization to complete.
Signed-off-by: Alistair Popple <apopple at nvidia.com>
Co-developed-by: Joel Fernandes <joelagnelf at nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf at nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 8 +
drivers/gpu/nova-core/gsp/commands.rs | 63 +++++++
drivers/gpu/nova-core/gsp/fw.rs | 3 +
.../gpu/nova-core/gsp/fw/r570_144/bindings.rs | 163 ++++++++++++++++++
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/util.rs | 16 ++
6 files changed, 254 insertions(+)
create mode 100644 drivers/gpu/nova-core/util.rs
diff --git a/drivers/gpu/nova-core/gsp/boot.rs
b/drivers/gpu/nova-core/gsp/boot.rs
index 0dd8099f5f8c..b8588ff8d21e 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -20,6 +20,7 @@
use crate::gpu::Chipset;
use crate::gsp::commands::{
build_registry,
+ get_gsp_info,
gsp_init_done,
set_system_info, //
};
@@ -31,6 +32,7 @@
GspFwWprMeta, //
};
use crate::regs;
+use crate::util;
use crate::vbios::Vbios;
impl super::Gsp {
@@ -226,6 +228,12 @@ pub(crate) fn boot(
GspSequencer::run(&mut self.cmdq, seq_params,
Delta::from_secs(10))?;
gsp_init_done(&mut self.cmdq, Delta::from_secs(10))?;
+ let info = get_gsp_info(&mut self.cmdq, bar)?;
+ dev_info!(
+ pdev.as_ref(),
+ "GPU name: {}\n",
+ util::str_from_null_terminated(&info.gpu_name)
+ );
Ok(())
}
diff --git a/drivers/gpu/nova-core/gsp/commands.rs
b/drivers/gpu/nova-core/gsp/commands.rs
index 521e252c2805..e70067a49d85 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -11,6 +11,7 @@
};
use super::fw::commands::*;
+use super::fw::GspStaticConfigInfo_t;
use super::fw::MsgFunction;
use crate::driver::Bar0;
use crate::gsp::cmdq::Cmdq;
@@ -23,6 +24,17 @@
use crate::gsp::GSP_PAGE_SIZE;
use crate::sbuffer::SBufferIter;
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for GspStaticConfigInfo_t {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for GspStaticConfigInfo_t {}
+
+pub(crate) struct GspStaticConfigInfo {
+ pub gpu_name: [u8; 40],
+}
+
/// Message type for GSP initialization done notification.
struct GspInitDone {}
@@ -49,6 +61,57 @@ pub(crate) fn gsp_init_done(cmdq: &mut Cmdq, timeout:
Delta) -> Result {
}
}
+impl MessageFromGsp for GspStaticConfigInfo_t {
+ const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
+}
+
+impl CommandToGspBase for GspStaticConfigInfo_t {
+ const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
+}
+
+impl CommandToGsp for GspStaticConfigInfo_t {}
+
+// SAFETY: This struct only contains integer types and fixed-size arrays for
which
+// all bit patterns are valid.
+unsafe impl Zeroable for GspStaticConfigInfo_t {}
+
+impl GspStaticConfigInfo_t {
+ fn init() -> impl Init<Self> {
+ init!(GspStaticConfigInfo_t {
+ ..Zeroable::init_zeroed()
+ })
+ }
+}
+
+pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) ->
Result<GspStaticConfigInfo> {
+ cmdq.send_gsp_command(bar, GspStaticConfigInfo_t::init())?;
+ cmdq.receive_msg_from_gsp::<GspStaticConfigInfo_t,
GspStaticConfigInfo>(
+ Delta::from_secs(5),
+ |info, _| {
+ let gpu_name_str = info
+ .gpuNameString
+ .get(
+ 0..=info
+ .gpuNameString
+ .iter()
+ .position(|&b| b == 0)
+ .unwrap_or(info.gpuNameString.len() - 1),
+ )
+ .and_then(|bytes| CStr::from_bytes_with_nul(bytes).ok())
+ .and_then(|cstr| cstr.to_str().ok())
+ .unwrap_or("invalid utf8");
+
+ let mut gpu_name = [0u8; 40];
+ let bytes = gpu_name_str.as_bytes();
+ let copy_len = core::cmp::min(bytes.len(), gpu_name.len());
+ gpu_name[..copy_len].copy_from_slice(&bytes[..copy_len]);
+ gpu_name[copy_len] = b'\0';
+
+ Ok(GspStaticConfigInfo { gpu_name })
+ },
+ )
+}
+
// For now we hard-code the registry entries. Future work will allow others to
// be added as module parameters.
const GSP_REGISTRY_NUM_ENTRIES: usize = 3;
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index bb79f92432aa..62bac19fcdee 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -547,6 +547,9 @@ pub(crate) fn element_count(&self) -> u32 {
// GSP sequencer run structure with information on how to run the
sequencer.
rpc_run_cpu_sequencer_v17_00,
+ // GSP static configuration information.
+ GspStaticConfigInfo_t,
+
// GSP sequencer structures.
GSP_SEQUENCER_BUFFER_CMD,
GSP_SEQ_BUF_OPCODE,
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index c5c589c1e2ac..f081ac1708e6 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -320,6 +320,77 @@ fn fmt(&self, fmt: &mut
::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
pub const NV_VGPU_MSG_EVENT_NUM_EVENTS: _bindgen_ty_3 = 4131;
pub type _bindgen_ty_3 = ffi::c_uint;
#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS {
+ pub totalVFs: u32_,
+ pub firstVfOffset: u32_,
+ pub vfFeatureMask: u32_,
+ pub FirstVFBar0Address: u64_,
+ pub FirstVFBar1Address: u64_,
+ pub FirstVFBar2Address: u64_,
+ pub bar0Size: u64_,
+ pub bar1Size: u64_,
+ pub bar2Size: u64_,
+ pub b64bitBar0: u8_,
+ pub b64bitBar1: u8_,
+ pub b64bitBar2: u8_,
+ pub bSriovEnabled: u8_,
+ pub bSriovHeavyEnabled: u8_,
+ pub bEmulateVFBar0TlbInvalidationRegister: u8_,
+ pub bClientRmAllocatedCtxBuffer: u8_,
+ pub bNonPowerOf2ChannelCountSupported: u8_,
+ pub bVfResizableBAR1Supported: u8_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS {
+ pub BoardID: u32_,
+ pub chipSKU: [ffi::c_char; 9usize],
+ pub chipSKUMod: [ffi::c_char; 5usize],
+ pub skuConfigVersion: u32_,
+ pub project: [ffi::c_char; 5usize],
+ pub projectSKU: [ffi::c_char; 5usize],
+ pub CDP: [ffi::c_char; 6usize],
+ pub projectSKUMod: [ffi::c_char; 2usize],
+ pub businessCycle: u32_,
+}
+pub type NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG = [u8_;
17usize];
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO {
+ pub base: u64_,
+ pub limit: u64_,
+ pub reserved: u64_,
+ pub performance: u32_,
+ pub supportCompressed: u8_,
+ pub supportISO: u8_,
+ pub bProtected: u8_,
+ pub blackList: NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS {
+ pub numFBRegions: u32_,
+ pub fbRegion: [NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
+ pub index: u32_,
+ pub flags: u32_,
+ pub length: u32_,
+ pub data: [u8_; 256usize],
+}
+impl Default for NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
#[derive(Debug, Default, Copy, Clone, Zeroable)]
pub struct DOD_METHOD_DATA {
pub status: u32_,
@@ -367,6 +438,19 @@ pub struct ACPI_METHOD_DATA {
pub capsMethodData: CAPS_METHOD_DATA,
}
#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS {
+ pub headIndex: u32_,
+ pub maxHResolution: u32_,
+ pub maxVResolution: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS {
+ pub numHeads: u32_,
+ pub maxNumHeads: u32_,
+}
+#[repr(C)]
#[derive(Debug, Default, Copy, Clone, Zeroable)]
pub struct BUSINFO {
pub deviceID: u16_,
@@ -395,6 +479,85 @@ pub struct GSP_PCIE_CONFIG_REG {
pub linkCap: u32_,
}
#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct EcidManufacturingInfo {
+ pub ecidLow: u32_,
+ pub ecidHigh: u32_,
+ pub ecidExtended: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct FW_WPR_LAYOUT_OFFSET {
+ pub nonWprHeapOffset: u64_,
+ pub frtsOffset: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct GspStaticConfigInfo_t {
+ pub grCapsBits: [u8_; 23usize],
+ pub gidInfo: NV2080_CTRL_GPU_GET_GID_INFO_PARAMS,
+ pub SKUInfo: NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS,
+ pub fbRegionInfoParams: NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS,
+ pub sriovCaps: NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS,
+ pub sriovMaxGfid: u32_,
+ pub engineCaps: [u32_; 3usize],
+ pub poisonFuseEnabled: u8_,
+ pub fb_length: u64_,
+ pub fbio_mask: u64_,
+ pub fb_bus_width: u32_,
+ pub fb_ram_type: u32_,
+ pub fbp_mask: u64_,
+ pub l2_cache_size: u32_,
+ pub gpuNameString: [u8_; 64usize],
+ pub gpuShortNameString: [u8_; 64usize],
+ pub gpuNameString_Unicode: [u16_; 64usize],
+ pub bGpuInternalSku: u8_,
+ pub bIsQuadroGeneric: u8_,
+ pub bIsQuadroAd: u8_,
+ pub bIsNvidiaNvs: u8_,
+ pub bIsVgx: u8_,
+ pub bGeforceSmb: u8_,
+ pub bIsTitan: u8_,
+ pub bIsTesla: u8_,
+ pub bIsMobile: u8_,
+ pub bIsGc6Rtd3Allowed: u8_,
+ pub bIsGc8Rtd3Allowed: u8_,
+ pub bIsGcOffRtd3Allowed: u8_,
+ pub bIsGcoffLegacyAllowed: u8_,
+ pub bIsMigSupported: u8_,
+ pub RTD3GC6TotalBoardPower: u16_,
+ pub RTD3GC6PerstDelay: u16_,
+ pub bar1PdeBase: u64_,
+ pub bar2PdeBase: u64_,
+ pub bVbiosValid: u8_,
+ pub vbiosSubVendor: u32_,
+ pub vbiosSubDevice: u32_,
+ pub bPageRetirementSupported: u8_,
+ pub bSplitVasBetweenServerClientRm: u8_,
+ pub bClRootportNeedsNosnoopWAR: u8_,
+ pub displaylessMaxHeads: VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS,
+ pub displaylessMaxResolution: VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS,
+ pub displaylessMaxPixels: u64_,
+ pub hInternalClient: u32_,
+ pub hInternalDevice: u32_,
+ pub hInternalSubdevice: u32_,
+ pub bSelfHostedMode: u8_,
+ pub bAtsSupported: u8_,
+ pub bIsGpuUefi: u8_,
+ pub bIsEfiInit: u8_,
+ pub ecidInfo: [EcidManufacturingInfo; 2usize],
+ pub fwWprLayoutOffset: FW_WPR_LAYOUT_OFFSET,
+}
+impl Default for GspStaticConfigInfo_t {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
#[derive(Debug, Default, Copy, Clone, Zeroable)]
pub struct GspSystemInfo {
pub gpuPhysAddr: u64_,
diff --git a/drivers/gpu/nova-core/nova_core.rs
b/drivers/gpu/nova-core/nova_core.rs
index c1121e7c64c5..b98a1c03f13d 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -16,6 +16,7 @@
mod num;
mod regs;
mod sbuffer;
+mod util;
mod vbios;
pub(crate) const MODULE_NAME: &kernel::str::CStr = <LocalModule as
kernel::ModuleMetadata>::NAME;
diff --git a/drivers/gpu/nova-core/util.rs b/drivers/gpu/nova-core/util.rs
new file mode 100644
index 000000000000..f1a4dea44c10
--- /dev/null
+++ b/drivers/gpu/nova-core/util.rs
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/// Converts a null-terminated byte array to a string slice.
+///
+/// Returns "invalid" if the bytes are not valid UTF-8 or not
null-terminated.
+pub(crate) fn str_from_null_terminated(bytes: &[u8]) -> &str {
+ use kernel::str::CStr;
+
+ // Find the first null byte, then create a slice that includes it.
+ bytes
+ .iter()
+ .position(|&b| b == 0)
+ .and_then(|null_pos|
CStr::from_bytes_with_nul(&bytes[..=null_pos]).ok())
+ .and_then(|cstr| cstr.to_str().ok())
+ .unwrap_or("invalid")
+}
--
2.34.1