[PATCH v3 18/33] gpu: nova-core: gsp: add the GMC boot event dispatcher
From: John Hubbard
Date: Thu Sep 17 2026 - 21:12:40 EST
The two load-and-execute events of the r000 boot protocol arrive as GMC
events, each named by a command id.
Nova-core had a handler for each event, but no dispatcher that read the
id and picked the handler, and each handler ended with its own copy of
the core resume.
Add the dispatcher, and move the core resume out of the two handlers
into it. Each handler now ends with the falcon halted, and the
dispatcher runs the core resume once after whichever handler ran. The
dispatcher has no caller yet.
Assisted-by: LLM
Reviewed-by: Timur Tabi <ttabi@xxxxxxxxxx>
Reviewed-by: Zhi Wang <zhiw@xxxxxxxxxx>
Signed-off-by: John Hubbard <jhubbard@xxxxxxxxxx>
---
drivers/gpu/nova-core/gsp/boot.rs | 69 +++++++++++++++++++++++--------
drivers/gpu/nova-core/gsp/fw.rs | 10 +++++
2 files changed, 62 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index ee366d782218..c805b42dd7bc 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -39,7 +39,11 @@
},
gsp::{
cmdq::Cmdq,
- commands, //
+ commands,
+ fw::{
+ GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER,
+ GMCAPI_CMD_EXEC_HS_BINARY, //
+ }, //
},
regs,
sbuffer::SBufferIter, //
@@ -141,10 +145,51 @@ fn core_resume(&self) -> Result {
Ok(())
}
+ /// Runs the load-and-execute handler that `command_id` names on the event payload, which the
+ /// ring may have split in two, and then runs the core resume.
+ ///
+ /// # Errors
+ ///
+ /// - `EINVAL` if `command_id` is not a load-and-execute command.
+ ///
+ /// Errors from the handlers and from [`Self::core_resume`] are propagated as-is.
+ #[expect(dead_code)]
+ fn dispatch_gmc_boot_event(
+ &self,
+ command_id: u32,
+ payload_0: &[u8],
+ payload_1: &[u8],
+ ) -> Result {
+ let handled = match command_id {
+ GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER => {
+ self.handle_load_exec_bootloader(payload_0, payload_1)
+ }
+ GMCAPI_CMD_EXEC_HS_BINARY => self.handle_load_exec_hs_binary(payload_0, payload_1),
+ _ => {
+ dev_err!(
+ self.dev,
+ "Unexpected GMC boot event: command_id={:#010x}\n",
+ command_id
+ );
+ return Err(EINVAL);
+ }
+ };
+
+ handled.and_then(|()| self.core_resume()).inspect_err(|e| {
+ dev_err!(
+ self.dev,
+ "GMC boot event {:#010x} failed: {:?}\n",
+ command_id,
+ e
+ );
+ })
+ }
+
/// Runs the generic bootloader on the GSP falcon, as a `GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER`
- /// event requests, and then restarts GSP-RM.
+ /// event requests.
///
- /// The descriptor that the event carries names the image that the bootloader loads.
+ /// The descriptor that the event carries names the image that the bootloader loads. The GSP
+ /// falcon is left halted.
///
/// # Errors
///
@@ -153,9 +198,6 @@ fn core_resume(&self) -> Result {
/// it, or if the event names a context DMA slot or an aperture that does not exist.
/// - `ETIMEDOUT` if the RISC-V core does not suspend within two seconds, or the GSP falcon does
/// not halt within two seconds of starting the image.
- ///
- /// Errors from [`Self::core_resume`] are propagated as-is.
- #[expect(dead_code)]
fn handle_load_exec_bootloader(&self, payload_0: &[u8], payload_1: &[u8]) -> Result {
let Self {
gsp_falcon, dev, ..
@@ -205,16 +247,14 @@ fn handle_load_exec_bootloader(&self, payload_0: &[u8], payload_1: &[u8]) -> Res
Ok(())
},
- )?;
-
- self.core_resume()
+ )
}
/// Runs a Heavy-Secured (HS) binary on the GSP falcon, as a `GMCAPI_CMD_EXEC_HS_BINARY` event
- /// requests, and then restarts GSP-RM.
+ /// requests.
///
/// GSP-RM has placed the binary in the framebuffer, and the falcon's boot ROM (BROM) verifies
- /// the binary's signature before the binary runs.
+ /// the binary's signature before the binary runs. The GSP falcon is left halted.
///
/// # Errors
///
@@ -222,9 +262,6 @@ fn handle_load_exec_bootloader(&self, payload_0: &[u8], payload_1: &[u8]) -> Res
/// fit the BROM register field.
/// - `ETIMEDOUT` if the RISC-V core does not suspend within two seconds, or the GSP falcon does
/// not halt within two seconds of starting the binary.
- ///
- /// Errors from [`Self::core_resume`] are propagated as-is.
- #[expect(dead_code)]
fn handle_load_exec_hs_binary(&self, payload_0: &[u8], payload_1: &[u8]) -> Result {
let Self {
gsp_falcon, dev, ..
@@ -297,9 +334,7 @@ fn handle_load_exec_hs_binary(&self, payload_0: &[u8], payload_1: &[u8]) -> Resu
Ok(())
},
- )?;
-
- self.core_resume()
+ )
}
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index f23d071f0e16..e86283f67358 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -1055,6 +1055,16 @@ pub(crate) struct GmcApiHeader {
/// Bits of [`GmcApiHeader::command`] that hold the command id. The high byte holds flags.
const GMCAPI_COMMAND_ID_MASK: u32 = 0x00ff_ffff;
+/// GMC event that requests the driver to run the generic falcon bootloader on the descriptor that
+/// the event carries.
+pub(crate) const GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER: u32 =
+ r000_00::GMCAPI_COMMANDS_GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER;
+
+/// GMC event that requests the driver to run a Heavy-Secured (HS) binary that GSP-RM has placed in
+/// the framebuffer.
+pub(crate) const GMCAPI_CMD_EXEC_HS_BINARY: u32 =
+ r000_00::GMCAPI_COMMANDS_GMCAPI_CMD_EXEC_HS_BINARY;
+
static_assert!(size_of::<GmcApiHeader>() == size_of::<r000_00::GMCAPI_HEADER>());
static_assert!(
core::mem::offset_of!(GmcApiHeader, command)
--
2.55.0