public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/8] gpu: nova-core: gsp: add RM control command infrastructure
@ 2026-04-17 15:29 Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 1/8] gpu: nova-core: gsp: add NV_STATUS error code bindings Eliot Courtney
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-04-17 15:29 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	rust-for-linux, dri-devel, linux-kernel, Eliot Courtney

Add the infrastructure for sending RM control RPCs. This is needed e.g.
for channel allocation.

This series adds:
- `NV_STATUS` bindings and wrapping `GspMsgRmStatus` and similar enums,
  used by generic RPCs and RM control RPCs.
- The necessary bindings for the RM control RPCs.
- `RmControlMsgFunction` to identify individual control commands, like
  `MsgFunction` for GSP commands.
- `SBufferIter::read_to_vec` (using KVVec) for reading large RPC payloads
- A typed `RmControl` command that can send RM control commands.

Each new RM control command can be added by extending
`RmControlMsgFunction`, adding the bindings and wrappers for their
parameters, and writing a type-safe wrapper to send and receive the
reply for the RM control rpc, using `RmControl`.

This series applies on latest drm-rust-next at the listed commit.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
Changes in v4:
- Collect Reviewed-by on patches that haven't changed too much
- Split NvStatus enum into GspMsgRmStatus/Warning/Error
- Added GspRpcError for Cmdq errors
- Log NvStatus warnings
- Exhaustively match for NvStatus error -> errno errors
- Change a few NvStatus->errno mappings to match openrm
- Drop fault method buffer size patches
- Link to v3: https://patch.msgid.link/20260325-rmcontrol-v3-0-f3101093484e@nvidia.com

Changes in v3:
- Remove `send_rm_control` in favour of a typed `RmControl`
- Print out fault method buffer size at gsp boot
- Remove Reviewed-by and Tested-by on some patches that have changed
  substantively.
- Link to v2: https://lore.kernel.org/r/20260318-rmcontrol-v2-0-9a9fa6f1c4c3@nvidia.com

Changes in v2:
- Introduce typed Handle<T> for RM objects.
- Improve naming
- Improve doc comments
- Change SBufferIter to always use KVVec
- flush_into_kvec -> read_to_vec
- Rebased on latest cmdq locking
- Link to v1: https://lore.kernel.org/r/20260227-rmcontrol-v1-0-86648e4869f9@nvidia.com

---
Eliot Courtney (8):
      gpu: nova-core: gsp: add NV_STATUS error code bindings
      gpu: nova-core: gsp: add GSP-RM status types
      gpu: nova-core: gsp: add GspRpcError for Cmdq RPC error handling
      gpu: nova-core: gsp: expose GSP-RM internal client and subdevice handles
      gpu: nova-core: gsp: add RM control RPC structure binding
      gpu: nova-core: gsp: add types for RM control RPCs
      gpu: nova-core: use KVVec for SBufferIter flush
      gpu: nova-core: gsp: add RM control command infrastructure

 drivers/gpu/nova-core/gsp.rs                      |   1 +
 drivers/gpu/nova-core/gsp/cmdq.rs                 | 112 +++--
 drivers/gpu/nova-core/gsp/commands.rs             |  71 ++-
 drivers/gpu/nova-core/gsp/fw.rs                   | 544 +++++++++++++++++++++-
 drivers/gpu/nova-core/gsp/fw/commands.rs          |  19 +-
 drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 156 +++++++
 drivers/gpu/nova-core/gsp/fw/rm.rs                |  90 ++++
 drivers/gpu/nova-core/gsp/rm.rs                   |   3 +
 drivers/gpu/nova-core/gsp/rm/commands.rs          | 155 ++++++
 drivers/gpu/nova-core/gsp/sequencer.rs            |   9 +-
 drivers/gpu/nova-core/sbuffer.rs                  |   6 +-
 11 files changed, 1117 insertions(+), 49 deletions(-)
---
base-commit: a7a080bb4236ebe577b6776d940d1717912ff6dd
change-id: 20260225-rmcontrol-bd8a06fc3a0d

