* [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