Re: [PATCH 17/27] gpu: nova-core: gsp: add GMC dispatch on receive
From: Zhi Wang
Date: Thu Aug 20 2026 - 08:34:58 EST
On Tue, 18 Aug 2026 20:52:10 -0700
John Hubbard <jhubbard@xxxxxxxxxx> wrote:
> The r000 boot path delivers the load-and-execute steps as GMC events
> keyed by command id, where the RPC path keys on a function code, so
> the boot loop needs its own dispatch.
snip
> + fn receive_gmc_and_dispatch<R>(
> + &mut self,
> + timeout: Delta,
> + handler: impl FnOnce(u32, &[u8], &[u8]) -> Option<R>,
> + ) -> Result<Option<R>> {
> + let message = self.wait_for_gmc_msg(timeout)?;
> + let header = message.header;
> + let length = header.length();
> +
> + // The RPC and GMC elements share every field through
> `nvdm_header`, so `gmc` holds an
> + // RPC header rather than a GMC one unless the NVDM type
> says otherwise.
> + let result = if header.is_gmc_api() {
> + let command_id = header.gmc.command_id();
> +
IMO, should we check header.gmc.size == payload_length()? as now we
know it is a GMC message now. Not sure if gmc.size < payload_length()
is a valid use case.
> + dev_dbg!(
> + &self.dev,
> + "GSP GMC: event: seq# {}, command_id=0x{:x},
> length=0x{:x}\n",
> + header.gmc.sequence,
> + command_id,
> + length,
> + );
> +
> + handler(command_id, message.contents.0,
> message.contents.1)
> + } else {
> + dev_warn!(&self.dev, "GSP GMC: dropping non-GMC queue
> element\n");
> + None
> + };
> +
> + self.gsp_mem
> +
> .advance_cpu_read_ptr(u32::try_from(length.div_ceil(GSP_PAGE_SIZE))?);
> +
> + Ok(result)
> + }
> }
> diff --git a/drivers/gpu/nova-core/gsp/fw.rs
> b/drivers/gpu/nova-core/gsp/fw.rs index 9e6b5ec6aadb..83f7d2042aa1
> 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs
> +++ b/drivers/gpu/nova-core/gsp/fw.rs
> @@ -975,8 +975,19 @@ pub(crate) struct GmcApiHeader {
> reserved: [u32; 5],
> }
>
> +/// Command identifier bits of [`GmcApiHeader::command`], matching
> Open RM's +/// `GMCAPI_HEADER_COMMAND_ID_MASK`. The remaining byte
> carries flags. +const GMCAPI_COMMAND_ID_MASK: u32 = 0x00ff_ffff;
> +
> static_assert!(size_of::<GmcApiHeader>() == 40);
>
> +impl GmcApiHeader {
> + /// Returns the command identifier, without the flag byte.
> + pub(crate) fn command_id(&self) -> u32 {
> + self.command & GMCAPI_COMMAND_ID_MASK
> + }
> +}
> +
> // SAFETY: All fields are integer types with no uninitialized
> padding bytes. unsafe impl AsBytes for GmcApiHeader {}
>
> @@ -1070,6 +1081,15 @@ pub(crate) fn has_valid_magic(&self) -> bool {
> self.mctp_magic == MCTP_MAGIC
> }
>
> + /// Returns `true` if the NVDM header routes this element to the
> GSP's GMC dispatch.
> + ///
> + /// A [`GspMsgElement`] and a [`GspGmcMsgElement`] share every
> field through `nvdm_header`,
> + /// so the NVDM type is what distinguishes the two on the queue,
> and `gmc` holds an RPC
> + /// header rather than a GMC one when this returns `false`.
> + pub(crate) fn is_gmc_api(&self) -> bool {
> + self.nvdm_header.validate(NvdmType::GmcApi)
> + }
> +
> /// Returns the number of elements (i.e. memory pages) used by
> this message. pub(crate) fn element_count(&self) -> u32 {
> self.mctp_payload_size