Best regards,
--  
Eliot Courtney <ecourtney@nvidia.com>


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v4 1/8] gpu: nova-core: gsp: add NV_STATUS error code bindings
  2026-04-17 15:29 [PATCH v4 0/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
@ 2026-04-17 15:29 ` Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 2/8] gpu: nova-core: gsp: add GSP-RM status types Eliot Courtney
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-04-17 15:29 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	rust-for-linux, dri-devel, linux-kernel, Eliot Courtney

Add bindgen generated constants for NV_STATUS. This is used for RM
control messages and also generic RPC status.

Reviewed-by: Joel Fernandes <joelagnelf@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 144 ++++++++++++++++++++++
 1 file changed, 144 insertions(+)

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 334e8be5fde8..dd37a7fd58c6 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -379,6 +379,150 @@ pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS {
     pub __bindgen_padding_0: [u8; 4usize],
     pub fbRegion: [NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO; 16usize],
 }
+pub const NV_OK: _bindgen_ty_4 = 0;
+pub const NV_ERR_GENERIC: _bindgen_ty_4 = 65535;
+pub const NV_ERR_BROKEN_FB: _bindgen_ty_4 = 1;
+pub const NV_ERR_BUFFER_TOO_SMALL: _bindgen_ty_4 = 2;
+pub const NV_ERR_BUSY_RETRY: _bindgen_ty_4 = 3;
+pub const NV_ERR_CALLBACK_NOT_SCHEDULED: _bindgen_ty_4 = 4;
+pub const NV_ERR_CARD_NOT_PRESENT: _bindgen_ty_4 = 5;
+pub const NV_ERR_CYCLE_DETECTED: _bindgen_ty_4 = 6;
+pub const NV_ERR_DMA_IN_USE: _bindgen_ty_4 = 7;
+pub const NV_ERR_DMA_MEM_NOT_LOCKED: _bindgen_ty_4 = 8;
+pub const NV_ERR_DMA_MEM_NOT_UNLOCKED: _bindgen_ty_4 = 9;
+pub const NV_ERR_DUAL_LINK_INUSE: _bindgen_ty_4 = 10;
+pub const NV_ERR_ECC_ERROR: _bindgen_ty_4 = 11;
+pub const NV_ERR_FIFO_BAD_ACCESS: _bindgen_ty_4 = 12;
+pub const NV_ERR_FREQ_NOT_SUPPORTED: _bindgen_ty_4 = 13;
+pub const NV_ERR_GPU_DMA_NOT_INITIALIZED: _bindgen_ty_4 = 14;
+pub const NV_ERR_GPU_IS_LOST: _bindgen_ty_4 = 15;
+pub const NV_ERR_GPU_IN_FULLCHIP_RESET: _bindgen_ty_4 = 16;
+pub const NV_ERR_GPU_NOT_FULL_POWER: _bindgen_ty_4 = 17;
+pub const NV_ERR_GPU_UUID_NOT_FOUND: _bindgen_ty_4 = 18;
+pub const NV_ERR_HOT_SWITCH: _bindgen_ty_4 = 19;
+pub const NV_ERR_I2C_ERROR: _bindgen_ty_4 = 20;
+pub const NV_ERR_I2C_SPEED_TOO_HIGH: _bindgen_ty_4 = 21;
+pub const NV_ERR_ILLEGAL_ACTION: _bindgen_ty_4 = 22;
+pub const NV_ERR_IN_USE: _bindgen_ty_4 = 23;
+pub const NV_ERR_INFLATE_COMPRESSED_DATA_FAILED: _bindgen_ty_4 = 24;
+pub const NV_ERR_INSERT_DUPLICATE_NAME: _bindgen_ty_4 = 25;
+pub const NV_ERR_INSUFFICIENT_RESOURCES: _bindgen_ty_4 = 26;
+pub const NV_ERR_INSUFFICIENT_PERMISSIONS: _bindgen_ty_4 = 27;
+pub const NV_ERR_INSUFFICIENT_POWER: _bindgen_ty_4 = 28;
+pub const NV_ERR_INVALID_ACCESS_TYPE: _bindgen_ty_4 = 29;
+pub const NV_ERR_INVALID_ADDRESS: _bindgen_ty_4 = 30;
+pub const NV_ERR_INVALID_ARGUMENT: _bindgen_ty_4 = 31;
+pub const NV_ERR_INVALID_BASE: _bindgen_ty_4 = 32;
+pub const NV_ERR_INVALID_CHANNEL: _bindgen_ty_4 = 33;
+pub const NV_ERR_INVALID_CLASS: _bindgen_ty_4 = 34;
+pub const NV_ERR_INVALID_CLIENT: _bindgen_ty_4 = 35;
+pub const NV_ERR_INVALID_COMMAND: _bindgen_ty_4 = 36;
+pub const NV_ERR_INVALID_DATA: _bindgen_ty_4 = 37;
+pub const NV_ERR_INVALID_DEVICE: _bindgen_ty_4 = 38;
+pub const NV_ERR_INVALID_DMA_SPECIFIER: _bindgen_ty_4 = 39;
+pub const NV_ERR_INVALID_EVENT: _bindgen_ty_4 = 40;
+pub const NV_ERR_INVALID_FLAGS: _bindgen_ty_4 = 41;
+pub const NV_ERR_INVALID_FUNCTION: _bindgen_ty_4 = 42;
+pub const NV_ERR_INVALID_HEAP: _bindgen_ty_4 = 43;
+pub const NV_ERR_INVALID_INDEX: _bindgen_ty_4 = 44;
+pub const NV_ERR_INVALID_IRQ_LEVEL: _bindgen_ty_4 = 45;
+pub const NV_ERR_INVALID_LIMIT: _bindgen_ty_4 = 46;
+pub const NV_ERR_INVALID_LOCK_STATE: _bindgen_ty_4 = 47;
+pub const NV_ERR_INVALID_METHOD: _bindgen_ty_4 = 48;
+pub const NV_ERR_INVALID_OBJECT: _bindgen_ty_4 = 49;
+pub const NV_ERR_INVALID_OBJECT_BUFFER: _bindgen_ty_4 = 50;
+pub const NV_ERR_INVALID_OBJECT_HANDLE: _bindgen_ty_4 = 51;
+pub const NV_ERR_INVALID_OBJECT_NEW: _bindgen_ty_4 = 52;
+pub const NV_ERR_INVALID_OBJECT_OLD: _bindgen_ty_4 = 53;
+pub const NV_ERR_INVALID_OBJECT_PARENT: _bindgen_ty_4 = 54;
+pub const NV_ERR_INVALID_OFFSET: _bindgen_ty_4 = 55;
+pub const NV_ERR_INVALID_OPERATION: _bindgen_ty_4 = 56;
+pub const NV_ERR_INVALID_OWNER: _bindgen_ty_4 = 57;
+pub const NV_ERR_INVALID_PARAM_STRUCT: _bindgen_ty_4 = 58;
+pub const NV_ERR_INVALID_PARAMETER: _bindgen_ty_4 = 59;
+pub const NV_ERR_INVALID_PATH: _bindgen_ty_4 = 60;
+pub const NV_ERR_INVALID_POINTER: _bindgen_ty_4 = 61;
+pub const NV_ERR_INVALID_REGISTRY_KEY: _bindgen_ty_4 = 62;
+pub const NV_ERR_INVALID_REQUEST: _bindgen_ty_4 = 63;
+pub const NV_ERR_INVALID_STATE: _bindgen_ty_4 = 64;
+pub const NV_ERR_INVALID_STRING_LENGTH: _bindgen_ty_4 = 65;
+pub const NV_ERR_INVALID_READ: _bindgen_ty_4 = 66;
+pub const NV_ERR_INVALID_WRITE: _bindgen_ty_4 = 67;
+pub const NV_ERR_INVALID_XLATE: _bindgen_ty_4 = 68;
+pub const NV_ERR_IRQ_NOT_FIRING: _bindgen_ty_4 = 69;
+pub const NV_ERR_IRQ_EDGE_TRIGGERED: _bindgen_ty_4 = 70;
+pub const NV_ERR_MEMORY_TRAINING_FAILED: _bindgen_ty_4 = 71;
+pub const NV_ERR_MISMATCHED_SLAVE: _bindgen_ty_4 = 72;
+pub const NV_ERR_MISMATCHED_TARGET: _bindgen_ty_4 = 73;
+pub const NV_ERR_MISSING_TABLE_ENTRY: _bindgen_ty_4 = 74;
+pub const NV_ERR_MODULE_LOAD_FAILED: _bindgen_ty_4 = 75;
+pub const NV_ERR_MORE_DATA_AVAILABLE: _bindgen_ty_4 = 76;
+pub const NV_ERR_MORE_PROCESSING_REQUIRED: _bindgen_ty_4 = 77;
+pub const NV_ERR_MULTIPLE_MEMORY_TYPES: _bindgen_ty_4 = 78;
+pub const NV_ERR_NO_FREE_FIFOS: _bindgen_ty_4 = 79;
+pub const NV_ERR_NO_INTR_PENDING: _bindgen_ty_4 = 80;
+pub const NV_ERR_NO_MEMORY: _bindgen_ty_4 = 81;
+pub const NV_ERR_NO_SUCH_DOMAIN: _bindgen_ty_4 = 82;
+pub const NV_ERR_NO_VALID_PATH: _bindgen_ty_4 = 83;
+pub const NV_ERR_NOT_COMPATIBLE: _bindgen_ty_4 = 84;
+pub const NV_ERR_NOT_READY: _bindgen_ty_4 = 85;
+pub const NV_ERR_NOT_SUPPORTED: _bindgen_ty_4 = 86;
+pub const NV_ERR_OBJECT_NOT_FOUND: _bindgen_ty_4 = 87;
+pub const NV_ERR_OBJECT_TYPE_MISMATCH: _bindgen_ty_4 = 88;
+pub const NV_ERR_OPERATING_SYSTEM: _bindgen_ty_4 = 89;
+pub const NV_ERR_OTHER_DEVICE_FOUND: _bindgen_ty_4 = 90;
+pub const NV_ERR_OUT_OF_RANGE: _bindgen_ty_4 = 91;
+pub const NV_ERR_OVERLAPPING_UVM_COMMIT: _bindgen_ty_4 = 92;
+pub const NV_ERR_PAGE_TABLE_NOT_AVAIL: _bindgen_ty_4 = 93;
+pub const NV_ERR_PID_NOT_FOUND: _bindgen_ty_4 = 94;
+pub const NV_ERR_PROTECTION_FAULT: _bindgen_ty_4 = 95;
+pub const NV_ERR_RC_ERROR: _bindgen_ty_4 = 96;
+pub const NV_ERR_REJECTED_VBIOS: _bindgen_ty_4 = 97;
+pub const NV_ERR_RESET_REQUIRED: _bindgen_ty_4 = 98;
+pub const NV_ERR_STATE_IN_USE: _bindgen_ty_4 = 99;
+pub const NV_ERR_SIGNAL_PENDING: _bindgen_ty_4 = 100;
+pub const NV_ERR_TIMEOUT: _bindgen_ty_4 = 101;
+pub const NV_ERR_TIMEOUT_RETRY: _bindgen_ty_4 = 102;
+pub const NV_ERR_TOO_MANY_PRIMARIES: _bindgen_ty_4 = 103;
+pub const NV_ERR_UVM_ADDRESS_IN_USE: _bindgen_ty_4 = 104;
+pub const NV_ERR_MAX_SESSION_LIMIT_REACHED: _bindgen_ty_4 = 105;
+pub const NV_ERR_LIB_RM_VERSION_MISMATCH: _bindgen_ty_4 = 106;
+pub const NV_ERR_PRIV_SEC_VIOLATION: _bindgen_ty_4 = 107;
+pub const NV_ERR_GPU_IN_DEBUG_MODE: _bindgen_ty_4 = 108;
+pub const NV_ERR_FEATURE_NOT_ENABLED: _bindgen_ty_4 = 109;
+pub const NV_ERR_RESOURCE_LOST: _bindgen_ty_4 = 110;
+pub const NV_ERR_PMU_NOT_READY: _bindgen_ty_4 = 111;
+pub const NV_ERR_FLCN_ERROR: _bindgen_ty_4 = 112;
+pub const NV_ERR_FATAL_ERROR: _bindgen_ty_4 = 113;
+pub const NV_ERR_MEMORY_ERROR: _bindgen_ty_4 = 114;
+pub const NV_ERR_INVALID_LICENSE: _bindgen_ty_4 = 115;
+pub const NV_ERR_NVLINK_INIT_ERROR: _bindgen_ty_4 = 116;
+pub const NV_ERR_NVLINK_MINION_ERROR: _bindgen_ty_4 = 117;
+pub const NV_ERR_NVLINK_CLOCK_ERROR: _bindgen_ty_4 = 118;
+pub const NV_ERR_NVLINK_TRAINING_ERROR: _bindgen_ty_4 = 119;
+pub const NV_ERR_NVLINK_CONFIGURATION_ERROR: _bindgen_ty_4 = 120;
+pub const NV_ERR_RISCV_ERROR: _bindgen_ty_4 = 121;
+pub const NV_ERR_FABRIC_MANAGER_NOT_PRESENT: _bindgen_ty_4 = 122;
+pub const NV_ERR_ALREADY_SIGNALLED: _bindgen_ty_4 = 123;
+pub const NV_ERR_QUEUE_TASK_SLOT_NOT_AVAILABLE: _bindgen_ty_4 = 124;
+pub const NV_ERR_KEY_ROTATION_IN_PROGRESS: _bindgen_ty_4 = 125;
+pub const NV_ERR_TEST_ONLY_CODE_NOT_ENABLED: _bindgen_ty_4 = 126;
+pub const NV_ERR_SECURE_BOOT_FAILED: _bindgen_ty_4 = 127;
+pub const NV_ERR_INSUFFICIENT_ZBC_ENTRY: _bindgen_ty_4 = 128;
+pub const NV_ERR_NVLINK_FABRIC_NOT_READY: _bindgen_ty_4 = 129;
+pub const NV_ERR_NVLINK_FABRIC_FAILURE: _bindgen_ty_4 = 130;
+pub const NV_ERR_GPU_MEMORY_ONLINING_FAILURE: _bindgen_ty_4 = 131;
+pub const NV_ERR_REDUCTION_MANAGER_NOT_AVAILABLE: _bindgen_ty_4 = 132;
+pub const NV_ERR_RESOURCE_RETIREMENT_ERROR: _bindgen_ty_4 = 134;
+pub const NV_WARN_HOT_SWITCH: _bindgen_ty_4 = 65537;
+pub const NV_WARN_INCORRECT_PERFMON_DATA: _bindgen_ty_4 = 65538;
+pub const NV_WARN_MISMATCHED_SLAVE: _bindgen_ty_4 = 65539;
+pub const NV_WARN_MISMATCHED_TARGET: _bindgen_ty_4 = 65540;
+pub const NV_WARN_MORE_PROCESSING_REQUIRED: _bindgen_ty_4 = 65541;
+pub const NV_WARN_NOTHING_TO_DO: _bindgen_ty_4 = 65542;
+pub const NV_WARN_NULL_OBJECT: _bindgen_ty_4 = 65543;
+pub const NV_WARN_OUT_OF_RANGE: _bindgen_ty_4 = 65544;
+pub type _bindgen_ty_4 = ffi::c_uint;
 #[repr(C)]
 #[derive(Debug, Copy, Clone, MaybeZeroable)]
 pub struct NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 2/8] gpu: nova-core: gsp: add GSP-RM status types
  2026-04-17 15:29 [PATCH v4 0/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 1/8] gpu: nova-core: gsp: add NV_STATUS error code bindings Eliot Courtney
@ 2026-04-17 15:29 ` Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 3/8] gpu: nova-core: gsp: add GspRpcError for Cmdq RPC error handling Eliot Courtney
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-04-17 15:29 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	rust-for-linux, dri-devel, linux-kernel, Eliot Courtney

These types represent the status value returned by GSP-RM for generic
RPCs and also for RM control messages. These are split into a success
status, a warning status, and an error status. Give a rust side type to
each of these, which lets errors be mapped to a rust `Error`.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gsp/fw.rs | 508 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 508 insertions(+)

diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 0c8a74f0e8ac..a8d7c62af097 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -97,6 +97,514 @@ pub(in crate::gsp) fn advance_cpu_write_ptr(qs: &Coherent<GspMem>, count: u32) {
 pub(crate) const GSP_MSG_QUEUE_ELEMENT_SIZE_MAX: usize =
     num::u32_as_usize(bindings::GSP_MSG_QUEUE_ELEMENT_SIZE_MAX);
 
+/// Status code returned by GSP-RM operations.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[expect(dead_code)]
+pub(crate) enum GspMsgRmStatus {
+    /// The operation succeeded.
+    Ok,
+    /// The operation completed with a non-fatal warning.
+    Warning(GspMsgRmWarning),
+    /// The operation failed with a GSP-RM-specific error.
+    Error(GspMsgRmError),
+}
+
+/// Warning code returned by GSP-RM RPCs.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u32)]
+pub(crate) enum GspMsgRmWarning {
+    HotSwitch = bindings::NV_WARN_HOT_SWITCH,
+    IncorrectPerfmonData = bindings::NV_WARN_INCORRECT_PERFMON_DATA,
+    MismatchedSlave = bindings::NV_WARN_MISMATCHED_SLAVE,
+    MismatchedTarget = bindings::NV_WARN_MISMATCHED_TARGET,
+    MoreProcessingRequired = bindings::NV_WARN_MORE_PROCESSING_REQUIRED,
+    NothingToDo = bindings::NV_WARN_NOTHING_TO_DO,
+    NullObject = bindings::NV_WARN_NULL_OBJECT,
+    OutOfRange = bindings::NV_WARN_OUT_OF_RANGE,
+}
+
+// TODO[FPRI]: This is a temporary solution to be replaced with the corresponding derive macros
+// once they land.
+impl TryFrom<u32> for GspMsgRmWarning {
+    type Error = Error;
+
+    fn try_from(value: u32) -> Result<Self> {
+        match value {
+            bindings::NV_WARN_HOT_SWITCH => Ok(Self::HotSwitch),
+            bindings::NV_WARN_INCORRECT_PERFMON_DATA => Ok(Self::IncorrectPerfmonData),
+            bindings::NV_WARN_MISMATCHED_SLAVE => Ok(Self::MismatchedSlave),
+            bindings::NV_WARN_MISMATCHED_TARGET => Ok(Self::MismatchedTarget),
+            bindings::NV_WARN_MORE_PROCESSING_REQUIRED => Ok(Self::MoreProcessingRequired),
+            bindings::NV_WARN_NOTHING_TO_DO => Ok(Self::NothingToDo),
+            bindings::NV_WARN_NULL_OBJECT => Ok(Self::NullObject),
+            bindings::NV_WARN_OUT_OF_RANGE => Ok(Self::OutOfRange),
+            _ => Err(EINVAL),
+        }
+    }
+}
+
+/// Error code returned by GSP-RM RPCs.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u32)]
+pub(crate) enum GspMsgRmError {
+    AlreadySignalled = bindings::NV_ERR_ALREADY_SIGNALLED,
+    BrokenFb = bindings::NV_ERR_BROKEN_FB,
+    BufferTooSmall = bindings::NV_ERR_BUFFER_TOO_SMALL,
+    BusyRetry = bindings::NV_ERR_BUSY_RETRY,
+    CallbackNotScheduled = bindings::NV_ERR_CALLBACK_NOT_SCHEDULED,
+    CardNotPresent = bindings::NV_ERR_CARD_NOT_PRESENT,
+    CycleDetected = bindings::NV_ERR_CYCLE_DETECTED,
+    DmaInUse = bindings::NV_ERR_DMA_IN_USE,
+    DmaMemNotLocked = bindings::NV_ERR_DMA_MEM_NOT_LOCKED,
+    DmaMemNotUnlocked = bindings::NV_ERR_DMA_MEM_NOT_UNLOCKED,
+    DualLinkInuse = bindings::NV_ERR_DUAL_LINK_INUSE,
+    EccError = bindings::NV_ERR_ECC_ERROR,
+    FabricManagerNotPresent = bindings::NV_ERR_FABRIC_MANAGER_NOT_PRESENT,
+    FatalError = bindings::NV_ERR_FATAL_ERROR,
+    FeatureNotEnabled = bindings::NV_ERR_FEATURE_NOT_ENABLED,
+    FifoBadAccess = bindings::NV_ERR_FIFO_BAD_ACCESS,
+    FlcnError = bindings::NV_ERR_FLCN_ERROR,
+    FreqNotSupported = bindings::NV_ERR_FREQ_NOT_SUPPORTED,
+    Generic = bindings::NV_ERR_GENERIC,
+    GpuDmaNotInitialized = bindings::NV_ERR_GPU_DMA_NOT_INITIALIZED,
+    GpuInDebugMode = bindings::NV_ERR_GPU_IN_DEBUG_MODE,
+    GpuInFullchipReset = bindings::NV_ERR_GPU_IN_FULLCHIP_RESET,
+    GpuIsLost = bindings::NV_ERR_GPU_IS_LOST,
+    GpuMemoryOnliningFailure = bindings::NV_ERR_GPU_MEMORY_ONLINING_FAILURE,
+    GpuNotFullPower = bindings::NV_ERR_GPU_NOT_FULL_POWER,
+    GpuUuidNotFound = bindings::NV_ERR_GPU_UUID_NOT_FOUND,
+    HotSwitch = bindings::NV_ERR_HOT_SWITCH,
+    I2cError = bindings::NV_ERR_I2C_ERROR,
+    I2cSpeedTooHigh = bindings::NV_ERR_I2C_SPEED_TOO_HIGH,
+    IllegalAction = bindings::NV_ERR_ILLEGAL_ACTION,
+    InUse = bindings::NV_ERR_IN_USE,
+    InflateCompressedDataFailed = bindings::NV_ERR_INFLATE_COMPRESSED_DATA_FAILED,
+    InsertDuplicateName = bindings::NV_ERR_INSERT_DUPLICATE_NAME,
+    InsufficientPermissions = bindings::NV_ERR_INSUFFICIENT_PERMISSIONS,
+    InsufficientPower = bindings::NV_ERR_INSUFFICIENT_POWER,
+    InsufficientResources = bindings::NV_ERR_INSUFFICIENT_RESOURCES,
+    InsufficientZbcEntry = bindings::NV_ERR_INSUFFICIENT_ZBC_ENTRY,
+    InvalidAccessType = bindings::NV_ERR_INVALID_ACCESS_TYPE,
+    InvalidAddress = bindings::NV_ERR_INVALID_ADDRESS,
+    InvalidArgument = bindings::NV_ERR_INVALID_ARGUMENT,
+    InvalidBase = bindings::NV_ERR_INVALID_BASE,
+    InvalidChannel = bindings::NV_ERR_INVALID_CHANNEL,
+    InvalidClass = bindings::NV_ERR_INVALID_CLASS,
+    InvalidClient = bindings::NV_ERR_INVALID_CLIENT,
+    InvalidCommand = bindings::NV_ERR_INVALID_COMMAND,
+    InvalidData = bindings::NV_ERR_INVALID_DATA,
+    InvalidDevice = bindings::NV_ERR_INVALID_DEVICE,
+    InvalidDmaSpecifier = bindings::NV_ERR_INVALID_DMA_SPECIFIER,
+    InvalidEvent = bindings::NV_ERR_INVALID_EVENT,
+    InvalidFlags = bindings::NV_ERR_INVALID_FLAGS,
+    InvalidFunction = bindings::NV_ERR_INVALID_FUNCTION,
+    InvalidHeap = bindings::NV_ERR_INVALID_HEAP,
+    InvalidIndex = bindings::NV_ERR_INVALID_INDEX,
+    InvalidIrqLevel = bindings::NV_ERR_INVALID_IRQ_LEVEL,
+    InvalidLicense = bindings::NV_ERR_INVALID_LICENSE,
+    InvalidLimit = bindings::NV_ERR_INVALID_LIMIT,
+    InvalidLockState = bindings::NV_ERR_INVALID_LOCK_STATE,
+    InvalidMethod = bindings::NV_ERR_INVALID_METHOD,
+    InvalidObject = bindings::NV_ERR_INVALID_OBJECT,
+    InvalidObjectBuffer = bindings::NV_ERR_INVALID_OBJECT_BUFFER,
+    InvalidObjectHandle = bindings::NV_ERR_INVALID_OBJECT_HANDLE,
+    InvalidObjectNew = bindings::NV_ERR_INVALID_OBJECT_NEW,
+    InvalidObjectOld = bindings::NV_ERR_INVALID_OBJECT_OLD,
+    InvalidObjectParent = bindings::NV_ERR_INVALID_OBJECT_PARENT,
+    InvalidOffset = bindings::NV_ERR_INVALID_OFFSET,
+    InvalidOperation = bindings::NV_ERR_INVALID_OPERATION,
+    InvalidOwner = bindings::NV_ERR_INVALID_OWNER,
+    InvalidParamStruct = bindings::NV_ERR_INVALID_PARAM_STRUCT,
+    InvalidParameter = bindings::NV_ERR_INVALID_PARAMETER,
+    InvalidPath = bindings::NV_ERR_INVALID_PATH,
+    InvalidPointer = bindings::NV_ERR_INVALID_POINTER,
+    InvalidRead = bindings::NV_ERR_INVALID_READ,
+    InvalidRegistryKey = bindings::NV_ERR_INVALID_REGISTRY_KEY,
+    InvalidRequest = bindings::NV_ERR_INVALID_REQUEST,
+    InvalidState = bindings::NV_ERR_INVALID_STATE,
+    InvalidStringLength = bindings::NV_ERR_INVALID_STRING_LENGTH,
+    InvalidWrite = bindings::NV_ERR_INVALID_WRITE,
+    InvalidXlate = bindings::NV_ERR_INVALID_XLATE,
+    IrqEdgeTriggered = bindings::NV_ERR_IRQ_EDGE_TRIGGERED,
+    IrqNotFiring = bindings::NV_ERR_IRQ_NOT_FIRING,
+    KeyRotationInProgress = bindings::NV_ERR_KEY_ROTATION_IN_PROGRESS,
+    LibRmVersionMismatch = bindings::NV_ERR_LIB_RM_VERSION_MISMATCH,
+    MaxSessionLimitReached = bindings::NV_ERR_MAX_SESSION_LIMIT_REACHED,
+    MemoryError = bindings::NV_ERR_MEMORY_ERROR,
+    MemoryTrainingFailed = bindings::NV_ERR_MEMORY_TRAINING_FAILED,
+    MismatchedSlave = bindings::NV_ERR_MISMATCHED_SLAVE,
+    MismatchedTarget = bindings::NV_ERR_MISMATCHED_TARGET,
+    MissingTableEntry = bindings::NV_ERR_MISSING_TABLE_ENTRY,
+    ModuleLoadFailed = bindings::NV_ERR_MODULE_LOAD_FAILED,
+    MoreDataAvailable = bindings::NV_ERR_MORE_DATA_AVAILABLE,
+    MoreProcessingRequired = bindings::NV_ERR_MORE_PROCESSING_REQUIRED,
+    MultipleMemoryTypes = bindings::NV_ERR_MULTIPLE_MEMORY_TYPES,
+    NoFreeFifos = bindings::NV_ERR_NO_FREE_FIFOS,
+    NoIntrPending = bindings::NV_ERR_NO_INTR_PENDING,
+    NoMemory = bindings::NV_ERR_NO_MEMORY,
+    NoSuchDomain = bindings::NV_ERR_NO_SUCH_DOMAIN,
+    NoValidPath = bindings::NV_ERR_NO_VALID_PATH,
+    NotCompatible = bindings::NV_ERR_NOT_COMPATIBLE,
+    NotReady = bindings::NV_ERR_NOT_READY,
+    NotSupported = bindings::NV_ERR_NOT_SUPPORTED,
+    NvlinkClockError = bindings::NV_ERR_NVLINK_CLOCK_ERROR,
+    NvlinkConfigurationError = bindings::NV_ERR_NVLINK_CONFIGURATION_ERROR,
+    NvlinkFabricFailure = bindings::NV_ERR_NVLINK_FABRIC_FAILURE,
+    NvlinkFabricNotReady = bindings::NV_ERR_NVLINK_FABRIC_NOT_READY,
+    NvlinkInitError = bindings::NV_ERR_NVLINK_INIT_ERROR,
+    NvlinkMinionError = bindings::NV_ERR_NVLINK_MINION_ERROR,
+    NvlinkTrainingError = bindings::NV_ERR_NVLINK_TRAINING_ERROR,
+    ObjectNotFound = bindings::NV_ERR_OBJECT_NOT_FOUND,
+    ObjectTypeMismatch = bindings::NV_ERR_OBJECT_TYPE_MISMATCH,
+    OperatingSystem = bindings::NV_ERR_OPERATING_SYSTEM,
+    OtherDeviceFound = bindings::NV_ERR_OTHER_DEVICE_FOUND,
+    OutOfRange = bindings::NV_ERR_OUT_OF_RANGE,
+    OverlappingUvmCommit = bindings::NV_ERR_OVERLAPPING_UVM_COMMIT,
+    PageTableNotAvail = bindings::NV_ERR_PAGE_TABLE_NOT_AVAIL,
+    PidNotFound = bindings::NV_ERR_PID_NOT_FOUND,
+    PmuNotReady = bindings::NV_ERR_PMU_NOT_READY,
+    PrivSecViolation = bindings::NV_ERR_PRIV_SEC_VIOLATION,
+    ProtectionFault = bindings::NV_ERR_PROTECTION_FAULT,
+    QueueTaskSlotNotAvailable = bindings::NV_ERR_QUEUE_TASK_SLOT_NOT_AVAILABLE,
+    RcError = bindings::NV_ERR_RC_ERROR,
+    ReductionManagerNotAvailable = bindings::NV_ERR_REDUCTION_MANAGER_NOT_AVAILABLE,
+    RejectedVbios = bindings::NV_ERR_REJECTED_VBIOS,
+    ResetRequired = bindings::NV_ERR_RESET_REQUIRED,
+    ResourceLost = bindings::NV_ERR_RESOURCE_LOST,
+    ResourceRetirementError = bindings::NV_ERR_RESOURCE_RETIREMENT_ERROR,
+    RiscvError = bindings::NV_ERR_RISCV_ERROR,
+    SecureBootFailed = bindings::NV_ERR_SECURE_BOOT_FAILED,
+    SignalPending = bindings::NV_ERR_SIGNAL_PENDING,
+    StateInUse = bindings::NV_ERR_STATE_IN_USE,
+    TestOnlyCodeNotEnabled = bindings::NV_ERR_TEST_ONLY_CODE_NOT_ENABLED,
+    Timeout = bindings::NV_ERR_TIMEOUT,
+    TimeoutRetry = bindings::NV_ERR_TIMEOUT_RETRY,
+    TooManyPrimaries = bindings::NV_ERR_TOO_MANY_PRIMARIES,
+    UvmAddressInUse = bindings::NV_ERR_UVM_ADDRESS_IN_USE,
+}
+
+impl From<GspMsgRmError> for Error {
+    fn from(status: GspMsgRmError) -> Self {
+        match status {
+            GspMsgRmError::BufferTooSmall | GspMsgRmError::MoreDataAvailable => ETOOSMALL,
+
+            GspMsgRmError::DmaInUse
+            | GspMsgRmError::DmaMemNotUnlocked
+            | GspMsgRmError::DualLinkInuse
+            | GspMsgRmError::GpuInDebugMode
+            | GspMsgRmError::GpuInFullchipReset
+            | GspMsgRmError::KeyRotationInProgress
+            | GspMsgRmError::NotReady
+            | GspMsgRmError::NvlinkFabricNotReady
+            | GspMsgRmError::PmuNotReady
+            | GspMsgRmError::StateInUse
+            | GspMsgRmError::UvmAddressInUse => EBUSY,
+
+            GspMsgRmError::CardNotPresent
+            | GspMsgRmError::FabricManagerNotPresent
+            | GspMsgRmError::GpuDmaNotInitialized
+            | GspMsgRmError::GpuIsLost
+            | GspMsgRmError::GpuUuidNotFound
+            | GspMsgRmError::OtherDeviceFound
+            | GspMsgRmError::ReductionManagerNotAvailable
+            | GspMsgRmError::ResetRequired => ENODEV,
+
+            GspMsgRmError::FeatureNotEnabled
+            | GspMsgRmError::FreqNotSupported
+            | GspMsgRmError::NotSupported
+            | GspMsgRmError::TestOnlyCodeNotEnabled => ENOTSUPP,
+
+            GspMsgRmError::CallbackNotScheduled
+            | GspMsgRmError::MissingTableEntry
+            | GspMsgRmError::NoIntrPending
+            | GspMsgRmError::NoSuchDomain
+            | GspMsgRmError::NoValidPath
+            | GspMsgRmError::ObjectNotFound
+            | GspMsgRmError::ResourceLost => ENOENT,
+
+            GspMsgRmError::DmaMemNotLocked
+            | GspMsgRmError::I2cSpeedTooHigh
+            | GspMsgRmError::InflateCompressedDataFailed
+            | GspMsgRmError::InvalidArgument
+            | GspMsgRmError::InvalidBase
+            | GspMsgRmError::InvalidChannel
+            | GspMsgRmError::InvalidClass
+            | GspMsgRmError::InvalidClient
+            | GspMsgRmError::InvalidCommand
+            | GspMsgRmError::InvalidData
+            | GspMsgRmError::InvalidDevice
+            | GspMsgRmError::InvalidDmaSpecifier
+            | GspMsgRmError::InvalidEvent
+            | GspMsgRmError::InvalidFlags
+            | GspMsgRmError::InvalidFunction
+            | GspMsgRmError::InvalidHeap
+            | GspMsgRmError::InvalidIndex
+            | GspMsgRmError::InvalidIrqLevel
+            | GspMsgRmError::InvalidLimit
+            | GspMsgRmError::InvalidLockState
+            | GspMsgRmError::InvalidMethod
+            | GspMsgRmError::InvalidObject
+            | GspMsgRmError::InvalidObjectBuffer
+            | GspMsgRmError::InvalidObjectHandle
+            | GspMsgRmError::InvalidObjectNew
+            | GspMsgRmError::InvalidObjectOld
+            | GspMsgRmError::InvalidObjectParent
+            | GspMsgRmError::InvalidOffset
+            | GspMsgRmError::InvalidOperation
+            | GspMsgRmError::InvalidOwner
+            | GspMsgRmError::InvalidParamStruct
+            | GspMsgRmError::InvalidParameter
+            | GspMsgRmError::InvalidPath
+            | GspMsgRmError::InvalidRegistryKey
+            | GspMsgRmError::InvalidRequest
+            | GspMsgRmError::InvalidState
+            | GspMsgRmError::InvalidStringLength
+            | GspMsgRmError::InvalidXlate
+            | GspMsgRmError::LibRmVersionMismatch
+            | GspMsgRmError::MismatchedSlave
+            | GspMsgRmError::MismatchedTarget
+            | GspMsgRmError::MultipleMemoryTypes
+            | GspMsgRmError::NotCompatible
+            | GspMsgRmError::ObjectTypeMismatch
+            | GspMsgRmError::OverlappingUvmCommit
+            | GspMsgRmError::RejectedVbios => EINVAL,
+
+            GspMsgRmError::IllegalAction | GspMsgRmError::InsufficientPermissions => EPERM,
+
+            GspMsgRmError::AlreadySignalled
+            | GspMsgRmError::InUse
+            | GspMsgRmError::InsertDuplicateName => EEXIST,
+
+            GspMsgRmError::FifoBadAccess
+            | GspMsgRmError::InvalidAccessType
+            | GspMsgRmError::InvalidLicense
+            | GspMsgRmError::PrivSecViolation
+            | GspMsgRmError::SecureBootFailed => EACCES,
+
+            GspMsgRmError::GpuMemoryOnliningFailure
+            | GspMsgRmError::InsufficientResources
+            | GspMsgRmError::NoMemory
+            | GspMsgRmError::PageTableNotAvail => ENOMEM,
+
+            GspMsgRmError::InsufficientZbcEntry
+            | GspMsgRmError::MaxSessionLimitReached
+            | GspMsgRmError::NoFreeFifos
+            | GspMsgRmError::QueueTaskSlotNotAvailable
+            | GspMsgRmError::TooManyPrimaries => ENOSPC,
+
+            GspMsgRmError::InvalidAddress
+            | GspMsgRmError::InvalidPointer
+            | GspMsgRmError::InvalidRead
+            | GspMsgRmError::InvalidWrite
+            | GspMsgRmError::ProtectionFault => EFAULT,
+
+            GspMsgRmError::BusyRetry
+            | GspMsgRmError::GpuNotFullPower
+            | GspMsgRmError::HotSwitch
+            | GspMsgRmError::InsufficientPower
+            | GspMsgRmError::MoreProcessingRequired => EAGAIN,
+
+            GspMsgRmError::OutOfRange => EOVERFLOW,
+
+            GspMsgRmError::PidNotFound => ESRCH,
+
+            GspMsgRmError::SignalPending => EINTR,
+
+            GspMsgRmError::Timeout | GspMsgRmError::TimeoutRetry => ETIMEDOUT,
+
+            GspMsgRmError::ModuleLoadFailed => ENXIO,
+
+            GspMsgRmError::BrokenFb
+            | GspMsgRmError::CycleDetected
+            | GspMsgRmError::EccError
+            | GspMsgRmError::FatalError
+            | GspMsgRmError::FlcnError
+            | GspMsgRmError::Generic
+            | GspMsgRmError::I2cError
+            | GspMsgRmError::IrqEdgeTriggered
+            | GspMsgRmError::IrqNotFiring
+            | GspMsgRmError::MemoryError
+            | GspMsgRmError::MemoryTrainingFailed
+            | GspMsgRmError::NvlinkClockError
+            | GspMsgRmError::NvlinkConfigurationError
+            | GspMsgRmError::NvlinkFabricFailure
+            | GspMsgRmError::NvlinkInitError
+            | GspMsgRmError::NvlinkMinionError
+            | GspMsgRmError::NvlinkTrainingError
+            | GspMsgRmError::OperatingSystem
+            | GspMsgRmError::RcError
+            | GspMsgRmError::ResourceRetirementError
+            | GspMsgRmError::RiscvError => EIO,
+        }
+    }
+}
+
+// TODO[FPRI]: This is a temporary solution to be replaced with the corresponding derive macros
+// once they land.
+impl TryFrom<u32> for GspMsgRmError {
+    type Error = Error;
+
+    fn try_from(value: u32) -> Result<Self> {
+        match value {
+            bindings::NV_ERR_ALREADY_SIGNALLED => Ok(Self::AlreadySignalled),
+            bindings::NV_ERR_BROKEN_FB => Ok(Self::BrokenFb),
+            bindings::NV_ERR_BUFFER_TOO_SMALL => Ok(Self::BufferTooSmall),
+            bindings::NV_ERR_BUSY_RETRY => Ok(Self::BusyRetry),
+            bindings::NV_ERR_CALLBACK_NOT_SCHEDULED => Ok(Self::CallbackNotScheduled),
+            bindings::NV_ERR_CARD_NOT_PRESENT => Ok(Self::CardNotPresent),
+            bindings::NV_ERR_CYCLE_DETECTED => Ok(Self::CycleDetected),
+            bindings::NV_ERR_DMA_IN_USE => Ok(Self::DmaInUse),
+            bindings::NV_ERR_DMA_MEM_NOT_LOCKED => Ok(Self::DmaMemNotLocked),
+            bindings::NV_ERR_DMA_MEM_NOT_UNLOCKED => Ok(Self::DmaMemNotUnlocked),
+            bindings::NV_ERR_DUAL_LINK_INUSE => Ok(Self::DualLinkInuse),
+            bindings::NV_ERR_ECC_ERROR => Ok(Self::EccError),
+            bindings::NV_ERR_FABRIC_MANAGER_NOT_PRESENT => Ok(Self::FabricManagerNotPresent),
+            bindings::NV_ERR_FATAL_ERROR => Ok(Self::FatalError),
+            bindings::NV_ERR_FEATURE_NOT_ENABLED => Ok(Self::FeatureNotEnabled),
+            bindings::NV_ERR_FIFO_BAD_ACCESS => Ok(Self::FifoBadAccess),
+            bindings::NV_ERR_FLCN_ERROR => Ok(Self::FlcnError),
+            bindings::NV_ERR_FREQ_NOT_SUPPORTED => Ok(Self::FreqNotSupported),
+            bindings::NV_ERR_GENERIC => Ok(Self::Generic),
+            bindings::NV_ERR_GPU_DMA_NOT_INITIALIZED => Ok(Self::GpuDmaNotInitialized),
+            bindings::NV_ERR_GPU_IN_DEBUG_MODE => Ok(Self::GpuInDebugMode),
+            bindings::NV_ERR_GPU_IN_FULLCHIP_RESET => Ok(Self::GpuInFullchipReset),
+            bindings::NV_ERR_GPU_IS_LOST => Ok(Self::GpuIsLost),
+            bindings::NV_ERR_GPU_MEMORY_ONLINING_FAILURE => Ok(Self::GpuMemoryOnliningFailure),
+            bindings::NV_ERR_GPU_NOT_FULL_POWER => Ok(Self::GpuNotFullPower),
+            bindings::NV_ERR_GPU_UUID_NOT_FOUND => Ok(Self::GpuUuidNotFound),
+            bindings::NV_ERR_HOT_SWITCH => Ok(Self::HotSwitch),
+            bindings::NV_ERR_I2C_ERROR => Ok(Self::I2cError),
+            bindings::NV_ERR_I2C_SPEED_TOO_HIGH => Ok(Self::I2cSpeedTooHigh),
+            bindings::NV_ERR_ILLEGAL_ACTION => Ok(Self::IllegalAction),
+            bindings::NV_ERR_IN_USE => Ok(Self::InUse),
+            bindings::NV_ERR_INFLATE_COMPRESSED_DATA_FAILED => {
+                Ok(Self::InflateCompressedDataFailed)
+            }
+            bindings::NV_ERR_INSERT_DUPLICATE_NAME => Ok(Self::InsertDuplicateName),
+            bindings::NV_ERR_INSUFFICIENT_PERMISSIONS => Ok(Self::InsufficientPermissions),
+            bindings::NV_ERR_INSUFFICIENT_POWER => Ok(Self::InsufficientPower),
+            bindings::NV_ERR_INSUFFICIENT_RESOURCES => Ok(Self::InsufficientResources),
+            bindings::NV_ERR_INSUFFICIENT_ZBC_ENTRY => Ok(Self::InsufficientZbcEntry),
+            bindings::NV_ERR_INVALID_ACCESS_TYPE => Ok(Self::InvalidAccessType),
+            bindings::NV_ERR_INVALID_ADDRESS => Ok(Self::InvalidAddress),
+            bindings::NV_ERR_INVALID_ARGUMENT => Ok(Self::InvalidArgument),
+            bindings::NV_ERR_INVALID_BASE => Ok(Self::InvalidBase),
+            bindings::NV_ERR_INVALID_CHANNEL => Ok(Self::InvalidChannel),
+            bindings::NV_ERR_INVALID_CLASS => Ok(Self::InvalidClass),
+            bindings::NV_ERR_INVALID_CLIENT => Ok(Self::InvalidClient),
+            bindings::NV_ERR_INVALID_COMMAND => Ok(Self::InvalidCommand),
+            bindings::NV_ERR_INVALID_DATA => Ok(Self::InvalidData),
+            bindings::NV_ERR_INVALID_DEVICE => Ok(Self::InvalidDevice),
+            bindings::NV_ERR_INVALID_DMA_SPECIFIER => Ok(Self::InvalidDmaSpecifier),
+            bindings::NV_ERR_INVALID_EVENT => Ok(Self::InvalidEvent),
+            bindings::NV_ERR_INVALID_FLAGS => Ok(Self::InvalidFlags),
+            bindings::NV_ERR_INVALID_FUNCTION => Ok(Self::InvalidFunction),
+            bindings::NV_ERR_INVALID_HEAP => Ok(Self::InvalidHeap),
+            bindings::NV_ERR_INVALID_INDEX => Ok(Self::InvalidIndex),
+            bindings::NV_ERR_INVALID_IRQ_LEVEL => Ok(Self::InvalidIrqLevel),
+            bindings::NV_ERR_INVALID_LICENSE => Ok(Self::InvalidLicense),
+            bindings::NV_ERR_INVALID_LIMIT => Ok(Self::InvalidLimit),
+            bindings::NV_ERR_INVALID_LOCK_STATE => Ok(Self::InvalidLockState),
+            bindings::NV_ERR_INVALID_METHOD => Ok(Self::InvalidMethod),
+            bindings::NV_ERR_INVALID_OBJECT => Ok(Self::InvalidObject),
+            bindings::NV_ERR_INVALID_OBJECT_BUFFER => Ok(Self::InvalidObjectBuffer),
+            bindings::NV_ERR_INVALID_OBJECT_HANDLE => Ok(Self::InvalidObjectHandle),
+            bindings::NV_ERR_INVALID_OBJECT_NEW => Ok(Self::InvalidObjectNew),
+            bindings::NV_ERR_INVALID_OBJECT_OLD => Ok(Self::InvalidObjectOld),
+            bindings::NV_ERR_INVALID_OBJECT_PARENT => Ok(Self::InvalidObjectParent),
+            bindings::NV_ERR_INVALID_OFFSET => Ok(Self::InvalidOffset),
+            bindings::NV_ERR_INVALID_OPERATION => Ok(Self::InvalidOperation),
+            bindings::NV_ERR_INVALID_OWNER => Ok(Self::InvalidOwner),
+            bindings::NV_ERR_INVALID_PARAM_STRUCT => Ok(Self::InvalidParamStruct),
+            bindings::NV_ERR_INVALID_PARAMETER => Ok(Self::InvalidParameter),
+            bindings::NV_ERR_INVALID_PATH => Ok(Self::InvalidPath),
+            bindings::NV_ERR_INVALID_POINTER => Ok(Self::InvalidPointer),
+            bindings::NV_ERR_INVALID_READ => Ok(Self::InvalidRead),
+            bindings::NV_ERR_INVALID_REGISTRY_KEY => Ok(Self::InvalidRegistryKey),
+            bindings::NV_ERR_INVALID_REQUEST => Ok(Self::InvalidRequest),
+            bindings::NV_ERR_INVALID_STATE => Ok(Self::InvalidState),
+            bindings::NV_ERR_INVALID_STRING_LENGTH => Ok(Self::InvalidStringLength),
+            bindings::NV_ERR_INVALID_WRITE => Ok(Self::InvalidWrite),
+            bindings::NV_ERR_INVALID_XLATE => Ok(Self::InvalidXlate),
+            bindings::NV_ERR_IRQ_EDGE_TRIGGERED => Ok(Self::IrqEdgeTriggered),
+            bindings::NV_ERR_IRQ_NOT_FIRING => Ok(Self::IrqNotFiring),
+            bindings::NV_ERR_KEY_ROTATION_IN_PROGRESS => Ok(Self::KeyRotationInProgress),
+            bindings::NV_ERR_LIB_RM_VERSION_MISMATCH => Ok(Self::LibRmVersionMismatch),
+            bindings::NV_ERR_MAX_SESSION_LIMIT_REACHED => Ok(Self::MaxSessionLimitReached),
+            bindings::NV_ERR_MEMORY_ERROR => Ok(Self::MemoryError),
+            bindings::NV_ERR_MEMORY_TRAINING_FAILED => Ok(Self::MemoryTrainingFailed),
+            bindings::NV_ERR_MISMATCHED_SLAVE => Ok(Self::MismatchedSlave),
+            bindings::NV_ERR_MISMATCHED_TARGET => Ok(Self::MismatchedTarget),
+            bindings::NV_ERR_MISSING_TABLE_ENTRY => Ok(Self::MissingTableEntry),
+            bindings::NV_ERR_MODULE_LOAD_FAILED => Ok(Self::ModuleLoadFailed),
+            bindings::NV_ERR_MORE_DATA_AVAILABLE => Ok(Self::MoreDataAvailable),
+            bindings::NV_ERR_MORE_PROCESSING_REQUIRED => Ok(Self::MoreProcessingRequired),
+            bindings::NV_ERR_MULTIPLE_MEMORY_TYPES => Ok(Self::MultipleMemoryTypes),
+            bindings::NV_ERR_NO_FREE_FIFOS => Ok(Self::NoFreeFifos),
+            bindings::NV_ERR_NO_INTR_PENDING => Ok(Self::NoIntrPending),
+            bindings::NV_ERR_NO_MEMORY => Ok(Self::NoMemory),
+            bindings::NV_ERR_NO_SUCH_DOMAIN => Ok(Self::NoSuchDomain),
+            bindings::NV_ERR_NO_VALID_PATH => Ok(Self::NoValidPath),
+            bindings::NV_ERR_NOT_COMPATIBLE => Ok(Self::NotCompatible),
+            bindings::NV_ERR_NOT_READY => Ok(Self::NotReady),
+            bindings::NV_ERR_NOT_SUPPORTED => Ok(Self::NotSupported),
+            bindings::NV_ERR_NVLINK_CLOCK_ERROR => Ok(Self::NvlinkClockError),
+            bindings::NV_ERR_NVLINK_CONFIGURATION_ERROR => Ok(Self::NvlinkConfigurationError),
+            bindings::NV_ERR_NVLINK_FABRIC_FAILURE => Ok(Self::NvlinkFabricFailure),
+            bindings::NV_ERR_NVLINK_FABRIC_NOT_READY => Ok(Self::NvlinkFabricNotReady),
+            bindings::NV_ERR_NVLINK_INIT_ERROR => Ok(Self::NvlinkInitError),
+            bindings::NV_ERR_NVLINK_MINION_ERROR => Ok(Self::NvlinkMinionError),
+            bindings::NV_ERR_NVLINK_TRAINING_ERROR => Ok(Self::NvlinkTrainingError),
+            bindings::NV_ERR_OBJECT_NOT_FOUND => Ok(Self::ObjectNotFound),
+            bindings::NV_ERR_OBJECT_TYPE_MISMATCH => Ok(Self::ObjectTypeMismatch),
+            bindings::NV_ERR_OPERATING_SYSTEM => Ok(Self::OperatingSystem),
+            bindings::NV_ERR_OTHER_DEVICE_FOUND => Ok(Self::OtherDeviceFound),
+            bindings::NV_ERR_OUT_OF_RANGE => Ok(Self::OutOfRange),
+            bindings::NV_ERR_OVERLAPPING_UVM_COMMIT => Ok(Self::OverlappingUvmCommit),
+            bindings::NV_ERR_PAGE_TABLE_NOT_AVAIL => Ok(Self::PageTableNotAvail),
+            bindings::NV_ERR_PID_NOT_FOUND => Ok(Self::PidNotFound),
+            bindings::NV_ERR_PMU_NOT_READY => Ok(Self::PmuNotReady),
+            bindings::NV_ERR_PRIV_SEC_VIOLATION => Ok(Self::PrivSecViolation),
+            bindings::NV_ERR_PROTECTION_FAULT => Ok(Self::ProtectionFault),
+            bindings::NV_ERR_QUEUE_TASK_SLOT_NOT_AVAILABLE => Ok(Self::QueueTaskSlotNotAvailable),
+            bindings::NV_ERR_RC_ERROR => Ok(Self::RcError),
+            bindings::NV_ERR_REDUCTION_MANAGER_NOT_AVAILABLE => {
+                Ok(Self::ReductionManagerNotAvailable)
+            }
+            bindings::NV_ERR_REJECTED_VBIOS => Ok(Self::RejectedVbios),
+            bindings::NV_ERR_RESET_REQUIRED => Ok(Self::ResetRequired),
+            bindings::NV_ERR_RESOURCE_LOST => Ok(Self::ResourceLost),
+            bindings::NV_ERR_RESOURCE_RETIREMENT_ERROR => Ok(Self::ResourceRetirementError),
+            bindings::NV_ERR_RISCV_ERROR => Ok(Self::RiscvError),
+            bindings::NV_ERR_SECURE_BOOT_FAILED => Ok(Self::SecureBootFailed),
+            bindings::NV_ERR_SIGNAL_PENDING => Ok(Self::SignalPending),
+            bindings::NV_ERR_STATE_IN_USE => Ok(Self::StateInUse),
+            bindings::NV_ERR_TEST_ONLY_CODE_NOT_ENABLED => Ok(Self::TestOnlyCodeNotEnabled),
+            bindings::NV_ERR_TIMEOUT => Ok(Self::Timeout),
+            bindings::NV_ERR_TIMEOUT_RETRY => Ok(Self::TimeoutRetry),
+            bindings::NV_ERR_TOO_MANY_PRIMARIES => Ok(Self::TooManyPrimaries),
+            bindings::NV_ERR_UVM_ADDRESS_IN_USE => Ok(Self::UvmAddressInUse),
+            _ => Err(EINVAL),
+        }
+    }
+}
+
+impl TryFrom<u32> for GspMsgRmStatus {
+    type Error = Error;
+
+    fn try_from(value: u32) -> Result<Self> {
+        if value == bindings::NV_OK {
+            return Ok(Self::Ok);
+        }
+
+        if let Ok(warning) = GspMsgRmWarning::try_from(value) {
+            return Ok(Self::Warning(warning));
+        }
+
+        Ok(Self::Error(GspMsgRmError::try_from(value)?))
+    }
+}
+
 /// Empty type to group methods related to heap parameters for running the GSP firmware.
 enum GspFwHeapParams {}
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 3/8] gpu: nova-core: gsp: add GspRpcError for Cmdq RPC error handling
  2026-04-17 15:29 [PATCH v4 0/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 1/8] gpu: nova-core: gsp: add NV_STATUS error code bindings Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 2/8] gpu: nova-core: gsp: add GSP-RM status types Eliot Courtney
@ 2026-04-17 15:29 ` Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 4/8] gpu: nova-core: gsp: expose GSP-RM internal client and subdevice handles Eliot Courtney
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-04-17 15:29 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	rust-for-linux, dri-devel, linux-kernel, Eliot Courtney

Currently, the RPC status value is ignored, but it can actually indicate
failure from GSP-RM. Add a new error type used by `Cmdq` to surface this
failure mode.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gsp/cmdq.rs      | 112 ++++++++++++++++++++++-----------
 drivers/gpu/nova-core/gsp/commands.rs  |   7 ++-
 drivers/gpu/nova-core/gsp/fw.rs        |  37 ++++++++++-
 drivers/gpu/nova-core/gsp/sequencer.rs |   5 +-
 4 files changed, 117 insertions(+), 44 deletions(-)

diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 569bb1a2501c..501d01e2bedd 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -41,6 +41,7 @@
     gsp::{
         fw::{
             GspMsgElement,
+            GspMsgRmError,
             MsgFunction,
             MsgqRxHeader,
             MsgqTxHeader,
@@ -55,6 +56,24 @@
     sbuffer::SBufferIter, //
 };
 
+/// Error returned by GSP RPC operations.
+#[derive(Debug)]
+pub(crate) enum GspRpcError {
+    /// The command transport or reply decoding failed.
+    Transport(Error),
+    /// The GSP-RM RPC returned an RM-specific error status.
+    Rm(GspMsgRmError),
+}
+
+impl From<GspRpcError> for Error {
+    fn from(err: GspRpcError) -> Self {
+        match err {
+            GspRpcError::Transport(err) => err,
+            GspRpcError::Rm(status) => status.into(),
+        }
+    }
+}
+
 /// Marker type representing the absence of a reply for a command. Commands using this as their
 /// reply type are sent using [`Cmdq::send_command_no_wait`].
 pub(crate) struct NoReply;
@@ -547,13 +566,14 @@ fn notify_gsp(bar: &Bar0) {
     ///
     /// # Errors
     ///
-    /// - `ETIMEDOUT` if space does not become available to send the command, or if the reply is
-    ///   not received within the timeout.
-    /// - `EIO` if the variable payload requested by the command has not been entirely
+    /// - `Transport(ETIMEDOUT)` if space does not become available to send the command, or if the
+    ///   reply is not received within the timeout.
+    /// - `Transport(EIO)` if the variable payload requested by the command has not been entirely
     ///   written to by its [`CommandToGsp::init_variable_payload`] method.
+    /// - `Rm(status)` if GSP-RM returned an error for this RPC.
     ///
-    /// Error codes returned by the command and reply initializers are propagated as-is.
-    pub(crate) fn send_command<M>(&self, bar: &Bar0, command: M) -> Result<M::Reply>
+    /// Error codes returned by the command and reply initializers are propagated as `Transport`.
+    pub(crate) fn send_command<M>(&self, bar: &Bar0, command: M) -> Result<M::Reply, GspRpcError>
     where
         M: CommandToGsp,
         M::Reply: MessageFromGsp,
@@ -561,12 +581,14 @@ pub(crate) fn send_command<M>(&self, bar: &Bar0, command: M) -> Result<M::Reply>
         Error: From<<M::Reply as MessageFromGsp>::InitError>,
     {
         let mut inner = self.inner.lock();
-        inner.send_command(bar, command)?;
+        inner
+            .send_command(bar, command)
+            .map_err(GspRpcError::Transport)?;
 
         loop {
             match inner.receive_msg::<M::Reply>(Self::RECEIVE_TIMEOUT) {
                 Ok(reply) => break Ok(reply),
-                Err(ERANGE) => continue,
+                Err(GspRpcError::Transport(ERANGE)) => continue,
                 Err(e) => break Err(e),
             }
         }
@@ -592,7 +614,7 @@ pub(crate) fn send_command_no_wait<M>(&self, bar: &Bar0, command: M) -> Result
     /// Receive a message from the GSP.
     ///
     /// See [`CmdqInner::receive_msg`] for details.
-    pub(crate) fn receive_msg<M: MessageFromGsp>(&self, timeout: Delta) -> Result<M>
+    pub(crate) fn receive_msg<M: MessageFromGsp>(&self, timeout: Delta) -> Result<M, GspRpcError>
     where
         // This allows all error types, including `Infallible`, to be used for `M::InitError`.
         Error: From<M::InitError>,
@@ -801,52 +823,68 @@ fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> {
     /// Receive a message from the GSP.
     ///
     /// The expected message type is specified using the `M` generic parameter. If the pending
-    /// message has a different function code, `ERANGE` is returned and the message is consumed.
+    /// message has a different function code, `Transport(ERANGE)` is returned and the message is
+    /// consumed.
     ///
     /// The read pointer is always advanced past the message, regardless of whether it matched.
     ///
     /// # Errors
     ///
-    /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available.
-    /// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the
-    ///   message queue.
-    /// - `EINVAL` if the function code of the message was not recognized.
-    /// - `ERANGE` if the message had a recognized but non-matching function code.
+    /// - `Transport(ETIMEDOUT)` if `timeout` has elapsed before any message becomes available.
+    /// - `Transport(EIO)` if there was some inconsistency (e.g. message shorter than advertised)
+    ///   on the message queue.
+    /// - `Transport(EINVAL)` if the function code of the message was not recognized.
+    /// - `Transport(ERANGE)` if the message had a recognized but non-matching function code.
+    /// - `Rm(status)` if GSP-RM returned an error for this RPC.
     ///
-    /// Error codes returned by [`MessageFromGsp::read`] are propagated as-is.
-    fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M>
+    /// Error codes returned by [`MessageFromGsp::read`] are propagated as `Transport`.
+    fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M, GspRpcError>
     where
         // This allows all error types, including `Infallible`, to be used for `M::InitError`.
         Error: From<M::InitError>,
     {
-        let message = self.wait_for_msg(timeout)?;
-        let function = message.header.function().map_err(|_| EINVAL)?;
+        let message = self.wait_for_msg(timeout).map_err(GspRpcError::Transport)?;
+        let function = message
+            .header
+            .function()
+            .map_err(|_| GspRpcError::Transport(EINVAL))?;
 
         // Extract the message. Store the result as we want to advance the read pointer even in
         // case of failure.
-        let result = if function == M::FUNCTION {
-            let (cmd, contents_1) = M::Message::from_bytes_prefix(message.contents.0).ok_or(EIO)?;
+        let result = (|| -> Result<M, GspRpcError> {
+            message
+                .header
+                .status()
+                .map_err(GspRpcError::Transport)?
+                .log_if_warning(&self.dev, function)
+                .map_err(GspRpcError::Rm)?;
+
+            if function != M::FUNCTION {
+                return Err(GspRpcError::Transport(ERANGE));
+            }
+
+            let (cmd, contents_1) = M::Message::from_bytes_prefix(message.contents.0)
+                .ok_or(GspRpcError::Transport(EIO))?;
             let mut sbuffer = SBufferIter::new_reader([contents_1, message.contents.1]);
 
-            M::read(cmd, &mut sbuffer)
-                .map_err(|e| e.into())
-                .inspect(|_| {
-                    if !sbuffer.is_empty() {
-                        dev_warn!(
-                            &self.dev,
-                            "GSP message {:?} has unprocessed data\n",
-                            function
-                        );
-                    }
-                })
-        } else {
-            Err(ERANGE)
-        };
+            let msg = M::read(cmd, &mut sbuffer).map_err(|e| GspRpcError::Transport(e.into()))?;
+
+            if !sbuffer.is_empty() {
+                dev_warn!(
+                    &self.dev,
+                    "GSP message {:?} has unprocessed data\n",
+                    function
+                );
+            }
+
+            Ok(msg)
+        })();
 
         // Advance the read pointer past this message.
-        self.gsp_mem.advance_cpu_read_ptr(u32::try_from(
-            message.header.length().div_ceil(GSP_PAGE_SIZE),
-        )?);
+        self.gsp_mem.advance_cpu_read_ptr(
+            u32::try_from(message.header.length().div_ceil(GSP_PAGE_SIZE))
+                .map_err(|_| GspRpcError::Transport(EINVAL))?,
+        );
 
         result
     }
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index c89c7b57a751..b8f64bfe9313 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -23,6 +23,7 @@
         cmdq::{
             Cmdq,
             CommandToGsp,
+            GspRpcError,
             MessageFromGsp,
             NoReply, //
         },
@@ -169,8 +170,8 @@ pub(crate) fn wait_gsp_init_done(cmdq: &Cmdq) -> Result {
     loop {
         match cmdq.receive_msg::<GspInitDone>(Cmdq::RECEIVE_TIMEOUT) {
             Ok(_) => break Ok(()),
-            Err(ERANGE) => continue,
-            Err(e) => break Err(e),
+            Err(GspRpcError::Transport(ERANGE)) => continue,
+            Err(e) => break Err(e.into()),
         }
     }
 }
@@ -234,6 +235,6 @@ pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> {
 }
 
 /// Send the [`GetGspInfo`] command and awaits for its reply.
-pub(crate) fn get_gsp_info(cmdq: &Cmdq, bar: &Bar0) -> Result<GetGspStaticInfoReply> {
+pub(crate) fn get_gsp_info(cmdq: &Cmdq, bar: &Bar0) -> Result<GetGspStaticInfoReply, GspRpcError> {
     cmdq.send_command(bar, GetGspStaticInfo)
 }
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index a8d7c62af097..5fabb815a919 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -6,9 +6,13 @@
 // Alias to avoid repeating the version number with every use.
 use r570_144 as bindings;
 
-use core::ops::Range;
+use core::{
+    fmt,
+    ops::Range, //
+};
 
 use kernel::{
+    device,
     dma::Coherent,
     prelude::*,
     ptr::{
@@ -99,7 +103,6 @@ pub(in crate::gsp) fn advance_cpu_write_ptr(qs: &Coherent<GspMem>, count: u32) {
 
 /// Status code returned by GSP-RM operations.
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
-#[expect(dead_code)]
 pub(crate) enum GspMsgRmStatus {
     /// The operation succeeded.
     Ok,
@@ -605,6 +608,31 @@ fn try_from(value: u32) -> Result<Self> {
     }
 }
 
+impl GspMsgRmStatus {
+    /// Converts [`GspMsgRmStatus`] to a [`Result`], logging if the status is a warning.
+    ///
+    /// `rpc_name` identifies the RPC for the log message.
+    pub(super) fn log_if_warning(
+        self,
+        dev: &device::Device,
+        rpc_name: impl fmt::Debug,
+    ) -> Result<(), GspMsgRmError> {
+        match self {
+            Self::Ok => Ok(()),
+            Self::Warning(warning) => {
+                dev_warn!(
+                    dev,
+                    "GSP RPC {:?} returned warning {:?}\n",
+                    rpc_name,
+                    warning
+                );
+                Ok(())
+            }
+            Self::Error(status) => Err(status),
+        }
+    }
+}
+
 /// Empty type to group methods related to heap parameters for running the GSP firmware.
 enum GspFwHeapParams {}
 
@@ -1347,6 +1375,11 @@ pub(crate) fn function(&self) -> Result<MsgFunction, u32> {
             .map_err(|_| self.inner.rpc.function)
     }
 
+    /// Returns the RPC status from the message header.
+    pub(super) fn status(&self) -> Result<GspMsgRmStatus> {
+        self.inner.rpc.rpc_result.try_into()
+    }
+
     // Returns the number of elements (i.e. memory pages) used by this message.
     pub(crate) fn element_count(&self) -> u32 {
         self.inner.elemCount
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs
index 474e4c8021db..672c7d6c3cf6 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -29,6 +29,7 @@
     gsp::{
         cmdq::{
             Cmdq,
+            GspRpcError,
             MessageFromGsp, //
         },
         fw,
@@ -360,8 +361,8 @@ pub(crate) fn run(cmdq: &Cmdq, params: GspSequencerParams<'a>) -> Result {
         let seq_info = loop {
             match cmdq.receive_msg::<GspSequence>(Cmdq::RECEIVE_TIMEOUT) {
                 Ok(seq_info) => break seq_info,
-                Err(ERANGE) => continue,
-                Err(e) => return Err(e),
+                Err(GspRpcError::Transport(ERANGE)) => continue,
+                Err(e) => return Err(e.into()),
             }
         };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 4/8] gpu: nova-core: gsp: expose GSP-RM internal client and subdevice handles
  2026-04-17 15:29 [PATCH v4 0/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
                   ` (2 preceding siblings ...)
  2026-04-17 15:29 ` [PATCH v4 3/8] gpu: nova-core: gsp: add GspRpcError for Cmdq RPC error handling Eliot Courtney
@ 2026-04-17 15:29 ` Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 5/8] gpu: nova-core: gsp: add RM control RPC structure binding Eliot Courtney
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-04-17 15:29 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	rust-for-linux, dri-devel, linux-kernel, Eliot Courtney

Expose the `hInternalClient` and `hInternalSubdevice` handles. These are
needed for RM control calls.

Reviewed-by: Joel Fernandes <joelagnelf@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gsp/commands.rs    | 65 ++++++++++++++++++++++++++++++++
 drivers/gpu/nova-core/gsp/fw/commands.rs | 19 +++++++++-
 2 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index b8f64bfe9313..8a19ef689731 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -4,6 +4,7 @@
     array,
     convert::Infallible,
     ffi::FromBytesUntilNulError,
+    marker::PhantomData,
     str::Utf8Error, //
 };
 
@@ -35,6 +36,54 @@
     sbuffer::SBufferIter,
 };
 
+/// Marker type for a GSP-RM client handle.
+///
+/// A client handle identifies a client which provides a namespace for RM objects. Lookup of objects
+/// happens within the client namespace.
+pub(crate) struct Client;
+
+/// Marker type for a GSP-RM device handle.
+///
+/// A device handle identifies a logical GPU device instance under a client. In multi-GPU
+/// configurations it can represent a grouped or logical device.
+#[expect(dead_code)]
+pub(crate) struct Device;
+
+/// Marker type for a GSP-RM subdevice handle.
+///
+/// A subdevice handle identifies a child of a device that selects one specific GPU within that
+/// device.
+pub(crate) struct Subdevice;
+
+/// A typed GSP-RM object handle.
+///
+/// These handles form a tree structure with different types of RM objects, with client at the root.
+/// An RM object is anything identified by a valid handle. For example, the tree may look like
+/// Client -> Device -> Subdevice, where there may be multiple devices under each client, and
+/// multiple subdevices under each device.
+#[derive(Debug)]
+pub(crate) struct Handle<T>(u32, PhantomData<T>);
+
+impl<T> Clone for Handle<T> {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+impl<T> Copy for Handle<T> {}
+
+impl<T> Handle<T> {
+    /// Creates a new handle from a raw value.
+    pub(crate) fn new(raw: u32) -> Self {
+        Self(raw, PhantomData)
+    }
+
+    /// Returns the raw handle value.
+    #[expect(dead_code)]
+    pub(crate) fn as_raw(self) -> u32 {
+        self.0
+    }
+}
+
 /// The `GspSetSystemInfo` command.
 pub(crate) struct SetSystemInfo<'a> {
     pdev: &'a pci::Device<device::Bound>,
@@ -193,6 +242,8 @@ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
 /// The reply from the GSP to the [`GetGspInfo`] command.
 pub(crate) struct GetGspStaticInfoReply {
     gpu_name: [u8; 64],
+    client: Handle<Client>,
+    subdevice: Handle<Subdevice>,
 }
 
 impl MessageFromGsp for GetGspStaticInfoReply {
@@ -206,6 +257,8 @@ fn read(
     ) -> Result<Self, Self::InitError> {
         Ok(GetGspStaticInfoReply {
             gpu_name: msg.gpu_name_str(),
+            client: msg.client(),
+            subdevice: msg.subdevice(),
         })
     }
 }
@@ -232,6 +285,18 @@ pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> {
             .to_str()
             .map_err(GpuNameError::InvalidUtf8)
     }
+
+    /// Returns the client handle allocated by GSP-RM.
+    #[expect(dead_code)]
+    pub(crate) fn client(&self) -> Handle<Client> {
+        self.client
+    }
+
+    /// Returns the subdevice handle allocated by GSP-RM.
+    #[expect(dead_code)]
+    pub(crate) fn subdevice(&self) -> Handle<Subdevice> {
+        self.subdevice
+    }
 }
 
 /// Send the [`GetGspInfo`] command and awaits for its reply.
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index db46276430be..9fce6ffefbce 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -10,7 +10,14 @@
     }, //
 };
 
-use crate::gsp::GSP_PAGE_SIZE;
+use crate::gsp::{
+    commands::{
+        Client,
+        Handle,
+        Subdevice, //
+    },
+    GSP_PAGE_SIZE, //
+};
 
 use super::bindings;
 
@@ -121,6 +128,16 @@ impl GspStaticConfigInfo {
     pub(crate) fn gpu_name_str(&self) -> [u8; 64] {
         self.0.gpuNameString
     }
+
+    /// Returns the client handle allocated by GSP-RM.
+    pub(crate) fn client(&self) -> Handle<Client> {
+        Handle::new(self.0.hInternalClient)
+    }
+
+    /// Returns the subdevice handle allocated by GSP-RM.
+    pub(crate) fn subdevice(&self) -> Handle<Subdevice> {
+        Handle::new(self.0.hInternalSubdevice)
+    }
 }
 
 // SAFETY: Padding is explicit and will not contain uninitialized data.

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 5/8] gpu: nova-core: gsp: add RM control RPC structure binding
  2026-04-17 15:29 [PATCH v4 0/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
                   ` (3 preceding siblings ...)
  2026-04-17 15:29 ` [PATCH v4 4/8] gpu: nova-core: gsp: expose GSP-RM internal client and subdevice handles Eliot Courtney
@ 2026-04-17 15:29 ` Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 6/8] gpu: nova-core: gsp: add types for RM control RPCs Eliot Courtney
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-04-17 15:29 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	rust-for-linux, dri-devel, linux-kernel, Eliot Courtney

Add the bindgen rpc_gsp_rm_control_v03_00 structure. This is the
structure for sending RM control commands.

Reviewed-by: Joel Fernandes <joelagnelf@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 11 +++++++++++
 1 file changed, 11 insertions(+)

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 dd37a7fd58c6..05e205e6dc58 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -1025,6 +1025,17 @@ fn default() -> Self {
 }
 #[repr(C)]
 #[derive(Debug, Default, MaybeZeroable)]
+pub struct rpc_gsp_rm_control_v03_00 {
+    pub hClient: u32_,
+    pub hObject: u32_,
+    pub cmd: u32_,
+    pub status: u32_,
+    pub paramsSize: u32_,
+    pub flags: u32_,
+    pub params: __IncompleteArrayField<u8_>,
+}
+#[repr(C)]
+#[derive(Debug, Default, MaybeZeroable)]
 pub struct rpc_run_cpu_sequencer_v17_00 {
     pub bufferSizeDWord: u32_,
     pub cmdIndex: u32_,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 6/8] gpu: nova-core: gsp: add types for RM control RPCs
  2026-04-17 15:29 [PATCH v4 0/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
                   ` (4 preceding siblings ...)
  2026-04-17 15:29 ` [PATCH v4 5/8] gpu: nova-core: gsp: add RM control RPC structure binding Eliot Courtney
@ 2026-04-17 15:29 ` Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 7/8] gpu: nova-core: use KVVec for SBufferIter flush Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 8/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
  7 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-04-17 15:29 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	rust-for-linux, dri-devel, linux-kernel, Eliot Courtney

Add `RmControlMsgFunction` which mirrors `MsgFunction` in fw.rs. This
denotes the type of RM control RPC. For now it contains a single
discriminant only (which will be used later), which is needed to prevent
compile errors when using an otherwise empty enum.

Add `GspRmControl` which wraps the RM control RPC structure from the
bindings.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gsp/commands.rs             |  1 -
 drivers/gpu/nova-core/gsp/fw.rs                   |  1 +
 drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs |  1 +
 drivers/gpu/nova-core/gsp/fw/rm.rs                | 91 +++++++++++++++++++++++
 4 files changed, 93 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 8a19ef689731..bf0e8533a71c 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -78,7 +78,6 @@ pub(crate) fn new(raw: u32) -> Self {
     }
 
     /// Returns the raw handle value.
-    #[expect(dead_code)]
     pub(crate) fn as_raw(self) -> u32 {
         self.0
     }
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 5fabb815a919..1f5c61bea645 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -2,6 +2,7 @@
 
 pub(crate) mod commands;
 mod r570_144;
+pub(crate) mod rm;
 
 // Alias to avoid repeating the version number with every use.
 use r570_144 as bindings;
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 05e205e6dc58..ece31cc32f5b 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -44,6 +44,7 @@ fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
 pub const GSP_FW_WPR_META_MAGIC: i64 = -2577556379034558285;
 pub const REGISTRY_TABLE_ENTRY_TYPE_DWORD: u32 = 1;
 pub const GSP_MSG_QUEUE_ELEMENT_SIZE_MAX: u32 = 65536;
+pub const NV2080_CTRL_CMD_CE_GET_FAULT_METHOD_BUFFER_SIZE: u32 = 545270280;
 pub type __u8 = ffi::c_uchar;
 pub type __u16 = ffi::c_ushort;
 pub type __u32 = ffi::c_uint;
diff --git a/drivers/gpu/nova-core/gsp/fw/rm.rs b/drivers/gpu/nova-core/gsp/fw/rm.rs
new file mode 100644
index 000000000000..e51fdcec1f27
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/fw/rm.rs
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+    prelude::*,
+    transmute::{
+        AsBytes,
+        FromBytes, //
+    }, //
+};
+
+use crate::gsp::commands::{
+    Client,
+    Handle, //
+};
+
+use super::{
+    bindings,
+    GspMsgRmStatus, //
+};
+
+/// Command code for RM control RPCs sent using [`MsgFunction::GspRmControl`].
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[repr(u32)]
+pub(crate) enum RmControlMsgFunction {
+    /// Get the CE fault method buffer size.
+    CeGetFaultMethodBufferSize = bindings::NV2080_CTRL_CMD_CE_GET_FAULT_METHOD_BUFFER_SIZE,
+}
+
+// TODO[FPRI]: This is a temporary solution to be replaced with the corresponding derive macros
+// once they land.
+impl TryFrom<u32> for RmControlMsgFunction {
+    type Error = kernel::error::Error;
+
+    fn try_from(value: u32) -> Result<Self> {
+        match value {
+            bindings::NV2080_CTRL_CMD_CE_GET_FAULT_METHOD_BUFFER_SIZE => {
+                Ok(Self::CeGetFaultMethodBufferSize)
+            }
+            _ => Err(EINVAL),
+        }
+    }
+}
+
+impl From<RmControlMsgFunction> for u32 {
+    fn from(value: RmControlMsgFunction) -> Self {
+        // CAST: `RmControlMsgFunction` is `repr(u32)` and can thus be cast losslessly.
+        value as u32
+    }
+}
+
+/// RM control message element structure.
+#[repr(transparent)]
+pub(crate) struct GspRmControl {
+    inner: bindings::rpc_gsp_rm_control_v03_00,
+}
+
+#[expect(dead_code)]
+impl GspRmControl {
+    /// Creates a new RM control command.
+    pub(crate) fn new<T>(
+        client: Handle<Client>,
+        object: Handle<T>,
+        cmd: RmControlMsgFunction,
+        params_size: u32,
+    ) -> Self {
+        Self {
+            inner: bindings::rpc_gsp_rm_control_v03_00 {
+                hClient: client.as_raw(),
+                hObject: object.as_raw(),
+                cmd: u32::from(cmd),
+                status: 0,
+                paramsSize: params_size,
+                flags: 0,
+                params: Default::default(),
+            },
+        }
+    }
+
+    /// Returns the status from the RM control response.
+    ///
+    /// This is distinct from the RPC transport status in the RPC message header.
+    pub(crate) fn status(&self) -> Result<GspMsgRmStatus> {
+        self.inner.status.try_into()
+    }
+}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for GspRmControl {}
+
+// SAFETY: This struct contains no padding.
+unsafe impl AsBytes for GspRmControl {}

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 7/8] gpu: nova-core: use KVVec for SBufferIter flush
  2026-04-17 15:29 [PATCH v4 0/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
                   ` (5 preceding siblings ...)
  2026-04-17 15:29 ` [PATCH v4 6/8] gpu: nova-core: gsp: add types for RM control RPCs Eliot Courtney
@ 2026-04-17 15:29 ` Eliot Courtney
  2026-04-17 15:29 ` [PATCH v4 8/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
  7 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-04-17 15:29 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	rust-for-linux, dri-devel, linux-kernel, Eliot Courtney

Change flush_into_kvec to return KVVec instead of KVec. KVVec uses
vmalloc for large allocations, which is appropriate since RPC reply
payloads can be large (>=20 KiB).

Update GspSequence to use KVVec accordingly.

Reviewed-by: Joel Fernandes <joelagnelf@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gsp/sequencer.rs | 4 ++--
 drivers/gpu/nova-core/sbuffer.rs       | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs
index 672c7d6c3cf6..62aab20ce8c6 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -43,7 +43,7 @@ struct GspSequence {
     /// Current command index for error reporting.
     cmd_index: u32,
     /// Command data buffer containing the sequence of commands.
-    cmd_data: KVec<u8>,
+    cmd_data: KVVec<u8>,
 }
 
 impl MessageFromGsp for GspSequence {
@@ -55,7 +55,7 @@ fn read(
         msg: &Self::Message,
         sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
     ) -> Result<Self, Self::InitError> {
-        let cmd_data = sbuffer.flush_into_kvec(GFP_KERNEL)?;
+        let cmd_data = sbuffer.read_to_vec(GFP_KERNEL)?;
         Ok(GspSequence {
             cmd_index: msg.cmd_index(),
             cmd_data,
diff --git a/drivers/gpu/nova-core/sbuffer.rs b/drivers/gpu/nova-core/sbuffer.rs
index 3a41d224c77a..ae2facdcbdd4 100644
--- a/drivers/gpu/nova-core/sbuffer.rs
+++ b/drivers/gpu/nova-core/sbuffer.rs
@@ -162,11 +162,11 @@ pub(crate) fn read_exact(&mut self, mut dst: &mut [u8]) -> Result {
         Ok(())
     }
 
-    /// Read all the remaining data into a [`KVec`].
+    /// Read all the remaining data into a [`KVVec`].
     ///
     /// `self` will be empty after this operation.
-    pub(crate) fn flush_into_kvec(&mut self, flags: kernel::alloc::Flags) -> Result<KVec<u8>> {
-        let mut buf = KVec::<u8>::new();
+    pub(crate) fn read_to_vec(&mut self, flags: kernel::alloc::Flags) -> Result<KVVec<u8>> {
+        let mut buf = KVVec::<u8>::new();
 
         if let Some(slice) = core::mem::take(&mut self.cur_slice) {
             buf.extend_from_slice(slice, flags)?;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 8/8] gpu: nova-core: gsp: add RM control command infrastructure
  2026-04-17 15:29 [PATCH v4 0/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
                   ` (6 preceding siblings ...)
  2026-04-17 15:29 ` [PATCH v4 7/8] gpu: nova-core: use KVVec for SBufferIter flush Eliot Courtney
@ 2026-04-17 15:29 ` Eliot Courtney
  7 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-04-17 15:29 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	rust-for-linux, dri-devel, linux-kernel, Eliot Courtney

Add `RawRmControl` which implements CommandToGsp for sending RM control
RPCs. Add `RmControlReply` which implements MessageFromGsp for getting
the reply back. Add the `RmControlCommand` trait for defining an RM
control message.

Add `RmControl` which sends an RM control RPC via the command queue
using the above structures.

This gives a generic way to send each RM control RPC. Each new RM
control RPC can be added by extending RmControlMsgFunction, adding its
bindings wrappers and writing an implementation of `RmControlCommand`
that can be sent using `RmControl`.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gsp.rs             |   1 +
 drivers/gpu/nova-core/gsp/fw/rm.rs       |   1 -
 drivers/gpu/nova-core/gsp/rm.rs          |   3 +
 drivers/gpu/nova-core/gsp/rm/commands.rs | 155 +++++++++++++++++++++++++++++++
 4 files changed, 159 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index ba5b7f990031..58e3d7d21557 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -21,6 +21,7 @@
 pub(crate) mod cmdq;
 pub(crate) mod commands;
 mod fw;
+pub(crate) mod rm;
 mod sequencer;
 
 pub(crate) use fw::{
diff --git a/drivers/gpu/nova-core/gsp/fw/rm.rs b/drivers/gpu/nova-core/gsp/fw/rm.rs
index e51fdcec1f27..baa3857533e5 100644
--- a/drivers/gpu/nova-core/gsp/fw/rm.rs
+++ b/drivers/gpu/nova-core/gsp/fw/rm.rs
@@ -54,7 +54,6 @@ pub(crate) struct GspRmControl {
     inner: bindings::rpc_gsp_rm_control_v03_00,
 }
 
-#[expect(dead_code)]
 impl GspRmControl {
     /// Creates a new RM control command.
     pub(crate) fn new<T>(
diff --git a/drivers/gpu/nova-core/gsp/rm.rs b/drivers/gpu/nova-core/gsp/rm.rs
new file mode 100644
index 000000000000..10e879a3e842
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/rm.rs
@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: GPL-2.0
+
+pub(crate) mod commands;
diff --git a/drivers/gpu/nova-core/gsp/rm/commands.rs b/drivers/gpu/nova-core/gsp/rm/commands.rs
new file mode 100644
index 000000000000..33be7a663a8c
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/rm/commands.rs
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use core::array;
+
+use kernel::{
+    device,
+    prelude::*, //
+};
+
+use crate::{
+    driver::Bar0,
+    gsp::{
+        cmdq::{
+            Cmdq,
+            CommandToGsp,
+            GspRpcError,
+            MessageFromGsp, //
+        },
+        commands::{
+            Client,
+            Handle, //
+        },
+        fw::{
+            rm::*,
+            GspMsgRmStatus,
+            MsgFunction, //
+        },
+    },
+    sbuffer::SBufferIter, //
+};
+
+/// Wraps a [`RmControl`] command to provide the infrastructure for sending it on the command queue.
+struct RawRmControl<'a, T>(&'a RmControl<T>)
+where
+    T: RmControlCommand;
+
+impl<'a, T: RmControlCommand> CommandToGsp for RawRmControl<'a, T> {
+    const FUNCTION: MsgFunction = MsgFunction::GspRmControl;
+    type Command = GspRmControl;
+    type Reply = RmControlReply;
+    type InitError = Error;
+
+    fn init(&self) -> impl Init<Self::Command, Self::InitError> {
+        let params_size: u32 = T::LEN.try_into()?;
+        Ok(GspRmControl::new(
+            self.0.client,
+            self.0.object,
+            T::FUNCTION,
+            params_size,
+        ))
+    }
+
+    fn variable_payload_len(&self) -> usize {
+        T::LEN
+    }
+
+    fn init_variable_payload(
+        &self,
+        dst: &mut SBufferIter<array::IntoIter<&mut [u8], 2>>,
+    ) -> Result {
+        self.0.cmd.write_payload(dst)
+    }
+}
+
+/// Command for sending an RM control message to the GSP.
+///
+/// RM control messages are used to query or control RM objects (see [`Handle`] for more info on RM
+/// objects). It takes a client handle and an RM object handle identifying the target of the
+/// message, within the given client.
+pub(crate) struct RmControl<T>
+where
+    T: RmControlCommand,
+{
+    /// The client handle under which `object` is allocated.
+    client: Handle<Client>,
+    /// The RM object handle to query or control.
+    object: Handle<T::Target>,
+    /// The specific RM control command to send.
+    cmd: T,
+}
+
+#[expect(unused)]
+impl<T: RmControlCommand> RmControl<T> {
+    /// Creates a new RM control command.
+    pub(crate) fn new(client: Handle<Client>, object: Handle<T::Target>, cmd: T) -> Self {
+        Self {
+            client,
+            object,
+            cmd,
+        }
+    }
+
+    /// Sends an RM control command, checks the reply status, and returns the reply.
+    pub(crate) fn send(
+        self,
+        dev: &device::Device,
+        cmdq: &Cmdq,
+        bar: &Bar0,
+    ) -> Result<T::Reply, GspRpcError> {
+        let reply = cmdq.send_command(bar, RawRmControl(&self))?;
+
+        reply
+            .status
+            .log_if_warning(dev, T::FUNCTION)
+            .map_err(GspRpcError::Rm)?;
+
+        self.cmd
+            .parse_reply(&reply.params)
+            .map_err(GspRpcError::Transport)
+    }
+}
+
+/// Response from an RM control message.
+struct RmControlReply {
+    status: GspMsgRmStatus,
+    params: KVVec<u8>,
+}
+
+impl MessageFromGsp for RmControlReply {
+    const FUNCTION: MsgFunction = MsgFunction::GspRmControl;
+    type Message = GspRmControl;
+    type InitError = Error;
+
+    fn read(
+        msg: &Self::Message,
+        sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
+    ) -> Result<Self, Self::InitError> {
+        Ok(RmControlReply {
+            status: msg.status()?,
+            params: sbuffer.read_to_vec(GFP_KERNEL)?,
+        })
+    }
+}
+
+/// Trait for RM control commands that can be sent using [`RmControl`].
+pub(crate) trait RmControlCommand {
+    /// The specific RM control message kind to send.
+    const FUNCTION: RmControlMsgFunction;
+
+    /// Length of the command-specific payload to send with the RM control message.
+    const LEN: usize;
+
+    /// The type of the reply after parsing the raw bytes from the RM control reply message.
+    type Reply;
+
+    /// The type of the RM object that this command targets.
+    type Target;
+
+    /// Writes the payload of the command into `dst`.
+    fn write_payload(&self, dst: &mut SBufferIter<array::IntoIter<&mut [u8], 2>>) -> Result;
+
+    /// Parses the reply bytes from an RM control reply message into the command-specific
+    /// reply type.
+    fn parse_reply(&self, params: &[u8]) -> Result<Self::Reply>;
+}

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-04-17 15:34 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-17 15:29 [PATCH v4 0/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney
2026-04-17 15:29 ` [PATCH v4 1/8] gpu: nova-core: gsp: add NV_STATUS error code bindings Eliot Courtney
2026-04-17 15:29 ` [PATCH v4 2/8] gpu: nova-core: gsp: add GSP-RM status types Eliot Courtney
2026-04-17 15:29 ` [PATCH v4 3/8] gpu: nova-core: gsp: add GspRpcError for Cmdq RPC error handling Eliot Courtney
2026-04-17 15:29 ` [PATCH v4 4/8] gpu: nova-core: gsp: expose GSP-RM internal client and subdevice handles Eliot Courtney
2026-04-17 15:29 ` [PATCH v4 5/8] gpu: nova-core: gsp: add RM control RPC structure binding Eliot Courtney
2026-04-17 15:29 ` [PATCH v4 6/8] gpu: nova-core: gsp: add types for RM control RPCs Eliot Courtney
2026-04-17 15:29 ` [PATCH v4 7/8] gpu: nova-core: use KVVec for SBufferIter flush Eliot Courtney
2026-04-17 15:29 ` [PATCH v4 8/8] gpu: nova-core: gsp: add RM control command infrastructure Eliot Courtney

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox