* [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware
@ 2026-08-19 3:51 John Hubbard
2026-08-19 3:51 ` [PATCH 01/27] gpu: nova-core: firmware: add r000 bindings John Hubbard
` (27 more replies)
0 siblings, 28 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:51 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
Nova-core boots the 570.144 GSP firmware today. This series moves the
driver to the r000 firmware ABI, which is stable across releases, so
firmware file names no longer carry a version number.
The actual firmware is not yet publicly available, so this series is for
early review, and should not be merged until the corresponding firmware
shows up.
Beyond drm-rust-next, the series sits on these patchsets:
* The GIN interrupt controller series, which needs Danilo Krummrich's
PCI and irq rework and Joel Fernandes' wait_for_completion_timeout().
* Eliot Courtney's NVKV encoder and decoder, which GSP_INIT is built on,
and his Vec::push_init.
I've created a branch that has everything, for reviewer convenience,
here:
https://github.com/johnhubbard/linux/tree/nova-core-run-on-r615-or-later-v1
Patch 25 makes the big switch from r570 to r000 in one commit. Before
that commit, everything works on r570. After that commit, only r000 is
supported. Patches 1 to 24 add the pieces the switch needs, and patches
26 and 27 delete and clean up after it.
Tested on TU117 (Turing, T400), GA104 (Ampere, RTX A4000) and GB202
(Blackwell, RTX PRO 6000), covering PCIe probe, chipset and GPU name
readback, the interrupt delivery self-test, and the KUnit suites.
Also, the vGPU team has tested an earlier, nearly identical version of
this patchset (minus the cleanups at the end), and it works there too.
John Hubbard (27):
gpu: nova-core: firmware: add r000 bindings
gpu: nova-core: extract radix3 page table into its own module
gpu: nova-core: set MCTP transport header version to 1
gpu: nova-core: add Falcon helpers for r000 LOAD_EXEC events
gpu: nova-core: zero-pad radix3 page table levels to page boundary
gpu: nova-core: distinguish async GSP RPC traffic in debug logs
gpu: nova-core: add optional ucodes firmware loading
gpu: nova-core: add LIBOS3 log buffers and state monitor buffer
gpu: nova-core: add build ID headers to debugfs log buffer dumps
gpu: nova-core: rename the FbRanges elf field to fw_image
gpu: nova-core: regs: add msgq v2 BAR0 register declarations
gpu: nova-core: gsp: add msgq v2 internals
gpu: nova-core: generalize allocate_command() for variable headers
gpu: nova-core: add GMC API message types
gpu: nova-core: add GMC send path
gpu: nova-core: add GMC transport receive path
gpu: nova-core: gsp: add GMC dispatch on receive
gpu: nova-core: separate the generic falcon bootloader from FWSEC
gpu: nova-core: handle the r000 load-and-execute HS binary event
gpu: nova-core: handle the r000 load-and-execute bootloader event
gpu: nova-core: gsp: add the GMC boot event dispatcher
gpu: nova-core: gsp: add the GSP_INIT request builder
gpu: nova-core: gsp: send GSP_INIT and decode its reply
gpu: nova-core: gsp: pass the remaining log buffers to GSP-RM
gpu: nova-core: switch to the r000 GSP firmware
gpu: nova-core: gsp: remove the retired system-info and static-info
RPCs
gpu: nova-core: firmware: delete the r570 bindings
drivers/gpu/nova-core/driver.rs | 2 +-
drivers/gpu/nova-core/falcon.rs | 152 +++-
drivers/gpu/nova-core/fb.rs | 18 +-
drivers/gpu/nova-core/fb/hal/gb100.rs | 25 +-
drivers/gpu/nova-core/fb/hal/gb202.rs | 5 +-
drivers/gpu/nova-core/firmware.rs | 44 +-
drivers/gpu/nova-core/firmware/bindata.rs | 37 +
.../nova-core/firmware/fwsec/bootloader.rs | 112 +--
.../gpu/nova-core/firmware/gen_bootloader.rs | 215 +++++
drivers/gpu/nova-core/firmware/gsp.rs | 124 +--
drivers/gpu/nova-core/firmware/radix3.rs | 145 ++++
drivers/gpu/nova-core/firmware/tlv.rs | 56 ++
drivers/gpu/nova-core/fsp.rs | 3 +-
drivers/gpu/nova-core/gpu.rs | 23 +-
drivers/gpu/nova-core/gsp.rs | 284 ++++++-
drivers/gpu/nova-core/gsp/boot.rs | 549 +++++++++++-
drivers/gpu/nova-core/gsp/cmdq.rs | 792 ++++++++++++------
.../gpu/nova-core/gsp/cmdq/continuation.rs | 1 +
drivers/gpu/nova-core/gsp/commands.rs | 293 +++++--
drivers/gpu/nova-core/gsp/fw.rs | 740 ++++++++--------
drivers/gpu/nova-core/gsp/fw/commands.rs | 200 ++---
.../gsp/fw/{r570_144.rs => r000_00.rs} | 2 +-
.../gsp/fw/{r570_144 => r000_00}/bindings.rs | 548 ++++++++----
drivers/gpu/nova-core/gsp/hal.rs | 22 +-
drivers/gpu/nova-core/gsp/hal/tu102.rs | 18 +-
drivers/gpu/nova-core/gsp/regs.rs | 30 +
drivers/gpu/nova-core/gsp/sequencer.rs | 379 ---------
drivers/gpu/nova-core/irq/gsp.rs | 2 +-
drivers/gpu/nova-core/mctp.rs | 18 +-
drivers/gpu/nova-core/regs.rs | 8 +-
30 files changed, 3131 insertions(+), 1716 deletions(-)
create mode 100644 drivers/gpu/nova-core/firmware/bindata.rs
create mode 100644 drivers/gpu/nova-core/firmware/gen_bootloader.rs
create mode 100644 drivers/gpu/nova-core/firmware/radix3.rs
rename drivers/gpu/nova-core/gsp/fw/{r570_144.rs => r000_00.rs} (95%)
rename drivers/gpu/nova-core/gsp/fw/{r570_144 => r000_00}/bindings.rs (73%)
delete mode 100644 drivers/gpu/nova-core/gsp/sequencer.rs
--
2.55.0
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH 01/27] gpu: nova-core: firmware: add r000 bindings
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
@ 2026-08-19 3:51 ` John Hubbard
2026-08-19 3:51 ` [PATCH 02/27] gpu: nova-core: extract radix3 page table into its own module John Hubbard
` (26 subsequent siblings)
27 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:51 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
"r000" means "latest firmware", without getting bogged down into naming
actual versions, because those will vary. The story is: nova uses
r570 at this point, but will transition soon to some later version, and
that version will have a stable ABI, which in turn means that the
firmware file names will stop including version numbers in the file name
itself. Version information will still be recorded inside each firmware
image, though.
The *_SI_* items are for key-value access to "System Info" items that
nova will received from an early GSP-RM RPC call.
Add generated firmware bindings from the r000 Open RM headers. The
module is registered but not yet wired as the active binding set.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/fw.rs | 1 +
drivers/gpu/nova-core/gsp/fw/r000_00.rs | 31 +
.../gpu/nova-core/gsp/fw/r000_00/bindings.rs | 1288 +++++++++++++++++
3 files changed, 1320 insertions(+)
create mode 100644 drivers/gpu/nova-core/gsp/fw/r000_00.rs
create mode 100644 drivers/gpu/nova-core/gsp/fw/r000_00/bindings.rs
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 0b01c81ec092..9b29308678a7 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -2,6 +2,7 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
pub(crate) mod commands;
+mod r000_00;
mod r570_144;
// Alias to avoid repeating the version number with every use.
diff --git a/drivers/gpu/nova-core/gsp/fw/r000_00.rs b/drivers/gpu/nova-core/gsp/fw/r000_00.rs
new file mode 100644
index 000000000000..095e5dddf6cf
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/fw/r000_00.rs
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Firmware bindings.
+//!
+//! Imports the generated bindings by `bindgen`.
+//!
+//! This module may not be directly used. Please abstract or re-export the needed symbols in the
+//! parent module instead.
+
+#![allow(
+ dead_code,
+ clippy::all,
+ clippy::undocumented_unsafe_blocks,
+ clippy::ptr_as_ptr,
+ clippy::ref_as_ptr,
+ missing_docs,
+ non_camel_case_types,
+ non_upper_case_globals,
+ non_snake_case,
+ improper_ctypes,
+ unreachable_pub,
+ unsafe_op_in_unsafe_fn
+)]
+use kernel::ffi;
+use pin_init::MaybeZeroable;
+
+include!("r000_00/bindings.rs");
+
+// SAFETY: This type has a size of zero, so its inclusion into another type should not affect their
+// ability to implement `Zeroable`.
+unsafe impl<T> kernel::prelude::Zeroable for __IncompleteArrayField<T> {}
diff --git a/drivers/gpu/nova-core/gsp/fw/r000_00/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r000_00/bindings.rs
new file mode 100644
index 000000000000..e6bab20baa65
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/fw/r000_00/bindings.rs
@@ -0,0 +1,1288 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::core::marker::PhantomData<T>, [T; 0]);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub const fn new() -> Self {
+ __IncompleteArrayField(::core::marker::PhantomData, [])
+ }
+ #[inline]
+ pub fn as_ptr(&self) -> *const T {
+ self as *const _ as *const T
+ }
+ #[inline]
+ pub fn as_mut_ptr(&mut self) -> *mut T {
+ self as *mut _ as *mut T
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::core::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::core::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::core::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+#[repr(C)]
+pub struct __BindgenUnionField<T>(::core::marker::PhantomData<T>);
+impl<T> __BindgenUnionField<T> {
+ #[inline]
+ pub const fn new() -> Self {
+ __BindgenUnionField(::core::marker::PhantomData)
+ }
+ #[inline]
+ pub unsafe fn as_ref(&self) -> &T {
+ ::core::mem::transmute(self)
+ }
+ #[inline]
+ pub unsafe fn as_mut(&mut self) -> &mut T {
+ ::core::mem::transmute(self)
+ }
+}
+impl<T> ::core::default::Default for __BindgenUnionField<T> {
+ #[inline]
+ fn default() -> Self {
+ Self::new()
+ }
+}
+impl<T> ::core::clone::Clone for __BindgenUnionField<T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+impl<T> ::core::marker::Copy for __BindgenUnionField<T> {}
+impl<T> ::core::fmt::Debug for __BindgenUnionField<T> {
+ fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ fmt.write_str("__BindgenUnionField")
+ }
+}
+impl<T> ::core::hash::Hash for __BindgenUnionField<T> {
+ fn hash<H: ::core::hash::Hasher>(&self, _state: &mut H) {}
+}
+impl<T> ::core::cmp::PartialEq for __BindgenUnionField<T> {
+ fn eq(&self, _other: &__BindgenUnionField<T>) -> bool {
+ true
+ }
+}
+impl<T> ::core::cmp::Eq for __BindgenUnionField<T> {}
+pub const NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_0: u32 = 0;
+pub const NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3: u32 = 3;
+pub const NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_7: u32 = 7;
+pub const NV_VGPU_MSG_SIGNATURE_VALID: u32 = 1129337430;
+pub const NVGMC_SI_PCI_DEVICE_ID: u32 = 1;
+pub const NVGMC_SI_PCI_SUB_DEVICE_ID: u32 = 2;
+pub const NVGMC_SI_PCI_REVISION_ID: u32 = 3;
+pub const NVGMC_SI_PCI_CONFIG_MIRROR_BASE: u32 = 16;
+pub const NVGMC_SI_PCI_CONFIG_MIRROR_SIZE: u32 = 17;
+pub const NVGMC_SI_PCIE_ATOMICS_CPL_DEV_CAP_MASK: u32 = 32;
+pub const NVGMC_SI_CHIPSET: u32 = 48;
+pub const NVGMC_SI_CHIPSET_L1SS_ENABLE: u32 = 49;
+pub const NVGMC_SI_HYPERVISOR_TYPE: u32 = 64;
+pub const NVGMC_SI_VIRTUAL_CONFIG_BITS: u32 = 65;
+pub const NVGMC_SI_PCIE_POWER_CONTROL_VALUE: u32 = 80;
+pub const NVGMC_SI_PF0_DEVICE_CONTROL2_REG: u32 = 81;
+pub const NVGMC_SI_PCIE_LINK_CAP: u32 = 82;
+pub const NVGMC_SI_GRID_BUILD_CSP: u32 = 96;
+pub const NVGMC_SI_HOST_ARCH: u32 = 112;
+pub const NVGMC_SI_HOST_ARCH_NONE: u32 = 0;
+pub const NVGMC_SI_HOST_ARCH_X86_64: u32 = 1;
+pub const NVGMC_SI_HOST_ARCH_PPC64LE: u32 = 2;
+pub const NVGMC_SI_HOST_ARCH_ARM: u32 = 3;
+pub const NVGMC_SI_HOST_ARCH_AARCH64: u32 = 4;
+pub const NVGMC_SI_HOST_ARCH_RISCV64: u32 = 5;
+pub const NVGMC_SI_HOST_OS_TYPE: u32 = 113;
+pub const NVGMC_SI_HOST_OS_TYPE_OTHER: u32 = 0;
+pub const NVGMC_SI_HOST_OS_TYPE_UNIX: u32 = 1;
+pub const NVGMC_SI_HOST_OS_TYPE_WINDOWS: u32 = 2;
+pub const NVGMC_SI_HOST_PAGE_SIZE: u32 = 120;
+pub const NVGMC_SI_VF_TOTAL_VFS: u32 = 128;
+pub const NVGMC_SI_VF_FIRST_VF_OFFSET: u32 = 129;
+pub const NVGMC_SI_FHB_BUSINFO_REVISION_ID: u32 = 144;
+pub const NVGMC_SI_CHIPSET_BUSINFO_REVISION_ID: u32 = 145;
+pub const NVGMC_SI_ACPI_DOD_STATUS: u32 = 512;
+pub const NVGMC_SI_ACPI_DOD_ACPI_IDS: u32 = 528;
+pub const NVGMC_SI_ACPI_JT_STATUS: u32 = 544;
+pub const NVGMC_SI_ACPI_JT_CAPS: u32 = 545;
+pub const NVGMC_SI_ACPI_JT_REV_ID: u32 = 546;
+pub const NVGMC_SI_ACPI_JT_B_SBIOS_CAPS: u32 = 547;
+pub const NVGMC_SI_ACPI_MUX_TABLE_LEN: u32 = 560;
+pub const NVGMC_SI_ACPI_MUX_MODE_ACPI_ID: u32 = 576;
+pub const NVGMC_SI_ACPI_MUX_MODE_MODE: u32 = 577;
+pub const NVGMC_SI_ACPI_MUX_MODE_STATUS: u32 = 578;
+pub const NVGMC_SI_ACPI_MUX_PART_ACPI_ID: u32 = 592;
+pub const NVGMC_SI_ACPI_MUX_PART_MODE: u32 = 593;
+pub const NVGMC_SI_ACPI_MUX_PART_STATUS: u32 = 594;
+pub const NVGMC_SI_ACPI_MUX_STATE_ACPI_ID: u32 = 608;
+pub const NVGMC_SI_ACPI_MUX_STATE_MODE: u32 = 609;
+pub const NVGMC_SI_ACPI_MUX_STATE_STATUS: u32 = 610;
+pub const NVGMC_SI_ACPI_CAPS_STATUS: u32 = 624;
+pub const NVGMC_SI_ACPI_CAPS_OPTIMUS_CAPS: u32 = 625;
+pub const NVGMC_SI_PCIE_FLAGS: u32 = 4096;
+pub const NVGMC_SI_PLATFORM_FLAGS: u32 = 4097;
+pub const NVGMC_SI_FEATURE_FLAGS: u32 = 4098;
+pub const NVGMC_SI_VF_FLAGS: u32 = 4099;
+pub const NVGMC_SI_GPU_PHYS_ADDR: u32 = 4112;
+pub const NVGMC_SI_GPU_PHYS_FB_ADDR: u32 = 4113;
+pub const NVGMC_SI_GPU_PHYS_INST_ADDR: u32 = 4114;
+pub const NVGMC_SI_GPU_PHYS_IO_ADDR: u32 = 4115;
+pub const NVGMC_SI_NV_DOMAIN_BUS_DEVICE_FUNC: u32 = 4128;
+pub const NVGMC_SI_SIM_ACCESS_BUF_PHYS_ADDR: u32 = 4129;
+pub const NVGMC_SI_NOTIFY_OP_SHARED_SURF_PHYS_ADDR: u32 = 4130;
+pub const NVGMC_SI_PCIE_ATOMICS_OP_MASK: u32 = 4144;
+pub const NVGMC_SI_CONSOLE_MEM_SIZE: u32 = 4145;
+pub const NVGMC_SI_MAX_USER_VA: u32 = 4146;
+pub const NVGMC_SI_SYS_TIMER_OFFSET_NS: u32 = 4160;
+pub const NVGMC_SI_VF_FIRST_BAR0_ADDRESS: u32 = 4176;
+pub const NVGMC_SI_VF_FIRST_BAR1_ADDRESS: u32 = 4177;
+pub const NVGMC_SI_VF_FIRST_BAR2_ADDRESS: u32 = 4178;
+pub const NVGMC_SI_FHB_BUSINFO: u32 = 4240;
+pub const NVGMC_SI_CHIPSET_BUSINFO: u32 = 4241;
+pub const NVGMC_SI_KERNEL_DRIVER_NAME: u32 = 4352;
+pub const NVGMC_SI_KERNEL_DRIVER_VERSION_STRING: u32 = 4353;
+pub const NVGMC_SI_KERNEL_DRIVER_CHANGELIST: u32 = 4354;
+pub const NVGMC_SI_UNSTABLE_INTERFACE_SUPPORT: u32 = 4368;
+pub const NVGMC_SI_REGKEY_NAME: u32 = 12400;
+pub const NVGMC_SI_REGKEY_VALUE_U32: u32 = 12401;
+pub const NVGMC_SI_REGKEY_VALUE_STRING: u32 = 12402;
+pub const NVGMC_SI_REGKEY_VALUE_BINARY: u32 = 12403;
+pub const NVGMC_SI_REGKEY_NAME_MAX_LEN: u32 = 256;
+pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2: u32 = 0;
+pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL: u32 = 23068672;
+pub const GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X: u32 = 8388608;
+pub const GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100: u32 = 18874368;
+pub const GSP_FW_HEAP_PARAM_SIZE_PER_GB: u32 = 98304;
+pub const GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE: u32 = 100663296;
+pub const GSP_FW_HEAP_SIZE_VGPU_DEFAULT: u32 = 609222656;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB: u32 = 64;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB: u32 = 256;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB: u32 = 88;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB: u32 = 280;
+pub const GSP_FW_WPR_META_REVISION: u32 = 1;
+pub const GSP_FW_WPR_META_MAGIC: i64 = -2577556379034558285;
+pub const REGISTRY_TABLE_ENTRY_TYPE_DWORD: u32 = 1;
+pub const NVGMC_SI_CL_FLAGS: u32 = 4147;
+pub type __u8 = ffi::c_uchar;
+pub type __u16 = ffi::c_ushort;
+pub type __u32 = ffi::c_uint;
+pub type __u64 = ffi::c_ulonglong;
+pub type u8_ = __u8;
+pub type u16_ = __u16;
+pub type u32_ = __u32;
+pub type u64_ = __u64;
+pub const NV_VGPU_MSG_FUNCTION_NOP: _bindgen_ty_2 = 0;
+pub const NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO: _bindgen_ty_2 = 1;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_ROOT: _bindgen_ty_2 = 2;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE: _bindgen_ty_2 = 3;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY: _bindgen_ty_2 = 4;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA: _bindgen_ty_2 = 5;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA: _bindgen_ty_2 = 6;
+pub const NV_VGPU_MSG_FUNCTION_MAP_MEMORY: _bindgen_ty_2 = 7;
+pub const NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA: _bindgen_ty_2 = 8;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT: _bindgen_ty_2 = 9;
+pub const NV_VGPU_MSG_FUNCTION_FREE: _bindgen_ty_2 = 10;
+pub const NV_VGPU_MSG_FUNCTION_LOG: _bindgen_ty_2 = 11;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_VIDMEM: _bindgen_ty_2 = 12;
+pub const NV_VGPU_MSG_FUNCTION_UNMAP_MEMORY: _bindgen_ty_2 = 13;
+pub const NV_VGPU_MSG_FUNCTION_MAP_MEMORY_DMA: _bindgen_ty_2 = 14;
+pub const NV_VGPU_MSG_FUNCTION_UNMAP_MEMORY_DMA: _bindgen_ty_2 = 15;
+pub const NV_VGPU_MSG_FUNCTION_GET_EDID: _bindgen_ty_2 = 16;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DISP_CHANNEL: _bindgen_ty_2 = 17;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DISP_OBJECT: _bindgen_ty_2 = 18;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_SUBDEVICE: _bindgen_ty_2 = 19;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DYNAMIC_MEMORY: _bindgen_ty_2 = 20;
+pub const NV_VGPU_MSG_FUNCTION_DUP_OBJECT: _bindgen_ty_2 = 21;
+pub const NV_VGPU_MSG_FUNCTION_IDLE_CHANNELS: _bindgen_ty_2 = 22;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_EVENT: _bindgen_ty_2 = 23;
+pub const NV_VGPU_MSG_FUNCTION_SEND_EVENT: _bindgen_ty_2 = 24;
+pub const NV_VGPU_MSG_FUNCTION_REMAPPER_CONTROL: _bindgen_ty_2 = 25;
+pub const NV_VGPU_MSG_FUNCTION_DMA_CONTROL: _bindgen_ty_2 = 26;
+pub const NV_VGPU_MSG_FUNCTION_DMA_FILL_PTE_MEM: _bindgen_ty_2 = 27;
+pub const NV_VGPU_MSG_FUNCTION_MANAGE_HW_RESOURCE: _bindgen_ty_2 = 28;
+pub const NV_VGPU_MSG_FUNCTION_BIND_ARBITRARY_CTX_DMA: _bindgen_ty_2 = 29;
+pub const NV_VGPU_MSG_FUNCTION_CREATE_FB_SEGMENT: _bindgen_ty_2 = 30;
+pub const NV_VGPU_MSG_FUNCTION_DESTROY_FB_SEGMENT: _bindgen_ty_2 = 31;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_SHARE_DEVICE: _bindgen_ty_2 = 32;
+pub const NV_VGPU_MSG_FUNCTION_DEFERRED_API_CONTROL: _bindgen_ty_2 = 33;
+pub const NV_VGPU_MSG_FUNCTION_REMOVE_DEFERRED_API: _bindgen_ty_2 = 34;
+pub const NV_VGPU_MSG_FUNCTION_SIM_ESCAPE_READ: _bindgen_ty_2 = 35;
+pub const NV_VGPU_MSG_FUNCTION_SIM_ESCAPE_WRITE: _bindgen_ty_2 = 36;
+pub const NV_VGPU_MSG_FUNCTION_SIM_MANAGE_DISPLAY_CONTEXT_DMA: _bindgen_ty_2 = 37;
+pub const NV_VGPU_MSG_FUNCTION_FREE_VIDMEM_VIRT: _bindgen_ty_2 = 38;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_PSTATE_INFO: _bindgen_ty_2 = 39;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_PERFMON_SAMPLE: _bindgen_ty_2 = 40;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_VIRTUAL_PSTATE_INFO: _bindgen_ty_2 = 41;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_LEVEL_INFO: _bindgen_ty_2 = 42;
+pub const NV_VGPU_MSG_FUNCTION_MAP_SEMA_MEMORY: _bindgen_ty_2 = 43;
+pub const NV_VGPU_MSG_FUNCTION_UNMAP_SEMA_MEMORY: _bindgen_ty_2 = 44;
+pub const NV_VGPU_MSG_FUNCTION_SET_SURFACE_PROPERTIES: _bindgen_ty_2 = 45;
+pub const NV_VGPU_MSG_FUNCTION_CLEANUP_SURFACE: _bindgen_ty_2 = 46;
+pub const NV_VGPU_MSG_FUNCTION_UNLOADING_GUEST_DRIVER: _bindgen_ty_2 = 47;
+pub const NV_VGPU_MSG_FUNCTION_TDR_SET_TIMEOUT_STATE: _bindgen_ty_2 = 48;
+pub const NV_VGPU_MSG_FUNCTION_SWITCH_TO_VGA: _bindgen_ty_2 = 49;
+pub const NV_VGPU_MSG_FUNCTION_GPU_EXEC_REG_OPS: _bindgen_ty_2 = 50;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO: _bindgen_ty_2 = 51;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_VIRTMEM: _bindgen_ty_2 = 52;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_PDE_2: _bindgen_ty_2 = 53;
+pub const NV_VGPU_MSG_FUNCTION_SET_PAGE_DIRECTORY: _bindgen_ty_2 = 54;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_PSTATE_INFO: _bindgen_ty_2 = 55;
+pub const NV_VGPU_MSG_FUNCTION_TRANSLATE_GUEST_GPU_PTES: _bindgen_ty_2 = 56;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED_57: _bindgen_ty_2 = 57;
+pub const NV_VGPU_MSG_FUNCTION_RESET_CURRENT_GR_CONTEXT: _bindgen_ty_2 = 58;
+pub const NV_VGPU_MSG_FUNCTION_SET_SEMA_MEM_VALIDATION_STATE: _bindgen_ty_2 = 59;
+pub const NV_VGPU_MSG_FUNCTION_GET_ENGINE_UTILIZATION: _bindgen_ty_2 = 60;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_GPU_PDES: _bindgen_ty_2 = 61;
+pub const NV_VGPU_MSG_FUNCTION_GET_ENCODER_CAPACITY: _bindgen_ty_2 = 62;
+pub const NV_VGPU_MSG_FUNCTION_VGPU_PF_REG_READ32: _bindgen_ty_2 = 63;
+pub const NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO_EXT: _bindgen_ty_2 = 64;
+pub const NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO: _bindgen_ty_2 = 65;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_INIT: _bindgen_ty_2 = 66;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_CLOSE_QUEUE: _bindgen_ty_2 = 67;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_CLEANUP: _bindgen_ty_2 = 68;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_TEST: _bindgen_ty_2 = 69;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_BAR_PDE: _bindgen_ty_2 = 70;
+pub const NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD: _bindgen_ty_2 = 71;
+pub const NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO: _bindgen_ty_2 = 72;
+pub const NV_VGPU_MSG_FUNCTION_SET_REGISTRY: _bindgen_ty_2 = 73;
+pub const NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU: _bindgen_ty_2 = 74;
+pub const NV_VGPU_MSG_FUNCTION_SUBDEV_EVENT_SET_NOTIFICATION: _bindgen_ty_2 = 75;
+pub const NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL: _bindgen_ty_2 = 76;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO2: _bindgen_ty_2 = 77;
+pub const NV_VGPU_MSG_FUNCTION_DUMP_PROTOBUF_COMPONENT: _bindgen_ty_2 = 78;
+pub const NV_VGPU_MSG_FUNCTION_UNSET_PAGE_DIRECTORY: _bindgen_ty_2 = 79;
+pub const NV_VGPU_MSG_FUNCTION_GET_CONSOLIDATED_STATIC_INFO: _bindgen_ty_2 = 80;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_REGISTER_FAULT_BUFFER: _bindgen_ty_2 = 81;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_UNREGISTER_FAULT_BUFFER: _bindgen_ty_2 = 82;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_REGISTER_CLIENT_SHADOW_FAULT_BUFFER: _bindgen_ty_2 = 83;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_UNREGISTER_CLIENT_SHADOW_FAULT_BUFFER: _bindgen_ty_2 = 84;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_VGPU_FB_USAGE: _bindgen_ty_2 = 85;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_NVFBC_SW_SESSION_UPDATE_INFO: _bindgen_ty_2 = 86;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_NVENC_SW_SESSION_UPDATE_INFO: _bindgen_ty_2 = 87;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESET_CHANNEL: _bindgen_ty_2 = 88;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESET_ISOLATED_CHANNEL: _bindgen_ty_2 = 89;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_HANDLE_VF_PRI_FAULT: _bindgen_ty_2 = 90;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CLK_GET_EXTENDED_INFO: _bindgen_ty_2 = 91;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_BOOST: _bindgen_ty_2 = 92;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_VPSTATES_GET_CONTROL: _bindgen_ty_2 = 93;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_ZBC_CLEAR_TABLE: _bindgen_ty_2 = 94;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_COLOR_CLEAR: _bindgen_ty_2 = 95;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_DEPTH_CLEAR: _bindgen_ty_2 = 96;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_SCHEDULE: _bindgen_ty_2 = 97;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_TIMESLICE: _bindgen_ty_2 = 98;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PREEMPT: _bindgen_ty_2 = 99;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_DISABLE_CHANNELS: _bindgen_ty_2 = 100;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_TSG_INTERLEAVE_LEVEL: _bindgen_ty_2 = 101;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_CHANNEL_INTERLEAVE_LEVEL: _bindgen_ty_2 = 102;
+pub const NV_VGPU_MSG_FUNCTION_GSP_RM_ALLOC: _bindgen_ty_2 = 103;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS_V2: _bindgen_ty_2 = 104;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_AES_ENCRYPT: _bindgen_ty_2 = 105;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_SESSION_KEY: _bindgen_ty_2 = 106;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_SESSION_KEY_STATUS: _bindgen_ty_2 = 107;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_CLEAR_ALL_SM_ERROR_STATES: _bindgen_ty_2 = 108;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_READ_ALL_SM_ERROR_STATES: _bindgen_ty_2 = 109;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_EXCEPTION_MASK: _bindgen_ty_2 = 110;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_PROMOTE_CTX: _bindgen_ty_2 = 111;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_CTXSW_PREEMPTION_BIND: _bindgen_ty_2 = 112;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_SET_CTXSW_PREEMPTION_MODE: _bindgen_ty_2 = 113;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_CTXSW_ZCULL_BIND: _bindgen_ty_2 = 114;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_INITIALIZE_CTX: _bindgen_ty_2 = 115;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_VASPACE_COPY_SERVER_RESERVED_PDES: _bindgen_ty_2 = 116;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_CLEAR_FAULTED_BIT: _bindgen_ty_2 = 117;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_LATEST_ECC_ADDRESSES: _bindgen_ty_2 = 118;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_MC_SERVICE_INTERRUPTS: _bindgen_ty_2 = 119;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DMA_SET_DEFAULT_VASPACE: _bindgen_ty_2 = 120;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_CE_PCE_MASK: _bindgen_ty_2 = 121;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_ZBC_CLEAR_TABLE_ENTRY: _bindgen_ty_2 = 122;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_NVLINK_PEER_ID_MASK: _bindgen_ty_2 = 123;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_NVLINK_STATUS: _bindgen_ty_2 = 124;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS: _bindgen_ty_2 = 125;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS_MATRIX: _bindgen_ty_2 = 126;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED_0: _bindgen_ty_2 = 127;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_PM_AREA_SMPC: _bindgen_ty_2 = 128;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_HWPM_LEGACY: _bindgen_ty_2 = 129;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_B0CC_EXEC_REG_OPS: _bindgen_ty_2 = 130;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_BIND_PM_RESOURCES: _bindgen_ty_2 = 131;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SUSPEND_CONTEXT: _bindgen_ty_2 = 132;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_RESUME_CONTEXT: _bindgen_ty_2 = 133;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_EXEC_REG_OPS: _bindgen_ty_2 = 134;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_MMU_DEBUG: _bindgen_ty_2 = 135;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_READ_SINGLE_SM_ERROR_STATE: _bindgen_ty_2 = 136;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_CLEAR_SINGLE_SM_ERROR_STATE: _bindgen_ty_2 = 137;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_ERRBAR_DEBUG: _bindgen_ty_2 = 138;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_NEXT_STOP_TRIGGER_TYPE: _bindgen_ty_2 = 139;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_ALLOC_PMA_STREAM: _bindgen_ty_2 = 140;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PMA_STREAM_UPDATE_GET_PUT: _bindgen_ty_2 = 141;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FB_GET_INFO_V2: _bindgen_ty_2 = 142;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_SET_CHANNEL_PROPERTIES: _bindgen_ty_2 = 143;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_GET_CTX_BUFFER_INFO: _bindgen_ty_2 = 144;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_KGR_GET_CTX_BUFFER_PTES: _bindgen_ty_2 = 145;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_EVICT_CTX: _bindgen_ty_2 = 146;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FB_GET_FS_INFO: _bindgen_ty_2 = 147;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GRMGR_GET_GR_FS_INFO: _bindgen_ty_2 = 148;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_STOP_CHANNEL: _bindgen_ty_2 = 149;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_PC_SAMPLING_MODE: _bindgen_ty_2 = 150;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_RATED_TDP_GET_STATUS: _bindgen_ty_2 = 151;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_RATED_TDP_SET_CONTROL: _bindgen_ty_2 = 152;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FREE_PMA_STREAM: _bindgen_ty_2 = 153;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_TIMER_SET_GR_TICK_FREQ: _bindgen_ty_2 = 154;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_SETUP_VF_ZOMBIE_SUBCTX_PDB: _bindgen_ty_2 = 155;
+pub const NV_VGPU_MSG_FUNCTION_GET_CONSOLIDATED_GR_STATIC_INFO: _bindgen_ty_2 = 156;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_SINGLE_SM_SINGLE_STEP: _bindgen_ty_2 = 157;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_GET_TPC_PARTITION_MODE: _bindgen_ty_2 = 158;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_SET_TPC_PARTITION_MODE: _bindgen_ty_2 = 159;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_ALLOCATE: _bindgen_ty_2 = 160;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_DESTROY: _bindgen_ty_2 = 161;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_MAP: _bindgen_ty_2 = 162;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_UNMAP: _bindgen_ty_2 = 163;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_PUSH_STREAM: _bindgen_ty_2 = 164;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_SET_HANDLES: _bindgen_ty_2 = 165;
+pub const NV_VGPU_MSG_FUNCTION_UVM_METHOD_STREAM_GUEST_PAGES_OPERATION: _bindgen_ty_2 = 166;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_QUIESCE_PMA_CHANNEL: _bindgen_ty_2 = 167;
+pub const NV_VGPU_MSG_FUNCTION_DCE_RM_INIT: _bindgen_ty_2 = 168;
+pub const NV_VGPU_MSG_FUNCTION_REGISTER_VIRTUAL_EVENT_BUFFER: _bindgen_ty_2 = 169;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EVENT_BUFFER_UPDATE_GET: _bindgen_ty_2 = 170;
+pub const NV_VGPU_MSG_FUNCTION_GET_PLCABLE_ADDRESS_KIND: _bindgen_ty_2 = 171;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_LIMITS_SET_STATUS_V2: _bindgen_ty_2 = 172;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_SRIOV_PROMOTE_PMA_STREAM: _bindgen_ty_2 = 173;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_MMU_DEBUG_MODE: _bindgen_ty_2 = 174;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_PROMOTE_FAULT_METHOD_BUFFERS: _bindgen_ty_2 = 175;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FLCN_GET_CTX_BUFFER_SIZE: _bindgen_ty_2 = 176;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FLCN_GET_CTX_BUFFER_INFO: _bindgen_ty_2 = 177;
+pub const NV_VGPU_MSG_FUNCTION_DISABLE_CHANNELS: _bindgen_ty_2 = 178;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FABRIC_MEMORY_DESCRIBE: _bindgen_ty_2 = 179;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FABRIC_MEM_STATS: _bindgen_ty_2 = 180;
+pub const NV_VGPU_MSG_FUNCTION_SAVE_HIBERNATION_DATA: _bindgen_ty_2 = 181;
+pub const NV_VGPU_MSG_FUNCTION_RESTORE_HIBERNATION_DATA: _bindgen_ty_2 = 182;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_MEMSYS_SET_ZBC_REFERENCED: _bindgen_ty_2 = 183;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_CREATE: _bindgen_ty_2 = 184;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_DELETE: _bindgen_ty_2 = 185;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_GET_WORK_SUBMIT_TOKEN: _bindgen_ty_2 = 186;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX: _bindgen_ty_2 = 187;
+pub const NV_VGPU_MSG_FUNCTION_PMA_SCRUBBER_SHARED_BUFFER_GUEST_PAGES_OPERATION: _bindgen_ty_2 =
+ 188;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_MASTER_GET_VIRTUAL_FUNCTION_ERROR_CONT_INTR_MASK:
+ _bindgen_ty_2 = 189;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED_190: _bindgen_ty_2 = 190;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_P2P_CAPS: _bindgen_ty_2 = 191;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_BUS_SET_P2P_MAPPING: _bindgen_ty_2 = 192;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_BUS_UNSET_P2P_MAPPING: _bindgen_ty_2 = 193;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FLA_SETUP_INSTANCE_MEM_BLOCK: _bindgen_ty_2 = 194;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_MIGRATABLE_OPS: _bindgen_ty_2 = 195;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_TOTAL_HS_CREDITS: _bindgen_ty_2 = 196;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_HS_CREDITS: _bindgen_ty_2 = 197;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_HS_CREDITS: _bindgen_ty_2 = 198;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PM_AREA_PC_SAMPLER: _bindgen_ty_2 = 199;
+pub const NV_VGPU_MSG_FUNCTION_INVALIDATE_TLB: _bindgen_ty_2 = 200;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_QUERY_ECC_STATUS: _bindgen_ty_2 = 201;
+pub const NV_VGPU_MSG_FUNCTION_ECC_NOTIFIER_WRITE_ACK: _bindgen_ty_2 = 202;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_GET_MODE_MMU_DEBUG: _bindgen_ty_2 = 203;
+pub const NV_VGPU_MSG_FUNCTION_RM_API_CONTROL: _bindgen_ty_2 = 204;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_GPU_START_FABRIC_PROBE: _bindgen_ty_2 = 205;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_NVLINK_GET_INBAND_RECEIVED_DATA: _bindgen_ty_2 = 206;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_DATA: _bindgen_ty_2 = 207;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED_208: _bindgen_ty_2 = 208;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_GET_INFO_V2: _bindgen_ty_2 = 209;
+pub const NV_VGPU_MSG_FUNCTION_GET_BRAND_CAPS: _bindgen_ty_2 = 210;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_NVLINK_INBAND_SEND_DATA: _bindgen_ty_2 = 211;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_GPM_GUEST_BUFFER_INFO: _bindgen_ty_2 = 212;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_CONTROL_GSP_TRACE: _bindgen_ty_2 = 213;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_STENCIL_CLEAR: _bindgen_ty_2 = 214;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_VGPU_HEAP_STATS: _bindgen_ty_2 = 215;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_LIBOS_HEAP_STATS: _bindgen_ty_2 = 216;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_MMU_GCC_DEBUG: _bindgen_ty_2 = 217;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_GET_MODE_MMU_GCC_DEBUG: _bindgen_ty_2 = 218;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_HES: _bindgen_ty_2 = 219;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RELEASE_HES: _bindgen_ty_2 = 220;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_CCU_PROF: _bindgen_ty_2 = 221;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RELEASE_CCU_PROF: _bindgen_ty_2 = 222;
+pub const NV_VGPU_MSG_FUNCTION_SETUP_HIBERNATION_BUFFER: _bindgen_ty_2 = 223;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_GET_CHIPLET_HS_CREDIT_POOL: _bindgen_ty_2 = 224;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_GET_HS_CREDITS_MAPPING: _bindgen_ty_2 = 225;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_EXPORT: _bindgen_ty_2 = 226;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_GPU_CHECK_CTS_ID_VALID: _bindgen_ty_2 = 227;
+pub const NV_VGPU_MSG_FUNCTION_INIT_GSP_TRACE_CRASH_BUFFER: _bindgen_ty_2 = 228;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_SET_MIGRATION_BLOCK: _bindgen_ty_2 = 229;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_VGPU_SWRUNLIST_SUBMIT: _bindgen_ty_2 = 230;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_VGPU_SWRUNLIST_CHANNEL_UPDATE: _bindgen_ty_2 = 231;
+pub const NV_VGPU_MSG_FUNCTION_NUM_FUNCTIONS: _bindgen_ty_2 = 232;
+pub type _bindgen_ty_2 = ffi::c_uint;
+pub const NV_VGPU_MSG_EVENT_FIRST_EVENT: _bindgen_ty_3 = 4096;
+pub const NV_VGPU_MSG_EVENT_GSP_INIT_DONE: _bindgen_ty_3 = 4097;
+pub const NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER: _bindgen_ty_3 = 4098;
+pub const NV_VGPU_MSG_EVENT_POST_EVENT: _bindgen_ty_3 = 4099;
+pub const NV_VGPU_MSG_EVENT_RC_TRIGGERED: _bindgen_ty_3 = 4100;
+pub const NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED: _bindgen_ty_3 = 4101;
+pub const NV_VGPU_MSG_EVENT_OS_ERROR_LOG: _bindgen_ty_3 = 4102;
+pub const NV_VGPU_MSG_EVENT_RG_LINE_INTR: _bindgen_ty_3 = 4103;
+pub const NV_VGPU_MSG_EVENT_GPUACCT_PERFMON_UTIL_SAMPLES: _bindgen_ty_3 = 4104;
+pub const NV_VGPU_MSG_EVENT_SIM_READ: _bindgen_ty_3 = 4105;
+pub const NV_VGPU_MSG_EVENT_SIM_WRITE: _bindgen_ty_3 = 4106;
+pub const NV_VGPU_MSG_EVENT_SEMAPHORE_SCHEDULE_CALLBACK: _bindgen_ty_3 = 4107;
+pub const NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT: _bindgen_ty_3 = 4108;
+pub const NV_VGPU_MSG_EVENT_VGPU_GSP_PLUGIN_TRIGGERED: _bindgen_ty_3 = 4109;
+pub const NV_VGPU_MSG_EVENT_PERF_GPU_BOOST_SYNC_LIMITS_CALLBACK: _bindgen_ty_3 = 4110;
+pub const NV_VGPU_MSG_EVENT_PERF_BRIDGELESS_INFO_UPDATE: _bindgen_ty_3 = 4111;
+pub const NV_VGPU_MSG_EVENT_VGPU_CONFIG: _bindgen_ty_3 = 4112;
+pub const NV_VGPU_MSG_EVENT_DISPLAY_MODESET: _bindgen_ty_3 = 4113;
+pub const NV_VGPU_MSG_EVENT_EXTDEV_INTR_SERVICE: _bindgen_ty_3 = 4114;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_256: _bindgen_ty_3 = 4115;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_512: _bindgen_ty_3 = 4116;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_1024: _bindgen_ty_3 = 4117;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_2048: _bindgen_ty_3 = 4118;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_4096: _bindgen_ty_3 = 4119;
+pub const NV_VGPU_MSG_EVENT_TIMED_SEMAPHORE_RELEASE: _bindgen_ty_3 = 4120;
+pub const NV_VGPU_MSG_EVENT_NVLINK_IS_GPU_DEGRADED: _bindgen_ty_3 = 4121;
+pub const NV_VGPU_MSG_EVENT_PFM_REQ_HNDLR_STATE_SYNC_CALLBACK: _bindgen_ty_3 = 4122;
+pub const NV_VGPU_MSG_EVENT_NVLINK_FAULT_UP: _bindgen_ty_3 = 4123;
+pub const NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE: _bindgen_ty_3 = 4124;
+pub const NV_VGPU_MSG_EVENT_MIG_CI_CONFIG_UPDATE: _bindgen_ty_3 = 4125;
+pub const NV_VGPU_MSG_EVENT_UPDATE_GSP_TRACE: _bindgen_ty_3 = 4126;
+pub const NV_VGPU_MSG_EVENT_NVLINK_FATAL_ERROR_RECOVERY: _bindgen_ty_3 = 4127;
+pub const NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD: _bindgen_ty_3 = 4128;
+pub const NV_VGPU_MSG_EVENT_FECS_ERROR: _bindgen_ty_3 = 4129;
+pub const NV_VGPU_MSG_EVENT_RECOVERY_ACTION: _bindgen_ty_3 = 4130;
+pub const NV_VGPU_MSG_EVENT_TRIGGER_BUGCHECK: _bindgen_ty_3 = 4131;
+pub const NV_VGPU_MSG_EVENT_BIND_BAR2: _bindgen_ty_3 = 4132;
+pub const NV_VGPU_MSG_EVENT_FORCED_DRIVER_SHUTDOWN: _bindgen_ty_3 = 4133;
+pub const NV_VGPU_MSG_EVENT_GSP_LOAD_EXEC_GENERIC_BOOTLOADER: _bindgen_ty_3 = 4134;
+pub const NV_VGPU_MSG_EVENT_GSP_LOAD_EXEC_HS_BINARY: _bindgen_ty_3 = 4135;
+pub const NV_VGPU_MSG_EVENT_UPDATE_GRID_DISPLAYLESS_PARAMS: _bindgen_ty_3 = 4136;
+pub const NV_VGPU_MSG_EVENT_PMU_HALTED: _bindgen_ty_3 = 4137;
+pub const NV_VGPU_MSG_EVENT_ECC_POSSIBLE_ERR_FAST_PATH: _bindgen_ty_3 = 4138;
+pub const NV_VGPU_MSG_EVENT_NVLINK_TRAFFIC_QUIESCE_NOTIFY: _bindgen_ty_3 = 4139;
+pub const NV_VGPU_MSG_EVENT_GPU_GFM_STATE_CHANGE: _bindgen_ty_3 = 4140;
+pub const NV_VGPU_MSG_EVENT_NVLINK_ABM_FABRIC_HEALTH_MASK_UPDATE: _bindgen_ty_3 = 4141;
+pub const NV_VGPU_MSG_EVENT_NUM_EVENTS: _bindgen_ty_3 = 4142;
+pub type _bindgen_ty_3 = ffi::c_uint;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS {
+ pub totalVFs: u32_,
+ pub firstVfOffset: u32_,
+ pub vfFeatureMask: u32_,
+ pub __bindgen_padding_0: [u8; 4usize],
+ pub FirstVFBar0Address: u64_,
+ pub FirstVFBar1Address: u64_,
+ pub FirstVFBar2Address: u64_,
+ pub bar0Size: u64_,
+ pub bar1Size: u64_,
+ pub bar2Size: u64_,
+ pub b64bitBar0: u8_,
+ pub b64bitBar1: u8_,
+ pub b64bitBar2: u8_,
+ pub bSriovEnabled: u8_,
+ pub bSriovHeavyEnabled: u8_,
+ pub bEmulateVFBar0TlbInvalidationRegister: u8_,
+ pub bClientRmAllocatedCtxBuffer: u8_,
+ pub bNonPowerOf2ChannelCountSupported: u8_,
+ pub bVfResizableBAR1Supported: u8_,
+ pub __bindgen_padding_1: [u8; 7usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS {
+ pub BoardID: u32_,
+ pub chipSKU: [ffi::c_char; 9usize],
+ pub chipSKUMod: [ffi::c_char; 5usize],
+ pub __bindgen_padding_0: [u8; 2usize],
+ pub skuConfigVersion: u32_,
+ pub project: [ffi::c_char; 5usize],
+ pub projectSKU: [ffi::c_char; 5usize],
+ pub CDP: [ffi::c_char; 6usize],
+ pub projectSKUMod: [ffi::c_char; 2usize],
+ pub __bindgen_padding_1: [u8; 2usize],
+ pub businessCycle: u32_,
+}
+pub const NV2080_FB_REGION_TAG_NV2080_FB_REGION_TAG_NONE: NV2080_FB_REGION_TAG = 0;
+pub const NV2080_FB_REGION_TAG_NV2080_FB_REGION_TAG_GSP_CARVEOUT: NV2080_FB_REGION_TAG = 1;
+pub const NV2080_FB_REGION_TAG_NV2080_FB_REGION_TAG_CPU_RM_RESERVED: NV2080_FB_REGION_TAG = 2;
+pub const NV2080_FB_REGION_TAG_NV2080_FB_REGION_TAG_CPU_RM_RESERVED_HEAP: NV2080_FB_REGION_TAG = 3;
+pub const NV2080_FB_REGION_TAG_NV2080_FB_REGION_TAG_GSP_RM_RESERVED: NV2080_FB_REGION_TAG = 4;
+pub const NV2080_FB_REGION_TAG_NV2080_FB_REGION_TAG_GSP_RM_RESERVED_HEAP: NV2080_FB_REGION_TAG = 5;
+pub type NV2080_FB_REGION_TAG = ffi::c_uint;
+#[repr(C)]
+#[derive(Debug, Copy, Clone, MaybeZeroable)]
+pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO {
+ pub base: u64_,
+ pub limit: u64_,
+ pub reserved: u64_,
+ pub performance: u32_,
+ pub supportCompressed: u8_,
+ pub supportISO: u8_,
+ pub bProtected: u8_,
+ pub blackList: [u8_; 18usize],
+ pub __bindgen_padding_0: [u8; 3usize],
+ pub regionTag: NV2080_FB_REGION_TAG,
+}
+impl Default for NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, MaybeZeroable)]
+pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS {
+ pub numFBRegions: u32_,
+ pub __bindgen_padding_0: [u8; 4usize],
+ pub fbRegion: [NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO; 16usize],
+}
+impl Default for NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, MaybeZeroable)]
+pub struct NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
+ pub index: u32_,
+ pub flags: u32_,
+ pub length: u32_,
+ pub data: [u8_; 256usize],
+}
+impl Default for NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct DOD_METHOD_DATA {
+ pub status: u32_,
+ pub acpiIdListLen: u32_,
+ pub acpiIdList: [u32_; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct JT_METHOD_DATA {
+ pub status: u32_,
+ pub jtCaps: u32_,
+ pub jtRevId: u16_,
+ pub bSBIOSCaps: u8_,
+ pub __bindgen_padding_0: u8,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct MUX_METHOD_DATA_ELEMENT {
+ pub acpiId: u32_,
+ pub mode: u32_,
+ pub status: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct MUX_METHOD_DATA {
+ pub tableLen: u32_,
+ pub acpiIdMuxModeTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
+ pub acpiIdMuxPartTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
+ pub acpiIdMuxStateTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct CAPS_METHOD_DATA {
+ pub status: u32_,
+ pub optimusCaps: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct ACPI_METHOD_DATA {
+ pub bValid: u8_,
+ pub __bindgen_padding_0: [u8; 3usize],
+ pub dodMethodData: DOD_METHOD_DATA,
+ pub jtMethodData: JT_METHOD_DATA,
+ pub muxMethodData: MUX_METHOD_DATA,
+ pub capsMethodData: CAPS_METHOD_DATA,
+}
+pub type GspStaticConfigInfo = GspStaticConfigInfo_t;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct BUSINFO {
+ pub deviceID: u16_,
+ pub vendorID: u16_,
+ pub subdeviceID: u16_,
+ pub subvendorID: u16_,
+ pub revisionID: u8_,
+ pub __bindgen_padding_0: u8,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_VF_INFO {
+ pub totalVFs: u32_,
+ pub firstVFOffset: u32_,
+ pub FirstVFBar0Address: u64_,
+ pub FirstVFBar1Address: u64_,
+ pub FirstVFBar2Address: u64_,
+ pub b64bitBar0: u8_,
+ pub b64bitBar1: u8_,
+ pub b64bitBar2: u8_,
+ pub __bindgen_padding_0: [u8; 5usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_PCIE_CONFIG_REG {
+ pub linkCap: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct EcidManufacturingInfo {
+ pub info: [u64_; 2usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct FW_WPR_LAYOUT_OFFSET {
+ pub nonWprHeapOffset: u64_,
+ pub frtsOffset: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, MaybeZeroable)]
+pub struct GspStaticConfigInfo_t {
+ pub gidInfo: NV2080_CTRL_GPU_GET_GID_INFO_PARAMS,
+ pub SKUInfo: NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS,
+ pub __bindgen_padding_0: [u8; 4usize],
+ pub fbRegionInfoParams: NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS,
+ pub bPdiValid: u8_,
+ pub __bindgen_padding_1: [u8; 7usize],
+ pub pdi: u64_,
+ pub sriovCaps: NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS,
+ pub sriovMaxGfid: u32_,
+ pub engineCaps: [u32_; 3usize],
+ pub poisonFuseEnabled: u8_,
+ pub __bindgen_padding_2: [u8; 7usize],
+ pub fb_length: u64_,
+ pub gpuNameString: [u8_; 64usize],
+ pub gpuShortNameString: [u8_; 64usize],
+ pub bGpuInternalSku: u8_,
+ pub bIsQuadroGeneric: u8_,
+ pub bIsQuadroAd: u8_,
+ pub bIsNvidiaNvs: u8_,
+ pub bIsVgx: u8_,
+ pub bGeforceSmb: u8_,
+ pub bIsTitan: u8_,
+ pub bIsTesla: u8_,
+ pub bIsMobile: u8_,
+ pub bIsCmpSku: u8_,
+ pub bIsGc6Rtd3Allowed: u8_,
+ pub bIsGc8Rtd3Allowed: u8_,
+ pub bIsGcOffRtd3Allowed: u8_,
+ pub bIsGcoffLegacyAllowed: u8_,
+ pub bIsMigSupported: u8_,
+ pub __bindgen_padding_3: u8,
+ pub RTD3GC6TotalBoardPower: u16_,
+ pub RTD3GC6PerstDelay: u16_,
+ pub __bindgen_padding_4: [u8; 4usize],
+ pub bar1PdeBase: u64_,
+ pub bar2PdeBase: u64_,
+ pub bVbiosValid: u8_,
+ pub __bindgen_padding_5: [u8; 3usize],
+ pub vbiosSubVendor: u32_,
+ pub vbiosSubDevice: u32_,
+ pub vbiosRevision: u32_,
+ pub vbiosOemRevision: u32_,
+ pub bPageRetirementSupported: u8_,
+ pub bSplitVasBetweenServerClientRm: u8_,
+ pub bClRootportNeedsNosnoopWAR: u8_,
+ pub __bindgen_padding_6: u8,
+ pub hInternalClient: u32_,
+ pub hInternalDevice: u32_,
+ pub hInternalSubdevice: u32_,
+ pub bSelfHostedMode: u8_,
+ pub bAtsSupported: u8_,
+ pub bSysL2CacheCoherentMode: u8_,
+ pub bIsGpuUefi: u8_,
+ pub bIsEfiInit: u8_,
+ pub __bindgen_padding_7: [u8; 7usize],
+ pub ecidInfo: EcidManufacturingInfo,
+ pub fwWprLayoutOffset: FW_WPR_LAYOUT_OFFSET,
+ pub bBusResetRequired: u8_,
+ pub chipSubRev: u8_,
+ pub __bindgen_padding_8: [u8; 2usize],
+ pub emulationRev1: u32_,
+}
+impl Default for GspStaticConfigInfo_t {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GspSystemInfo {
+ pub gpuPhysAddr: u64_,
+ pub gpuPhysFbAddr: u64_,
+ pub gpuPhysInstAddr: u64_,
+ pub gpuPhysIoAddr: u64_,
+ pub nvDomainBusDeviceFunc: u64_,
+ pub simAccessBufPhysAddr: u64_,
+ pub notifyOpSharedSurfacePhysAddr: u64_,
+ pub pcieAtomicsOpMask: u64_,
+ pub consoleMemSize: u64_,
+ pub maxUserVa: u64_,
+ pub pciConfigMirrorBase: u32_,
+ pub pciConfigMirrorSize: u32_,
+ pub PCIDeviceID: u32_,
+ pub PCISubDeviceID: u32_,
+ pub PCIRevisionID: u32_,
+ pub pcieAtomicsCplDeviceCapMask: u32_,
+ pub oorArch: u8_,
+ pub bUnstableRpcs: u8_,
+ pub bUnstableEvents: u8_,
+ pub __bindgen_padding_0: [u8; 5usize],
+ pub clPdbProperties: u64_,
+ pub Chipset: u32_,
+ pub bGpuBehindBridge: u8_,
+ pub bFlrSupported: u8_,
+ pub b64bBar0Supported: u8_,
+ pub bMnocAvailable: u8_,
+ pub chipsetL1ssEnable: u32_,
+ pub bUpstreamL0sUnsupported: u8_,
+ pub bUpstreamL1Unsupported: u8_,
+ pub bUpstreamL1PorSupported: u8_,
+ pub bUpstreamL1PorMobileOnly: u8_,
+ pub bSystemHasMux: u8_,
+ pub upstreamAddressValid: u8_,
+ pub FHBBusInfo: BUSINFO,
+ pub chipsetIDInfo: BUSINFO,
+ pub __bindgen_padding_1: [u8; 2usize],
+ pub acpiMethodData: ACPI_METHOD_DATA,
+ pub hypervisorType: u32_,
+ pub virtualConfigBits: u16_,
+ pub bIsPassthru: u8_,
+ pub __bindgen_padding_2: [u8; 5usize],
+ pub sysTimerOffsetNs: u64_,
+ pub gspVFInfo: GSP_VF_INFO,
+ pub bIsPrimary: u8_,
+ pub bIsUnixHdmiFrlComplianceEnabled: u8_,
+ pub isGridBuild: u8_,
+ pub __bindgen_padding_3: u8,
+ pub pcieConfigReg: GSP_PCIE_CONFIG_REG,
+ pub gridBuildCsp: u32_,
+ pub bPreserveVideoMemoryAllocations: u8_,
+ pub bTdrEventSupported: u8_,
+ pub bFeatureStretchVblankCapable: u8_,
+ pub bEnableDynamicGranularityPageArrays: u8_,
+ pub bClockBoostSupported: u8_,
+ pub __bindgen_padding_4: [u8; 7usize],
+ pub hostPageSize: u64_,
+ pub bIsCmcBasedHws: u8_,
+ pub bGspNocatEnabled: u8_,
+ pub bS0ixSupport: u8_,
+ pub bWindowChannelAlwaysMapped: u8_,
+ pub pciePowerControlValue: u32_,
+ pub bPciePowerControlPresent: u8_,
+ pub __bindgen_padding_5: [u8; 3usize],
+ pub pf0DeviceControl2Reg: u32_,
+ pub bIsCxlDevice: u8_,
+ pub bReserveZeroFbAddressAsRegion: u8_,
+ pub __bindgen_padding_6: [u8; 6usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct MESSAGE_QUEUE_INIT_ARGUMENTS {
+ pub flags: u64_,
+ pub sharedMemPhysAddr: u64_,
+ pub cmdQueueOffset: u64_,
+ pub statQueueOffset: u64_,
+ pub queueElementHdrSize: u32_,
+ pub queueElementSizeMin: u32_,
+ pub queueElementSizeMax: u32_,
+ pub queueHeaderAlign: u32_,
+ pub queueElementAlign: u32_,
+ pub pageTableEntryCount: u32_,
+ pub reserved: [u8_; 8usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_SR_INIT_ARGUMENTS {
+ pub oldLevel: u32_,
+ pub flags: u32_,
+ pub reserved: [u8_; 8usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_ARGUMENTS_CACHED {
+ pub magic: u32_,
+ pub size: u16_,
+ pub reserved: [u8_; 2usize],
+ pub flags: u64_,
+ pub messageQueueInitArguments: MESSAGE_QUEUE_INIT_ARGUMENTS,
+ pub srInitArguments: GSP_SR_INIT_ARGUMENTS,
+ pub reserved2: [u8_; 4usize],
+ pub gpuInstance: u32_,
+ pub profilerArgs: GSP_ARGUMENTS_CACHED__bindgen_ty_1,
+ pub sysmemHeapArgs: GSP_ARGUMENTS_CACHED__bindgen_ty_2,
+ pub rmStateMonitorBufferArgs: GSP_ARGUMENTS_CACHED__bindgen_ty_3,
+ pub bindataArgs: GSP_ARGUMENTS_CACHED__bindgen_ty_4,
+ pub vbiosOverrideArgs: GSP_ARGUMENTS_CACHED__bindgen_ty_5,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_1 {
+ pub pa: u64_,
+ pub size: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_2 {
+ pub pa: u64_,
+ pub size: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_3 {
+ pub pa: u64_,
+ pub size: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_4 {
+ pub radix3: u64_,
+ pub size: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_5 {
+ pub pa: u64_,
+ pub size: u64_,
+}
+#[repr(C)]
+#[derive(Copy, Clone, MaybeZeroable)]
+pub union rpc_message_rpc_union_field_v03_00 {
+ pub spare: u32_,
+ pub cpuRmGfid: u32_,
+}
+impl Default for rpc_message_rpc_union_field_v03_00 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+pub type rpc_message_rpc_union_field_v = rpc_message_rpc_union_field_v03_00;
+#[repr(C)]
+#[derive(MaybeZeroable)]
+pub struct rpc_message_header_v03_00 {
+ pub header_version: u32_,
+ pub signature: u32_,
+ pub length: u32_,
+ pub function: u32_,
+ pub rpc_result: u32_,
+ pub rpc_result_private: u32_,
+ pub sequence: u32_,
+ pub u: rpc_message_rpc_union_field_v,
+ pub rpc_message_data: __IncompleteArrayField<u8_>,
+}
+impl Default for rpc_message_header_v03_00 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+pub type rpc_message_header_v = rpc_message_header_v03_00;
+#[repr(C)]
+#[derive(Copy, Clone, MaybeZeroable)]
+pub struct GspFwWprMeta {
+ pub magic: u64_,
+ pub revision: u64_,
+ pub sysmemAddrOfRadix3Elf: u64_,
+ pub sizeOfRadix3Elf: u64_,
+ pub sysmemAddrOfBootloader: u64_,
+ pub sizeOfBootloader: u64_,
+ pub bootloaderCodeOffset: u64_,
+ pub bootloaderDataOffset: u64_,
+ pub bootloaderManifestOffset: u64_,
+ pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1,
+ pub gspFwRsvdStart: u64_,
+ pub nonWprHeapOffset: u64_,
+ pub nonWprHeapSize: u64_,
+ pub gspFwWprStart: u64_,
+ pub gspFwHeapOffset: u64_,
+ pub gspFwHeapSize: u64_,
+ pub gspFwOffset: u64_,
+ pub bootBinOffset: u64_,
+ pub frtsOffset: u64_,
+ pub frtsSize: u64_,
+ pub gspFwWprEnd: u64_,
+ pub fbSize: u64_,
+ pub vgaWorkspaceOffset: u64_,
+ pub vgaWorkspaceSize: u64_,
+ pub bootCount: u64_,
+ pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2,
+ pub gspFwHeapVfPartitionCount: u8_,
+ pub flags: u8_,
+ pub pagingConfig: u16_,
+ pub pmuReservedSize: u32_,
+ pub verified: u64_,
+}
+#[repr(C)]
+#[derive(Copy, Clone, MaybeZeroable)]
+pub union GspFwWprMeta__bindgen_ty_1 {
+ pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1__bindgen_ty_1,
+ pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_1__bindgen_ty_2,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_1 {
+ pub sysmemAddrOfSignature: u64_,
+ pub sizeOfSignature: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_2 {
+ pub gspFwHeapFreeListWprOffset: u32_,
+ pub unused0: u32_,
+ pub unused1: u64_,
+}
+impl Default for GspFwWprMeta__bindgen_ty_1 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone, MaybeZeroable)]
+pub union GspFwWprMeta__bindgen_ty_2 {
+ pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_2__bindgen_ty_1,
+ pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2__bindgen_ty_2,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 {
+ pub partitionRpcAddr: u64_,
+ pub partitionRpcRequestOffset: u16_,
+ pub partitionRpcReplyOffset: u16_,
+ pub elfCodeOffset: u32_,
+ pub elfDataOffset: u32_,
+ pub elfCodeSize: u32_,
+ pub elfDataSize: u32_,
+ pub lsUcodeVersion: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_2 {
+ pub partitionRpcPadding: [u32_; 4usize],
+ pub sysmemAddrOfCrashReportQueue: u64_,
+ pub sizeOfCrashReportQueue: u32_,
+ pub lsUcodeVersionPadding: [u32_; 1usize],
+}
+impl Default for GspFwWprMeta__bindgen_ty_2 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+impl Default for GspFwWprMeta {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+pub type LibosAddress = u64_;
+pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_NONE: LibosMemoryRegionKind = 0;
+pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS: LibosMemoryRegionKind = 1;
+pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_RADIX3: LibosMemoryRegionKind = 2;
+pub type LibosMemoryRegionKind = ffi::c_uint;
+pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_NONE: LibosMemoryRegionLoc = 0;
+pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM: LibosMemoryRegionLoc = 1;
+pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_FB: LibosMemoryRegionLoc = 2;
+pub type LibosMemoryRegionLoc = ffi::c_uint;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct LibosMemoryRegionInitArgument {
+ pub id8: LibosAddress,
+ pub pa: LibosAddress,
+ pub size: LibosAddress,
+ pub kind: u8_,
+ pub loc: u8_,
+ pub __bindgen_padding_0: [u8; 6usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct PACKED_REGISTRY_ENTRY {
+ pub nameOffset: u32_,
+ pub type_: u8_,
+ pub __bindgen_padding_0: [u8; 3usize],
+ pub data: u32_,
+ pub length: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, MaybeZeroable)]
+pub struct PACKED_REGISTRY_TABLE {
+ pub size: u32_,
+ pub numEntries: u32_,
+ pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct msgqTxHeader {
+ pub versionMajor: u16_,
+ pub versionMinor: u16_,
+ pub size: u32_,
+ pub msgSize: u32_,
+ pub msgCount: u32_,
+ pub entryOff: u32_,
+ pub reserved: [u32_; 3usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_MSG_QUEUE_ENCRYPTION_TAG {
+ pub authTagBuffer: [u8_; 16usize],
+}
+#[repr(C)]
+#[derive(MaybeZeroable)]
+pub struct GSP_MSG_QUEUE_ELEMENT {
+ pub mctpMagic: u32_,
+ pub mctpPayloadSize: u32_,
+ pub mctpHeader: u32_,
+ pub nvdmHeader: u32_,
+ pub __bindgen_anon_1: GSP_MSG_QUEUE_ELEMENT__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(MaybeZeroable)]
+pub struct GSP_MSG_QUEUE_ELEMENT__bindgen_ty_1 {
+ pub withEncryption: __BindgenUnionField<GSP_MSG_QUEUE_ELEMENT__bindgen_ty_1__bindgen_ty_1>,
+ pub noEncryption: __BindgenUnionField<GSP_MSG_QUEUE_ELEMENT__bindgen_ty_1__bindgen_ty_2>,
+ pub bindgen_union_field: [u32; 6usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, MaybeZeroable)]
+pub struct GSP_MSG_QUEUE_ELEMENT__bindgen_ty_1__bindgen_ty_1 {
+ pub encryptionTag: GSP_MSG_QUEUE_ENCRYPTION_TAG,
+ pub nvdmPayloadSize: u32_,
+ pub reserved: u32_,
+ pub payload: __IncompleteArrayField<u8_>,
+}
+#[repr(C)]
+#[derive(Debug, Default, MaybeZeroable)]
+pub struct GSP_MSG_QUEUE_ELEMENT__bindgen_ty_1__bindgen_ty_2 {
+ pub nvdmPayloadSize: u32_,
+ pub reserved: u32_,
+ pub payload: __IncompleteArrayField<u8_>,
+}
+impl Default for GSP_MSG_QUEUE_ELEMENT__bindgen_ty_1 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+impl Default for GSP_MSG_QUEUE_ELEMENT {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(MaybeZeroable)]
+pub struct GMCAPI_HEADER {
+ pub command: u32_,
+ pub size: u32_,
+ pub sequence: u64_,
+ pub __bindgen_anon_1: GMCAPI_HEADER__bindgen_ty_1,
+ pub reserved: [u32_; 5usize],
+ pub data: __IncompleteArrayField<u8_>,
+}
+#[repr(C)]
+#[derive(Copy, Clone, MaybeZeroable)]
+pub union GMCAPI_HEADER__bindgen_ty_1 {
+ pub request: GMCAPI_HEADER__bindgen_ty_1__bindgen_ty_1,
+ pub response: GMCAPI_HEADER__bindgen_ty_1__bindgen_ty_2,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GMCAPI_HEADER__bindgen_ty_1__bindgen_ty_1 {
+ pub max_response_size: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GMCAPI_HEADER__bindgen_ty_1__bindgen_ty_2 {
+ pub status: u32_,
+}
+impl Default for GMCAPI_HEADER__bindgen_ty_1 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+impl Default for GMCAPI_HEADER {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_RESERVED_LAST: GMCAPI_COMMANDS = 65535;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_GSP_INIT: GMCAPI_COMMANDS = 65537;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER: GMCAPI_COMMANDS = 65538;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_EXEC_HS_BINARY: GMCAPI_COMMANDS = 65539;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_GSP_SUSPEND: GMCAPI_COMMANDS = 65540;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_GSP_RESUME_DONE: GMCAPI_COMMANDS = 65541;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_ADD_VGPU_TYPE: GMCAPI_COMMANDS = 131073;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_SUPPORTED_VGPU_TYPES: GMCAPI_COMMANDS = 131074;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_CREATABLE_VGPU_TYPES: GMCAPI_COMMANDS = 131075;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_ASSIGN_VGPU_TYPE: GMCAPI_COMMANDS = 131076;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_DEASSIGN_VGPU_TYPE: GMCAPI_COMMANDS = 131077;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_VGPU_PROPERTIES: GMCAPI_COMMANDS = 131078;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE: GMCAPI_COMMANDS = 131079;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_BOOTLOAD_GSP_VGPU_PLUGIN_TASK: GMCAPI_COMMANDS = 131104;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_SHUTDOWN_GSP_VGPU_PLUGIN_TASK: GMCAPI_COMMANDS = 131105;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_SHUTDOWN_GSP_VGPU_PLUGIN_TASK_COMPLETE: GMCAPI_COMMANDS =
+ 131106;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_CLEANUP_GSP_VGPU_PLUGIN_RESOURCES: GMCAPI_COMMANDS = 131107;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_VGPU_PLUGIN_TRIGGERED_EVENT: GMCAPI_COMMANDS = 131108;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_VGPU_MGR_SCRUB_GUEST_FB: GMCAPI_COMMANDS = 131109;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_VGPU_MGR_ALLOC_GSP_CEUTILS: GMCAPI_COMMANDS = 131110;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_VGPU_MGR_FREE_GSP_CEUTILS: GMCAPI_COMMANDS = 131111;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_SCHED_CONTROL: GMCAPI_COMMANDS = 196609;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_MMU_FAULT_QUEUED: GMCAPI_COMMANDS = 262145;
+pub const GMCAPI_COMMANDS_GMCAPI_CMD_INVALID: GMCAPI_COMMANDS = 4294967295;
+pub type GMCAPI_COMMANDS = ffi::c_uint;
+pub const GSP_DMA_TARGET_GSP_DMA_TARGET_LOCAL_FB: GSP_DMA_TARGET = 0;
+pub const GSP_DMA_TARGET_GSP_DMA_TARGET_COHERENT_SYSTEM: GSP_DMA_TARGET = 1;
+pub const GSP_DMA_TARGET_GSP_DMA_TARGET_NONCOHERENT_SYSTEM: GSP_DMA_TARGET = 2;
+pub const GSP_DMA_TARGET_GSP_DMA_TARGET_COUNT: GSP_DMA_TARGET = 3;
+pub type GSP_DMA_TARGET = ffi::c_uint;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_FMC_INIT_PARAMS {
+ pub regkeys: u32_,
+ pub reserved: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, MaybeZeroable)]
+pub struct GSP_ACR_BOOT_GSP_RM_PARAMS {
+ pub target: GSP_DMA_TARGET,
+ pub gspRmDescSize: u32_,
+ pub gspRmDescOffset: u64_,
+ pub wprCarveoutOffset: u64_,
+ pub wprCarveoutSize: u32_,
+ pub bIsGspRmBoot: u8_,
+ pub bInstInSysMode: u8_,
+ pub bIcuEnabled: u8_,
+ pub bScrubCbcSr: u8_,
+}
+impl Default for GSP_ACR_BOOT_GSP_RM_PARAMS {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, MaybeZeroable)]
+pub struct GSP_RM_PARAMS {
+ pub target: GSP_DMA_TARGET,
+ pub reserved: u32_,
+ pub bootArgsOffset: u64_,
+}
+impl Default for GSP_RM_PARAMS {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, MaybeZeroable)]
+pub struct GSP_SPDM_PARAMS {
+ pub target: GSP_DMA_TARGET,
+ pub payloadBufferSize: u32_,
+ pub payloadBufferOffset: u64_,
+}
+impl Default for GSP_SPDM_PARAMS {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct GSP_RM_MEM_PARAMS {
+ pub flushSysmemAddrValLo: u32_,
+ pub flushSysmemAddrValHi: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, MaybeZeroable)]
+pub struct GSP_FMC_BOOT_PARAMS {
+ pub magic: u32_,
+ pub size: u16_,
+ pub reserved0: u16_,
+ pub reserved1: [u64_; 3usize],
+ pub initParams: GSP_FMC_INIT_PARAMS,
+ pub reserved2: [u64_; 3usize],
+ pub bootGspRmParams: GSP_ACR_BOOT_GSP_RM_PARAMS,
+ pub reserved3: [u64_; 4usize],
+ pub gspRmParams: GSP_RM_PARAMS,
+ pub reserved4: [u64_; 2usize],
+ pub gspSpdmParams: GSP_SPDM_PARAMS,
+ pub reserved5: [u64_; 2usize],
+ pub gspRmMemParams: GSP_RM_MEM_PARAMS,
+ pub reserved6: [u64_; 7usize],
+}
+impl Default for GSP_FMC_BOOT_PARAMS {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
+pub struct rpc_unloading_guest_driver_v1F_07 {
+ pub bInPMTransition: u8_,
+ pub bGc6Entering: u8_,
+ pub __bindgen_padding_0: [u8; 2usize],
+ pub newLevel: u32_,
+}
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 02/27] gpu: nova-core: extract radix3 page table into its own module
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
2026-08-19 3:51 ` [PATCH 01/27] gpu: nova-core: firmware: add r000 bindings John Hubbard
@ 2026-08-19 3:51 ` John Hubbard
2026-08-19 17:37 ` Timur Tabi
2026-08-19 3:51 ` [PATCH 03/27] gpu: nova-core: set MCTP transport header version to 1 John Hubbard
` (25 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:51 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
Move the 3-level radix page table construction out of GspFirmware
into a standalone Radix3 type in firmware/radix3.rs. The ucodes
(bindata) firmware that GSP-RM loads at runtime also needs a radix3
page table, so the logic needs to be reusable.
Refactor GspFirmware to use the new Radix3 type and expose firmware
size through a size() method instead of a public field.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/fb.rs | 2 +-
drivers/gpu/nova-core/firmware.rs | 1 +
drivers/gpu/nova-core/firmware/gsp.rs | 116 ++++----------------
drivers/gpu/nova-core/firmware/radix3.rs | 133 +++++++++++++++++++++++
drivers/gpu/nova-core/gsp/fw.rs | 4 +-
5 files changed, 159 insertions(+), 97 deletions(-)
create mode 100644 drivers/gpu/nova-core/firmware/radix3.rs
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index 1576399389b1..f208d6deb5e3 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -236,7 +236,7 @@ pub(crate) fn new(
let elf = {
const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
- let elf_size = u64::from_safe_cast(gsp_fw.size);
+ let elf_size = u64::from_safe_cast(gsp_fw.size());
let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
FbRange(elf_addr..elf_addr + elf_size)
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index b49613a90bf0..34657004568c 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -26,6 +26,7 @@
pub(crate) mod fsp;
pub(crate) mod fwsec;
pub(crate) mod gsp;
+pub(crate) mod radix3;
pub(crate) mod riscv;
pub(crate) mod tlv;
diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index e8f9491e84cc..48c73a676ecb 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -4,21 +4,16 @@
device,
dma::{
Coherent,
- CoherentBox,
- DataDirection,
DmaAddress, //
},
firmware,
prelude::*,
- scatterlist::{
- Owned,
- SGTable, //
- },
str::CString,
};
use crate::{
firmware::{
+ radix3::Radix3,
riscv::RiscvFirmware, //
tlv::{
request_tlv, //
@@ -26,38 +21,21 @@
},
},
gpu::Chipset,
- gsp::GSP_PAGE_SIZE,
num::FromSafeCast,
};
/// GSP firmware with 3-level radix page tables for the GSP bootloader.
///
-/// The bootloader expects firmware to be mapped starting at address 0 in GSP's virtual address
-/// space:
-///
-/// ```text
-/// Level 0: 1 page, 1 entry -> points to first level 1 page
-/// Level 1: Multiple pages/entries -> each entry points to a level 2 page
-/// Level 2: Multiple pages/entries -> each entry points to a firmware page
-/// ```
-///
-/// Each page is 4KB, each entry is 8 bytes (64-bit DMA address).
/// Also known as "Radix3" firmware.
#[pin_data]
pub(crate) struct GspFirmware {
- /// The GSP firmware inside a [`VVec`], device-mapped via a SG table.
- #[pin]
- fw: SGTable<Owned<VVec<u8>>>,
- /// Level 2 page table whose entries contain DMA addresses of firmware pages.
- #[pin]
- level2: SGTable<Owned<VVec<u8>>>,
- /// Level 1 page table whose entries contain DMA addresses of level 2 pages.
+ /// The GSP firmware image mapped via a 3-level radix page table.
#[pin]
- level1: SGTable<Owned<VVec<u8>>>,
- /// Level 0 page table (single 4KB page) with one entry: DMA address of first level 1 page.
- level0: Coherent<[u64]>,
- /// Size in bytes of the firmware contained in [`Self::fw`].
- pub(crate) size: usize,
+ radix3: Radix3,
+ /// Firmware file path requested from userspace.
+ pub(crate) fw_path: CString,
+ /// Firmware version from the TLV metadata.
+ pub(crate) fw_version: CString,
/// Device-mapped GSP signatures matching the GPU's [`Chipset`].
pub(crate) signatures: Coherent<[u8]>,
/// GSP bootloader, verifies the GSP firmware before loading and running it.
@@ -74,7 +52,12 @@ pub(crate) fn new<'a>(
pin_init::pin_init_scope(move || {
let firmware = request_tlv(dev, chipset, "gsp")?;
let tlv = Tlv::new(firmware.data())?;
- dev_dbg!(dev, "loaded gsp firmware v{}\n", tlv.get_string(b"VERS")?);
+ let fw_version = CString::try_from_fmt(fmt!("{}", tlv.get_string(b"VERS")?))?;
+ dev_dbg!(
+ dev,
+ "loaded gsp firmware v{}\n",
+ fw_version.to_str().unwrap_or("unknown")
+ );
let size = usize::from_safe_cast(tlv.get_u32(b"SIZE")?);
let mut fw_vvec = VVec::zeroed(size, GFP_KERNEL).map_err(|_| ENOMEM)?;
@@ -87,49 +70,9 @@ pub(crate) fn new<'a>(
let signatures = Coherent::from_slice(dev, tlv.get_bytes(b"SIGN")?, GFP_KERNEL)?;
Ok(try_pin_init!(Self {
- fw <- SGTable::new(dev, fw_vvec, DataDirection::ToDevice, GFP_KERNEL),
- level2 <- {
- // Allocate the level 2 page table, map the firmware onto it, and map it into
- // the device address space.
- VVec::<u8>::with_capacity(
- fw.iter().count() * core::mem::size_of::<u64>(),
- GFP_KERNEL,
- )
- .map_err(|_| ENOMEM)
- .and_then(|level2| map_into_lvl(&fw, level2))
- .map(|level2| SGTable::new(dev, level2, DataDirection::ToDevice, GFP_KERNEL))?
- },
- level1 <- {
- // Allocate the level 1 page table, map the level 2 page table onto it, and map
- // it into the device address space.
- VVec::<u8>::with_capacity(
- level2.iter().count() * core::mem::size_of::<u64>(),
- GFP_KERNEL,
- )
- .map_err(|_| ENOMEM)
- .and_then(|level1| map_into_lvl(&level2, level1))
- .map(|level1| SGTable::new(dev, level1, DataDirection::ToDevice, GFP_KERNEL))?
- },
- level0: {
- // Allocate the level 0 page table as a device-visible DMA object, and map the
- // level 1 page table onto it.
-
- // Fill level 1 page entry.
- let level1_entry = level1.iter().next().ok_or(EINVAL)?;
- let level1_entry_addr = level1_entry.dma_address();
-
- // Create level 0 page table data and fill its first entry with the level 1
- // table.
- let mut level0 = CoherentBox::<[u64]>::zeroed_slice(
- dev,
- GSP_PAGE_SIZE / size_of::<u64>(),
- GFP_KERNEL
- )?;
- level0[0] = level1_entry_addr.to_le();
-
- level0.into()
- },
- size,
+ radix3 <- Radix3::new(dev, fw_vvec),
+ fw_path: filename,
+ fw_version,
signatures,
bootloader: {
let bl = request_tlv(dev, chipset, "gsp_bootloader")?;
@@ -140,28 +83,13 @@ pub(crate) fn new<'a>(
})
}
- /// Returns the DMA address of the radix3 level 0 page table.
- pub(crate) fn radix3_dma_address(&self) -> DmaAddress {
- self.level0.dma_address()
+ /// Returns the size of the GSP firmware image, in bytes.
+ pub(crate) fn size(&self) -> usize {
+ self.radix3.size()
}
-}
-/// Build a page table from a scatter-gather list.
-///
-/// Takes each DMA-mapped region from `sg_table` and writes page table entries
-/// for all 4KB pages within that region. For example, a 16KB SG entry becomes
-/// 4 consecutive page table entries.
-fn map_into_lvl(sg_table: &SGTable<Owned<VVec<u8>>>, mut dst: VVec<u8>) -> Result<VVec<u8>> {
- for sg_entry in sg_table.iter() {
- // Number of pages we need to map.
- let num_pages = usize::from_safe_cast(sg_entry.dma_len()).div_ceil(GSP_PAGE_SIZE);
-
- for i in 0..num_pages {
- let entry = sg_entry.dma_address()
- + (u64::from_safe_cast(i) * u64::from_safe_cast(GSP_PAGE_SIZE));
- dst.extend_from_slice(&entry.to_le_bytes(), GFP_KERNEL)?;
- }
+ /// Returns the DMA address of the radix3 level 0 page table.
+ pub(crate) fn radix3_dma_address(&self) -> DmaAddress {
+ self.radix3.dma_address()
}
-
- Ok(dst)
}
diff --git a/drivers/gpu/nova-core/firmware/radix3.rs b/drivers/gpu/nova-core/firmware/radix3.rs
new file mode 100644
index 000000000000..b60611c7bea0
--- /dev/null
+++ b/drivers/gpu/nova-core/firmware/radix3.rs
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! 3-level radix page table for GSP firmware data.
+//!
+//! The GSP bootloader expects data to be mapped via a 3-level page table:
+//!
+//! ```text
+//! Level 0: 1 page, 1 entry -> points to first level 1 page
+//! Level 1: Multiple pages/entries -> each entry points to a level 2 page
+//! Level 2: Multiple pages/entries -> each entry points to a data page
+//! ```
+//!
+//! Each page is 4KB, each entry is 8 bytes (64-bit DMA address).
+
+use core::mem::size_of;
+
+use kernel::{
+ device,
+ dma::{
+ Coherent,
+ CoherentBox,
+ DataDirection,
+ DmaAddress, //
+ },
+ prelude::*,
+ scatterlist::{
+ Owned,
+ SGTable, //
+ },
+};
+
+use crate::{
+ gsp::GSP_PAGE_SIZE,
+ num::FromSafeCast, //
+};
+
+/// 3-level radix page table mapping arbitrary data for the GSP.
+#[pin_data]
+pub(crate) struct Radix3 {
+ /// The data mapped via a SG table.
+ #[pin]
+ data: SGTable<Owned<VVec<u8>>>,
+ /// Level 2 page table whose entries contain DMA addresses of data pages.
+ #[pin]
+ level2: SGTable<Owned<VVec<u8>>>,
+ /// Level 1 page table whose entries contain DMA addresses of level 2 pages.
+ #[pin]
+ level1: SGTable<Owned<VVec<u8>>>,
+ /// Level 0 page table (single 4KB page) with one entry: DMA address of first level 1 page.
+ level0: Coherent<[u64]>,
+ /// Size in bytes of the data contained in [`Self::data`].
+ size: usize,
+}
+
+impl Radix3 {
+ /// Builds a 3-level radix page table that maps `data` into `dev`'s DMA address space.
+ ///
+ /// Takes ownership of `data`. May sleep.
+ pub(crate) fn new<'a>(
+ dev: &'a device::Device<device::Bound>,
+ data: VVec<u8>,
+ ) -> impl PinInit<Self, Error> + 'a {
+ let size = data.len();
+
+ pin_init::pin_init_scope(move || {
+ Ok(try_pin_init!(Self {
+ data <- SGTable::new(dev, data, DataDirection::ToDevice, GFP_KERNEL),
+ level2 <- {
+ VVec::<u8>::with_capacity(
+ data.iter().count() * core::mem::size_of::<u64>(),
+ GFP_KERNEL,
+ )
+ .map_err(|_| ENOMEM)
+ .and_then(|level2| map_into_lvl(&data, level2))
+ .map(|level2| SGTable::new(dev, level2, DataDirection::ToDevice, GFP_KERNEL))?
+ },
+ level1 <- {
+ VVec::<u8>::with_capacity(
+ level2.iter().count() * core::mem::size_of::<u64>(),
+ GFP_KERNEL,
+ )
+ .map_err(|_| ENOMEM)
+ .and_then(|level1| map_into_lvl(&level2, level1))
+ .map(|level1| SGTable::new(dev, level1, DataDirection::ToDevice, GFP_KERNEL))?
+ },
+ level0: {
+ let level1_entry = level1.iter().next().ok_or(EINVAL)?;
+ let level1_entry_addr = level1_entry.dma_address();
+
+ let mut level0 = CoherentBox::<[u64]>::zeroed_slice(
+ dev,
+ GSP_PAGE_SIZE / size_of::<u64>(),
+ GFP_KERNEL,
+ )?;
+ level0[0] = level1_entry_addr.to_le();
+
+ level0.into()
+ },
+ size,
+ }))
+ })
+ }
+
+ /// Returns the DMA address of the radix3 level 0 page table.
+ pub(crate) fn dma_address(&self) -> DmaAddress {
+ self.level0.dma_address()
+ }
+
+ /// Returns the size of the mapped data, in bytes.
+ pub(crate) fn size(&self) -> usize {
+ self.size
+ }
+}
+
+/// Build a page table from a scatter-gather list.
+///
+/// Takes each DMA-mapped region from `sg_table` and writes page table entries
+/// for all 4KB pages within that region. For example, a 16KB SG entry becomes
+/// 4 consecutive page table entries.
+fn map_into_lvl(sg_table: &SGTable<Owned<VVec<u8>>>, mut dst: VVec<u8>) -> Result<VVec<u8>> {
+ for sg_entry in sg_table.iter() {
+ let num_pages = usize::from_safe_cast(sg_entry.dma_len()).div_ceil(GSP_PAGE_SIZE);
+
+ for i in 0..num_pages {
+ let entry = sg_entry.dma_address()
+ + (u64::from_safe_cast(i) * u64::from_safe_cast(GSP_PAGE_SIZE));
+ dst.extend_from_slice(&entry.to_le_bytes(), GFP_KERNEL)?;
+ }
+ }
+
+ Ok(dst)
+}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 9b29308678a7..d3678f16750a 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -188,7 +188,7 @@ pub(crate) fn from_ranges<'a>(
magic: bindings::GSP_FW_WPR_META_MAGIC as u64,
revision: u64::from(bindings::GSP_FW_WPR_META_REVISION),
sysmemAddrOfRadix3Elf: gsp_firmware.radix3_dma_address(),
- sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size),
+ sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size()),
sysmemAddrOfBootloader: gsp_firmware.bootloader.ucode.dma_address(),
sizeOfBootloader: u64::from_safe_cast(gsp_firmware.bootloader.ucode.size()),
bootloaderCodeOffset: u64::from(gsp_firmware.bootloader.code_offset),
@@ -243,7 +243,7 @@ pub(crate) fn from_sizes<'a>(
magic: bindings::GSP_FW_WPR_META_MAGIC as u64,
revision: u64::from(bindings::GSP_FW_WPR_META_REVISION),
sysmemAddrOfRadix3Elf: gsp_firmware.radix3_dma_address(),
- sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size),
+ sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size()),
sysmemAddrOfBootloader: gsp_firmware.bootloader.ucode.dma_address(),
sizeOfBootloader: u64::from_safe_cast(gsp_firmware.bootloader.ucode.size()),
bootloaderCodeOffset: u64::from(gsp_firmware.bootloader.code_offset),
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 03/27] gpu: nova-core: set MCTP transport header version to 1
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
2026-08-19 3:51 ` [PATCH 01/27] gpu: nova-core: firmware: add r000 bindings John Hubbard
2026-08-19 3:51 ` [PATCH 02/27] gpu: nova-core: extract radix3 page table into its own module John Hubbard
@ 2026-08-19 3:51 ` John Hubbard
2026-08-20 9:38 ` Zhi Wang
2026-08-19 3:51 ` [PATCH 04/27] gpu: nova-core: add Falcon helpers for r000 LOAD_EXEC events John Hubbard
` (24 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:51 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
FSP requires MCTP transport header version 1. Nova-core leaves version
bits 3:0 at zero, so every CoT and PRC request carries an invalid
transport header.
Define the version and destination endpoint ID fields. Set the version
to 1 for single-packet requests, and leave the unused destination
endpoint ID at zero.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/mctp.rs | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/mctp.rs b/drivers/gpu/nova-core/mctp.rs
index 90c642c91a72..03546213da28 100644
--- a/drivers/gpu/nova-core/mctp.rs
+++ b/drivers/gpu/nova-core/mctp.rs
@@ -42,13 +42,23 @@ pub(crate) struct MctpHeader(u32) {
29:28 seq;
/// Source endpoint ID.
23:16 seid;
+ /// Destination endpoint ID.
+ 15:8 deid;
+ /// MCTP header version.
+ 3:0 version;
}
}
impl MctpHeader {
- /// Builds a single-packet MCTP header (`SOM=1`, `EOM=1`, `SEQ=0`, `SEID=0`).
+ /// MCTP specification version 1.0.
+ const VERSION: u32 = 1;
+
+ /// Builds a single-packet MCTP header (`SOM=1`, `EOM=1`, `SEQ=0`, `SEID=0`, `VERSION=1`).
pub(crate) fn single_packet() -> Self {
- Self::zeroed().with_som(true).with_eom(true)
+ Self::zeroed()
+ .with_const_version::<{ Self::VERSION }>()
+ .with_som(true)
+ .with_eom(true)
}
/// Returns whether this is a complete single-packet message (`SOM=1` and `EOM=1`).
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 04/27] gpu: nova-core: add Falcon helpers for r000 LOAD_EXEC events
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (2 preceding siblings ...)
2026-08-19 3:51 ` [PATCH 03/27] gpu: nova-core: set MCTP transport header version to 1 John Hubbard
@ 2026-08-19 3:51 ` John Hubbard
2026-08-20 9:53 ` Zhi Wang
2026-08-19 3:51 ` [PATCH 05/27] gpu: nova-core: zero-pad radix3 page table levels to page boundary John Hubbard
` (23 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:51 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The r000 LOAD_EXEC events provide addresses for images that the driver
must copy into GSP Falcon IMEM and DMEM. The existing Falcon DMA path
creates its source buffer from a FalconFirmware object, so it cannot use
those addresses. The protocol also reports GSP RISC-V suspension through
MAILBOX0 bit 31 rather than CPUCTL.halted.
Add Falcon operations for DMA from event-provided addresses and for the
RISC-V suspend indication.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 136 +++++++++++++++++++++++++++++++-
drivers/gpu/nova-core/regs.rs | 2 +-
2 files changed, 136 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 65cb12d26e2b..20a288050c37 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -133,12 +133,25 @@ pub(crate) enum FalconMem {
/// Secure Instruction Memory.
ImemSecure,
/// Non-Secure Instruction Memory.
- #[expect(unused)]
+ #[expect(dead_code)]
ImemNonSecure,
/// Data Memory.
Dmem,
}
+/// Source offset of a raw falcon DMA transfer, added to the DMA base address.
+#[expect(dead_code)]
+#[derive(Copy, Clone)]
+pub(crate) enum FalconDmaSrcOffset {
+ /// Byte offset from the DMA base address.
+ Offset(u32),
+ /// DMEM virtual address. The DMA engine also tags each loaded block with this value, so the
+ /// falcon reaches the data through its DMEM VA. The caller must bias the DMA base address so
+ /// that base plus this value addresses the first byte of the image. Only valid with
+ /// [`FalconMem::Dmem`].
+ DmemVa(u32),
+}
+
bounded_enum! {
/// Defines the Framebuffer Interface (FBIF) aperture type.
/// This determines the memory type for external memory access during a DMA transfer, which is
@@ -592,6 +605,109 @@ fn dma_wr(
Ok(())
}
+ /// Perform a raw DMA transfer from a physical address to falcon IMEM or DMEM.
+ ///
+ /// Used by the GSP boot event handlers where the firmware provides the source
+ /// address directly (in GPU physical address space, routed through the FBIF
+ /// aperture). The caller must configure the FBIF TRANSCFG register
+ /// corresponding to `ctx_dma` before calling this method.
+ ///
+ /// A [`FalconDmaSrcOffset::DmemVa`] source offset also sets `DMATRFCMD.SET_DMTAG`, and
+ /// requires `target_mem` to be [`FalconMem::Dmem`].
+ ///
+ /// # Errors
+ ///
+ /// - `EINVAL` if `ctx_dma` is not a valid context DMA slot, if `src_addr` is not aligned to
+ /// 256 bytes, or if a [`FalconDmaSrcOffset::DmemVa`] source offset is paired with an IMEM
+ /// target.
+ /// - `ERANGE` if `src_addr` does not fit the `DMATRFBASE` register pair.
+ #[expect(dead_code)]
+ pub(crate) fn raw_dma_transfer(
+ &self,
+ ctx_dma: u8,
+ src_addr: u64,
+ target_mem: FalconMem,
+ src: FalconDmaSrcOffset,
+ dst_offset: u32,
+ len: u32,
+ ) -> Result {
+ const DMA_LEN: u32 = num::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>();
+ const NUM_CTXDMA_SLOTS: u8 = 8;
+
+ if ctx_dma >= NUM_CTXDMA_SLOTS {
+ dev_err!(self.dev, "raw DMA: ctx_dma {} out of range\n", ctx_dma);
+ return Err(EINVAL);
+ }
+
+ if src_addr % u64::from(DMA_LEN) > 0 {
+ dev_err!(
+ self.dev,
+ "raw DMA: source address {:#x} not 256B-aligned\n",
+ src_addr
+ );
+ return Err(EINVAL);
+ }
+
+ if src_addr >> 40 > u64::from(regs::NV_PFALCON_FALCON_DMATRFBASE1::BASE_MASK) {
+ dev_err!(
+ self.dev,
+ "raw DMA: source address {:#x} does not fit DMATRFBASE\n",
+ src_addr
+ );
+ return Err(ERANGE);
+ }
+
+ let (src_offset, set_dmtag) = match src {
+ FalconDmaSrcOffset::Offset(offset) => (offset, false),
+ // `SET_DMTAG` applies to DMEM only.
+ FalconDmaSrcOffset::DmemVa(_) if target_mem != FalconMem::Dmem => return Err(EINVAL),
+ FalconDmaSrcOffset::DmemVa(va) => (va, true),
+ };
+
+ let num_transfers = len.div_ceil(DMA_LEN);
+
+ self.bar.write(
+ WithBase::of::<E>(),
+ regs::NV_PFALCON_FALCON_DMATRFBASE::zeroed().with_base(
+ // CAST: `as u32` is used on purpose since we do want to strip the upper bits,
+ // which will be written to `NV_PFALCON_FALCON_DMATRFBASE1`.
+ (src_addr >> 8) as u32,
+ ),
+ );
+ self.bar.write(
+ WithBase::of::<E>(),
+ regs::NV_PFALCON_FALCON_DMATRFBASE1::zeroed().try_with_base(src_addr >> 40)?,
+ );
+
+ let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::zeroed()
+ .with_size(DmaTrfCmdSize::Size256B)
+ .try_with_ctxdma(u32::from(ctx_dma))?
+ .with_falcon_mem(target_mem)
+ .with_set_dmtag(set_dmtag);
+
+ for pos in (0..num_transfers).map(|i| i * DMA_LEN) {
+ self.bar.write(
+ WithBase::of::<E>(),
+ regs::NV_PFALCON_FALCON_DMATRFMOFFS::zeroed().try_with_offs(dst_offset + pos)?,
+ );
+ self.bar.write(
+ WithBase::of::<E>(),
+ regs::NV_PFALCON_FALCON_DMATRFFBOFFS::zeroed().with_offs(src_offset + pos),
+ );
+
+ self.bar.write(WithBase::of::<E>(), cmd);
+
+ read_poll_timeout(
+ || Ok(self.bar.read(regs::NV_PFALCON_FALCON_DMATRFCMD::of::<E>())),
+ |r| r.idle(),
+ Delta::ZERO,
+ Delta::from_secs(2),
+ )?;
+ }
+
+ Ok(())
+ }
+
/// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(&self, fw: &F) -> Result {
// DMA object with firmware content as the source of the DMA engine.
@@ -647,6 +763,24 @@ pub(crate) fn wait_till_halted(&self) -> Result<()> {
Ok(())
}
+ /// Wait until the GSP processor has suspended.
+ ///
+ /// The RISC-V GSP signals suspension by setting bit 31 (`0x8000_0000`) in `MAILBOX0`, rather
+ /// than through `CPUCTL.halted`.
+ #[expect(dead_code)]
+ pub(crate) fn wait_for_processor_suspend(&self) -> Result<()> {
+ const INTERRUPT_PROCESSOR_SUSPENDED: u32 = 0x8000_0000;
+
+ read_poll_timeout(
+ || Ok(self.read_mailbox0()),
+ |val| (*val & INTERRUPT_PROCESSOR_SUSPENDED) != 0,
+ Delta::ZERO,
+ Delta::from_secs(2),
+ )?;
+
+ Ok(())
+ }
+
/// Start the falcon CPU.
pub(crate) fn start(&self) -> Result<()> {
match self
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 01fde2c5e5a6..5d265b5788e3 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -264,7 +264,7 @@ pub(crate) fn usable_fb_size(self) -> u64 {
}
pub(crate) NV_PFALCON_FALCON_DMATRFCMD(u32) @ PFalconBase + 0x00000118 {
- 16:16 set_dmtag;
+ 16:16 set_dmtag => bool;
14:12 ctxdma;
10:8 size ?=> DmaTrfCmdSize;
5:5 is_write => bool;
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 05/27] gpu: nova-core: zero-pad radix3 page table levels to page boundary
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (3 preceding siblings ...)
2026-08-19 3:51 ` [PATCH 04/27] gpu: nova-core: add Falcon helpers for r000 LOAD_EXEC events John Hubbard
@ 2026-08-19 3:51 ` John Hubbard
2026-08-19 17:41 ` Timur Tabi
2026-08-19 3:51 ` [PATCH 06/27] gpu: nova-core: distinguish async GSP RPC traffic in debug logs John Hubbard
` (22 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:51 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
GSP-RM allocates the whole radix3 page table region in one block and
zeroes it before writing entries. The booter reads each level a full
page at a time when it walks the table, so every byte of a level page is
firmware-visible whether or not it holds a valid entry.
The driver allocates each level separately and writes only the valid
entries, so the last page of each level is only partly written. The
booter reads the whole page regardless, and a non-zero word in the
unwritten remainder is indistinguishable from an entry.
Zero-pad each level to the next GSP_PAGE_SIZE boundary after writing
entries, and size each level buffer to that padded length so the pad
does not force a reallocation.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware/radix3.rs | 40 +++++++++++++++---------
1 file changed, 26 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/radix3.rs b/drivers/gpu/nova-core/firmware/radix3.rs
index b60611c7bea0..f14ad4e82d3d 100644
--- a/drivers/gpu/nova-core/firmware/radix3.rs
+++ b/drivers/gpu/nova-core/firmware/radix3.rs
@@ -67,22 +67,18 @@ pub(crate) fn new<'a>(
Ok(try_pin_init!(Self {
data <- SGTable::new(dev, data, DataDirection::ToDevice, GFP_KERNEL),
level2 <- {
- VVec::<u8>::with_capacity(
- data.iter().count() * core::mem::size_of::<u64>(),
- GFP_KERNEL,
- )
- .map_err(|_| ENOMEM)
- .and_then(|level2| map_into_lvl(&data, level2))
- .map(|level2| SGTable::new(dev, level2, DataDirection::ToDevice, GFP_KERNEL))?
+ let level2 = VVec::<u8>::with_capacity(lvl_size(&data), GFP_KERNEL)
+ .map_err(|_| ENOMEM)
+ .and_then(|level2| map_into_lvl(&data, level2))?;
+
+ SGTable::new(dev, level2, DataDirection::ToDevice, GFP_KERNEL)
},
level1 <- {
- VVec::<u8>::with_capacity(
- level2.iter().count() * core::mem::size_of::<u64>(),
- GFP_KERNEL,
- )
- .map_err(|_| ENOMEM)
- .and_then(|level1| map_into_lvl(&level2, level1))
- .map(|level1| SGTable::new(dev, level1, DataDirection::ToDevice, GFP_KERNEL))?
+ let level1 = VVec::<u8>::with_capacity(lvl_size(&level2), GFP_KERNEL)
+ .map_err(|_| ENOMEM)
+ .and_then(|level1| map_into_lvl(&level2, level1))?;
+
+ SGTable::new(dev, level1, DataDirection::ToDevice, GFP_KERNEL)
},
level0: {
let level1_entry = level1.iter().next().ok_or(EINVAL)?;
@@ -113,6 +109,17 @@ pub(crate) fn size(&self) -> usize {
}
}
+/// Returns the size, in bytes, of the page table level that maps `sg_table`: one `u64` entry per
+/// 4KB page it spans, rounded up to the page boundary that [`map_into_lvl`] pads to.
+fn lvl_size(sg_table: &SGTable<Owned<VVec<u8>>>) -> usize {
+ let entries: usize = sg_table
+ .iter()
+ .map(|sg_entry| usize::from_safe_cast(sg_entry.dma_len()).div_ceil(GSP_PAGE_SIZE))
+ .sum();
+
+ (entries * size_of::<u64>()).next_multiple_of(GSP_PAGE_SIZE)
+}
+
/// Build a page table from a scatter-gather list.
///
/// Takes each DMA-mapped region from `sg_table` and writes page table entries
@@ -129,5 +136,10 @@ fn map_into_lvl(sg_table: &SGTable<Owned<VVec<u8>>>, mut dst: VVec<u8>) -> Resul
}
}
+ // The last page of a level is only partly filled, and the booter DMAs each level a
+ // whole page at a time, so no entry past the last valid one may hold a stale address.
+ let padded = dst.len().next_multiple_of(GSP_PAGE_SIZE);
+ dst.resize(padded, 0, GFP_KERNEL)?;
+
Ok(dst)
}
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 06/27] gpu: nova-core: distinguish async GSP RPC traffic in debug logs
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (4 preceding siblings ...)
2026-08-19 3:51 ` [PATCH 05/27] gpu: nova-core: zero-pad radix3 page table levels to page boundary John Hubbard
@ 2026-08-19 3:51 ` John Hubbard
2026-08-20 10:26 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 07/27] gpu: nova-core: add optional ucodes firmware loading John Hubbard
` (21 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:51 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The receive path logged every message at the transport layer and did not
distinguish events from command replies. Async sends shared the RPC
sequence with waited-for commands, and GSP-initiated events arrived
with that field unset.
Print a distinct debug line for async send, sync send, event, and
command reply. Number async sends and events with driver-local counters.
IS_ASYNC selects only the send log line. Every command still carries the
next RPC sequence on the wire.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 111 ++++++++++++++----
.../gpu/nova-core/gsp/cmdq/continuation.rs | 1 +
drivers/gpu/nova-core/gsp/commands.rs | 2 +
drivers/gpu/nova-core/gsp/fw.rs | 19 +++
4 files changed, 107 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index ac3e6642031a..d3afe3f9bb37 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -87,6 +87,12 @@ pub(crate) trait CommandToGsp {
/// Function identifying this command to the GSP.
const FUNCTION: MsgFunction;
+ /// Classifies the send-side debug log as an async send.
+ ///
+ /// Default is `false`. Set this only on commands that are sent without waiting for a reply. A
+ /// [`NoReply`] continuation of a waited-for command stays `false`.
+ const IS_ASYNC: bool = false;
+
/// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer.
type Command: FromBytes + AsBytes;
@@ -528,6 +534,8 @@ pub(crate) fn new(dev: &device::Device<device::Bound>) -> impl PinInit<Self, Err
gsp_mem,
elem_seq: 0,
rpc_seq: 0,
+ tx_async_seq: 0,
+ rx_event_seq: 0,
poisoned: Cell::new(false),
}),
}))
@@ -672,6 +680,12 @@ struct CmdqInner {
/// [`CmdqInner::receive_msg`] match that reply to the awaiting command. Advances once per
/// logical command.
rpc_seq: u32,
+ /// Debug-log sequence for async sends. Those commands do not wait for a reply, so this
+ /// counter is the number printed in the send log.
+ tx_async_seq: u32,
+ /// Debug-log sequence for GSP-initiated events. The GSP leaves the RPC sequence unset on
+ /// those messages, so the driver numbers them itself.
+ rx_event_seq: u32,
/// Set once a message with corrupt framing or a bad checksum is seen. Such a message has an
/// untrusted length, so the queue cannot be advanced past it, and every later receive fails
/// until the queue is torn down and reset.
@@ -738,13 +752,24 @@ fn send_single_command<M>(&mut self, bar: Bar0<'_>, command: M, rpc_seq: u32) ->
dst.contents.1,
])));
- dev_dbg!(
- &self.dev,
- "GSP RPC: send: seq# {}, function={:?}, length=0x{:x}\n",
- rpc_seq,
- M::FUNCTION,
- dst.header.length(),
- );
+ if M::IS_ASYNC {
+ dev_dbg!(
+ &self.dev,
+ "GSP RPC: async send: seq# {}, function={:?}, length=0x{:x}\n",
+ self.tx_async_seq,
+ M::FUNCTION,
+ dst.header.length(),
+ );
+ self.tx_async_seq += 1;
+ } else {
+ dev_dbg!(
+ &self.dev,
+ "GSP RPC: send: seq# {}, function={:?}, length=0x{:x}\n",
+ rpc_seq,
+ M::FUNCTION,
+ dst.header.length(),
+ );
+ }
// All set - update the write pointer and inform the GSP of the new command.
let elem_count = dst.header.element_count();
@@ -826,14 +851,6 @@ fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> {
return Err(EIO);
};
- dev_dbg!(
- &self.dev,
- "GSP RPC: receive: seq# {}, function={:?}, length=0x{:x}\n",
- header.sequence(),
- header.function(),
- header.length(),
- );
-
let payload_length = header.payload_length();
// Check that the driver read area is large enough for the message.
@@ -877,6 +894,41 @@ fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> {
})
}
+ /// Writes the classified receive debug line for a decoded GSP message.
+ ///
+ /// Events print [`Self::rx_event_seq`]. Command replies print `seq`. An unrecognized function
+ /// code prints nothing here.
+ fn log_received(&self, function: Result<MsgFunction, u32>, seq: u32, length: usize) {
+ match function {
+ Ok(f) if f.is_event() => {
+ dev_dbg!(
+ &self.dev,
+ "GSP RPC: async received: seq# {}, function={:?}, length=0x{:x}\n",
+ self.rx_event_seq,
+ f,
+ length,
+ );
+ }
+ Ok(f) => {
+ dev_dbg!(
+ &self.dev,
+ "GSP RPC: response received: seq# {}, function={:?}, length=0x{:x}\n",
+ seq,
+ f,
+ length,
+ );
+ }
+ Err(_) => {}
+ }
+ }
+
+ /// Advances [`Self::rx_event_seq`] after a GSP-initiated event has been consumed.
+ fn advance_rx_event_seq(&mut self, function: Result<MsgFunction, u32>) {
+ if matches!(function, Ok(f) if f.is_event()) {
+ self.rx_event_seq += 1;
+ }
+ }
+
/// Receive a message from the GSP.
///
/// The expected message type is given by the `M` generic parameter. With `expected_seq` set,
@@ -910,9 +962,12 @@ fn receive_msg<M: MessageFromGsp>(
let message = self.wait_for_msg(timeout)?;
let function = message.header.function();
let seq = message.header.sequence();
+ let length = message.header.length();
let func_matches = matches!(function, Ok(f) if f == M::FUNCTION);
let matched = func_matches && expected_seq.is_none_or(|expected| seq == expected);
+ self.log_received(function, seq, length);
+
// Every path must advance the read pointer past this message, including a failed decode.
let result = if matched {
match M::Message::from_bytes_prefix(message.contents.0) {
@@ -938,9 +993,10 @@ fn receive_msg<M: MessageFromGsp>(
};
// 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(length.div_ceil(GSP_PAGE_SIZE))?);
+
+ self.advance_rx_event_seq(function);
if !matched {
if func_matches {
@@ -962,8 +1018,8 @@ fn receive_msg<M: MessageFromGsp>(
/// Routes a GSP message that is not the reply a caller is waiting for.
///
/// GSP-reported errors are logged at error level and unrecognized function codes at warning
- /// level. Every other known function code is consumed without a log line, because the RPC
- /// receive trace in [`Self::wait_for_msg`] already records its arrival.
+ /// level. Every other known function code is consumed without a log line, because
+ /// [`Self::log_received`] already records its arrival.
fn dispatch_event(&self, function: Result<MsgFunction, u32>, seq: u32) {
match function {
Ok(MsgFunction::OsErrorLog) => {
@@ -1004,16 +1060,19 @@ fn drain(&mut self) -> Result {
while !self.gsp_mem.driver_read_area().0.is_empty() {
// A message is available, so this returns without waiting.
let msg = self.wait_for_msg(Delta::ZERO)?;
-
- let pages =
- u32::try_from(msg.header.length().div_ceil(GSP_PAGE_SIZE)).map_err(|_| {
- dev_err!(&self.dev, "GSP drain: message length overflow\n");
- EIO
- })?;
let function = msg.header.function();
let seq = msg.header.sequence();
+ let length = msg.header.length();
+
+ self.log_received(function, seq, length);
+
+ let pages = u32::try_from(length.div_ceil(GSP_PAGE_SIZE)).map_err(|_| {
+ dev_err!(&self.dev, "GSP drain: message length overflow\n");
+ EIO
+ })?;
self.gsp_mem.advance_cpu_read_ptr(pages);
+ self.advance_rx_event_seq(function);
self.dispatch_event(function, seq);
}
diff --git a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
index 05e904f18097..a606c7be52a7 100644
--- a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
@@ -147,6 +147,7 @@ fn new(command: C, payload: KVVec<u8>) -> Self {
impl<C: CommandToGsp> CommandToGsp for SplitCommand<C> {
const FUNCTION: MsgFunction = C::FUNCTION;
+ const IS_ASYNC: bool = C::IS_ASYNC;
type Command = C::Command;
type Reply = C::Reply;
type InitError = C::InitError;
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 61fe93db9e7e..d5575c036eb9 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -52,6 +52,7 @@ pub(crate) fn new(pdev: &'a pci::Device<device::Bound>, chipset: Chipset) -> Sel
impl<'a> CommandToGsp for SetSystemInfo<'a> {
const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo;
+ const IS_ASYNC: bool = true;
type Command = fw::commands::GspSetSystemInfo;
type Reply = NoReply;
type InitError = Error;
@@ -122,6 +123,7 @@ pub(crate) fn new(vgpu_state: VgpuState) -> Result<Self> {
impl CommandToGsp for SetRegistry {
const FUNCTION: MsgFunction = MsgFunction::SetRegistry;
+ const IS_ASYNC: bool = true;
type Command = fw::commands::PackedRegistryTable;
type Reply = NoReply;
type InitError = Infallible;
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index d3678f16750a..857d53c77297 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -359,6 +359,25 @@ fn try_from(value: u32) -> Result<MsgFunction> {
}
}
+impl MsgFunction {
+ /// Returns true if this is a GSP-initiated async event (`NV_VGPU_MSG_EVENT_*`), as opposed to
+ /// a command response (`NV_VGPU_MSG_FUNCTION_*`).
+ pub(crate) fn is_event(&self) -> bool {
+ matches!(
+ self,
+ Self::GspInitDone
+ | Self::GspRunCpuSequencer
+ | Self::PostEvent
+ | Self::RcTriggered
+ | Self::MmuFaultQueued
+ | Self::OsErrorLog
+ | Self::GspPostNoCat
+ | Self::GspLockdownNotice
+ | Self::UcodeLibOsPrint //
+ )
+ }
+}
+
impl From<MsgFunction> for u32 {
fn from(value: MsgFunction) -> Self {
// CAST: `MsgFunction` is `repr(u32)` and can thus be cast losslessly.
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 07/27] gpu: nova-core: add optional ucodes firmware loading
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (5 preceding siblings ...)
2026-08-19 3:51 ` [PATCH 06/27] gpu: nova-core: distinguish async GSP RPC traffic in debug logs John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 17:55 ` Timur Tabi
2026-08-19 18:41 ` Timur Tabi
2026-08-19 3:52 ` [PATCH 08/27] gpu: nova-core: add LIBOS3 log buffers and state monitor buffer John Hubbard
` (20 subsequent siblings)
27 siblings, 2 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The new GSP firmware takes an optional ucodes image, a bindata blob of
microcode that GSP-RM loads at runtime. Open RM fetches it for every
chip family except GB10Y and fails firmware load without it, so the
driver will require it too once it boots the new firmware. The helper
added here reports a missing metadata file rather than failing, and
leaves that policy to its caller.
Load the ucodes metadata with the existing TLV request helper, and add
support for the generic TLV payload convention it uses: either an inline
BLOB, or a FILE basename with its SIZE. A FILE tag may only name a
basename in the metadata file's own directory, so one carrying a path
separator, a special directory name, or a control byte is rejected.
Nothing calls this until the driver switches to the new firmware.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware.rs | 8 +++-
drivers/gpu/nova-core/firmware/bindata.rs | 38 +++++++++++++++
drivers/gpu/nova-core/firmware/tlv.rs | 56 +++++++++++++++++++++++
3 files changed, 101 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/nova-core/firmware/bindata.rs
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 34657004568c..dfd41364cc77 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -22,6 +22,7 @@
num::IntoSafeCast, //
};
+pub(crate) mod bindata;
pub(crate) mod booter;
pub(crate) mod fsp;
pub(crate) mod fwsec;
@@ -348,7 +349,12 @@ const fn make_entry_chipset(self, chipset: gpu::Chipset) -> Self {
let mut this = self
.make_entry_file(name, "gsp_bootloader.tlv")
.make_entry_file(name, "gsp.tlv")
- .make_entry_file(name, "gsp.bin");
+ .make_entry_file(name, "gsp.bin")
+ .make_entry_file(name, "ucodes.tlv")
+ // `ucodes.tlv` may name another safe basename in its FILE tag, but module firmware
+ // metadata cannot describe a runtime-selected filename. Advertise the conventional
+ // filename used by the distributed metadata.
+ .make_entry_file(name, "ucodes.bin");
// Add the firmware files specific to the GSP boot method of `chipset`.
let boot_files = boot_firmware_files(chipset);
diff --git a/drivers/gpu/nova-core/firmware/bindata.rs b/drivers/gpu/nova-core/firmware/bindata.rs
new file mode 100644
index 000000000000..d9625ab7d738
--- /dev/null
+++ b/drivers/gpu/nova-core/firmware/bindata.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! GSP bindata firmware loading.
+
+use kernel::{
+ device,
+ prelude::*, //
+};
+
+use crate::{
+ firmware::tlv::{
+ request_tlv,
+ Tlv, //
+ },
+ gpu::Chipset,
+};
+
+/// Requests the optional `ucodes` bindata metadata and loads its payload.
+///
+/// A missing metadata file is reported as [`None`] so that the eventual caller can decide whether
+/// ucodes are optional for its boot path. Once the metadata has been found, all parse and payload
+/// loading errors, including a missing referenced file, are returned as errors.
+#[expect(dead_code)]
+pub(crate) fn request_ucodes_firmware(
+ dev: &device::Device,
+ chipset: Chipset,
+) -> Result<Option<VVec<u8>>> {
+ let firmware = match request_tlv(dev, chipset, "ucodes") {
+ Ok(firmware) => firmware,
+ Err(e) if e == ENOENT => return Ok(None),
+ Err(e) => return Err(e),
+ };
+
+ let tlv = Tlv::new(firmware.data())?;
+
+ Ok(Some(tlv.load_blob_or_file(dev, chipset)?))
+}
diff --git a/drivers/gpu/nova-core/firmware/tlv.rs b/drivers/gpu/nova-core/firmware/tlv.rs
index 7b879f13a61e..b9aabf9ee952 100644
--- a/drivers/gpu/nova-core/firmware/tlv.rs
+++ b/drivers/gpu/nova-core/firmware/tlv.rs
@@ -198,6 +198,62 @@ fn iter(&self) -> TlvIter<'_, 'a> {
self.iter().find(|b| b.tag == *tag).ok_or(EINVAL)
}
+ fn contains(&self, tag: &[u8; 4]) -> bool {
+ self.iter().any(|b| b.tag == *tag)
+ }
+
+ /// Loads the payload described by this TLV into owned memory.
+ ///
+ /// The generic firmware TLV representation is either an inline `BLOB`, or a `FILE` basename
+ /// together with its `SIZE`. Mixing the representations or specifying only half of the file
+ /// representation is invalid. Referenced files are constrained to the metadata file's
+ /// directory.
+ pub(crate) fn load_blob_or_file(
+ &self,
+ dev: &device::Device,
+ chipset: gpu::Chipset,
+ ) -> Result<VVec<u8>> {
+ let has_blob = self.contains(b"BLOB");
+ let has_file = self.contains(b"FILE");
+ let has_size = self.contains(b"SIZE");
+
+ match (has_blob, has_file, has_size) {
+ (true, false, false) => {
+ let blob = self.get_bytes(b"BLOB")?;
+ let mut data = VVec::with_capacity(blob.len(), GFP_KERNEL).map_err(|_| ENOMEM)?;
+ data.extend_from_slice(blob, GFP_KERNEL)?;
+ Ok(data)
+ }
+ (false, true, true) => {
+ let file = self.get_string(b"FILE")?;
+
+ // FILE is a basename relative to the TLV's directory, not an arbitrary firmware
+ // path. Reject path separators, special directory names, NULs and control bytes.
+ if file.is_empty()
+ || matches!(file, "." | "..")
+ || file
+ .bytes()
+ .any(|b| b == b'/' || b == b'\\' || b.is_ascii_control())
+ {
+ return Err(EINVAL);
+ }
+
+ let size = usize::from_safe_cast(self.get_u32(b"SIZE")?);
+ if size == 0 {
+ return Err(EINVAL); // TODO: Use ENODATA once available.
+ }
+
+ let mut data = VVec::zeroed(size, GFP_KERNEL).map_err(|_| ENOMEM)?;
+ let chip_name = chipset.name();
+ let path = CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{file}"))?;
+ firmware::request_into_buf(&path, dev, data.as_mut_slice())?;
+
+ Ok(data)
+ }
+ _ => Err(EINVAL),
+ }
+ }
+
/// Return a slice of bytes.
///
/// Returns `EINVAL` if the value is empty.
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 08/27] gpu: nova-core: add LIBOS3 log buffers and state monitor buffer
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (6 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 07/27] gpu: nova-core: add optional ucodes firmware loading John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 18:12 ` Timur Tabi
2026-08-19 3:52 ` [PATCH 09/27] gpu: nova-core: add build ID headers to debugfs log buffer dumps John Hubbard
` (19 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
GSP-RM on the new firmware logs from six LIBOS3 tasks on GA102 and
later, where r570 logs from three, and it maps a small buffer during
init to report RM state for diagnostics. Turing and GA100 run LIBOS2,
where only the init and RM task logs exist. Two of the six task logs are
a single page, where the rest are 64KB.
Allocate the three missing log buffers and the state monitor buffer,
give LogBuffer const parameters for its size and page count so the
single-page buffers can exist, and expose the new log buffers through
the debugfs directory that already carries the others.
Nothing passes these to GSP-RM yet. The libos init arguments gain them
when the driver switches to the new firmware.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp.rs | 71 ++++++++++++++++++++++++++++--------
1 file changed, 55 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index f9a622edf299..f19a47cf396f 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -118,33 +118,63 @@ fn init(view: CoherentView<'_, Self>, start: DmaAddress) -> Result<()> {
/// then pp points to index into the buffer where the next logging entry will
/// be written. Therefore, the logging data is valid if:
/// 1 <= pp < sizeof(buffer)/sizeof(u64)
-struct LogBuffer(Coherent<[u8; LOG_BUFFER_SIZE]>);
+///
+/// `SIZE` is the buffer size in bytes and `NUM_PAGES` is the same size in GSP pages, checked
+/// against each other at build time. Computing one from the other in a type position is not
+/// stable Rust, so both are parameters.
+struct LogBuffer<const SIZE: usize, const NUM_PAGES: usize>(Coherent<[u8; SIZE]>);
+
+/// Log buffer for a task that GSP-RM logs to at its default size.
+///
+/// Matches the registry defaults for the init, interrupt, RM, and MNOC tasks
+/// (`NV_REG_STR_RM_GSP_LOG_BUFFER_SIZE_TASK_*_DEFAULT`).
+type TaskLogBuffer = LogBuffer<LOG_BUFFER_SIZE, RM_LOG_BUFFER_NUM_PAGES>;
+
+/// Log buffer for a task that GSP-RM gives a single page.
+///
+/// Matches the size GSP-RM hardcodes for the root and RM state monitor tasks.
+type SmallLogBuffer = LogBuffer<GSP_PAGE_SIZE, 1>;
-impl LogBuffer {
+impl<const SIZE: usize, const NUM_PAGES: usize> LogBuffer<SIZE, NUM_PAGES> {
/// Creates a new `LogBuffer` mapped on `dev`.
fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
+ build_assert!(SIZE == NUM_PAGES * GSP_PAGE_SIZE);
+
let obj = Self(Coherent::zeroed(dev, GFP_KERNEL)?);
let start_addr = obj.0.dma_address();
let pte_view = io_project!(
obj.0,
- [build: size_of::<u64>()..][build: ..RM_LOG_BUFFER_NUM_PAGES * size_of::<u64>()]
+ [build: size_of::<u64>()..][build: ..NUM_PAGES * size_of::<u64>()]
)
- .try_cast::<PteArray<RM_LOG_BUFFER_NUM_PAGES>>()?;
+ .try_cast::<PteArray<NUM_PAGES>>()?;
PteArray::init(pte_view, start_addr)?;
Ok(obj)
}
}
+/// Log buffers used by GSP-RM for debug logging.
+///
+/// r000+ firmware expects log buffers for all LIBOS3 tasks. Each buffer is
+/// registered as a libos memory region entry, identified by its id8 name.
+///
+/// The Open RM equivalents are `_kgspInitLibosLoggingStructures`, which allocates the buffers,
+/// and `kgspSetupLibosInitArgs_IMPL`, which builds the `pLibosInitArgs[]` array.
struct LogBuffers {
- /// Init log buffer.
- loginit: LogBuffer,
- /// Interrupts log buffer.
- logintr: LogBuffer,
- /// RM log buffer.
- logrm: LogBuffer,
+ /// Init task log buffer (LOGINIT).
+ loginit: TaskLogBuffer,
+ /// Interrupt task log buffer (LOGINTR).
+ logintr: TaskLogBuffer,
+ /// RM task log buffer (LOGRM).
+ logrm: TaskLogBuffer,
+ /// MNOC task log buffer (LOGMNOC).
+ logmnoc: TaskLogBuffer,
+ /// Root task log buffer (LOGROOT).
+ logroot: SmallLogBuffer,
+ /// RM state monitor task log buffer (LOGRMON).
+ logrmon: SmallLogBuffer,
}
/// GSP runtime data.
@@ -159,6 +189,8 @@ pub(crate) struct Gsp {
pub(crate) cmdq: Arc<Cmdq>,
/// RM arguments.
rmargs: Coherent<GspArgumentsPadded>,
+ /// RM state monitor buffer (required by r000+ GSP-RM for diagnostics).
+ rm_state_monitor: Coherent<[u8; GSP_PAGE_SIZE]>,
}
impl Gsp {
@@ -167,16 +199,17 @@ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error
pin_init::pin_init_scope(move || {
let dev = pdev.as_ref();
- let loginit = LogBuffer::new(dev)?;
- let logintr = LogBuffer::new(dev)?;
- let logrm = LogBuffer::new(dev)?;
+ let loginit = TaskLogBuffer::new(dev)?;
+ let logintr = TaskLogBuffer::new(dev)?;
+ let logrm = TaskLogBuffer::new(dev)?;
+ let logmnoc = TaskLogBuffer::new(dev)?;
+ let logroot = SmallLogBuffer::new(dev)?;
+ let logrmon = SmallLogBuffer::new(dev)?;
- // Initialise the logging structures. The OpenRM equivalents are in:
- // _kgspInitLibosLoggingStructures (allocates memory for buffers)
- // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array)
Ok(try_pin_init!(Self {
cmdq: Arc::pin_init(Cmdq::new(dev), GFP_KERNEL)?,
rmargs: Coherent::init(dev, GFP_KERNEL, GspArgumentsPadded::new(cmdq.as_ref()))?,
+ rm_state_monitor: Coherent::zeroed(dev, GFP_KERNEL)?,
libos: {
let mut libos = CoherentBox::zeroed_slice(
dev,
@@ -196,6 +229,9 @@ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error
loginit,
logintr,
logrm,
+ logmnoc,
+ logroot,
+ logrmon,
};
#[allow(static_mut_refs)]
@@ -212,6 +248,9 @@ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error
dir.read_binary_file(c"loginit", &logs.loginit.0);
dir.read_binary_file(c"logintr", &logs.logintr.0);
dir.read_binary_file(c"logrm", &logs.logrm.0);
+ dir.read_binary_file(c"logmnoc", &logs.logmnoc.0);
+ dir.read_binary_file(c"logroot", &logs.logroot.0);
+ dir.read_binary_file(c"logrmon", &logs.logrmon.0);
})
},
}))
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 09/27] gpu: nova-core: add build ID headers to debugfs log buffer dumps
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (7 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 08/27] gpu: nova-core: add LIBOS3 log buffers and state monitor buffer John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 18:20 ` Timur Tabi
2026-08-19 3:52 ` [PATCH 10/27] gpu: nova-core: rename the FbRanges elf field to fw_image John Hubbard
` (18 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
A raw log buffer dump cannot be decoded without knowing which firmware
build produced it, and which GPU and metadata format it belongs to.
GSP-RM's own decoder takes that from a header ahead of the data, in the
LIBOS_LOG_NVLOG_BUFFER_V2 layout.
Prepend that header to each debugfs dump. The build ID comes from the
BLID tag of gsp.tlv, so request the metadata once when the GSP manager
is created and hand it to the firmware loader rather than requesting it
again during boot. A buffer whose build ID is missing serves its
contents with no header.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware.rs | 34 +++++
drivers/gpu/nova-core/firmware/gsp.rs | 8 +-
drivers/gpu/nova-core/gpu.rs | 2 +-
drivers/gpu/nova-core/gsp.rs | 185 ++++++++++++++++++++++----
drivers/gpu/nova-core/gsp/boot.rs | 2 +-
5 files changed, 200 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index dfd41364cc77..e0befe84aa3e 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -31,6 +31,40 @@
pub(crate) mod riscv;
pub(crate) mod tlv;
+/// Maximum length of a build ID, matching Open RM's `BUILD_ID_MAX_LENGTH`.
+const BUILD_ID_MAX_LENGTH: usize = 32;
+
+/// Build ID extracted from firmware, used to correlate debugfs log buffer dumps
+/// with the correct firmware symbols.
+pub(crate) struct BuildId {
+ bytes: [u8; BUILD_ID_MAX_LENGTH],
+ len: u8,
+}
+
+impl BuildId {
+ /// Constructs a [`BuildId`] from raw descriptor bytes.
+ ///
+ /// Returns `None` if `data` is empty or exceeds [`BUILD_ID_MAX_LENGTH`].
+ pub(crate) fn from_raw(data: &[u8]) -> Option<Self> {
+ if data.is_empty() || data.len() > BUILD_ID_MAX_LENGTH {
+ return None;
+ }
+
+ let mut bytes = [0u8; BUILD_ID_MAX_LENGTH];
+ bytes[..data.len()].copy_from_slice(data);
+
+ Some(Self {
+ bytes,
+ len: data.len() as u8,
+ })
+ }
+
+ /// Returns the build ID bytes.
+ pub(crate) fn as_bytes(&self) -> &[u8] {
+ &self.bytes[..usize::from(self.len)]
+ }
+}
+
/// Structure used to describe some firmwares, notably FWSEC-FRTS.
#[repr(C)]
#[derive(Debug, Clone, FromBytes)]
diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index 48c73a676ecb..832debb82767 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -16,8 +16,8 @@
radix3::Radix3,
riscv::RiscvFirmware, //
tlv::{
- request_tlv, //
- Tlv,
+ request_tlv,
+ Tlv, //
},
},
gpu::Chipset,
@@ -48,10 +48,10 @@ impl GspFirmware {
pub(crate) fn new<'a>(
dev: &'a device::Device<device::Bound>,
chipset: Chipset,
+ gsp_tlv: &'a firmware::Firmware,
) -> impl PinInit<Self, Error> + 'a {
pin_init::pin_init_scope(move || {
- let firmware = request_tlv(dev, chipset, "gsp")?;
- let tlv = Tlv::new(firmware.data())?;
+ let tlv = Tlv::new(gsp_tlv.data())?;
let fw_version = CString::try_from_fmt(fmt!("{}", tlv.get_string(b"VERS")?))?;
dev_dbg!(
dev,
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 11a66a597298..c0ba561a3bf1 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -396,7 +396,7 @@ pub(crate) fn new(
vgpu: VgpuManager::new(pdev, spec.chipset, fsp.as_mut()),
- gsp <- Gsp::new(pdev),
+ gsp <- Gsp::new(pdev, spec.chipset),
// This member must be initialized last, so the `UnloadBundle` can never be dropped
// from outside of the constructed `GspResources`, ensuring that the unload sequence
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index f19a47cf396f..232905638169 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -12,6 +12,7 @@
CoherentView,
DmaAddress, //
},
+ fs::file,
io::{
io_project,
io_write,
@@ -19,7 +20,8 @@
},
pci,
prelude::*,
- sync::Arc, //
+ sync::Arc,
+ uaccess::UserSliceWriter, //
};
pub(crate) mod cmdq;
@@ -45,6 +47,13 @@
sec2::Sec2 as Sec2Falcon,
Falcon, //
},
+ firmware::{
+ tlv::{
+ request_tlv,
+ Tlv, //
+ },
+ BuildId, //
+ },
fsp::Fsp,
gpu::Chipset,
gsp::{
@@ -104,6 +113,50 @@ fn init(view: CoherentView<'_, Self>, start: DmaAddress) -> Result<()> {
}
}
+/// Size of the header prepended to debugfs log buffer dumps.
+///
+/// This header makes each dump self-describing so that decoding tools can
+/// identify the firmware build, GPU architecture, and metadata format without
+/// out-of-band information.
+const LOG_BUFFER_HEADER_SIZE: usize = 0x48;
+
+/// Build a log buffer header from GPU and firmware metadata.
+///
+/// Layout (all little-endian):
+/// 0x00 gpuArch (u32)
+/// 0x04 gpuImpl (u32)
+/// 0x08 version (u32) = 2
+/// 0x0C buildIdLength (u32)
+/// 0x10 taskPrefix[8]
+/// 0x18 localToGlobalTimerDelta (u64) = 0
+/// 0x20 buildId[32]
+/// 0x40 flags (u32) = 1 (packed metadata)
+/// 0x44 reserved (u32) = 0
+fn build_log_buffer_header(
+ chipset: Chipset,
+ build_id: &BuildId,
+ task_prefix: &str,
+) -> [u8; LOG_BUFFER_HEADER_SIZE] {
+ let mut h = [0u8; LOG_BUFFER_HEADER_SIZE];
+ let chipset_val = chipset as u32;
+
+ h[0x00..0x04].copy_from_slice(&(chipset_val >> 4).to_le_bytes());
+ h[0x04..0x08].copy_from_slice(&(chipset_val & 0xF).to_le_bytes());
+ h[0x08..0x0C].copy_from_slice(&2u32.to_le_bytes());
+
+ let bid = build_id.as_bytes();
+ h[0x0C..0x10].copy_from_slice(&(bid.len() as u32).to_le_bytes());
+
+ let prefix = task_prefix.as_bytes();
+ let prefix_len = prefix.len().min(8);
+ h[0x10..0x10 + prefix_len].copy_from_slice(&prefix[..prefix_len]);
+
+ h[0x20..0x20 + bid.len()].copy_from_slice(bid);
+ h[0x40..0x44].copy_from_slice(&1u32.to_le_bytes());
+
+ h
+}
+
/// The logging buffers are byte queues that contain encoded printf-like
/// messages from GSP-RM. They need to be decoded by a special application
/// that can parse the buffers.
@@ -122,7 +175,13 @@ fn init(view: CoherentView<'_, Self>, start: DmaAddress) -> Result<()> {
/// `SIZE` is the buffer size in bytes and `NUM_PAGES` is the same size in GSP pages, checked
/// against each other at build time. Computing one from the other in a type position is not
/// stable Rust, so both are parameters.
-struct LogBuffer<const SIZE: usize, const NUM_PAGES: usize>(Coherent<[u8; SIZE]>);
+///
+/// When a build ID is available, the debugfs file for this buffer prepends
+/// a header so the dump is self-describing.
+struct LogBuffer<const SIZE: usize, const NUM_PAGES: usize> {
+ header: Option<[u8; LOG_BUFFER_HEADER_SIZE]>,
+ buffer: Coherent<[u8; SIZE]>,
+}
/// Log buffer for a task that GSP-RM logs to at its default size.
///
@@ -137,21 +196,72 @@ fn init(view: CoherentView<'_, Self>, start: DmaAddress) -> Result<()> {
impl<const SIZE: usize, const NUM_PAGES: usize> LogBuffer<SIZE, NUM_PAGES> {
/// Creates a new `LogBuffer` mapped on `dev`.
- fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
+ fn new(
+ dev: &device::Device<device::Bound>,
+ chipset: Chipset,
+ build_id: Option<&BuildId>,
+ task_prefix: &str,
+ ) -> Result<Self> {
build_assert!(SIZE == NUM_PAGES * GSP_PAGE_SIZE);
- let obj = Self(Coherent::zeroed(dev, GFP_KERNEL)?);
-
- let start_addr = obj.0.dma_address();
+ let buffer = Coherent::zeroed(dev, GFP_KERNEL)?;
+ let start_addr = buffer.dma_address();
let pte_view = io_project!(
- obj.0,
+ buffer,
[build: size_of::<u64>()..][build: ..NUM_PAGES * size_of::<u64>()]
)
.try_cast::<PteArray<NUM_PAGES>>()?;
PteArray::init(pte_view, start_addr)?;
- Ok(obj)
+ let header = build_id.map(|bid| build_log_buffer_header(chipset, bid, task_prefix));
+
+ Ok(Self { header, buffer })
+ }
+}
+
+impl<const SIZE: usize, const NUM_PAGES: usize> debugfs::BinaryWriter
+ for LogBuffer<SIZE, NUM_PAGES>
+{
+ fn write_to_slice(
+ &self,
+ writer: &mut UserSliceWriter,
+ offset: &mut file::Offset,
+ ) -> Result<usize> {
+ if offset.is_negative() {
+ return Err(EINVAL);
+ }
+
+ let offset_val: usize = (*offset).try_into().map_err(|_| EINVAL)?;
+ let header = self.header.as_ref().map_or(&[][..], |h| h.as_slice());
+ let total_len = header.len() + self.buffer.size();
+
+ if offset_val >= total_len {
+ return Ok(0);
+ }
+
+ let count = (total_len - offset_val).min(writer.len());
+ if count == 0 {
+ return Ok(0);
+ }
+
+ let mut written = 0;
+
+ if offset_val < header.len() {
+ let hdr_count = (header.len() - offset_val).min(count);
+ writer.write_slice(&header[offset_val..offset_val + hdr_count])?;
+ written += hdr_count;
+ }
+
+ if written < count {
+ let buf_start = offset_val.saturating_sub(header.len());
+ let buf_count = count - written;
+ writer.write_dma(&self.buffer, buf_start, buf_count)?;
+ written += buf_count;
+ }
+
+ *offset += written as i64;
+ Ok(written)
}
}
@@ -180,9 +290,11 @@ struct LogBuffers {
/// GSP runtime data.
#[pin_data]
pub(crate) struct Gsp {
+ /// Preloaded GSP firmware TLV metadata used during boot.
+ gsp_tlv: kernel::firmware::Firmware,
/// Libos arguments.
pub(crate) libos: Coherent<[LibosMemoryRegionInitArgument]>,
- /// Log buffers, optionally exposed via debugfs.
+ /// Log buffers for all LIBOS3 tasks, exposed via debugfs.
#[pin]
logs: debugfs::Scope<LogBuffers>,
/// Command queue, shared with the GSP event interrupt handler.
@@ -195,18 +307,32 @@ pub(crate) struct Gsp {
impl Gsp {
// Creates an in-place initializer for a `Gsp` manager for `pdev`.
- pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error> + '_ {
+ pub(crate) fn new(
+ pdev: &pci::Device<device::Bound>,
+ chipset: Chipset,
+ ) -> impl PinInit<Self, Error> + '_ {
pin_init::pin_init_scope(move || {
let dev = pdev.as_ref();
- let loginit = TaskLogBuffer::new(dev)?;
- let logintr = TaskLogBuffer::new(dev)?;
- let logrm = TaskLogBuffer::new(dev)?;
- let logmnoc = TaskLogBuffer::new(dev)?;
- let logroot = SmallLogBuffer::new(dev)?;
- let logrmon = SmallLogBuffer::new(dev)?;
+ let gsp_tlv = request_tlv(dev, chipset, "gsp")?;
+ let tlv = Tlv::new(gsp_tlv.data())?;
+ let build_id = tlv.get_bytes(b"BLID").ok().and_then(BuildId::from_raw);
+ if build_id.is_none() {
+ dev_warn!(
+ pdev,
+ "GSP firmware build ID not found, log buffer headers omitted\n"
+ );
+ }
+
+ let loginit = TaskLogBuffer::new(dev, chipset, build_id.as_ref(), "INIT")?;
+ let logintr = TaskLogBuffer::new(dev, chipset, build_id.as_ref(), "INTR")?;
+ let logrm = TaskLogBuffer::new(dev, chipset, build_id.as_ref(), "RM")?;
+ let logmnoc = TaskLogBuffer::new(dev, chipset, build_id.as_ref(), "MNOC")?;
+ let logroot = SmallLogBuffer::new(dev, chipset, build_id.as_ref(), "ROOT")?;
+ let logrmon = SmallLogBuffer::new(dev, chipset, build_id.as_ref(), "RMON")?;
Ok(try_pin_init!(Self {
+ gsp_tlv,
cmdq: Arc::pin_init(Cmdq::new(dev), GFP_KERNEL)?,
rmargs: Coherent::init(dev, GFP_KERNEL, GspArgumentsPadded::new(cmdq.as_ref()))?,
rm_state_monitor: Coherent::zeroed(dev, GFP_KERNEL)?,
@@ -217,9 +343,18 @@ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error
GFP_KERNEL,
)?;
- libos.init_at(0, LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?;
- libos.init_at(1, LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?;
- libos.init_at(2, LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?;
+ libos.init_at(
+ 0,
+ LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.buffer),
+ )?;
+ libos.init_at(
+ 1,
+ LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.buffer),
+ )?;
+ libos.init_at(
+ 2,
+ LibosMemoryRegionInitArgument::new("LOGRM", &logrm.buffer),
+ )?;
libos.init_at(3, LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?;
libos.into()
@@ -245,12 +380,12 @@ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error
.expect("DEBUGFS_ROOT not initialized");
log_parent.scope(log_buffers, dev.name(), |logs, dir| {
- dir.read_binary_file(c"loginit", &logs.loginit.0);
- dir.read_binary_file(c"logintr", &logs.logintr.0);
- dir.read_binary_file(c"logrm", &logs.logrm.0);
- dir.read_binary_file(c"logmnoc", &logs.logmnoc.0);
- dir.read_binary_file(c"logroot", &logs.logroot.0);
- dir.read_binary_file(c"logrmon", &logs.logrmon.0);
+ dir.read_binary_file(c"loginit", &logs.loginit);
+ dir.read_binary_file(c"logintr", &logs.logintr);
+ dir.read_binary_file(c"logrm", &logs.logrm);
+ dir.read_binary_file(c"logmnoc", &logs.logmnoc);
+ dir.read_binary_file(c"logroot", &logs.logroot);
+ dir.read_binary_file(c"logrmon", &logs.logrmon);
})
},
}))
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index e03700ee7bea..a64313dca0a1 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -42,7 +42,7 @@ pub(crate) fn boot(
let dev = pdev.as_ref();
let hal = super::hal::gsp_hal(chipset);
- let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset), GFP_KERNEL)?;
+ let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, &self.gsp_tlv), GFP_KERNEL)?;
// Perform the chipset-specific boot sequence, and retrieve the unload bundle.
let unload_bundle = hal.boot(&self, &mut ctx, &gsp_fw)?.or_else(|| {
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 10/27] gpu: nova-core: rename the FbRanges elf field to fw_image
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (8 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 09/27] gpu: nova-core: add build ID headers to debugfs log buffer dumps John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 18:21 ` Timur Tabi
2026-08-19 3:52 ` [PATCH 11/27] gpu: nova-core: regs: add msgq v2 BAR0 register declarations John Hubbard
` (17 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The framebuffer region this field describes holds the GSP firmware
image. It is an ELF under 570.144 and is not one under the firmware this
series moves to, so name the field for what it holds rather than for its
current format.
No functional change.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/fb.rs | 18 +++++++++---------
drivers/gpu/nova-core/gsp/fw.rs | 2 +-
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index f208d6deb5e3..6952f8a6bf9b 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -159,7 +159,7 @@ pub(crate) struct FbRanges {
/// Memory area containing the GSP bootloader image.
pub(crate) boot: FbRange,
/// Memory area containing the GSP firmware image.
- pub(crate) elf: FbRange,
+ pub(crate) fw_image: FbRange,
/// WPR2 heap.
pub(crate) wpr2_heap: FbRange,
/// WPR2 region range, starting with an instance of `GspFwWprMeta`.
@@ -234,25 +234,25 @@ pub(crate) fn new(
FbRange(bootloader_base..bootloader_base + bootloader_size)
};
- let elf = {
- const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
- let elf_size = u64::from_safe_cast(gsp_fw.size());
- let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
+ let fw_image = {
+ const FW_IMAGE_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
+ let fw_image_size = u64::from_safe_cast(gsp_fw.size());
+ let fw_image_addr = (boot.start - fw_image_size).align_down(FW_IMAGE_DOWN_ALIGN);
- FbRange(elf_addr..elf_addr + elf_size)
+ FbRange(fw_image_addr..fw_image_addr + fw_image_size)
};
let (vf_partition_count, wpr2_heap_size) = wpr2_heap_params(chipset, vgpu_state, fb.end)?;
let wpr2_heap = {
const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
- let wpr2_heap_addr = elf
+ let wpr2_heap_addr = fw_image
.start
.checked_sub(wpr2_heap_size)
.ok_or(EOVERFLOW)?
.align_down(WPR2_HEAP_DOWN_ALIGN);
- FbRange(wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN))
+ FbRange(wpr2_heap_addr..(fw_image.start).align_down(WPR2_HEAP_DOWN_ALIGN))
};
let wpr2 = {
@@ -273,7 +273,7 @@ pub(crate) fn new(
vga_workspace,
frts,
boot,
- elf,
+ fw_image,
wpr2_heap,
wpr2,
non_wpr_heap,
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 857d53c77297..d10677e277c5 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -206,7 +206,7 @@ pub(crate) fn from_ranges<'a>(
gspFwWprStart: ranges.wpr2.start,
gspFwHeapOffset: ranges.wpr2_heap.start,
gspFwHeapSize: ranges.wpr2_heap.len(),
- gspFwOffset: ranges.elf.start,
+ gspFwOffset: ranges.fw_image.start,
bootBinOffset: ranges.boot.start,
frtsOffset: ranges.frts.start,
frtsSize: ranges.frts.len(),
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 11/27] gpu: nova-core: regs: add msgq v2 BAR0 register declarations
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (9 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 10/27] gpu: nova-core: rename the FbRanges elf field to fw_image John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-20 10:39 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 12/27] gpu: nova-core: gsp: add msgq v2 internals John Hubbard
` (16 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
Msgq v2 moves the four ring pointers out of the in-memory headers and
into BAR0. NV_PGSP_QUEUE_HEAD already exists as the v0 doorbell, so add
NV_PGSP_QUEUE_TAIL, NV_PGSP_MSGQ_HEAD, and NV_PGSP_MSGQ_TAIL at the
queue-0 offsets.
Nova only uses queue 0, so the registers are scalars rather than
8-element arrays. A comment records the full hardware layout for future
multi-queue work.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/regs.rs | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/gpu/nova-core/gsp/regs.rs b/drivers/gpu/nova-core/gsp/regs.rs
index 9a48aa87e7fb..d2e40ed13123 100644
--- a/drivers/gpu/nova-core/gsp/regs.rs
+++ b/drivers/gpu/nova-core/gsp/regs.rs
@@ -5,6 +5,18 @@
use crate::regs::NV_PBUS_SW_SCRATCH;
// PGSP
+//
+// The msgq v2 wire protocol replaces the in-memory ring pointers with four
+// BAR0 registers per queue:
+//
+// NV_PGSP_QUEUE_HEAD[i] @ 0x00110c00 + i*8 (CPU TX write, doorbell)
+// NV_PGSP_QUEUE_TAIL[i] @ 0x00110c04 + i*8 (GSP TX read)
+// NV_PGSP_MSGQ_HEAD[i] @ 0x00110c80 + i*8 (GSP RX write)
+// NV_PGSP_MSGQ_TAIL[i] @ 0x00110c84 + i*8 (CPU RX read)
+//
+// Nova only uses queue 0, so the four registers are declared as single
+// scalars at the i=0 offsets. NV_PGSP_QUEUE_HEAD is also used as the v0
+// doorbell, which is why it predates the others.
register! {
pub(super) NV_PGSP_QUEUE_HEAD(u32) @ 0x00110c00 {
@@ -12,6 +24,24 @@
}
}
+register! {
+ pub(super) NV_PGSP_QUEUE_TAIL(u32) @ 0x00110c04 {
+ 31:0 address;
+ }
+}
+
+register! {
+ pub(super) NV_PGSP_MSGQ_HEAD(u32) @ 0x00110c80 {
+ 31:0 address;
+ }
+}
+
+register! {
+ pub(super) NV_PGSP_MSGQ_TAIL(u32) @ 0x00110c84 {
+ 31:0 address;
+ }
+}
+
// PBUS
register! {
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 12/27] gpu: nova-core: gsp: add msgq v2 internals
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (10 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 11/27] gpu: nova-core: regs: add msgq v2 BAR0 register declarations John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 3:52 ` [PATCH 13/27] gpu: nova-core: generalize allocate_command() for variable headers John Hubbard
` (15 subsequent siblings)
27 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
Msgq v2 moves the four ring pointers out of shared memory into BAR0
registers and treats them as monotonic counters, so head == tail
distinguishes empty from full and the ring uses every slot. The pointers
can also be out of step, because a GSP reset zeroes them while the ring
keeps its contents.
Add the v2 TX header and the v2 ring helpers in parallel to the v0 ones,
so the flip commit can swap call sites without writing new logic. Read
the queue as empty while the read pointer is ahead of the write pointer,
rather than taking the difference as a page count.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 140 ++++++++++++++++++++++++++++++
drivers/gpu/nova-core/gsp/fw.rs | 36 ++++++++
2 files changed, 176 insertions(+)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index d3afe3f9bb37..1d86d2927a94 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -467,6 +467,146 @@ fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
}
}
+// Msgq v2 internals.
+//
+// Msgq v2 keeps the four ring pointers in BAR0 registers as monotonic `u32`
+// counters that wrap on overflow. The slot index is `ptr % MSGQ_NUM_PAGES`
+// only at the point of use, and the ring uses every slot (no "leave one
+// empty" rule, since `head == tail` distinguishes empty from full).
+//
+// Register-to-role mapping for queue 0:
+//
+// CPU TX write/doorbell: NV_PGSP_QUEUE_HEAD
+// GSP TX read: NV_PGSP_QUEUE_TAIL
+// GSP RX write: NV_PGSP_MSGQ_HEAD
+// CPU RX read: NV_PGSP_MSGQ_TAIL
+//
+// A GSP reset zeroes all four counters while the in-memory ring keeps its
+// contents, so the two ends can be out of step. Between the reset and
+// GSP-RM writing its counter back the read pointer is ahead of the write
+// pointer, which `driver_read_area_v2` reports as an empty ring.
+//
+// TODO: suspend/resume reset-recovery is not implemented. A power cycle
+// loses the driver-side counters as well, so both ends have to be
+// re-established.
+#[expect(dead_code)]
+impl DmaGspMem {
+ fn gsp_write_ptr_v2(bar: Bar0<'_>) -> u32 {
+ *bar.read(regs::NV_PGSP_MSGQ_HEAD).address()
+ }
+
+ fn gsp_read_ptr_v2(bar: Bar0<'_>) -> u32 {
+ *bar.read(regs::NV_PGSP_QUEUE_TAIL).address()
+ }
+
+ fn cpu_read_ptr_v2(bar: Bar0<'_>) -> u32 {
+ *bar.read(regs::NV_PGSP_MSGQ_TAIL).address()
+ }
+
+ fn cpu_write_ptr_v2(bar: Bar0<'_>) -> u32 {
+ *bar.read(regs::NV_PGSP_QUEUE_HEAD).address()
+ }
+
+ fn advance_cpu_read_ptr_v2(bar: Bar0<'_>, count: u32) {
+ let new_rptr = Self::cpu_read_ptr_v2(bar).wrapping_add(count);
+
+ // Order all reads from the message data ahead of the read-pointer
+ // update so the GSP cannot recycle the slots while we are still
+ // looking at them.
+ fence(Ordering::SeqCst);
+
+ bar.write_reg(regs::NV_PGSP_MSGQ_TAIL::zeroed().with_address(new_rptr));
+ }
+
+ fn advance_cpu_write_ptr_v2(bar: Bar0<'_>, count: u32) {
+ let new_wptr = Self::cpu_write_ptr_v2(bar).wrapping_add(count);
+
+ // Order all writes to the message data ahead of the write-pointer
+ // update. Writing the head register doubles as the GSP doorbell.
+ fence(Ordering::SeqCst);
+
+ bar.write_reg(regs::NV_PGSP_QUEUE_HEAD::zeroed().with_address(new_wptr));
+ }
+
+ /// Returns the region of the CPU message queue that the driver is currently allowed to write
+ /// to.
+ ///
+ /// As the message queue is a circular buffer, the region may be discontiguous in memory. In
+ /// that case the second slice will have a non-zero length.
+ fn driver_write_area_v2(
+ &mut self,
+ bar: Bar0<'_>,
+ ) -> (&mut [[u8; GSP_PAGE_SIZE]], &mut [[u8; GSP_PAGE_SIZE]]) {
+ let raw_w = Self::cpu_write_ptr_v2(bar);
+ let raw_r = Self::gsp_read_ptr_v2(bar);
+
+ let used = raw_w.wrapping_sub(raw_r);
+ let avail = num::u32_as_usize(MSGQ_NUM_PAGES.saturating_sub(used));
+ let w_slot = num::u32_as_usize(raw_w % MSGQ_NUM_PAGES);
+
+ // Pointer to the first entry of the CPU message queue.
+ let data = ptr::project!(mut self.0.as_mut_ptr(), .cpuq.msgq.data[build: 0]);
+
+ // SAFETY:
+ // - `data` points to `MSGQ_NUM_PAGES` valid message queue entries.
+ // - We will only access the driver-owned part of the shared memory.
+ // - Per the safety statement of the function, no concurrent access will be performed.
+ let data =
+ unsafe { core::slice::from_raw_parts_mut(data, num::u32_as_usize(MSGQ_NUM_PAGES)) };
+ let (before_w, after_w) = data.split_at_mut(w_slot);
+
+ let in_after = avail.min(after_w.len());
+ let in_before = avail - in_after;
+ (&mut after_w[..in_after], &mut before_w[..in_before])
+ }
+
+ /// Returns the size, in bytes, of the region of the CPU message queue that the driver is
+ /// currently allowed to write to.
+ fn driver_write_area_size_v2(bar: Bar0<'_>) -> usize {
+ let used = Self::cpu_write_ptr_v2(bar).wrapping_sub(Self::gsp_read_ptr_v2(bar));
+ let slots = MSGQ_NUM_PAGES.saturating_sub(used);
+ num::u32_as_usize(slots) * GSP_PAGE_SIZE
+ }
+
+ /// Returns the region of the GSP message queue that the driver is currently allowed to read
+ /// from.
+ ///
+ /// As the message queue is a circular buffer, the region may be discontiguous in memory. In
+ /// that case the second slice will have a non-zero length.
+ fn driver_read_area_v2(
+ &self,
+ bar: Bar0<'_>,
+ ) -> (&[[u8; GSP_PAGE_SIZE]], &[[u8; GSP_PAGE_SIZE]]) {
+ let raw_w = Self::gsp_write_ptr_v2(bar);
+ let raw_r = Self::cpu_read_ptr_v2(bar);
+
+ // A difference wider than the ring means the GSP has been reset and has not yet written
+ // its own counter back, so the read pointer is momentarily ahead of it. Report nothing
+ // readable, which holds the callers in their poll until GSP-RM restores the real value.
+ let pending = raw_w.wrapping_sub(raw_r);
+ let avail = if pending > MSGQ_NUM_PAGES {
+ 0
+ } else {
+ num::u32_as_usize(pending)
+ };
+ let r_slot = num::u32_as_usize(raw_r % MSGQ_NUM_PAGES);
+
+ // Pointer to the first entry of the GSP message queue.
+ let data = ptr::project!(self.0.as_ptr(), .gspq.msgq.data[build: 0]);
+
+ // SAFETY:
+ // - `data` points to `MSGQ_NUM_PAGES` valid message queue entries.
+ // - We will only access the driver-owned part of the shared memory.
+ // - Per the safety statement of the function, no concurrent access will be performed.
+ let data = unsafe { core::slice::from_raw_parts(data, num::u32_as_usize(MSGQ_NUM_PAGES)) };
+ let (before_r, after_r) = data.split_at(r_slot);
+
+ let in_after = avail.min(after_r.len());
+ let in_before = avail - in_after;
+ (&after_r[..in_after], &before_r[..in_before])
+ }
+}
+
/// A command ready to be sent on the command queue.
///
/// This is the type returned by [`DmaGspMem::allocate_command`].
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index d10677e277c5..79a7820ce99f 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -758,6 +758,42 @@ pub(crate) fn set_write_ptr(this: CoherentView<'_, Self>, val: u32) {
// SAFETY: Padding is explicit and does not contain uninitialized data.
unsafe impl AsBytes for MsgqTxHeader {}
+/// TX header for setting up a message queue with the GSP, msgq v2 layout.
+///
+/// Same wire size as [`MsgqTxHeader`] (32 bytes) with a different field
+/// layout: the v0 `writePtr`, `flags`, and `rxHdrOff` fields are gone, the
+/// `version` `u32` becomes split `versionMajor`/`versionMinor` `u16`s, and
+/// the trailing space holds three reserved `u32`s.
+#[repr(transparent)]
+pub(crate) struct MsgqTxHeaderV2(r000_00::msgqTxHeader);
+
+#[expect(dead_code)]
+impl MsgqTxHeaderV2 {
+ /// Creates a new v2 TX queue header.
+ ///
+ /// # Arguments
+ ///
+ /// * `msgq_size` - Total size of the message queue structure, in bytes.
+ /// * `msg_size` - Size of each message slot, in bytes.
+ /// * `msg_count` - Number of message slots in the ring.
+ /// * `entry_off` - Byte offset from the start of the queue at which the
+ /// message data array begins.
+ pub(crate) fn new(msgq_size: u32, msg_size: u32, msg_count: u32, entry_off: u32) -> Self {
+ Self(r000_00::msgqTxHeader {
+ versionMajor: 2,
+ versionMinor: 0,
+ size: msgq_size,
+ msgSize: msg_size,
+ msgCount: msg_count,
+ entryOff: entry_off,
+ reserved: [0; 3],
+ })
+ }
+}
+
+// SAFETY: Padding is explicit and does not contain uninitialized data.
+unsafe impl AsBytes for MsgqTxHeaderV2 {}
+
/// RX header for setting up a message queue with the GSP.
#[repr(transparent)]
pub(crate) struct MsgqRxHeader(bindings::msgqRxHeader);
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 13/27] gpu: nova-core: generalize allocate_command() for variable headers
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (11 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 12/27] gpu: nova-core: gsp: add msgq v2 internals John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-20 12:47 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 14/27] gpu: nova-core: add GMC API message types John Hubbard
` (14 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The GMC command path uses a different message element header from the
VGPU-style RPC path, but the two share the queue, the space accounting,
and the wrapping rules. Make allocate_command() and GspCommand generic
over the header type, defaulting to GspMsgElement so the existing RPC
callers are unchanged.
No functional changes.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 1d86d2927a94..0dab3d279426 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -365,6 +365,8 @@ fn driver_write_area_size(&self) -> usize {
/// Allocates a region on the command queue that is large enough to send a command of `size`
/// bytes, waiting for space to become available based on the provided timeout.
///
+ /// The type parameter `H` selects the message element header, [`GspMsgElement`] for RM RPC.
+ ///
/// This returns a [`GspCommand`] ready to be written to by the caller.
///
/// # Errors
@@ -372,13 +374,17 @@ fn driver_write_area_size(&self) -> usize {
/// - `EMSGSIZE` if the command is larger than [`GSP_MSG_QUEUE_ELEMENT_SIZE_MAX`].
/// - `ETIMEDOUT` if space does not become available within the timeout.
/// - `EIO` if the command header is not properly aligned.
- fn allocate_command(&mut self, size: usize, timeout: Delta) -> Result<GspCommand<'_>> {
- if size_of::<GspMsgElement>() + size > GSP_MSG_QUEUE_ELEMENT_SIZE_MAX {
+ fn allocate_command<H: FromBytes + AsBytes>(
+ &mut self,
+ size: usize,
+ timeout: Delta,
+ ) -> Result<GspCommand<'_, H>> {
+ if size_of::<H>() + size > GSP_MSG_QUEUE_ELEMENT_SIZE_MAX {
return Err(EMSGSIZE);
}
read_poll_timeout(
|| Ok(self.driver_write_area_size()),
- |available_bytes| *available_bytes >= size_of::<GspMsgElement>() + size,
+ |available_bytes| *available_bytes >= size_of::<H>() + size,
Delta::from_micros(1),
timeout,
)?;
@@ -390,8 +396,8 @@ fn allocate_command(&mut self, size: usize, timeout: Delta) -> Result<GspCommand
(slice_1.as_flattened_mut(), slice_2.as_flattened_mut())
};
- // Extract area for the `GspMsgElement`.
- let (header, slice_1) = GspMsgElement::from_bytes_mut_prefix(slice_1).ok_or(EIO)?;
+ // Extract area for the message element header.
+ let (header, slice_1) = H::from_bytes_mut_prefix(slice_1).ok_or(EIO)?;
// Create the contents area.
let (slice_1, slice_2) = if slice_1.len() > size {
@@ -609,10 +615,12 @@ fn driver_read_area_v2(
/// A command ready to be sent on the command queue.
///
+/// The type parameter `H` is the message element header type, [`GspMsgElement`] for RM RPC.
+///
/// This is the type returned by [`DmaGspMem::allocate_command`].
-struct GspCommand<'a> {
+struct GspCommand<'a, H = GspMsgElement> {
// Writable reference to the header of the command.
- header: &'a mut GspMsgElement,
+ header: &'a mut H,
// Writable slices to the contents of the command. The second slice is zero unless the command
// loops over the command queue.
contents: (&'a mut [u8], &'a mut [u8]),
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 14/27] gpu: nova-core: add GMC API message types
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (12 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 13/27] gpu: nova-core: generalize allocate_command() for variable headers John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-20 10:53 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 15/27] gpu: nova-core: add GMC send path John Hubbard
` (13 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The new firmware carries GMC commands over the same MCTP/NVDM transport
as the RPC path, under NVDM type 0x26 and with its own fixed header in
place of rpc_message_header_v. Add that header as GmcApiHeader, matching
Open RM's GMCAPI_HEADER, and the queue element that wraps it.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/fw.rs | 127 ++++++++++++++++++++++++++++++++
drivers/gpu/nova-core/mctp.rs | 2 +
2 files changed, 129 insertions(+)
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 79a7820ce99f..050a6af8c0c7 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -50,6 +50,11 @@
cmdq::Cmdq, //
GSP_PAGE_SIZE,
},
+ mctp::{
+ MctpHeader,
+ NvdmHeader,
+ NvdmType, //
+ },
num::{
self,
FromSafeCast, //
@@ -947,6 +952,128 @@ unsafe impl AsBytes for GspMsgElement {}
// are valid.
unsafe impl FromBytes for GspMsgElement {}
+/// Magic value that opens every MCTP-framed queue element: `"MCTP"` in ASCII.
+const MCTP_MAGIC: u32 = 0x4D43_5450;
+
+/// GMC API message header.
+///
+/// Matches the `GMCAPI_HEADER` struct from Open RM. The `command` field carries
+/// flags in the high byte and a 24-bit command ID in the low bits. `size` is the
+/// payload size for both requests and responses, while the union at offset 16
+/// carries the maximum response size for requests and the status for responses.
+#[repr(C)]
+#[derive(Zeroable)]
+pub(crate) struct GmcApiHeader {
+ /// GMC command identifier with flags in the high byte.
+ pub(crate) command: u32,
+ /// Payload size in bytes.
+ pub(crate) size: u32,
+ /// Sequence number for matching requests to responses.
+ pub(crate) sequence: u64,
+ /// Request: maximum response size. Response: `NV_STATUS` code.
+ pub(crate) max_resp_or_status: u32,
+ reserved: [u32; 5],
+}
+
+static_assert!(size_of::<GmcApiHeader>() == 40);
+
+// SAFETY: All fields are integer types with no uninitialized padding bytes.
+unsafe impl AsBytes for GmcApiHeader {}
+
+// SAFETY: All fields are integer types for which all bit patterns are valid.
+unsafe impl FromBytes for GmcApiHeader {}
+
+/// Header for an unencrypted GMC API message in the GSP command queue.
+///
+/// The fields before `gmc` match `GSP_MSG_QUEUE_ELEMENT` through its
+/// `noEncryption` union member. `gmc` occupies the start of that member's
+/// flexible payload array, and the GMC command payload follows this structure.
+#[repr(C)]
+pub(crate) struct GspGmcMsgElement {
+ mctp_magic: u32,
+ mctp_payload_size: u32,
+ mctp_header: MctpHeader,
+ nvdm_header: NvdmHeader,
+ nvdm_payload_size: u32,
+ reserved: u32,
+ pub(crate) gmc: GmcApiHeader,
+}
+
+static_assert!(
+ core::mem::offset_of!(GspGmcMsgElement, mctp_magic)
+ == core::mem::offset_of!(r000_00::GSP_MSG_QUEUE_ELEMENT, mctpMagic)
+);
+static_assert!(
+ core::mem::offset_of!(GspGmcMsgElement, mctp_payload_size)
+ == core::mem::offset_of!(r000_00::GSP_MSG_QUEUE_ELEMENT, mctpPayloadSize)
+);
+static_assert!(
+ core::mem::offset_of!(GspGmcMsgElement, mctp_header)
+ == core::mem::offset_of!(r000_00::GSP_MSG_QUEUE_ELEMENT, mctpHeader)
+);
+static_assert!(
+ core::mem::offset_of!(GspGmcMsgElement, nvdm_header)
+ == core::mem::offset_of!(r000_00::GSP_MSG_QUEUE_ELEMENT, nvdmHeader)
+);
+
+#[expect(dead_code)]
+impl GspGmcMsgElement {
+ /// Creates a new GMC message element.
+ ///
+ /// `payload_size` is the size of the command payload following this header.
+ /// `command_id` is the GMC command identifier.
+ /// `sequence` is the sequence number for request/response matching.
+ /// `max_response_size` is the maximum expected response payload size.
+ pub(crate) fn init(
+ command_id: u32,
+ sequence: u64,
+ payload_size: usize,
+ max_response_size: u32,
+ ) -> impl Init<Self, Error> {
+ try_init!(GspGmcMsgElement {
+ mctp_magic: MCTP_MAGIC,
+ // Despite the name, this counts the element header as well as the payload.
+ mctp_payload_size: size_of::<Self>()
+ .checked_add(payload_size)
+ .ok_or(EOVERFLOW)?
+ .try_into()
+ .map_err(|_| EOVERFLOW)?,
+ mctp_header: MctpHeader::single_packet(),
+ nvdm_header: NvdmHeader::new(NvdmType::GmcApi),
+ nvdm_payload_size: size_of::<GmcApiHeader>()
+ .checked_add(payload_size)
+ .ok_or(EOVERFLOW)?
+ .try_into()
+ .map_err(|_| EOVERFLOW)?,
+ reserved: 0u32,
+ gmc: GmcApiHeader {
+ command: command_id,
+ size: payload_size.try_into().map_err(|_| EOVERFLOW)?,
+ sequence,
+ max_resp_or_status: max_response_size,
+ reserved: [0; 5],
+ },
+ })
+ }
+
+ /// Returns the total length of the message, transport and GMC headers included.
+ pub(crate) fn length(&self) -> usize {
+ num::u32_as_usize(self.mctp_payload_size)
+ }
+
+ /// Returns the number of elements (i.e. memory pages) used by this message.
+ pub(crate) fn element_count(&self) -> u32 {
+ self.mctp_payload_size
+ .div_ceil(num::usize_into_u32::<GSP_PAGE_SIZE>())
+ }
+}
+
+// SAFETY: All fields are integer types with no uninitialized padding bytes.
+unsafe impl AsBytes for GspGmcMsgElement {}
+
+// SAFETY: All fields are integer types for which all bit patterns are valid.
+unsafe impl FromBytes for GspGmcMsgElement {}
+
/// Arguments for GSP startup.
#[repr(transparent)]
#[derive(Zeroable)]
diff --git a/drivers/gpu/nova-core/mctp.rs b/drivers/gpu/nova-core/mctp.rs
index 03546213da28..0ae88bef2a05 100644
--- a/drivers/gpu/nova-core/mctp.rs
+++ b/drivers/gpu/nova-core/mctp.rs
@@ -28,6 +28,8 @@ pub(crate) enum NvdmType with TryFrom<Bounded<u32, 8>> {
Cot = 0x14,
/// FSP command response.
FspResponse = 0x15,
+ /// GMC API message (GSP command queue).
+ GmcApi = 0x26,
}
}
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 15/27] gpu: nova-core: add GMC send path
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (13 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 14/27] gpu: nova-core: add GMC API message types John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-20 11:12 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 16/27] gpu: nova-core: add GMC transport receive path John Hubbard
` (12 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The r000 GSP-RM boot protocol requires the driver to send GSP_INIT as a
GMC command. GMC and RPC commands share the command queue and sequence
counter. GMC queue elements carry a GMC API header and no checksum.
Add the GMC send path needed for GSP_INIT.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 59 +++++++++++++++++++++++++++++++
drivers/gpu/nova-core/gsp/fw.rs | 1 -
2 files changed, 59 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 0dab3d279426..88b143c6a7b7 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -52,6 +52,7 @@
driver::Bar0,
gsp::{
fw::{
+ GspGmcMsgElement,
GspMsgElement,
MsgFunction,
MsgqRxHeader,
@@ -963,6 +964,64 @@ fn send_command<M>(&mut self, bar: Bar0<'_>, command: M) -> Result<u32>
Ok(rpc_seq)
}
+ /// Sends a GMC API command to the GSP.
+ ///
+ /// `command_id` is the GMC command identifier (from `GMCAPI_COMMANDS`).
+ /// `payload` is the command-specific data following the [`super::fw::GmcApiHeader`].
+ /// `max_response_size` is the maximum expected response payload size.
+ ///
+ /// The command carries the next RPC sequence number, which the GSP echoes in its response.
+ /// The number is consumed whether or not the send succeeds.
+ ///
+ /// # Errors
+ ///
+ /// - `EMSGSIZE` if the command exceeds the maximum queue element size.
+ /// - `ETIMEDOUT` if space does not become available within the timeout.
+ /// - `EIO` if the command header is not properly aligned.
+ #[expect(dead_code)]
+ fn send_gmc(
+ &mut self,
+ bar: Bar0<'_>,
+ command_id: u32,
+ payload: &[u8],
+ max_response_size: u32,
+ ) -> Result {
+ let rpc_seq = self.rpc_seq;
+ self.rpc_seq = self.rpc_seq.wrapping_add(1);
+
+ let dst = self
+ .gsp_mem
+ .allocate_command::<GspGmcMsgElement>(payload.len(), Self::ALLOCATE_TIMEOUT)?;
+
+ let msg_element = GspGmcMsgElement::init(
+ command_id,
+ u64::from(rpc_seq),
+ payload.len(),
+ max_response_size,
+ );
+ // SAFETY: `dst.header` points to a valid, writable `GspGmcMsgElement` region.
+ unsafe {
+ msg_element.__init(core::ptr::from_mut(dst.header))?;
+ }
+
+ SBufferIter::new_writer([&mut dst.contents.0[..], &mut dst.contents.1[..]])
+ .write_all(payload)?;
+
+ dev_dbg!(
+ &self.dev,
+ "GSP GMC: send: seq# {}, command_id=0x{:x}, length=0x{:x}\n",
+ rpc_seq,
+ command_id,
+ dst.header.length(),
+ );
+
+ let elem_count = dst.header.element_count();
+ self.gsp_mem.advance_cpu_write_ptr(elem_count);
+ Cmdq::notify_gsp(bar);
+
+ Ok(())
+ }
+
/// Wait for a message to become available on the message queue.
///
/// This works purely at the transport layer and does not interpret or validate the message
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 050a6af8c0c7..56f255a3d49c 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -1016,7 +1016,6 @@ pub(crate) struct GspGmcMsgElement {
== core::mem::offset_of!(r000_00::GSP_MSG_QUEUE_ELEMENT, nvdmHeader)
);
-#[expect(dead_code)]
impl GspGmcMsgElement {
/// Creates a new GMC message element.
///
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 16/27] gpu: nova-core: add GMC transport receive path
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (14 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 15/27] gpu: nova-core: add GMC send path John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-20 12:00 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 17/27] gpu: nova-core: gsp: add GMC dispatch on receive John Hubbard
` (11 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
GSP-RM posts GMC and RPC messages on the same message queue. Both use
the same MCTP and NVDM transport headers, but RPC messages continue with
rpc_message_header_v and GMC messages continue with GmcApiHeader.
Existing RPC receive code cannot parse the GMC layout.
Add a GMC receive path that validates the MCTP magic and rejects
payloads whose advertised length exceeds the available queue contents.
On a framing or length failure, poison the queue because the driver
cannot safely advance the read pointer without a trusted length.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 83 ++++++++++++++++++++++++++++++-
drivers/gpu/nova-core/gsp/fw.rs | 10 ++++
2 files changed, 92 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 88b143c6a7b7..fba94153e744 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -629,7 +629,7 @@ struct GspCommand<'a, H = GspMsgElement> {
/// A message ready to be processed from the message queue.
///
-/// This is the type returned by [`Cmdq::wait_for_msg`].
+/// This is the type returned by [`CmdqInner::wait_for_msg`].
struct GspMessage<'a> {
// Reference to the header of the message.
header: &'a GspMsgElement,
@@ -638,6 +638,18 @@ struct GspMessage<'a> {
contents: (&'a [u8], &'a [u8]),
}
+/// A GMC message ready to be processed from the message queue.
+///
+/// This is the type returned by [`CmdqInner::wait_for_gmc_msg`].
+#[expect(dead_code)]
+struct GmcMessage<'a> {
+ // Reference to the header of the message.
+ header: &'a GspGmcMsgElement,
+ // Slices of the payload following the `GmcApiHeader`. The second slice is empty unless the
+ // payload wraps around the end of the message queue.
+ contents: (&'a [u8], &'a [u8]),
+}
+
/// GSP command queue.
///
/// Provides the ability to send commands and receive messages from the GSP using a shared memory
@@ -1285,4 +1297,73 @@ fn drain(&mut self) -> Result {
Ok(())
}
+
+ /// Wait for a GMC message to become available on the message queue.
+ ///
+ /// This is the GMC counterpart to [`Self::wait_for_msg`] and reads the same queue. Like that
+ /// method it works purely at the transport layer, validating the MCTP framing and the
+ /// advertised length and nothing else.
+ ///
+ /// A [`GspGmcMsgElement`] and a [`GspMsgElement`] share every field through `nvdm_header`,
+ /// so a caller that may see either must check the NVDM type before reading `gmc`. Both
+ /// layouts put the element length in the same place, so the caller can advance the read
+ /// pointer past a returned message either way.
+ ///
+ /// # Errors
+ ///
+ /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available.
+ /// - `EIO` if the framing is invalid, or the queue was already poisoned by an earlier such
+ /// failure. Either failure poisons the queue, so recovery requires a reset.
+ #[expect(dead_code)]
+ fn wait_for_gmc_msg(&self, timeout: Delta) -> Result<GmcMessage<'_>> {
+ if self.poisoned.get() {
+ return Err(EIO);
+ }
+
+ let (slice_1, slice_2) = read_poll_timeout(
+ || Ok(self.gsp_mem.driver_read_area()),
+ |driver_area| !driver_area.0.is_empty(),
+ Delta::from_millis(1),
+ timeout,
+ )
+ .map(|(slice_1, slice_2)| (slice_1.as_flattened(), slice_2.as_flattened()))?;
+
+ let Some((header, slice_1)) = GspGmcMsgElement::from_bytes_prefix(slice_1) else {
+ self.poisoned.set(true);
+ return Err(EIO);
+ };
+
+ // Checked before any length field is read, since a bad magic leaves them untrusted.
+ if !header.has_valid_magic() {
+ dev_err!(&self.dev, "GSP GMC: receive: bad MCTP magic\n");
+ self.poisoned.set(true);
+ return Err(EIO);
+ }
+
+ let payload_length = header.payload_length();
+
+ // Check that the driver read area is large enough for the message.
+ if slice_1.len() + slice_2.len() < payload_length {
+ self.poisoned.set(true);
+ return Err(EIO);
+ }
+
+ // Cut the message slices down to the actual length of the message.
+ let (slice_1, slice_2) = if slice_1.len() > payload_length {
+ // PANIC: we checked above that `slice_1` is at least as long as `payload_length`.
+ (slice_1.split_at(payload_length).0, &slice_2[0..0])
+ } else {
+ (
+ slice_1,
+ // PANIC: we checked above that `slice_1.len() + slice_2.len()` is at least as
+ // large as `payload_length`.
+ slice_2.split_at(payload_length - slice_1.len()).0,
+ )
+ };
+
+ Ok(GmcMessage {
+ header,
+ contents: (slice_1, slice_2),
+ })
+ }
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 56f255a3d49c..9e6b5ec6aadb 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -1055,11 +1055,21 @@ pub(crate) fn init(
})
}
+ /// Returns the length of the response payload (data after the [`GmcApiHeader`]).
+ pub(crate) fn payload_length(&self) -> usize {
+ num::u32_as_usize(self.nvdm_payload_size).saturating_sub(size_of::<GmcApiHeader>())
+ }
+
/// Returns the total length of the message, transport and GMC headers included.
pub(crate) fn length(&self) -> usize {
num::u32_as_usize(self.mctp_payload_size)
}
+ /// Returns `true` if the MCTP magic field contains the expected value.
+ pub(crate) fn has_valid_magic(&self) -> bool {
+ self.mctp_magic == MCTP_MAGIC
+ }
+
/// Returns the number of elements (i.e. memory pages) used by this message.
pub(crate) fn element_count(&self) -> u32 {
self.mctp_payload_size
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 17/27] gpu: nova-core: gsp: add GMC dispatch on receive
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (15 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 16/27] gpu: nova-core: add GMC transport receive path John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-20 12:30 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 18/27] gpu: nova-core: separate the generic falcon bootloader from FWSEC John Hubbard
` (10 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The r000 boot path delivers the load-and-execute steps as GMC events
keyed by command id, where the RPC path keys on a function code, so the
boot loop needs its own dispatch.
Add a GMC receive that dispatches on the command id. A GMC element and
an RPC element share every field through the NVDM header, so this checks
the NVDM type before reading the GMC header and drops an element of the
other kind rather than handing the handler a command id decoded from an
RPC header. An element nobody claims yields Ok(None), and the read
pointer advances on every path.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 69 ++++++++++++++++++++++++++++++-
drivers/gpu/nova-core/gsp/fw.rs | 20 +++++++++
2 files changed, 87 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index fba94153e744..7da61bc9ad92 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -641,7 +641,6 @@ struct GspMessage<'a> {
/// A GMC message ready to be processed from the message queue.
///
/// This is the type returned by [`CmdqInner::wait_for_gmc_msg`].
-#[expect(dead_code)]
struct GmcMessage<'a> {
// Reference to the header of the message.
header: &'a GspGmcMsgElement,
@@ -791,6 +790,22 @@ fn receive_msg<M: MessageFromGsp>(&self, timeout: Delta) -> Result<M>
self.inner.lock().receive_msg(timeout, None)
}
+ /// Receives one GMC event from the GSP and passes its command id and raw payload slices to
+ /// `handler`.
+ ///
+ /// This method may sleep while waiting. The [`CmdqInner`] mutex stays locked across the wait
+ /// and across the `handler` call, so `handler` must not call back into this [`Cmdq`].
+ ///
+ /// See [`CmdqInner::receive_gmc_and_dispatch`] for return values, queue state, and errors.
+ #[expect(dead_code)]
+ pub(crate) fn receive_gmc_and_dispatch<R>(
+ &self,
+ timeout: Delta,
+ handler: impl FnOnce(u32, &[u8], &[u8]) -> Option<R>,
+ ) -> Result<Option<R>> {
+ self.inner.lock().receive_gmc_and_dispatch(timeout, handler)
+ }
+
/// Waits for an unsolicited GSP event of type `M`, dispatching any other event that arrives
/// first.
///
@@ -1314,7 +1329,6 @@ fn drain(&mut self) -> Result {
/// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available.
/// - `EIO` if the framing is invalid, or the queue was already poisoned by an earlier such
/// failure. Either failure poisons the queue, so recovery requires a reset.
- #[expect(dead_code)]
fn wait_for_gmc_msg(&self, timeout: Delta) -> Result<GmcMessage<'_>> {
if self.poisoned.get() {
return Err(EIO);
@@ -1366,4 +1380,55 @@ fn wait_for_gmc_msg(&self, timeout: Delta) -> Result<GmcMessage<'_>> {
contents: (slice_1, slice_2),
})
}
+
+ /// Receive the next GMC event from the GSP and dispatch it through a handler.
+ ///
+ /// The handler receives the GMC command id and the raw payload slices that follow the
+ /// [`super::fw::GmcApiHeader`] (two slices because the circular buffer may wrap). It returns
+ /// `None` for an event it does not handle.
+ ///
+ /// Where [`Self::receive_msg`] keys on [`MsgFunction`], this keys on the GMC command id,
+ /// which is the form the r000 firmware uses for boot events.
+ ///
+ /// Returns `Ok(None)` when nothing claimed the element, either because it is not a GMC
+ /// element or because the handler declined it. The read pointer is advanced on every path.
+ ///
+ /// # Errors
+ ///
+ /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available.
+ /// - `EIO` if the queue is poisoned or the element fails framing validation (see
+ /// [`Self::wait_for_gmc_msg`]).
+ fn receive_gmc_and_dispatch<R>(
+ &mut self,
+ timeout: Delta,
+ handler: impl FnOnce(u32, &[u8], &[u8]) -> Option<R>,
+ ) -> Result<Option<R>> {
+ let message = self.wait_for_gmc_msg(timeout)?;
+ let header = message.header;
+ let length = header.length();
+
+ // The RPC and GMC elements share every field through `nvdm_header`, so `gmc` holds an
+ // RPC header rather than a GMC one unless the NVDM type says otherwise.
+ let result = if header.is_gmc_api() {
+ let command_id = header.gmc.command_id();
+
+ dev_dbg!(
+ &self.dev,
+ "GSP GMC: event: seq# {}, command_id=0x{:x}, length=0x{:x}\n",
+ header.gmc.sequence,
+ command_id,
+ length,
+ );
+
+ handler(command_id, message.contents.0, message.contents.1)
+ } else {
+ dev_warn!(&self.dev, "GSP GMC: dropping non-GMC queue element\n");
+ None
+ };
+
+ self.gsp_mem
+ .advance_cpu_read_ptr(u32::try_from(length.div_ceil(GSP_PAGE_SIZE))?);
+
+ Ok(result)
+ }
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 9e6b5ec6aadb..83f7d2042aa1 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -975,8 +975,19 @@ pub(crate) struct GmcApiHeader {
reserved: [u32; 5],
}
+/// Command identifier bits of [`GmcApiHeader::command`], matching Open RM's
+/// `GMCAPI_HEADER_COMMAND_ID_MASK`. The remaining byte carries flags.
+const GMCAPI_COMMAND_ID_MASK: u32 = 0x00ff_ffff;
+
static_assert!(size_of::<GmcApiHeader>() == 40);
+impl GmcApiHeader {
+ /// Returns the command identifier, without the flag byte.
+ pub(crate) fn command_id(&self) -> u32 {
+ self.command & GMCAPI_COMMAND_ID_MASK
+ }
+}
+
// SAFETY: All fields are integer types with no uninitialized padding bytes.
unsafe impl AsBytes for GmcApiHeader {}
@@ -1070,6 +1081,15 @@ pub(crate) fn has_valid_magic(&self) -> bool {
self.mctp_magic == MCTP_MAGIC
}
+ /// Returns `true` if the NVDM header routes this element to the GSP's GMC dispatch.
+ ///
+ /// A [`GspMsgElement`] and a [`GspGmcMsgElement`] share every field through `nvdm_header`,
+ /// so the NVDM type is what distinguishes the two on the queue, and `gmc` holds an RPC
+ /// header rather than a GMC one when this returns `false`.
+ pub(crate) fn is_gmc_api(&self) -> bool {
+ self.nvdm_header.validate(NvdmType::GmcApi)
+ }
+
/// Returns the number of elements (i.e. memory pages) used by this message.
pub(crate) fn element_count(&self) -> u32 {
self.mctp_payload_size
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 18/27] gpu: nova-core: separate the generic falcon bootloader from FWSEC
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (16 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 17/27] gpu: nova-core: gsp: add GMC dispatch on receive John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-20 13:02 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 19/27] gpu: nova-core: handle the r000 load-and-execute HS binary event John Hubbard
` (9 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The r000 GSP boot protocol sends a load-and-execute event for the
generic falcon bootloader.
Nova-core stored the image and its load parameters in the FWSEC firmware
wrapper, which kept the r000 boot path from loading it independently.
Separate the bootloader image and load parameters from the FWSEC
wrapper. Place the bootloader at the end of the target falcon's
HWCFG-reported IMEM rather than below a fixed 64 KiB ceiling, so its
placement remains within bounds on falcons with less IMEM.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 15 +++
drivers/gpu/nova-core/firmware.rs | 1 +
.../nova-core/firmware/fwsec/bootloader.rs | 64 ++---------
.../gpu/nova-core/firmware/gen_bootloader.rs | 102 ++++++++++++++++++
drivers/gpu/nova-core/gsp/hal/tu102.rs | 6 +-
drivers/gpu/nova-core/regs.rs | 5 +
6 files changed, 135 insertions(+), 58 deletions(-)
create mode 100644 drivers/gpu/nova-core/firmware/gen_bootloader.rs
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 20a288050c37..b8013d2eb582 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -387,6 +387,21 @@ pub(crate) fn new(
})
}
+ /// Returns the size of this falcon's IMEM, in bytes.
+ ///
+ /// `NV_PFALCON_FALCON_IMEMC` addresses IMEM with a 16-bit byte offset, so the result never
+ /// exceeds 64KiB.
+ pub(crate) fn imem_size(&self) -> usize {
+ let blocks = usize::from_safe_cast(
+ *self
+ .bar
+ .read(regs::NV_PFALCON_FALCON_HWCFG::of::<E>())
+ .imem_size(),
+ );
+
+ blocks * MEM_BLOCK_ALIGNMENT
+ }
+
/// Resets DMA-related registers.
pub(crate) fn dma_reset(&self) {
self.bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| {
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index e0befe84aa3e..9ff764930bcd 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -26,6 +26,7 @@
pub(crate) mod booter;
pub(crate) mod fsp;
pub(crate) mod fwsec;
+pub(crate) mod gen_bootloader;
pub(crate) mod gsp;
pub(crate) mod radix3;
pub(crate) mod riscv;
diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
index ec4d92317a93..6670d17b4eeb 100644
--- a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
@@ -14,18 +14,12 @@
dma::Coherent,
io::{register::WithBase, Io},
prelude::*,
- ptr::{
- Alignable,
- Alignment, //
- },
- sizes,
transmute::AsBytes,
};
use crate::{
driver::Bar0,
falcon::{
- self,
gsp::Gsp,
Falcon,
FalconBromParams,
@@ -39,10 +33,7 @@
},
firmware::{
fwsec::FwsecFirmware,
- tlv::{
- request_tlv, //
- Tlv,
- },
+ gen_bootloader::GenericBootloader, //
},
gpu::Chipset,
num::FromSafeCast, //
@@ -101,16 +92,12 @@ unsafe impl AsBytes for BootloaderDmemDescV2 {}
pub(crate) struct FwsecFirmwareWithBl {
/// DMA object the bootloader will copy the firmware from.
_firmware_dma: Coherent<[u8]>,
- /// Code of the bootloader to be loaded into non-secure IMEM.
- ucode: KVec<u8>,
+ /// Bootloader that performs the load.
+ bootloader: GenericBootloader,
/// Descriptor to be loaded into DMEM for the bootloader to read.
dmem_desc: BootloaderDmemDescV2,
- /// Range-validated start offset of the firmware code in IMEM.
- imem_dst_start: u16,
/// BROM parameters of the loaded firmware.
brom_params: FalconBromParams,
- /// Range-validated `desc.start_tag`.
- start_tag: u16,
}
impl FwsecFirmwareWithBl {
@@ -120,29 +107,9 @@ pub(crate) fn new(
firmware: FwsecFirmware,
dev: &Device<device::Bound>,
chipset: Chipset,
+ falcon: &Falcon<'_, Gsp>,
) -> Result<Self> {
- let fw = request_tlv(dev, chipset, "gen_bootloader")?;
- let tlv = Tlv::new(fw.data())?;
- dev_dbg!(
- dev,
- "loaded generic bootloader firmware v{}\n",
- tlv.get_string(b"VERS")?
- );
-
- let ucode = {
- let blob = tlv.get_bytes(b"BLOB")?;
- let code_size = usize::from_safe_cast(tlv.get_u32(b"CDSZ")?);
- let code = blob.get(..code_size).ok_or(EINVAL)?;
- let aligned_code_size = code_size
- .align_up(Alignment::new::<{ falcon::MEM_BLOCK_ALIGNMENT }>())
- .ok_or(EINVAL)?;
-
- let mut ucode = KVec::with_capacity(aligned_code_size, GFP_KERNEL)?;
- ucode.extend_from_slice(code, GFP_KERNEL)?;
- ucode.resize(aligned_code_size, 0, GFP_KERNEL)?;
-
- ucode
- };
+ let bootloader = GenericBootloader::new(dev, chipset, falcon)?;
// `BootloaderDmemDescV2` expects the source to be a mirror image of the destination and
// uses the same offset parameter for both.
@@ -213,21 +180,11 @@ pub(crate) fn new(
}
};
- // The bootloader's code must be loaded in the area right below the first 64K of IMEM.
- const BOOTLOADER_LOAD_CEILING: usize = sizes::SZ_64K;
- let imem_dst_start = BOOTLOADER_LOAD_CEILING
- .checked_sub(ucode.len())
- .ok_or(EOVERFLOW)?;
-
- let start_tag = u16::try_from(tlv.get_u32(b"STRT")?)?;
-
Ok(Self {
_firmware_dma: firmware_dma,
- ucode,
+ bootloader,
dmem_desc,
brom_params: firmware.brom_params(),
- imem_dst_start: u16::try_from(imem_dst_start)?,
- start_tag,
})
}
@@ -282,7 +239,7 @@ fn brom_params(&self) -> FalconBromParams {
fn boot_addr(&self) -> u32 {
// On V2 platforms, the boot address is extracted from the generic bootloader, because the
// gbl is what actually copies FWSEC into memory, so that is what needs to be booted.
- u32::from(self.start_tag) << 8
+ self.bootloader.boot_addr()
}
}
@@ -292,12 +249,7 @@ fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
}
fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
- Some(FalconPioImemLoadTarget {
- data: self.ucode.as_ref(),
- dst_start: self.imem_dst_start,
- secure: false,
- start_tag: self.start_tag,
- })
+ Some(self.bootloader.imem_load_params())
}
fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> {
diff --git a/drivers/gpu/nova-core/firmware/gen_bootloader.rs b/drivers/gpu/nova-core/firmware/gen_bootloader.rs
new file mode 100644
index 000000000000..f949223af2d0
--- /dev/null
+++ b/drivers/gpu/nova-core/firmware/gen_bootloader.rs
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! The generic falcon bootloader.
+//!
+//! A small program loaded into falcon IMEM using PIO, which then DMAs a larger image into IMEM and
+//! DMEM from a descriptor the driver leaves in DMEM at offset 0. Open RM's
+//! `ksec2GetGenericBlUcode` supplies the same image for both SEC2 and GSP.
+
+use kernel::{
+ device,
+ prelude::*,
+ ptr::{
+ Alignable,
+ Alignment, //
+ },
+};
+
+use crate::{
+ falcon::{
+ self,
+ Falcon,
+ FalconEngine,
+ FalconPioImemLoadTarget, //
+ },
+ firmware::tlv::{
+ request_tlv, //
+ Tlv,
+ },
+ gpu::Chipset,
+ num::FromSafeCast, //
+};
+
+/// The generic falcon bootloader image and the IMEM placement it was loaded for.
+pub(crate) struct GenericBootloader {
+ /// Bootloader code, zero-padded to a whole number of falcon memory blocks.
+ ucode: KVec<u8>,
+ /// Byte offset in IMEM the code is loaded at.
+ imem_dst_start: u16,
+ /// Tag the first code block is loaded under.
+ start_tag: u16,
+}
+
+impl GenericBootloader {
+ /// Loads the generic bootloader image for `chipset`, placed in the last blocks of `falcon`'s
+ /// IMEM so the image it goes on to load has the rest to itself.
+ ///
+ /// # Errors
+ ///
+ /// - `EINVAL` if a required TLV field is absent or the image does not fit in IMEM.
+ /// - `ENOMEM` if the padded copy of the code cannot be allocated.
+ pub(crate) fn new<E: FalconEngine + 'static>(
+ dev: &device::Device<device::Bound>,
+ chipset: Chipset,
+ falcon: &Falcon<'_, E>,
+ ) -> Result<Self> {
+ let fw = request_tlv(dev, chipset, "gen_bootloader")?;
+ let tlv = Tlv::new(fw.data())?;
+ dev_dbg!(
+ dev,
+ "loaded generic bootloader firmware v{}\n",
+ tlv.get_string(b"VERS")?
+ );
+
+ let ucode = {
+ let blob = tlv.get_bytes(b"BLOB")?;
+ let code_size = usize::from_safe_cast(tlv.get_u32(b"CDSZ")?);
+ let code = blob.get(..code_size).ok_or(EINVAL)?;
+ let aligned_code_size = code_size
+ .align_up(Alignment::new::<{ falcon::MEM_BLOCK_ALIGNMENT }>())
+ .ok_or(EINVAL)?;
+
+ let mut ucode = KVec::with_capacity(aligned_code_size, GFP_KERNEL)?;
+ ucode.extend_from_slice(code, GFP_KERNEL)?;
+ ucode.resize(aligned_code_size, 0, GFP_KERNEL)?;
+
+ ucode
+ };
+
+ let imem_dst_start = falcon.imem_size().checked_sub(ucode.len()).ok_or(EINVAL)?;
+
+ Ok(Self {
+ ucode,
+ imem_dst_start: u16::try_from(imem_dst_start)?,
+ start_tag: u16::try_from(tlv.get_u32(b"STRT")?)?,
+ })
+ }
+
+ /// Returns the address the falcon must boot from to run this bootloader.
+ pub(crate) fn boot_addr(&self) -> u32 {
+ u32::from(self.start_tag) << 8
+ }
+
+ /// Returns the PIO parameters that place this bootloader in non-secure IMEM.
+ pub(crate) fn imem_load_params(&self) -> FalconPioImemLoadTarget<'_> {
+ FalconPioImemLoadTarget {
+ data: self.ucode.as_ref(),
+ dst_start: self.imem_dst_start,
+ secure: false,
+ start_tag: self.start_tag,
+ }
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs
index a5c0ca355493..68a48c882c0f 100644
--- a/drivers/gpu/nova-core/gsp/hal/tu102.rs
+++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs
@@ -166,7 +166,7 @@ fn run_fwsec_frts(
)?;
if self.needs_fwsec_bootloader {
- let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?;
+ let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset, falcon)?;
// Load and run the bootloader, which will load FWSEC-FRTS and run it.
fwsec_frts_bl.run(dev, falcon, bar)?;
} else {
@@ -224,7 +224,9 @@ fn build_unload_bundle(
// Load the FWSEC SB firmware, as well as its bootloader if required.
let fwsec_sb = FwsecFirmware::new(dev, gsp_falcon, bios, FwsecCommand::Sb)?;
let fwsec_sb = if self.needs_fwsec_bootloader {
- FwsecUnloadFirmware::WithBl(FwsecFirmwareWithBl::new(fwsec_sb, dev, chipset)?)
+ FwsecUnloadFirmware::WithBl(FwsecFirmwareWithBl::new(
+ fwsec_sb, dev, chipset, gsp_falcon,
+ )?)
} else {
FwsecUnloadFirmware::WithoutBl(fwsec_sb)
};
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 5d265b5788e3..5501c36a56af 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -247,6 +247,11 @@ pub(crate) fn usable_fb_size(self) -> u64 {
31:0 value => u32;
}
+ pub(crate) NV_PFALCON_FALCON_HWCFG(u32) @ PFalconBase + 0x00000108 {
+ /// Size of this falcon's IMEM, in blocks of [`crate::falcon::MEM_BLOCK_ALIGNMENT`] bytes.
+ 8:0 imem_size;
+ }
+
pub(crate) NV_PFALCON_FALCON_DMACTL(u32) @ PFalconBase + 0x0000010c {
7:7 secure_stat => bool;
6:3 dmaq_num;
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 19/27] gpu: nova-core: handle the r000 load-and-execute HS binary event
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (17 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 18/27] gpu: nova-core: separate the generic falcon bootloader from FWSEC John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 3:52 ` [PATCH 20/27] gpu: nova-core: handle the r000 load-and-execute bootloader event John Hubbard
` (8 subsequent siblings)
27 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The r000 GSP boot protocol asks the driver to run a high-security binary
on the GSP falcon, and sends the framebuffer addresses of its code and
data in the event payload.
Nova-core has no handler for that event. It also has no way to restart
GSP-RM afterwards, which every load-and-execute event needs.
Add the handler. It DMAs the image into falcon memory, programs the BROM
registers that make the falcon verify the PKC signature, runs the binary,
and resumes GSP-RM. Write FLCN_ERR_BINARY_NOT_STARTED to MAILBOX0 before
starting, which Open RM does so a binary that never runs is
distinguishable from one that succeeded, and which also clears the
suspend bit the next event polls.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 7 +-
drivers/gpu/nova-core/gsp/boot.rs | 229 +++++++++++++++++++++++++++++-
drivers/gpu/nova-core/regs.rs | 1 +
3 files changed, 232 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index b8013d2eb582..87cb6cf4f79d 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -140,7 +140,6 @@ pub(crate) enum FalconMem {
}
/// Source offset of a raw falcon DMA transfer, added to the DMA base address.
-#[expect(dead_code)]
#[derive(Copy, Clone)]
pub(crate) enum FalconDmaSrcOffset {
/// Byte offset from the DMA base address.
@@ -636,7 +635,6 @@ fn dma_wr(
/// 256 bytes, or if a [`FalconDmaSrcOffset::DmemVa`] source offset is paired with an IMEM
/// target.
/// - `ERANGE` if `src_addr` does not fit the `DMATRFBASE` register pair.
- #[expect(dead_code)]
pub(crate) fn raw_dma_transfer(
&self,
ctx_dma: u8,
@@ -782,7 +780,10 @@ pub(crate) fn wait_till_halted(&self) -> Result<()> {
///
/// The RISC-V GSP signals suspension by setting bit 31 (`0x8000_0000`) in `MAILBOX0`, rather
/// than through `CPUCTL.halted`.
- #[expect(dead_code)]
+ ///
+ /// Nothing else clears that bit, so the caller must write `MAILBOX0` before starting the
+ /// falcon CPU. A caller that runs this twice without an intervening write sees the first
+ /// suspension both times.
pub(crate) fn wait_for_processor_suspend(&self) -> Result<()> {
const INTERRUPT_PROCESSOR_SUSPENDED: u32 = 0x8000_0000;
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index a64313dca0a1..ae2aca5d935a 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -3,9 +3,15 @@
use kernel::{
bits,
- io::poll::read_poll_timeout,
+ device,
+ io::{
+ poll::read_poll_timeout,
+ register::WithBase,
+ Io, //
+ },
prelude::*,
time::Delta,
+ transmute::FromBytes,
types::ScopeGuard, //
};
@@ -13,13 +19,20 @@
driver::Bar0,
falcon::{
gsp::Gsp,
- Falcon, //
+ sec2::Sec2,
+ Falcon,
+ FalconDmaSrcOffset,
+ FalconFbifMemType,
+ FalconFbifTarget,
+ FalconMem,
+ FalconModSelAlgo, //
},
firmware::gsp::GspFirmware,
gsp::{
cmdq::Cmdq,
commands, //
},
+ regs, //
};
impl super::Gsp {
@@ -86,6 +99,182 @@ pub(crate) fn boot(
Ok(unload_guard.dismiss().1)
}
+ /// Restart GSP-RM once a load-and-execute image has run to completion.
+ ///
+ /// Resets the GSP falcon into RISC-V, hands it the libos boot arguments address through its
+ /// mailboxes, and starts SEC2, which is what brings GSP-RM back up. Open RM calls this
+ /// `kgspExecuteCoreResume`.
+ ///
+ /// # Errors
+ ///
+ /// - `EIO` if SEC2 reports a failure, or if the GSP is not running RISC-V afterwards.
+ /// - `ETIMEDOUT` if SEC2 does not complete the reload in time.
+ fn core_resume(
+ gsp_falcon: &Falcon<'_, Gsp>,
+ sec2_falcon: &Falcon<'_, Sec2>,
+ dev: &device::Device,
+ bootloader_app_version: u32,
+ libos_dma_handle: u64,
+ ) -> Result {
+ gsp_falcon.reset()?;
+
+ gsp_falcon.write_mailboxes(
+ Some(libos_dma_handle as u32),
+ Some((libos_dma_handle >> 32) as u32),
+ );
+
+ sec2_falcon.start()?;
+
+ gsp_falcon
+ .check_reload_completed(Delta::from_secs(2))
+ .inspect_err(|_| {
+ let mbox0 = sec2_falcon.read_mailbox0();
+ dev_err!(
+ dev,
+ "Timeout waiting for SEC2 to resume GSP-RM (SEC2 mbox0={:#x})\n",
+ mbox0
+ );
+ })?;
+
+ let sec2_mbox0 = sec2_falcon.read_mailbox0();
+ if sec2_mbox0 != 0 {
+ dev_err!(
+ dev,
+ "SEC2 reported error during core resume: {:#x}\n",
+ sec2_mbox0
+ );
+ return Err(EIO);
+ }
+
+ gsp_falcon.write_os_version(bootloader_app_version);
+
+ if !gsp_falcon.is_riscv_active() {
+ dev_err!(dev, "GSP RISC-V not active after core resume\n");
+ return Err(EIO);
+ }
+
+ Ok(())
+ }
+
+ /// Handle a `GSP_LOAD_EXEC_HS_BINARY` event.
+ ///
+ /// The GSP asks the driver to run a high-security binary that it has already placed in the
+ /// framebuffer. The driver DMAs the image into falcon memory, programs the BROM registers
+ /// that make the falcon verify its PKC signature, runs it, and resumes GSP-RM.
+ ///
+ /// # Errors
+ ///
+ /// - `EINVAL` if the payload is shorter than the parameter block, or the ucode id does not
+ /// fit the BROM register field.
+ /// - `ETIMEDOUT` if the GSP does not suspend, or the binary does not halt, in time.
+ #[expect(dead_code)]
+ #[allow(clippy::too_many_arguments)]
+ fn handle_load_exec_hs_binary(
+ payload: &[u8],
+ gsp_falcon: &Falcon<'_, Gsp>,
+ sec2_falcon: &Falcon<'_, Sec2>,
+ bar: Bar0<'_>,
+ dev: &device::Device,
+ bootloader_app_version: u32,
+ libos_dma_handle: u64,
+ ) -> Result {
+ let params = HsBinaryParams::from_bytes_prefix(payload).ok_or(EINVAL)?.0;
+
+ gsp_falcon.wait_for_processor_suspend().inspect_err(|_| {
+ dev_err!(
+ dev,
+ "Timeout waiting for GSP suspend (mbox0={:#x})\n",
+ gsp_falcon.read_mailbox0()
+ );
+ })?;
+
+ gsp_falcon.reset()?;
+
+ gsp_falcon.dma_reset();
+ bar.update(
+ regs::NV_PFALCON_FBIF_TRANSCFG::of::<Gsp>().at(usize::from(HS_BINARY_CTX_DMA)),
+ |v| {
+ v.with_target(FalconFbifTarget::LocalFb)
+ .with_mem_type(FalconFbifMemType::Physical)
+ .with_engine_id_flag(true)
+ },
+ );
+
+ if params.ucode_imem_size > 0 {
+ gsp_falcon.raw_dma_transfer(
+ HS_BINARY_CTX_DMA,
+ params.imem_phys_addr,
+ FalconMem::ImemSecure,
+ FalconDmaSrcOffset::Offset(params.ucode_imem_va),
+ params.ucode_imem_pa,
+ params.ucode_imem_size,
+ )?;
+ }
+
+ if params.ucode_dmem_size > 0 {
+ // A valid DMEM virtual address makes the engine tag each loaded block with it, which
+ // is how the binary reaches its data.
+ let src = if params.ucode_dmem_va == FLCN_DMEM_VA_INVALID {
+ FalconDmaSrcOffset::Offset(0)
+ } else {
+ FalconDmaSrcOffset::DmemVa(params.ucode_dmem_va)
+ };
+
+ gsp_falcon.raw_dma_transfer(
+ HS_BINARY_CTX_DMA,
+ params.dmem_phys_addr,
+ FalconMem::Dmem,
+ src,
+ params.ucode_dmem_pa,
+ params.ucode_dmem_size,
+ )?;
+ }
+
+ bar.write(
+ WithBase::of::<Gsp>().at(0),
+ regs::NV_PFALCON2_FALCON_BROM_PARAADDR::zeroed().with_value(params.hs_sig_dmem_addr),
+ );
+ bar.write(
+ WithBase::of::<Gsp>(),
+ regs::NV_PFALCON2_FALCON_BROM_ENGIDMASK::zeroed().with_value(params.engine_id_mask),
+ );
+ bar.write(
+ WithBase::of::<Gsp>(),
+ regs::NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID::zeroed()
+ .with_ucode_id(u8::try_from(params.ucode_id).map_err(|_| EINVAL)?),
+ );
+ bar.write(
+ WithBase::of::<Gsp>(),
+ regs::NV_PFALCON2_FALCON_MOD_SEL::zeroed().with_algo(FalconModSelAlgo::Rsa3k),
+ );
+
+ // Also clears the suspend bit that `wait_for_processor_suspend` polls, so the next
+ // load-and-execute event does not read this one's suspension.
+ gsp_falcon.write_mailboxes(Some(FLCN_ERR_BINARY_NOT_STARTED), None);
+
+ bar.write(
+ WithBase::of::<Gsp>(),
+ regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(params.ucode_imem_va),
+ );
+
+ gsp_falcon.start()?;
+ gsp_falcon.wait_till_halted().inspect_err(|_| {
+ dev_err!(
+ dev,
+ "Timeout waiting for HS binary to halt (mbox0={:#x})\n",
+ gsp_falcon.read_mailbox0()
+ );
+ })?;
+
+ Self::core_resume(
+ gsp_falcon,
+ sec2_falcon,
+ dev,
+ bootloader_app_version,
+ libos_dma_handle,
+ )
+ }
+
/// Shut down the GSP and wait until it is offline.
fn shutdown_gsp(
cmdq: &Cmdq,
@@ -146,3 +335,39 @@ pub(crate) fn unload(
res.inspect(|()| dev_info!(dev, "GSP successfully unloaded\n"))
}
}
+
+/// Value Open RM leaves in `MAILBOX0` before starting a falcon binary, so a binary that never
+/// runs is distinguishable from one that ran and returned success.
+const FLCN_ERR_BINARY_NOT_STARTED: u32 = 0xfe;
+
+/// `ucode_dmem_va` value meaning the binary has no DMEM virtual address.
+const FLCN_DMEM_VA_INVALID: u32 = 0xffff_ffff;
+
+/// Context DMA slot the HS binary is loaded through. Open RM hardcodes slot 0 for this event and
+/// points it at local framebuffer.
+const HS_BINARY_CTX_DMA: u8 = 0;
+
+/// Parameters for loading and executing an HS (High-Security) binary.
+///
+/// Sent by GSP-RM as the payload of `GSP_LOAD_EXEC_HS_BINARY`. The firmware
+/// code and data are located in framebuffer memory at the given physical addresses.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+struct HsBinaryParams {
+ imem_phys_addr: u64,
+ dmem_phys_addr: u64,
+ _reserved64: [u64; 2],
+ ucode_imem_va: u32,
+ ucode_imem_pa: u32,
+ ucode_imem_size: u32,
+ ucode_dmem_va: u32,
+ ucode_dmem_pa: u32,
+ ucode_dmem_size: u32,
+ hs_sig_dmem_addr: u32,
+ engine_id_mask: u32,
+ ucode_id: u32,
+ _reserved32: [u32; 3],
+}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for HsBinaryParams {}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 5501c36a56af..88bb60c8a259 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -353,6 +353,7 @@ pub(crate) fn usable_fb_size(self) -> u64 {
}
pub(crate) NV_PFALCON_FBIF_TRANSCFG(u32)[8] @ PFalconBase + 0x00000600 {
+ 3:3 engine_id_flag => bool;
2:2 mem_type => FalconFbifMemType;
1:0 target ?=> FalconFbifTarget;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 20/27] gpu: nova-core: handle the r000 load-and-execute bootloader event
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (18 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 19/27] gpu: nova-core: handle the r000 load-and-execute HS binary event John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-20 16:24 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 21/27] gpu: nova-core: gsp: add the GMC boot event dispatcher John Hubbard
` (7 subsequent siblings)
27 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The r000 GSP boot protocol asks the driver to run an image on the GSP
falcon, and passes a bootloader descriptor naming where the image lives.
Open RM does not copy the image itself. It runs the generic falcon
bootloader, which reads that descriptor from DMEM and does the copy.
Nova-core has no handler for that event, and keeps its copy of the
descriptor in the FWSEC module that first needed one.
Move the descriptor next to the bootloader that reads it, and add the
handler. It points the requested FBIF aperture at the image, loads the
descriptor and the bootloader, runs it, and restores the aperture.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
.../nova-core/firmware/fwsec/bootloader.rs | 52 +-----
.../gpu/nova-core/firmware/gen_bootloader.rs | 115 ++++++++++++-
drivers/gpu/nova-core/gsp/boot.rs | 161 +++++++++++++++++-
3 files changed, 278 insertions(+), 50 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
index 6670d17b4eeb..43908eb11f7d 100644
--- a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
@@ -33,60 +33,16 @@
},
firmware::{
fwsec::FwsecFirmware,
- gen_bootloader::GenericBootloader, //
+ gen_bootloader::{
+ BootloaderDmemDescV2,
+ GenericBootloader, //
+ },
},
gpu::Chipset,
num::FromSafeCast, //
regs,
};
-/// Structure used by the boot-loader to load the rest of the code.
-///
-/// This has to be filled by the GPU driver and copied into DMEM at offset
-/// [`BootloaderDesc.dmem_load_off`].
-#[repr(C, packed)]
-#[derive(Debug, Clone)]
-struct BootloaderDmemDescV2 {
- /// Reserved, should always be first element.
- reserved: [u32; 4],
- /// 16B signature for secure code, 0s if no secure code.
- signature: [u32; 4],
- /// DMA context used by the bootloader while loading code/data.
- ctx_dma: u32,
- /// 256B-aligned physical FB address where code is located.
- code_dma_base: u64,
- /// Offset from `code_dma_base` where the non-secure code is located.
- ///
- /// Also used as destination IMEM offset of non-secure code as the DMA firmware object is
- /// expected to be a mirror image of its loaded state.
- ///
- /// Must be multiple of 256.
- non_sec_code_off: u32,
- /// Size of the non-secure code part.
- non_sec_code_size: u32,
- /// Offset from `code_dma_base` where the secure code is located (must be multiple of 256).
- ///
- /// Also used as destination IMEM offset of secure code as the DMA firmware object is expected
- /// to be a mirror image of its loaded state.
- ///
- /// Must be multiple of 256.
- sec_code_off: u32,
- /// Size of the secure code part.
- sec_code_size: u32,
- /// Code entry point invoked by the bootloader after code is loaded.
- code_entry_point: u32,
- /// 256B-aligned physical FB address where data is located.
- data_dma_base: u64,
- /// Size of data block (should be multiple of 256B).
- data_size: u32,
- /// Number of arguments to be passed to the target firmware being loaded.
- argc: u32,
- /// Arguments to be passed to the target firmware being loaded.
- argv: u32,
-}
-// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
-unsafe impl AsBytes for BootloaderDmemDescV2 {}
-
/// Wrapper for [`FwsecFirmware`] that includes the bootloader performing the actual load
/// operation.
pub(crate) struct FwsecFirmwareWithBl {
diff --git a/drivers/gpu/nova-core/firmware/gen_bootloader.rs b/drivers/gpu/nova-core/firmware/gen_bootloader.rs
index f949223af2d0..f0a6c841d2aa 100644
--- a/drivers/gpu/nova-core/firmware/gen_bootloader.rs
+++ b/drivers/gpu/nova-core/firmware/gen_bootloader.rs
@@ -13,14 +13,23 @@
Alignable,
Alignment, //
},
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
};
use crate::{
falcon::{
self,
+ gsp::Gsp,
Falcon,
+ FalconBromParams,
FalconEngine,
- FalconPioImemLoadTarget, //
+ FalconFirmware,
+ FalconPioDmemLoadTarget,
+ FalconPioImemLoadTarget,
+ FalconPioLoadable, //
},
firmware::tlv::{
request_tlv, //
@@ -30,6 +39,57 @@
num::FromSafeCast, //
};
+/// Structure the generic bootloader reads from DMEM offset 0 to find the image it must load.
+///
+/// Mirrors Open RM's `RM_FLCN_BL_DMEM_DESC`. The driver fills one in when it loads a firmware
+/// through the bootloader, and GSP-RM sends one in a load-and-execute event.
+#[repr(C, packed)]
+#[derive(Debug, Clone)]
+pub(crate) struct BootloaderDmemDescV2 {
+ /// Reserved, should always be first element.
+ pub(crate) reserved: [u32; 4],
+ /// 16B signature for secure code, 0s if no secure code.
+ pub(crate) signature: [u32; 4],
+ /// DMA context used by the bootloader while loading code/data.
+ pub(crate) ctx_dma: u32,
+ /// 256B-aligned physical FB address where code is located.
+ pub(crate) code_dma_base: u64,
+ /// Offset from `code_dma_base` where the non-secure code is located.
+ ///
+ /// Also used as destination IMEM offset of non-secure code as the DMA firmware object is
+ /// expected to be a mirror image of its loaded state.
+ ///
+ /// Must be multiple of 256.
+ pub(crate) non_sec_code_off: u32,
+ /// Size of the non-secure code part.
+ pub(crate) non_sec_code_size: u32,
+ /// Offset from `code_dma_base` where the secure code is located (must be multiple of 256).
+ ///
+ /// Also used as destination IMEM offset of secure code as the DMA firmware object is expected
+ /// to be a mirror image of its loaded state.
+ ///
+ /// Must be multiple of 256.
+ pub(crate) sec_code_off: u32,
+ /// Size of the secure code part.
+ pub(crate) sec_code_size: u32,
+ /// Code entry point invoked by the bootloader after code is loaded.
+ pub(crate) code_entry_point: u32,
+ /// 256B-aligned physical FB address where data is located.
+ pub(crate) data_dma_base: u64,
+ /// Size of data block (should be multiple of 256B).
+ pub(crate) data_size: u32,
+ /// Number of arguments to be passed to the target firmware being loaded.
+ pub(crate) argc: u32,
+ /// Arguments to be passed to the target firmware being loaded.
+ pub(crate) argv: u32,
+}
+
+// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
+unsafe impl AsBytes for BootloaderDmemDescV2 {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for BootloaderDmemDescV2 {}
+
/// The generic falcon bootloader image and the IMEM placement it was loaded for.
pub(crate) struct GenericBootloader {
/// Bootloader code, zero-padded to a whole number of falcon memory blocks.
@@ -99,4 +159,57 @@ pub(crate) fn imem_load_params(&self) -> FalconPioImemLoadTarget<'_> {
start_tag: self.start_tag,
}
}
+
+ /// Pairs this bootloader with the descriptor of the image it is to load, giving something
+ /// [`Falcon::pio_load`] accepts.
+ pub(crate) fn with_descriptor<'a>(
+ &'a self,
+ dmem_desc: &'a BootloaderDmemDescV2,
+ ) -> GenericBootloaderLoad<'a> {
+ GenericBootloaderLoad {
+ bootloader: self,
+ dmem_desc,
+ }
+ }
+}
+
+/// The generic bootloader together with the descriptor it reads from DMEM offset 0.
+pub(crate) struct GenericBootloaderLoad<'a> {
+ bootloader: &'a GenericBootloader,
+ dmem_desc: &'a BootloaderDmemDescV2,
+}
+
+impl FalconFirmware for GenericBootloaderLoad<'_> {
+ type Target = Gsp;
+
+ fn brom_params(&self) -> FalconBromParams {
+ // The bootloader is not signed. Every chipset that loads it this way uses a falcon HAL
+ // whose BROM programming is a no-op, so these values are never written to hardware.
+ FalconBromParams {
+ pkc_data_offset: 0,
+ engine_id_mask: 0,
+ ucode_id: 0,
+ }
+ }
+
+ fn boot_addr(&self) -> u32 {
+ self.bootloader.boot_addr()
+ }
+}
+
+impl FalconPioLoadable for GenericBootloaderLoad<'_> {
+ fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
+ None
+ }
+
+ fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
+ Some(self.bootloader.imem_load_params())
+ }
+
+ fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> {
+ FalconPioDmemLoadTarget {
+ data: self.dmem_desc.as_bytes(),
+ dst_start: 0,
+ }
+ }
}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index ae2aca5d935a..d62a5c834ccf 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -27,7 +27,13 @@
FalconMem,
FalconModSelAlgo, //
},
- firmware::gsp::GspFirmware,
+ firmware::{
+ gen_bootloader::{
+ BootloaderDmemDescV2,
+ GenericBootloader, //
+ },
+ gsp::GspFirmware,
+ },
gsp::{
cmdq::Cmdq,
commands, //
@@ -156,6 +162,101 @@ fn core_resume(
Ok(())
}
+ /// Handle a `GSP_LOAD_EXEC_GENERIC_BOOTLOADER` event.
+ ///
+ /// The driver does not copy the image the GSP asks for. It writes the descriptor the event
+ /// carries to DMEM offset 0, places the generic bootloader in IMEM, points the requested FBIF
+ /// aperture at wherever the image lives, and runs the bootloader, which does the copy from
+ /// the descriptor and jumps to the image. The aperture is restored afterwards.
+ ///
+ /// # Errors
+ ///
+ /// - `EINVAL` if the payload is shorter than the parameter block, the descriptor is not the
+ /// size this driver mirrors, or the event names a context DMA slot or an aperture that does
+ /// not exist.
+ /// - `ETIMEDOUT` if the GSP does not suspend, or the image does not halt, in time.
+ #[expect(dead_code)]
+ #[allow(clippy::too_many_arguments)]
+ fn handle_load_exec_bootloader(
+ payload: &[u8],
+ bootloader: &GenericBootloader,
+ gsp_falcon: &Falcon<'_, Gsp>,
+ sec2_falcon: &Falcon<'_, Sec2>,
+ bar: Bar0<'_>,
+ dev: &device::Device,
+ bootloader_app_version: u32,
+ libos_dma_handle: u64,
+ ) -> Result {
+ let params = LoadExecGenericBootloaderParams::from_bytes_prefix(payload)
+ .ok_or(EINVAL)?
+ .0;
+
+ let desc_size =
+ u32::try_from(core::mem::size_of::<BootloaderDmemDescV2>()).map_err(|_| EOVERFLOW)?;
+ if params.dmem_desc_size != desc_size {
+ dev_err!(
+ dev,
+ "Load-exec descriptor is {} bytes, expected {}\n",
+ params.dmem_desc_size,
+ desc_size
+ );
+ return Err(EINVAL);
+ }
+
+ let ctx_dma = params.ctx_dma()?;
+ let fbif_target = params.fbif_target()?;
+ let transcfg = || {
+ regs::NV_PFALCON_FBIF_TRANSCFG::of::<Gsp>()
+ .try_at(usize::from(ctx_dma))
+ .ok_or(EINVAL)
+ };
+
+ gsp_falcon.wait_for_processor_suspend().inspect_err(|_| {
+ dev_err!(
+ dev,
+ "Timeout waiting for GSP suspend (mbox0={:#x})\n",
+ gsp_falcon.read_mailbox0()
+ );
+ })?;
+
+ gsp_falcon.reset()?;
+ gsp_falcon.dma_reset();
+
+ let saved_transcfg = bar.read(transcfg()?);
+ bar.update(transcfg()?, |v| {
+ v.with_target(fbif_target)
+ .with_mem_type(FalconFbifMemType::Physical)
+ });
+
+ let run = (|| -> Result {
+ gsp_falcon.pio_load(&bootloader.with_descriptor(¶ms.dmem_desc))?;
+
+ // Also clears the suspend bit that `wait_for_processor_suspend` polls, so the next
+ // load-and-execute event does not read this one's suspension.
+ gsp_falcon.write_mailboxes(Some(FLCN_ERR_BINARY_NOT_STARTED), None);
+
+ gsp_falcon.start()?;
+ gsp_falcon.wait_till_halted().inspect_err(|_| {
+ dev_err!(
+ dev,
+ "Timeout waiting for the loaded image to halt (mbox0={:#x})\n",
+ gsp_falcon.read_mailbox0()
+ );
+ })
+ })();
+
+ bar.update(transcfg()?, |_| saved_transcfg);
+ run?;
+
+ Self::core_resume(
+ gsp_falcon,
+ sec2_falcon,
+ dev,
+ bootloader_app_version,
+ libos_dma_handle,
+ )
+ }
+
/// Handle a `GSP_LOAD_EXEC_HS_BINARY` event.
///
/// The GSP asks the driver to run a high-security binary that it has already placed in the
@@ -347,6 +448,64 @@ pub(crate) fn unload(
/// points it at local framebuffer.
const HS_BINARY_CTX_DMA: u8 = 0;
+/// Number of FBIF context DMA slots a falcon has.
+const NUM_CTX_DMA: usize = 8;
+
+/// Parameters for loading and executing the generic bootloader.
+///
+/// Sent by GSP-RM as the payload of `GSP_LOAD_EXEC_GENERIC_BOOTLOADER`. The descriptor carries
+/// the code and data addresses, while `addr_space` and `cpu_cache_attrib` say which FBIF aperture
+/// reaches them.
+#[repr(C)]
+struct LoadExecGenericBootloaderParams {
+ dmem_desc: BootloaderDmemDescV2,
+ dmem_desc_size: u32,
+ addr_space: u32,
+ cpu_cache_attrib: u32,
+ _reserved: [u32; 4],
+}
+
+impl LoadExecGenericBootloaderParams {
+ const ADDR_SYSMEM: u32 = 1;
+ const ADDR_FBMEM: u32 = 2;
+ const NV_MEMORY_CACHED: u32 = 0;
+ const NV_MEMORY_UNCACHED: u32 = 1;
+
+ /// Returns the context DMA slot the bootloader is to fetch the image through.
+ ///
+ /// # Errors
+ ///
+ /// - `EINVAL` if the slot is outside the FBIF `TRANSCFG` array.
+ fn ctx_dma(&self) -> Result<u8> {
+ let ctx_dma = self.dmem_desc.ctx_dma;
+
+ u8::try_from(ctx_dma)
+ .ok()
+ .filter(|slot| usize::from(*slot) < NUM_CTX_DMA)
+ .ok_or(EINVAL)
+ }
+
+ /// Returns the FBIF aperture that reaches the image.
+ ///
+ /// # Errors
+ ///
+ /// - `EINVAL` if the address space and cache attribute pair is not one this driver maps.
+ fn fbif_target(&self) -> Result<FalconFbifTarget> {
+ match (self.addr_space, self.cpu_cache_attrib) {
+ (Self::ADDR_FBMEM, _) => Ok(FalconFbifTarget::LocalFb),
+ (Self::ADDR_SYSMEM, Self::NV_MEMORY_CACHED) => Ok(FalconFbifTarget::CoherentSysmem),
+ (Self::ADDR_SYSMEM, Self::NV_MEMORY_UNCACHED) => {
+ Ok(FalconFbifTarget::NoncoherentSysmem)
+ }
+ _ => Err(EINVAL),
+ }
+ }
+}
+
+// SAFETY: The nested descriptor is `FromBytes`, and every other field is an integer type for
+// which all bit patterns are valid.
+unsafe impl FromBytes for LoadExecGenericBootloaderParams {}
+
/// Parameters for loading and executing an HS (High-Security) binary.
///
/// Sent by GSP-RM as the payload of `GSP_LOAD_EXEC_HS_BINARY`. The firmware
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 21/27] gpu: nova-core: gsp: add the GMC boot event dispatcher
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (19 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 20/27] gpu: nova-core: handle the r000 load-and-execute bootloader event John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 3:52 ` [PATCH 22/27] gpu: nova-core: gsp: add the GSP_INIT request builder John Hubbard
` (6 subsequent siblings)
27 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The r000 GSP boot protocol sends its two load-and-execute events as GMC
commands, each identified by a command id.
Nova-core has a handler for each of the two events, but nothing that
reads a command id and selects between them.
Add the dispatcher, keyed on the two command ids from the r000 bindings.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 62 +++++++++++++++++++++++++++++--
drivers/gpu/nova-core/gsp/fw.rs | 10 +++++
2 files changed, 69 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index d62a5c834ccf..15933ecc061b 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -36,7 +36,11 @@
},
gsp::{
cmdq::Cmdq,
- commands, //
+ commands,
+ fw::{
+ GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER,
+ GMCAPI_CMD_EXEC_HS_BINARY, //
+ }, //
},
regs, //
};
@@ -162,6 +166,60 @@ fn core_resume(
Ok(())
}
+ /// Dispatch one GMC boot event to its load-and-execute handler.
+ ///
+ /// LIBOS2 chipsets send `GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER` and LIBOS3 chipsets send
+ /// `GMCAPI_CMD_EXEC_HS_BINARY`, so a given GPU only ever reaches one of the two handlers.
+ ///
+ /// # Errors
+ ///
+ /// - `EINVAL` if `command_id` is not a load-and-execute command.
+ ///
+ /// Errors from the handlers are propagated as-is.
+ #[expect(dead_code)]
+ #[allow(clippy::too_many_arguments)]
+ fn dispatch_gmc_boot_event(
+ command_id: u32,
+ payload: &[u8],
+ bootloader: &GenericBootloader,
+ gsp_falcon: &Falcon<'_, Gsp>,
+ sec2_falcon: &Falcon<'_, Sec2>,
+ bar: Bar0<'_>,
+ dev: &device::Device,
+ bootloader_app_version: u32,
+ libos_dma_handle: u64,
+ ) -> Result {
+ match command_id {
+ GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER => Self::handle_load_exec_bootloader(
+ payload,
+ bootloader,
+ gsp_falcon,
+ sec2_falcon,
+ bar,
+ dev,
+ bootloader_app_version,
+ libos_dma_handle,
+ ),
+ GMCAPI_CMD_EXEC_HS_BINARY => Self::handle_load_exec_hs_binary(
+ payload,
+ gsp_falcon,
+ sec2_falcon,
+ bar,
+ dev,
+ bootloader_app_version,
+ libos_dma_handle,
+ ),
+ _ => {
+ dev_err!(
+ dev,
+ "Unexpected GMC boot event: command_id={:#010x}\n",
+ command_id
+ );
+ Err(EINVAL)
+ }
+ }
+ }
+
/// Handle a `GSP_LOAD_EXEC_GENERIC_BOOTLOADER` event.
///
/// The driver does not copy the image the GSP asks for. It writes the descriptor the event
@@ -175,7 +233,6 @@ fn core_resume(
/// size this driver mirrors, or the event names a context DMA slot or an aperture that does
/// not exist.
/// - `ETIMEDOUT` if the GSP does not suspend, or the image does not halt, in time.
- #[expect(dead_code)]
#[allow(clippy::too_many_arguments)]
fn handle_load_exec_bootloader(
payload: &[u8],
@@ -268,7 +325,6 @@ fn handle_load_exec_bootloader(
/// - `EINVAL` if the payload is shorter than the parameter block, or the ucode id does not
/// fit the BROM register field.
/// - `ETIMEDOUT` if the GSP does not suspend, or the binary does not halt, in time.
- #[expect(dead_code)]
#[allow(clippy::too_many_arguments)]
fn handle_load_exec_hs_binary(
payload: &[u8],
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 83f7d2042aa1..4772af362117 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -979,6 +979,16 @@ pub(crate) struct GmcApiHeader {
/// `GMCAPI_HEADER_COMMAND_ID_MASK`. The remaining byte carries flags.
const GMCAPI_COMMAND_ID_MASK: u32 = 0x00ff_ffff;
+/// GMC command asking the driver to run the generic falcon bootloader against a descriptor the
+/// GSP supplies.
+pub(crate) const GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER: u32 =
+ r000_00::GMCAPI_COMMANDS_GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER;
+
+/// GMC command asking the driver to run a high-security binary the GSP has placed in the
+/// framebuffer.
+pub(crate) const GMCAPI_CMD_EXEC_HS_BINARY: u32 =
+ r000_00::GMCAPI_COMMANDS_GMCAPI_CMD_EXEC_HS_BINARY;
+
static_assert!(size_of::<GmcApiHeader>() == 40);
impl GmcApiHeader {
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 22/27] gpu: nova-core: gsp: add the GSP_INIT request builder
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (20 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 21/27] gpu: nova-core: gsp: add the GMC boot event dispatcher John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 3:52 ` [PATCH 23/27] gpu: nova-core: gsp: send GSP_INIT and decode its reply John Hubbard
` (5 subsequent siblings)
27 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The r000 GSP boot protocol replaces the separate system-info, registry
and static-info commands with one GSP_INIT request, whose payload is an
NVKV key-value blob rather than a packed struct.
Nova-core has the NVKV codec and the GSP_INIT schema, but nothing that
fills the schema in from a PCI device.
Add the builder. It encodes the PCI identifiers and config mirror range
the RPC path already sends, and the registry entries, whose names carry
the NULL that GSP-RM counts in the encoded length.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/commands.rs | 52 +++++++++++++++++++++
drivers/gpu/nova-core/gsp/fw/commands.rs | 58 +++++++++++++++++++++++-
2 files changed, 108 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index d5575c036eb9..0c3832ccf726 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -30,8 +30,16 @@
},
fw::{
self,
+ commands::{
+ GspInitRequest,
+ RegKey, //
+ },
MsgFunction, //
},
+ nvkv::{
+ Encodeable,
+ Encoder, //
+ },
},
sbuffer::SBufferIter,
vgpu::VgpuState, //
@@ -259,6 +267,50 @@ pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> {
}
}
+/// Registry entries the driver sends to GSP-RM on every boot, each with the NULL terminator that
+/// Open RM counts in the encoded name length.
+///
+/// `RMSecBusResetEnable` enables PCI secondary bus reset. `RMForcePcieConfigSave` makes GSP-RM
+/// preserve PCI configuration registers across any PCI reset. `RMDevidCheckIgnore` lets GSP-RM
+/// boot when the PCI device id is absent from its product name database.
+///
+/// [`SetRegistry::new`] carries the same entries for the RPC path, where the names have no
+/// terminator because that encoding appends one.
+const REGISTRY_ENTRIES: &[(&[u8], u32)] = &[
+ (b"RMSecBusResetEnable\0", 1),
+ (b"RMForcePcieConfigSave\0", 1),
+ (b"RMDevidCheckIgnore\0", 1),
+];
+
+/// Builds the NVKV-encoded payload of a `GSP_INIT` request.
+///
+/// The payload carries the system information GSP-RM reads before it starts, and
+/// [`REGISTRY_ENTRIES`] as `REGKEY_NAME` and `REGKEY_VALUE_U32` pairs. GSP-RM requires each name
+/// to be followed by its value, which is the order [`RegKey`] declares them in.
+///
+/// # Errors
+///
+/// - `ENOMEM` if the registry list or the encoder buffer cannot be allocated.
+#[expect(dead_code)]
+pub(crate) fn build_gsp_init_payload(
+ pdev: &pci::Device<device::Bound>,
+ chipset: Chipset,
+ vgpu_state: VgpuState,
+) -> Result<KVVec<u64>> {
+ let mut regkeys = KVVec::new();
+ for &(name, value) in REGISTRY_ENTRIES {
+ regkeys.push(RegKey::new(name, value), GFP_KERNEL)?;
+ }
+ if matches!(vgpu_state, VgpuState::Enabled { .. }) {
+ regkeys.push(RegKey::new(b"RMSetSriovMode\0", 1), GFP_KERNEL)?;
+ }
+
+ let mut encoder = Encoder::new();
+ GspInitRequest::new(pdev, chipset, regkeys).encode(&mut encoder)?;
+
+ Ok(encoder.finish())
+}
+
pub(crate) use fw::commands::PowerStateLevel;
/// The `UnloadingGuestDriver` command, used to shut down the GSP.
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index 5cd2d9646de3..bfd756813c64 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -245,6 +245,25 @@ pub(crate) enum OorArch {
Riscv64 = 5,
}
+impl OorArch {
+ /// Returns the variant naming the architecture this kernel is built for.
+ fn host() -> Self {
+ if cfg!(target_arch = "x86_64") {
+ Self::X86_64
+ } else if cfg!(target_arch = "aarch64") {
+ Self::Aarch64
+ } else if cfg!(target_arch = "powerpc64") {
+ Self::Ppc64le
+ } else if cfg!(target_arch = "arm") {
+ Self::Arm
+ } else if cfg!(target_arch = "riscv64") {
+ Self::Riscv64
+ } else {
+ Self::None
+ }
+ }
+}
+
// TODO[FPRI]: This is a temporary solution to be replaced with the corresponding derive macros once
// they land.
impl TryFrom<u32> for OorArch {
@@ -271,7 +290,7 @@ fn from(value: OorArch) -> Self {
nvkv_encode! {
/// A GSP registry entry.
- struct RegKey {
+ pub(crate) struct RegKey {
key_name: Key<&'static [u8], { Self::REGKEY_NAME_KEY }>,
key_value: Key<u32, { Self::REGKEY_VALUE_U32_KEY }>,
}
@@ -280,6 +299,15 @@ struct RegKey {
impl RegKey {
const REGKEY_NAME_KEY: KeyId = 0x3070;
const REGKEY_VALUE_U32_KEY: KeyId = 0x3071;
+
+ /// Creates a registry entry. `key_name` must include its NULL terminator, which GSP-RM counts
+ /// in the encoded name length.
+ pub(crate) fn new(key_name: &'static [u8], key_value: u32) -> Self {
+ Self {
+ key_name: key_name.into(),
+ key_value: key_value.into(),
+ }
+ }
}
impl Encodeable for KVVec<RegKey> {
@@ -317,7 +345,7 @@ impl VfInfo {
nvkv_encode! {
/// Payload of the `GSP_INIT` command.
#[cfg_attr(not(CONFIG_KUNIT), allow(dead_code))]
- struct GspInitRequest {
+ pub(crate) struct GspInitRequest {
pci_device_id: Key<u32, { Self::PCI_DEVICE_ID_KEY }>,
pci_sub_device_id: Key<u32, { Self::PCI_SUBDEVICE_ID_KEY }>,
pci_revision_id: Key<u32, { Self::PCI_REVISION_ID_KEY }>,
@@ -339,6 +367,32 @@ impl GspInitRequest {
const PCI_CONFIG_MIRROR_SIZE_KEY: KeyId = 0x0011;
const OOR_ARCH_KEY: KeyId = 0x0070;
const NV_DOMAIN_BUS_DEVICE_FUNC_KEY: KeyId = 0x1020;
+
+ /// Describes `dev` to GSP-RM and asks it to apply `regkeys`.
+ ///
+ /// The same identifiers reach GSP-RM through [`GspSetSystemInfo::init`] on the RPC path.
+ pub(crate) fn new(
+ dev: &pci::Device<device::Bound>,
+ chipset: Chipset,
+ regkeys: KVVec<RegKey>,
+ ) -> Self {
+ let mirror = chipset.pci_config_mirror_range();
+ let device_id = (u32::from(dev.device_id()) << 16) | u32::from(dev.vendor_id().as_raw());
+ let sub_device_id =
+ (u32::from(dev.subsystem_device_id()) << 16) | u32::from(dev.subsystem_vendor_id());
+
+ Self {
+ pci_device_id: device_id.into(),
+ pci_sub_device_id: sub_device_id.into(),
+ pci_revision_id: u32::from(dev.revision_id()).into(),
+ pci_config_mirror_base: mirror.start.into(),
+ pci_config_mirror_size: (mirror.end - mirror.start).into(),
+ oor_arch: OorArch::host().into(),
+ bus_device_func: u64::from(dev.dev_id()).into(),
+ regkeys,
+ vf_info: None,
+ }
+ }
}
// Decode:
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 23/27] gpu: nova-core: gsp: send GSP_INIT and decode its reply
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (21 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 22/27] gpu: nova-core: gsp: add the GSP_INIT request builder John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 3:52 ` [PATCH 24/27] gpu: nova-core: gsp: pass the remaining log buffers to GSP-RM John Hubbard
` (4 subsequent siblings)
27 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
GSP-RM answers a GSP_INIT request with the static GPU configuration, and
that reply is also what signals it has finished starting. It raises
load-and-execute events in the meantime, so a caller must handle them
rather than wait through them.
Nova-core can build the request but cannot send it: the GMC sender is
private, and the receive path drops the field carrying the reply status.
Add the sender and the receive-side status field it needs, and decode
the reply into the static-info type the RPC path already produces.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 49 ++++++--
drivers/gpu/nova-core/gsp/commands.rs | 140 ++++++++++++++++++++++-
drivers/gpu/nova-core/gsp/fw.rs | 4 +
drivers/gpu/nova-core/gsp/fw/commands.rs | 35 +++++-
4 files changed, 213 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 7da61bc9ad92..82ff46620911 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -790,22 +790,43 @@ fn receive_msg<M: MessageFromGsp>(&self, timeout: Delta) -> Result<M>
self.inner.lock().receive_msg(timeout, None)
}
- /// Receives one GMC event from the GSP and passes its command id and raw payload slices to
- /// `handler`.
+ /// Receives one GMC element from the GSP and passes its command id, the `max_resp_or_status`
+ /// field, and the raw payload slices to `handler`.
///
/// This method may sleep while waiting. The [`CmdqInner`] mutex stays locked across the wait
/// and across the `handler` call, so `handler` must not call back into this [`Cmdq`].
///
/// See [`CmdqInner::receive_gmc_and_dispatch`] for return values, queue state, and errors.
- #[expect(dead_code)]
pub(crate) fn receive_gmc_and_dispatch<R>(
&self,
timeout: Delta,
- handler: impl FnOnce(u32, &[u8], &[u8]) -> Option<R>,
+ handler: impl FnOnce(u32, u32, &[u8], &[u8]) -> Option<R>,
) -> Result<Option<R>> {
self.inner.lock().receive_gmc_and_dispatch(timeout, handler)
}
+ /// Sends a GMC API command to the GSP without waiting for its response.
+ ///
+ /// A caller that expects a response reads it with [`Self::receive_gmc_and_dispatch`], which
+ /// lets it handle the events GSP-RM interleaves before the response arrives.
+ ///
+ /// # Errors
+ ///
+ /// - `EMSGSIZE` if the command exceeds the maximum queue element size.
+ /// - `ETIMEDOUT` if space does not become available within the timeout.
+ /// - `EIO` if the command header is not properly aligned.
+ pub(crate) fn send_gmc_no_wait(
+ &self,
+ bar: Bar0<'_>,
+ command_id: u32,
+ payload: &[u8],
+ max_response_size: u32,
+ ) -> Result {
+ self.inner
+ .lock()
+ .send_gmc(bar, command_id, payload, max_response_size)
+ }
+
/// Waits for an unsolicited GSP event of type `M`, dispatching any other event that arrives
/// first.
///
@@ -1005,7 +1026,6 @@ fn send_command<M>(&mut self, bar: Bar0<'_>, command: M) -> Result<u32>
/// - `EMSGSIZE` if the command exceeds the maximum queue element size.
/// - `ETIMEDOUT` if space does not become available within the timeout.
/// - `EIO` if the command header is not properly aligned.
- #[expect(dead_code)]
fn send_gmc(
&mut self,
bar: Bar0<'_>,
@@ -1383,9 +1403,13 @@ fn wait_for_gmc_msg(&self, timeout: Delta) -> Result<GmcMessage<'_>> {
/// Receive the next GMC event from the GSP and dispatch it through a handler.
///
- /// The handler receives the GMC command id and the raw payload slices that follow the
- /// [`super::fw::GmcApiHeader`] (two slices because the circular buffer may wrap). It returns
- /// `None` for an event it does not handle.
+ /// The handler receives the GMC command id, the header's `max_resp_or_status` field, and the
+ /// raw payload slices that follow the [`super::fw::GmcApiHeader`] (two slices because the
+ /// circular buffer may wrap). It returns `None` for an element it does not handle.
+ ///
+ /// `max_resp_or_status` is a union: GSP-RM writes an `NV_STATUS` there when the element is a
+ /// response, and the maximum response size when it is a request. Only a handler that knows
+ /// which one it asked for can read it.
///
/// Where [`Self::receive_msg`] keys on [`MsgFunction`], this keys on the GMC command id,
/// which is the form the r000 firmware uses for boot events.
@@ -1401,7 +1425,7 @@ fn wait_for_gmc_msg(&self, timeout: Delta) -> Result<GmcMessage<'_>> {
fn receive_gmc_and_dispatch<R>(
&mut self,
timeout: Delta,
- handler: impl FnOnce(u32, &[u8], &[u8]) -> Option<R>,
+ handler: impl FnOnce(u32, u32, &[u8], &[u8]) -> Option<R>,
) -> Result<Option<R>> {
let message = self.wait_for_gmc_msg(timeout)?;
let header = message.header;
@@ -1420,7 +1444,12 @@ fn receive_gmc_and_dispatch<R>(
length,
);
- handler(command_id, message.contents.0, message.contents.1)
+ handler(
+ command_id,
+ header.gmc.max_resp_or_status,
+ message.contents.0,
+ message.contents.1,
+ )
} else {
dev_warn!(&self.dev, "GSP GMC: dropping non-GMC queue element\n");
None
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 0c3832ccf726..d55faf1a4e04 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -20,6 +20,7 @@
};
use crate::{
+ driver::Bar0,
gpu::Chipset,
gsp::{
cmdq::{
@@ -32,13 +33,18 @@
self,
commands::{
GspInitRequest,
+ GspInitResponse,
+ GspInitResponseSchema,
RegKey, //
},
- MsgFunction, //
+ MsgFunction,
+ GMCAPI_CMD_GSP_INIT, //
},
nvkv::{
+ Decoder,
Encodeable,
- Encoder, //
+ Encoder,
+ UnknownKeyPolicy, //
},
},
sbuffer::SBufferIter,
@@ -311,6 +317,136 @@ pub(crate) fn build_gsp_init_payload(
Ok(encoder.finish())
}
+/// Size of the buffer GSP-RM may fill with static configuration, matching the allocation Open RM
+/// makes in `kgspSendInitRpcs`.
+const GSP_INIT_MAX_RESPONSE_SIZE: u32 = 48 * 1024;
+
+/// Sends `GSP_INIT` and returns the static configuration its reply carries.
+///
+/// GSP-RM interleaves load-and-execute events between the request and the reply, and those events
+/// drive the falcon loads that let it finish starting, so each one is passed to `on_boot_event`
+/// rather than skipped. The reply arrives only once GSP-RM is up, which is what makes it the
+/// signal that boot is complete.
+///
+/// `payload` is the blob from [`build_gsp_init_payload`].
+///
+/// # Errors
+///
+/// - `EIO` if GSP-RM reports a failure status, or if the reply is not a whole number of NVKV
+/// words.
+/// - `ETIMEDOUT` if neither the reply nor another element arrives within
+/// [`Cmdq::RECEIVE_TIMEOUT`].
+///
+/// Errors from `on_boot_event` and from decoding the reply are propagated as-is.
+#[expect(dead_code)]
+pub(crate) fn gsp_init(
+ cmdq: &Cmdq,
+ bar: Bar0<'_>,
+ payload: &[u64],
+ mut on_boot_event: impl FnMut(u32, &[u8]) -> Result,
+) -> Result<GetGspStaticInfoReply> {
+ // Qualified because `zerocopy::IntoBytes` also gives `[T]` an `as_bytes`.
+ let payload = AsBytes::as_bytes(payload);
+
+ cmdq.send_gmc_no_wait(
+ bar,
+ GMCAPI_CMD_GSP_INIT,
+ payload,
+ GSP_INIT_MAX_RESPONSE_SIZE,
+ )?;
+
+ loop {
+ let reply = cmdq.receive_gmc_and_dispatch(
+ Cmdq::RECEIVE_TIMEOUT,
+ |command_id, max_resp_or_status, payload_0, payload_1| {
+ if command_id == GMCAPI_CMD_GSP_INIT {
+ Some(decode_gsp_init_reply(
+ max_resp_or_status,
+ payload_0,
+ payload_1,
+ ))
+ } else {
+ // A boot event. Keep waiting for the reply unless handling it failed.
+ match on_boot_event(command_id, payload_0) {
+ Ok(()) => None,
+ Err(e) => Some(Err(e)),
+ }
+ }
+ },
+ )?;
+
+ if let Some(reply) = reply {
+ return reply;
+ }
+ }
+}
+
+/// Decodes the `GSP_INIT` reply, whose `max_resp_or_status` field carries an `NV_STATUS`.
+fn decode_gsp_init_reply(
+ status: u32,
+ payload_0: &[u8],
+ payload_1: &[u8],
+) -> Result<GetGspStaticInfoReply> {
+ if status != 0 {
+ return Err(EIO);
+ }
+
+ decode_gsp_info(&nvkv_words(payload_0, payload_1)?)
+}
+
+/// Joins the two halves of a wrapped payload into the `u64` words an NVKV stream is made of.
+///
+/// # Errors
+///
+/// - `EIO` if the combined length is not a whole number of words.
+/// - `ENOMEM` if the buffer cannot be allocated.
+fn nvkv_words(payload_0: &[u8], payload_1: &[u8]) -> Result<KVVec<u64>> {
+ let bytes = SBufferIter::new_reader([payload_0, payload_1]).flush_into_kvec(GFP_KERNEL)?;
+ let words = bytes.chunks_exact(size_of::<u64>());
+ if !words.remainder().is_empty() {
+ return Err(EIO);
+ }
+
+ let mut out = KVVec::with_capacity(bytes.len() / size_of::<u64>(), GFP_KERNEL)?;
+ for word in words {
+ let word: [u8; size_of::<u64>()] = word.try_into().map_err(|_| EIO)?;
+ out.push(u64::from_le_bytes(word), GFP_KERNEL)?;
+ }
+
+ Ok(out)
+}
+
+/// Decodes the static GPU configuration from an NVKV stream.
+///
+/// # Errors
+///
+/// - `EINVAL` if the stream is malformed or omits a required key.
+/// - `ENOMEM` if the decoded regions cannot be allocated.
+fn decode_gsp_info(words: &[u64]) -> Result<GetGspStaticInfoReply> {
+ let decoder = Decoder::new(words, UnknownKeyPolicy::Ignore);
+ let decoded = KBox::try_init(
+ decoder.decode(GspInitResponseSchema::default())?,
+ GFP_KERNEL,
+ )?;
+
+ let mut gpu_name = [0u8; GspInitResponse::MAX_GPU_NAME_LEN];
+ let name = decoded.gpu_name();
+ gpu_name
+ .get_mut(..name.len())
+ .ok_or(EINVAL)?
+ .copy_from_slice(name);
+
+ let mut usable_fb_regions = KVec::new();
+ for region in decoded.usable_fb_regions() {
+ usable_fb_regions.push(region, GFP_KERNEL)?;
+ }
+
+ Ok(GetGspStaticInfoReply {
+ gpu_name,
+ usable_fb_regions,
+ })
+}
+
pub(crate) use fw::commands::PowerStateLevel;
/// The `UnloadingGuestDriver` command, used to shut down the GSP.
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 4772af362117..6958ee3a2e4e 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -979,6 +979,10 @@ pub(crate) struct GmcApiHeader {
/// `GMCAPI_HEADER_COMMAND_ID_MASK`. The remaining byte carries flags.
const GMCAPI_COMMAND_ID_MASK: u32 = 0x00ff_ffff;
+/// GMC command that hands GSP-RM its system information and registry keys and returns the static
+/// GPU configuration. Its reply is also what signals that GSP-RM has finished starting.
+pub(crate) const GMCAPI_CMD_GSP_INIT: u32 = r000_00::GMCAPI_COMMANDS_GMCAPI_CMD_GSP_INIT;
+
/// GMC command asking the driver to run the generic falcon bootloader against a descriptor the
/// GSP supplies.
pub(crate) const GMCAPI_CMD_EXEC_GENERIC_BOOTLOADER: u32 =
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index bfd756813c64..00e40a435053 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -402,7 +402,7 @@ pub(crate) fn new(
/// Schema for the `GSP_INIT` response.
#[cfg_attr(not(CONFIG_KUNIT), allow(dead_code))]
#[derive(Default)]
- struct GspInitResponseSchema => GspInitResponse {
+ pub(crate) struct GspInitResponseSchema => GspInitResponse {
gpu_name:
Array<u8, { GspInitResponse::MAX_GPU_NAME_LEN }, { Self::GPU_NAME_STRING_KEY }>,
fb_regions: Accumulated<FbRegionSchema>,
@@ -420,7 +420,7 @@ impl GspInitResponseSchema {
/// Payload of the `GSP_INIT` response.
#[cfg_attr(not(CONFIG_KUNIT), allow(dead_code))]
-struct GspInitResponse {
+pub(crate) struct GspInitResponse {
gpu_name: ArrayVec<u8, { Self::MAX_GPU_NAME_LEN }>,
fb_regions: KVVec<FbRegion>,
bar1_pde_base: u64,
@@ -428,7 +428,36 @@ struct GspInitResponse {
}
impl GspInitResponse {
- const MAX_GPU_NAME_LEN: usize = 64;
+ pub(crate) const MAX_GPU_NAME_LEN: usize = 64;
+
+ /// A region with no tag is general-purpose memory. A tagged region is reserved for a
+ /// firmware-internal use that the tag identifies.
+ const FB_REGION_TAG_NONE: u32 = 0;
+
+ /// Returns the GPU name, which GSP-RM sends with its NULL terminator.
+ pub(crate) fn gpu_name(&self) -> &[u8] {
+ self.gpu_name.as_slice()
+ }
+
+ /// Iterates over the FB regions the driver may allocate from.
+ ///
+ /// A region qualifies when it is untagged, unprotected, and supports both compression and
+ /// isochronous access, which is the same set the RPC path selects from
+ /// [`GspStaticConfigInfo::usable_fb_regions`].
+ pub(crate) fn usable_fb_regions(&self) -> impl Iterator<Item = Range<u64>> + '_ {
+ self.fb_regions.iter().filter_map(|region| {
+ if region.limit >= region.base
+ && region.tag == Self::FB_REGION_TAG_NONE
+ && !region.flags.protected()
+ && region.flags.support_compressed()
+ && region.flags.support_iso()
+ {
+ region.limit.checked_add(1).map(|end| region.base..end)
+ } else {
+ None
+ }
+ })
+ }
}
nvkv_decode! {
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 24/27] gpu: nova-core: gsp: pass the remaining log buffers to GSP-RM
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (22 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 23/27] gpu: nova-core: gsp: send GSP_INIT and decode its reply John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 3:52 ` [PATCH 26/27] gpu: nova-core: gsp: remove the retired system-info and static-info RPCs John Hubbard
` (3 subsequent siblings)
27 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The libos loader finds each log buffer by looking up its eight-byte name
in the init argument array, so a buffer the array does not name is never
written to.
Nova-core allocates six log buffers and gives all six a debugfs file,
but names only three, so the other three can never read back anything
but zeros.
Name all six. RMARGS moves to the end of the array, which does not
matter to a loader that searches by name.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp.rs | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 232905638169..f1bd66024b72 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -355,7 +355,19 @@ pub(crate) fn new(
2,
LibosMemoryRegionInitArgument::new("LOGRM", &logrm.buffer),
)?;
- libos.init_at(3, LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?;
+ libos.init_at(
+ 3,
+ LibosMemoryRegionInitArgument::new("LOGMNOC", &logmnoc.buffer),
+ )?;
+ libos.init_at(
+ 4,
+ LibosMemoryRegionInitArgument::new("LOGROOT", &logroot.buffer),
+ )?;
+ libos.init_at(
+ 5,
+ LibosMemoryRegionInitArgument::new("LOGRMON", &logrmon.buffer),
+ )?;
+ libos.init_at(6, LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?;
libos.into()
},
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 26/27] gpu: nova-core: gsp: remove the retired system-info and static-info RPCs
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (23 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 24/27] gpu: nova-core: gsp: pass the remaining log buffers to GSP-RM John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
2026-08-19 3:52 ` [PATCH 27/27] gpu: nova-core: firmware: delete the r570 bindings John Hubbard
` (2 subsequent siblings)
27 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
The r000 boot path folds the system information and the static GPU
configuration into the GSP_INIT request, whose reply carries that
configuration back to the driver.
The set-system-info and get-static-info commands stayed behind after the
switch, unreachable and marked dead, along with the two payload
structures that encoded them.
Remove both commands and their payloads, and keep the decoded static
configuration that the GSP_INIT reply fills in. The two message-function
entries stay, because that table catalogs the wire protocol rather than
what the driver implements, and already names many functions nova-core
never sends.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp.rs | 8 --
drivers/gpu/nova-core/gsp/commands.rs | 65 +------------
drivers/gpu/nova-core/gsp/fw/commands.rs | 115 +----------------------
3 files changed, 3 insertions(+), 185 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 8e912eaf8e2e..b128153fda86 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -407,14 +407,6 @@ pub(crate) fn new(
})
}
- /// Query the GSP for the static GPU information.
- ///
- /// The r000 boot path gets the same information from the `GSP_INIT` reply instead.
- #[expect(dead_code)]
- pub(crate) fn get_static_info(&self, bar: Bar0<'_>) -> Result<commands::GetGspStaticInfoReply> {
- self.cmdq.send_command(bar, commands::GetGspStaticInfo)
- }
-
/// Returns a shared handle to the GSP command queue.
pub(crate) fn cmdq(&self) -> Arc<Cmdq> {
self.cmdq.clone()
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 26ea07dc4a28..9079393da0b3 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -49,34 +49,6 @@
vgpu::VgpuState, //
};
-/// The `GspSetSystemInfo` command.
-///
-/// The r000 boot path folds this into the `GSP_INIT` payload instead.
-pub(crate) struct SetSystemInfo<'a> {
- pdev: &'a pci::Device<device::Bound>,
- chipset: Chipset,
-}
-
-#[expect(dead_code)]
-impl<'a> SetSystemInfo<'a> {
- /// Creates a new `GspSetSystemInfo` command using the parameters of `pdev`.
- pub(crate) fn new(pdev: &'a pci::Device<device::Bound>, chipset: Chipset) -> Self {
- Self { pdev, chipset }
- }
-}
-
-impl<'a> CommandToGsp for SetSystemInfo<'a> {
- const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo;
- const IS_ASYNC: bool = true;
- type Command = fw::commands::GspSetSystemInfo;
- type Reply = NoReply;
- type InitError = Error;
-
- fn init(&self) -> impl Init<Self::Command, Self::InitError> {
- Self::Command::init(self.pdev, self.chipset)
- }
-}
-
struct RegistryEntry {
key: &'static str,
value: u32,
@@ -186,48 +158,13 @@ fn init_variable_payload(
}
}
-/// The `GetGspStaticInfo` command.
-pub(crate) struct GetGspStaticInfo;
-
-impl CommandToGsp for GetGspStaticInfo {
- const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
- type Command = fw::commands::GspStaticConfigInfo;
- type Reply = GetGspStaticInfoReply;
- type InitError = Infallible;
-
- fn init(&self) -> impl Init<Self::Command, Self::InitError> {
- Self::Command::init_zeroed()
- }
-}
-
-/// The reply from the GSP to the [`GetGspStaticInfo`] command.
+/// The static GPU configuration, as decoded from the `GSP_INIT` reply.
pub(crate) struct GetGspStaticInfoReply {
gpu_name: [u8; 64],
/// Usable FB (VRAM) regions for driver memory allocation.
pub(crate) usable_fb_regions: KVec<Range<u64>>,
}
-impl MessageFromGsp for GetGspStaticInfoReply {
- const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
- type Message = fw::commands::GspStaticConfigInfo;
- type InitError = Error;
-
- fn read(
- msg: &Self::Message,
- _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
- ) -> Result<Self, Self::InitError> {
- let mut usable_fb_regions = KVec::new();
- for region in msg.usable_fb_regions() {
- usable_fb_regions.push(region, GFP_KERNEL)?;
- }
-
- Ok(GetGspStaticInfoReply {
- gpu_name: msg.gpu_name_str(),
- usable_fb_regions,
- })
- }
-}
-
/// Error type for [`GetGspStaticInfoReply::gpu_name`].
#[derive(Debug)]
pub(crate) enum GpuNameError {
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index 00e40a435053..e49234f35d0f 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -14,11 +14,7 @@
}, //
};
-use crate::{
- gpu::Chipset,
- gsp::GSP_PAGE_SIZE,
- num::IntoSafeCast, //
-};
+use crate::gpu::Chipset;
use crate::gsp::nvkv::{
nvkv_decode,
@@ -37,56 +33,6 @@
use super::bindings;
-/// Payload of the `GspSetSystemInfo` command.
-#[repr(transparent)]
-pub(crate) struct GspSetSystemInfo {
- inner: bindings::GspSystemInfo,
-}
-static_assert!(size_of::<GspSetSystemInfo>() < GSP_PAGE_SIZE);
-
-impl GspSetSystemInfo {
- /// Returns an in-place initializer for the `GspSetSystemInfo` command.
- pub(crate) fn init<'a>(
- dev: &'a pci::Device<device::Bound>,
- chipset: Chipset,
- ) -> impl Init<Self, Error> + 'a {
- type InnerGspSystemInfo = bindings::GspSystemInfo;
- let pci_config_mirror_range = chipset.pci_config_mirror_range();
- let init_inner = try_init!(InnerGspSystemInfo {
- gpuPhysAddr: dev.resource_start(0)?,
- gpuPhysFbAddr: dev.resource_start(1)?,
- gpuPhysInstAddr: dev.resource_start(3)?,
- nvDomainBusDeviceFunc: u64::from(dev.dev_id()),
-
- // Using TASK_SIZE in r535_gsp_rpc_set_system_info() seems wrong because
- // TASK_SIZE is per-task. That's probably a design issue in GSP-RM though.
- maxUserVa: (1 << 47) - 4096,
- pciConfigMirrorBase: pci_config_mirror_range.start,
- pciConfigMirrorSize: pci_config_mirror_range.end - pci_config_mirror_range.start,
-
- PCIDeviceID: (u32::from(dev.device_id()) << 16) | u32::from(dev.vendor_id().as_raw()),
- PCISubDeviceID: (u32::from(dev.subsystem_device_id()) << 16)
- | u32::from(dev.subsystem_vendor_id()),
- PCIRevisionID: u32::from(dev.revision_id()),
- bIsPrimary: 0,
- bPreserveVideoMemoryAllocations: 0,
- ..Zeroable::init_zeroed()
- });
-
- try_init!(GspSetSystemInfo {
- inner <- init_inner,
- })
- }
-}
-
-// SAFETY: These structs don't meet the no-padding requirements of AsBytes but
-// that is not a problem because they are not used outside the kernel.
-unsafe impl AsBytes for GspSetSystemInfo {}
-
-// SAFETY: These structs don't meet the no-padding requirements of FromBytes but
-// that is not a problem because they are not used outside the kernel.
-unsafe impl FromBytes for GspSetSystemInfo {}
-
#[repr(transparent)]
pub(crate) struct PackedRegistryEntry(bindings::PACKED_REGISTRY_ENTRY);
@@ -136,60 +82,6 @@ unsafe impl AsBytes for PackedRegistryTable {}
// are valid.
unsafe impl FromBytes for PackedRegistryTable {}
-/// Payload of the `GetGspStaticInfo` command and message.
-#[repr(transparent)]
-#[derive(Zeroable)]
-pub(crate) struct GspStaticConfigInfo(bindings::GspStaticConfigInfo_t);
-
-impl GspStaticConfigInfo {
- /// Returns a bytes array containing the (hopefully) zero-terminated name of this GPU.
- pub(crate) fn gpu_name_str(&self) -> [u8; 64] {
- self.0.gpuNameString
- }
-
- /// Returns an iterator over valid FB regions from GSP firmware data.
- fn fb_regions(
- &self,
- ) -> impl Iterator<Item = &bindings::NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO> {
- let fb_info = &self.0.fbRegionInfoParams;
- fb_info
- .fbRegion
- .iter()
- .take(fb_info.numFBRegions.into_safe_cast())
- .filter(|reg| reg.limit >= reg.base)
- }
-
- /// Iterates over usable FB regions from GSP firmware data.
- ///
- /// Each yielded region is a [`Range<u64>`] suitable for driver memory allocation.
- /// Usable regions are those that satisfy all the following properties:
- /// - Are not reserved for firmware internal use.
- /// - Are not protected (hardware-enforced access restrictions).
- /// - Support compression (can use GPU memory compression for bandwidth).
- /// - Support ISO (isochronous memory for display requiring guaranteed bandwidth).
- pub(crate) fn usable_fb_regions(&self) -> impl Iterator<Item = Range<u64>> + '_ {
- self.fb_regions().filter_map(|reg| {
- // Filter: not reserved, not protected, supports compression and ISO.
- if reg.reserved == 0
- && reg.bProtected == 0
- && reg.supportCompressed != 0
- && reg.supportISO != 0
- {
- reg.limit.checked_add(1).map(|end| reg.base..end)
- } else {
- None
- }
- })
- }
-}
-
-// SAFETY: Padding is explicit and will not contain uninitialized data.
-unsafe impl AsBytes for GspStaticConfigInfo {}
-
-// SAFETY: This struct only contains integer types for which all bit patterns
-// are valid.
-unsafe impl FromBytes for GspStaticConfigInfo {}
-
/// Power level requested to the [`UnloadingGuestDriver`] command.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
@@ -369,8 +261,6 @@ impl GspInitRequest {
const NV_DOMAIN_BUS_DEVICE_FUNC_KEY: KeyId = 0x1020;
/// Describes `dev` to GSP-RM and asks it to apply `regkeys`.
- ///
- /// The same identifiers reach GSP-RM through [`GspSetSystemInfo::init`] on the RPC path.
pub(crate) fn new(
dev: &pci::Device<device::Bound>,
chipset: Chipset,
@@ -442,8 +332,7 @@ pub(crate) fn gpu_name(&self) -> &[u8] {
/// Iterates over the FB regions the driver may allocate from.
///
/// A region qualifies when it is untagged, unprotected, and supports both compression and
- /// isochronous access, which is the same set the RPC path selects from
- /// [`GspStaticConfigInfo::usable_fb_regions`].
+ /// isochronous access.
pub(crate) fn usable_fb_regions(&self) -> impl Iterator<Item = Range<u64>> + '_ {
self.fb_regions.iter().filter_map(|region| {
if region.limit >= region.base
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH 27/27] gpu: nova-core: firmware: delete the r570 bindings
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (24 preceding siblings ...)
2026-08-19 3:52 ` [PATCH 26/27] gpu: nova-core: gsp: remove the retired system-info and static-info RPCs John Hubbard
@ 2026-08-19 3:52 ` John Hubbard
[not found] ` <20260819035221.336390-26-jhubbard@nvidia.com>
2026-08-19 19:25 ` [PATCH 00/27] gpu: nova-core: boot on " Timur Tabi
27 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-19 3:52 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Timur Tabi, Alistair Popple, Eliot Courtney, Zhi Wang,
David Airlie, Simona Vetter, Bjorn Helgaas, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
nova-gpu, LKML, John Hubbard
Nothing referenced the r570 firmware bindings once the driver started
compiling against the r000 headers, but the module stayed declared and
its generated definitions kept being built.
Delete the module and its bindings, along with the last two comments
that named r570.
Assisted-by: Cursor:claude-opus-5
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/fsp.rs | 3 +-
drivers/gpu/nova-core/gsp/fw.rs | 4 +-
drivers/gpu/nova-core/gsp/fw/r570_144.rs | 31 -
.../gpu/nova-core/gsp/fw/r570_144/bindings.rs | 1060 -----------------
4 files changed, 3 insertions(+), 1095 deletions(-)
delete mode 100644 drivers/gpu/nova-core/gsp/fw/r570_144.rs
delete mode 100644 drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index ab685fb4168f..668d64e8c8db 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -256,7 +256,8 @@ fn frts_vidmem_offset(hal: &dyn hal::FspHal, fb_info: &FbSizes) -> Result<u64> {
// As per OpenRM's `kfspPrepareBootCommands_GH100`.
if fb_info.pmu_reserved_size != 0 {
offset = (offset + u64::from(fb_info.pmu_reserved_size))
- // The 2 MiB alignment is r570-specific.
+ // Open RM aligns this to WPR granularity, which is 128 KiB. 2 MiB is a
+ // multiple of that and reserves at least as much.
.align_up(Alignment::new::<SZ_2M>())
.ok_or(EINVAL)?;
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index c24808e41941..55e032db649b 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -3,7 +3,6 @@
pub(crate) mod commands;
mod r000_00;
-mod r570_144;
// Alias to avoid repeating the version number with every use.
use r000_00 as bindings;
@@ -512,10 +511,9 @@ fn init(sequence: u32, cmd_size: usize, function: MsgFunction) -> impl Init<Self
}
}
-/// GSP Message Element (r000 MCTP/NVDM format).
+/// GSP Message Element, in the MCTP/NVDM format.
///
/// This is the transport-layer header for messages exchanged with GSP-RM.
-/// r000 firmware uses MCTP/NVDM framing instead of the r570 `GSP_MSG_QUEUE_ELEMENT`.
#[repr(C)]
pub(crate) struct GspMsgElement {
mctp_magic: u32,
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144.rs b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
deleted file mode 100644
index 2e6f0d298756..000000000000
--- a/drivers/gpu/nova-core/gsp/fw/r570_144.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-//! Firmware bindings.
-//!
-//! Imports the generated bindings by `bindgen`.
-//!
-//! This module may not be directly used. Please abstract or re-export the needed symbols in the
-//! parent module instead.
-
-#![allow(
- dead_code,
- clippy::all,
- clippy::undocumented_unsafe_blocks,
- clippy::ptr_as_ptr,
- clippy::ref_as_ptr,
- missing_docs,
- non_camel_case_types,
- non_upper_case_globals,
- non_snake_case,
- improper_ctypes,
- unreachable_pub,
- unsafe_op_in_unsafe_fn
-)]
-use kernel::ffi;
-use pin_init::MaybeZeroable;
-
-include!("r570_144/bindings.rs");
-
-// SAFETY: This type has a size of zero, so its inclusion into another type should not affect their
-// ability to implement `Zeroable`.
-unsafe impl<T> kernel::prelude::Zeroable for __IncompleteArrayField<T> {}
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
deleted file mode 100644
index afe3e007f088..000000000000
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ /dev/null
@@ -1,1060 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#[repr(C)]
-#[derive(Default)]
-pub struct __IncompleteArrayField<T>(::core::marker::PhantomData<T>, [T; 0]);
-impl<T> __IncompleteArrayField<T> {
- #[inline]
- pub const fn new() -> Self {
- __IncompleteArrayField(::core::marker::PhantomData, [])
- }
- #[inline]
- pub fn as_ptr(&self) -> *const T {
- self as *const _ as *const T
- }
- #[inline]
- pub fn as_mut_ptr(&mut self) -> *mut T {
- self as *mut _ as *mut T
- }
- #[inline]
- pub unsafe fn as_slice(&self, len: usize) -> &[T] {
- ::core::slice::from_raw_parts(self.as_ptr(), len)
- }
- #[inline]
- pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
- ::core::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
- }
-}
-impl<T> ::core::fmt::Debug for __IncompleteArrayField<T> {
- fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
- fmt.write_str("__IncompleteArrayField")
- }
-}
-pub const NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_0: u32 = 0;
-pub const NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3: u32 = 3;
-pub const NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_7: u32 = 7;
-pub const NV_VGPU_MSG_SIGNATURE_VALID: u32 = 1129337430;
-pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2: u32 = 0;
-pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL: u32 = 23068672;
-pub const GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X: u32 = 8388608;
-pub const GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100: u32 = 14680064;
-pub const GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB: u32 = 98304;
-pub const GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE: u32 = 100663296;
-pub const GSP_FW_HEAP_SIZE_VGPU_DEFAULT: u32 = 609222656;
-pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB: u32 = 64;
-pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB: u32 = 256;
-pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB: u32 = 88;
-pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB: u32 = 280;
-pub const GSP_FW_WPR_META_REVISION: u32 = 1;
-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 type __u8 = ffi::c_uchar;
-pub type __u16 = ffi::c_ushort;
-pub type __u32 = ffi::c_uint;
-pub type __u64 = ffi::c_ulonglong;
-pub type u8_ = __u8;
-pub type u16_ = __u16;
-pub type u32_ = __u32;
-pub type u64_ = __u64;
-pub const NV_VGPU_MSG_FUNCTION_NOP: _bindgen_ty_2 = 0;
-pub const NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO: _bindgen_ty_2 = 1;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_ROOT: _bindgen_ty_2 = 2;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE: _bindgen_ty_2 = 3;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY: _bindgen_ty_2 = 4;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA: _bindgen_ty_2 = 5;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA: _bindgen_ty_2 = 6;
-pub const NV_VGPU_MSG_FUNCTION_MAP_MEMORY: _bindgen_ty_2 = 7;
-pub const NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA: _bindgen_ty_2 = 8;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT: _bindgen_ty_2 = 9;
-pub const NV_VGPU_MSG_FUNCTION_FREE: _bindgen_ty_2 = 10;
-pub const NV_VGPU_MSG_FUNCTION_LOG: _bindgen_ty_2 = 11;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_VIDMEM: _bindgen_ty_2 = 12;
-pub const NV_VGPU_MSG_FUNCTION_UNMAP_MEMORY: _bindgen_ty_2 = 13;
-pub const NV_VGPU_MSG_FUNCTION_MAP_MEMORY_DMA: _bindgen_ty_2 = 14;
-pub const NV_VGPU_MSG_FUNCTION_UNMAP_MEMORY_DMA: _bindgen_ty_2 = 15;
-pub const NV_VGPU_MSG_FUNCTION_GET_EDID: _bindgen_ty_2 = 16;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_DISP_CHANNEL: _bindgen_ty_2 = 17;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_DISP_OBJECT: _bindgen_ty_2 = 18;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_SUBDEVICE: _bindgen_ty_2 = 19;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_DYNAMIC_MEMORY: _bindgen_ty_2 = 20;
-pub const NV_VGPU_MSG_FUNCTION_DUP_OBJECT: _bindgen_ty_2 = 21;
-pub const NV_VGPU_MSG_FUNCTION_IDLE_CHANNELS: _bindgen_ty_2 = 22;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_EVENT: _bindgen_ty_2 = 23;
-pub const NV_VGPU_MSG_FUNCTION_SEND_EVENT: _bindgen_ty_2 = 24;
-pub const NV_VGPU_MSG_FUNCTION_REMAPPER_CONTROL: _bindgen_ty_2 = 25;
-pub const NV_VGPU_MSG_FUNCTION_DMA_CONTROL: _bindgen_ty_2 = 26;
-pub const NV_VGPU_MSG_FUNCTION_DMA_FILL_PTE_MEM: _bindgen_ty_2 = 27;
-pub const NV_VGPU_MSG_FUNCTION_MANAGE_HW_RESOURCE: _bindgen_ty_2 = 28;
-pub const NV_VGPU_MSG_FUNCTION_BIND_ARBITRARY_CTX_DMA: _bindgen_ty_2 = 29;
-pub const NV_VGPU_MSG_FUNCTION_CREATE_FB_SEGMENT: _bindgen_ty_2 = 30;
-pub const NV_VGPU_MSG_FUNCTION_DESTROY_FB_SEGMENT: _bindgen_ty_2 = 31;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_SHARE_DEVICE: _bindgen_ty_2 = 32;
-pub const NV_VGPU_MSG_FUNCTION_DEFERRED_API_CONTROL: _bindgen_ty_2 = 33;
-pub const NV_VGPU_MSG_FUNCTION_REMOVE_DEFERRED_API: _bindgen_ty_2 = 34;
-pub const NV_VGPU_MSG_FUNCTION_SIM_ESCAPE_READ: _bindgen_ty_2 = 35;
-pub const NV_VGPU_MSG_FUNCTION_SIM_ESCAPE_WRITE: _bindgen_ty_2 = 36;
-pub const NV_VGPU_MSG_FUNCTION_SIM_MANAGE_DISPLAY_CONTEXT_DMA: _bindgen_ty_2 = 37;
-pub const NV_VGPU_MSG_FUNCTION_FREE_VIDMEM_VIRT: _bindgen_ty_2 = 38;
-pub const NV_VGPU_MSG_FUNCTION_PERF_GET_PSTATE_INFO: _bindgen_ty_2 = 39;
-pub const NV_VGPU_MSG_FUNCTION_PERF_GET_PERFMON_SAMPLE: _bindgen_ty_2 = 40;
-pub const NV_VGPU_MSG_FUNCTION_PERF_GET_VIRTUAL_PSTATE_INFO: _bindgen_ty_2 = 41;
-pub const NV_VGPU_MSG_FUNCTION_PERF_GET_LEVEL_INFO: _bindgen_ty_2 = 42;
-pub const NV_VGPU_MSG_FUNCTION_MAP_SEMA_MEMORY: _bindgen_ty_2 = 43;
-pub const NV_VGPU_MSG_FUNCTION_UNMAP_SEMA_MEMORY: _bindgen_ty_2 = 44;
-pub const NV_VGPU_MSG_FUNCTION_SET_SURFACE_PROPERTIES: _bindgen_ty_2 = 45;
-pub const NV_VGPU_MSG_FUNCTION_CLEANUP_SURFACE: _bindgen_ty_2 = 46;
-pub const NV_VGPU_MSG_FUNCTION_UNLOADING_GUEST_DRIVER: _bindgen_ty_2 = 47;
-pub const NV_VGPU_MSG_FUNCTION_TDR_SET_TIMEOUT_STATE: _bindgen_ty_2 = 48;
-pub const NV_VGPU_MSG_FUNCTION_SWITCH_TO_VGA: _bindgen_ty_2 = 49;
-pub const NV_VGPU_MSG_FUNCTION_GPU_EXEC_REG_OPS: _bindgen_ty_2 = 50;
-pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO: _bindgen_ty_2 = 51;
-pub const NV_VGPU_MSG_FUNCTION_ALLOC_VIRTMEM: _bindgen_ty_2 = 52;
-pub const NV_VGPU_MSG_FUNCTION_UPDATE_PDE_2: _bindgen_ty_2 = 53;
-pub const NV_VGPU_MSG_FUNCTION_SET_PAGE_DIRECTORY: _bindgen_ty_2 = 54;
-pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_PSTATE_INFO: _bindgen_ty_2 = 55;
-pub const NV_VGPU_MSG_FUNCTION_TRANSLATE_GUEST_GPU_PTES: _bindgen_ty_2 = 56;
-pub const NV_VGPU_MSG_FUNCTION_RESERVED_57: _bindgen_ty_2 = 57;
-pub const NV_VGPU_MSG_FUNCTION_RESET_CURRENT_GR_CONTEXT: _bindgen_ty_2 = 58;
-pub const NV_VGPU_MSG_FUNCTION_SET_SEMA_MEM_VALIDATION_STATE: _bindgen_ty_2 = 59;
-pub const NV_VGPU_MSG_FUNCTION_GET_ENGINE_UTILIZATION: _bindgen_ty_2 = 60;
-pub const NV_VGPU_MSG_FUNCTION_UPDATE_GPU_PDES: _bindgen_ty_2 = 61;
-pub const NV_VGPU_MSG_FUNCTION_GET_ENCODER_CAPACITY: _bindgen_ty_2 = 62;
-pub const NV_VGPU_MSG_FUNCTION_VGPU_PF_REG_READ32: _bindgen_ty_2 = 63;
-pub const NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO_EXT: _bindgen_ty_2 = 64;
-pub const NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO: _bindgen_ty_2 = 65;
-pub const NV_VGPU_MSG_FUNCTION_RMFS_INIT: _bindgen_ty_2 = 66;
-pub const NV_VGPU_MSG_FUNCTION_RMFS_CLOSE_QUEUE: _bindgen_ty_2 = 67;
-pub const NV_VGPU_MSG_FUNCTION_RMFS_CLEANUP: _bindgen_ty_2 = 68;
-pub const NV_VGPU_MSG_FUNCTION_RMFS_TEST: _bindgen_ty_2 = 69;
-pub const NV_VGPU_MSG_FUNCTION_UPDATE_BAR_PDE: _bindgen_ty_2 = 70;
-pub const NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD: _bindgen_ty_2 = 71;
-pub const NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO: _bindgen_ty_2 = 72;
-pub const NV_VGPU_MSG_FUNCTION_SET_REGISTRY: _bindgen_ty_2 = 73;
-pub const NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU: _bindgen_ty_2 = 74;
-pub const NV_VGPU_MSG_FUNCTION_SUBDEV_EVENT_SET_NOTIFICATION: _bindgen_ty_2 = 75;
-pub const NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL: _bindgen_ty_2 = 76;
-pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO2: _bindgen_ty_2 = 77;
-pub const NV_VGPU_MSG_FUNCTION_DUMP_PROTOBUF_COMPONENT: _bindgen_ty_2 = 78;
-pub const NV_VGPU_MSG_FUNCTION_UNSET_PAGE_DIRECTORY: _bindgen_ty_2 = 79;
-pub const NV_VGPU_MSG_FUNCTION_GET_CONSOLIDATED_STATIC_INFO: _bindgen_ty_2 = 80;
-pub const NV_VGPU_MSG_FUNCTION_GMMU_REGISTER_FAULT_BUFFER: _bindgen_ty_2 = 81;
-pub const NV_VGPU_MSG_FUNCTION_GMMU_UNREGISTER_FAULT_BUFFER: _bindgen_ty_2 = 82;
-pub const NV_VGPU_MSG_FUNCTION_GMMU_REGISTER_CLIENT_SHADOW_FAULT_BUFFER: _bindgen_ty_2 = 83;
-pub const NV_VGPU_MSG_FUNCTION_GMMU_UNREGISTER_CLIENT_SHADOW_FAULT_BUFFER: _bindgen_ty_2 = 84;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_VGPU_FB_USAGE: _bindgen_ty_2 = 85;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_NVFBC_SW_SESSION_UPDATE_INFO: _bindgen_ty_2 = 86;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_NVENC_SW_SESSION_UPDATE_INFO: _bindgen_ty_2 = 87;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_RESET_CHANNEL: _bindgen_ty_2 = 88;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_RESET_ISOLATED_CHANNEL: _bindgen_ty_2 = 89;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_HANDLE_VF_PRI_FAULT: _bindgen_ty_2 = 90;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_CLK_GET_EXTENDED_INFO: _bindgen_ty_2 = 91;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_BOOST: _bindgen_ty_2 = 92;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_VPSTATES_GET_CONTROL: _bindgen_ty_2 = 93;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_ZBC_CLEAR_TABLE: _bindgen_ty_2 = 94;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_COLOR_CLEAR: _bindgen_ty_2 = 95;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_DEPTH_CLEAR: _bindgen_ty_2 = 96;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_SCHEDULE: _bindgen_ty_2 = 97;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_TIMESLICE: _bindgen_ty_2 = 98;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_PREEMPT: _bindgen_ty_2 = 99;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_DISABLE_CHANNELS: _bindgen_ty_2 = 100;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_TSG_INTERLEAVE_LEVEL: _bindgen_ty_2 = 101;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_CHANNEL_INTERLEAVE_LEVEL: _bindgen_ty_2 = 102;
-pub const NV_VGPU_MSG_FUNCTION_GSP_RM_ALLOC: _bindgen_ty_2 = 103;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS_V2: _bindgen_ty_2 = 104;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_AES_ENCRYPT: _bindgen_ty_2 = 105;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_SESSION_KEY: _bindgen_ty_2 = 106;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_SESSION_KEY_STATUS: _bindgen_ty_2 = 107;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_CLEAR_ALL_SM_ERROR_STATES: _bindgen_ty_2 = 108;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_READ_ALL_SM_ERROR_STATES: _bindgen_ty_2 = 109;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_EXCEPTION_MASK: _bindgen_ty_2 = 110;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_PROMOTE_CTX: _bindgen_ty_2 = 111;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_CTXSW_PREEMPTION_BIND: _bindgen_ty_2 = 112;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_SET_CTXSW_PREEMPTION_MODE: _bindgen_ty_2 = 113;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_CTXSW_ZCULL_BIND: _bindgen_ty_2 = 114;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_INITIALIZE_CTX: _bindgen_ty_2 = 115;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_VASPACE_COPY_SERVER_RESERVED_PDES: _bindgen_ty_2 = 116;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_CLEAR_FAULTED_BIT: _bindgen_ty_2 = 117;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_LATEST_ECC_ADDRESSES: _bindgen_ty_2 = 118;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_MC_SERVICE_INTERRUPTS: _bindgen_ty_2 = 119;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DMA_SET_DEFAULT_VASPACE: _bindgen_ty_2 = 120;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_CE_PCE_MASK: _bindgen_ty_2 = 121;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_ZBC_CLEAR_TABLE_ENTRY: _bindgen_ty_2 = 122;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_NVLINK_PEER_ID_MASK: _bindgen_ty_2 = 123;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_NVLINK_STATUS: _bindgen_ty_2 = 124;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS: _bindgen_ty_2 = 125;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS_MATRIX: _bindgen_ty_2 = 126;
-pub const NV_VGPU_MSG_FUNCTION_RESERVED_0: _bindgen_ty_2 = 127;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_PM_AREA_SMPC: _bindgen_ty_2 = 128;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_HWPM_LEGACY: _bindgen_ty_2 = 129;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_B0CC_EXEC_REG_OPS: _bindgen_ty_2 = 130;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_BIND_PM_RESOURCES: _bindgen_ty_2 = 131;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SUSPEND_CONTEXT: _bindgen_ty_2 = 132;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_RESUME_CONTEXT: _bindgen_ty_2 = 133;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_EXEC_REG_OPS: _bindgen_ty_2 = 134;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_MMU_DEBUG: _bindgen_ty_2 = 135;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_READ_SINGLE_SM_ERROR_STATE: _bindgen_ty_2 = 136;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_CLEAR_SINGLE_SM_ERROR_STATE: _bindgen_ty_2 = 137;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_ERRBAR_DEBUG: _bindgen_ty_2 = 138;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_NEXT_STOP_TRIGGER_TYPE: _bindgen_ty_2 = 139;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_ALLOC_PMA_STREAM: _bindgen_ty_2 = 140;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_PMA_STREAM_UPDATE_GET_PUT: _bindgen_ty_2 = 141;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FB_GET_INFO_V2: _bindgen_ty_2 = 142;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_SET_CHANNEL_PROPERTIES: _bindgen_ty_2 = 143;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_GET_CTX_BUFFER_INFO: _bindgen_ty_2 = 144;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_KGR_GET_CTX_BUFFER_PTES: _bindgen_ty_2 = 145;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_EVICT_CTX: _bindgen_ty_2 = 146;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FB_GET_FS_INFO: _bindgen_ty_2 = 147;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GRMGR_GET_GR_FS_INFO: _bindgen_ty_2 = 148;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_STOP_CHANNEL: _bindgen_ty_2 = 149;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_PC_SAMPLING_MODE: _bindgen_ty_2 = 150;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_RATED_TDP_GET_STATUS: _bindgen_ty_2 = 151;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_RATED_TDP_SET_CONTROL: _bindgen_ty_2 = 152;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FREE_PMA_STREAM: _bindgen_ty_2 = 153;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_TIMER_SET_GR_TICK_FREQ: _bindgen_ty_2 = 154;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_SETUP_VF_ZOMBIE_SUBCTX_PDB: _bindgen_ty_2 = 155;
-pub const NV_VGPU_MSG_FUNCTION_GET_CONSOLIDATED_GR_STATIC_INFO: _bindgen_ty_2 = 156;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_SINGLE_SM_SINGLE_STEP: _bindgen_ty_2 = 157;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_GET_TPC_PARTITION_MODE: _bindgen_ty_2 = 158;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_SET_TPC_PARTITION_MODE: _bindgen_ty_2 = 159;
-pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_ALLOCATE: _bindgen_ty_2 = 160;
-pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_DESTROY: _bindgen_ty_2 = 161;
-pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_MAP: _bindgen_ty_2 = 162;
-pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_UNMAP: _bindgen_ty_2 = 163;
-pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_PUSH_STREAM: _bindgen_ty_2 = 164;
-pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_SET_HANDLES: _bindgen_ty_2 = 165;
-pub const NV_VGPU_MSG_FUNCTION_UVM_METHOD_STREAM_GUEST_PAGES_OPERATION: _bindgen_ty_2 = 166;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_QUIESCE_PMA_CHANNEL: _bindgen_ty_2 = 167;
-pub const NV_VGPU_MSG_FUNCTION_DCE_RM_INIT: _bindgen_ty_2 = 168;
-pub const NV_VGPU_MSG_FUNCTION_REGISTER_VIRTUAL_EVENT_BUFFER: _bindgen_ty_2 = 169;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_EVENT_BUFFER_UPDATE_GET: _bindgen_ty_2 = 170;
-pub const NV_VGPU_MSG_FUNCTION_GET_PLCABLE_ADDRESS_KIND: _bindgen_ty_2 = 171;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_LIMITS_SET_STATUS_V2: _bindgen_ty_2 = 172;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_SRIOV_PROMOTE_PMA_STREAM: _bindgen_ty_2 = 173;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_MMU_DEBUG_MODE: _bindgen_ty_2 = 174;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_PROMOTE_FAULT_METHOD_BUFFERS: _bindgen_ty_2 = 175;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FLCN_GET_CTX_BUFFER_SIZE: _bindgen_ty_2 = 176;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FLCN_GET_CTX_BUFFER_INFO: _bindgen_ty_2 = 177;
-pub const NV_VGPU_MSG_FUNCTION_DISABLE_CHANNELS: _bindgen_ty_2 = 178;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FABRIC_MEMORY_DESCRIBE: _bindgen_ty_2 = 179;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FABRIC_MEM_STATS: _bindgen_ty_2 = 180;
-pub const NV_VGPU_MSG_FUNCTION_SAVE_HIBERNATION_DATA: _bindgen_ty_2 = 181;
-pub const NV_VGPU_MSG_FUNCTION_RESTORE_HIBERNATION_DATA: _bindgen_ty_2 = 182;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_MEMSYS_SET_ZBC_REFERENCED: _bindgen_ty_2 = 183;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_CREATE: _bindgen_ty_2 = 184;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_DELETE: _bindgen_ty_2 = 185;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_GET_WORK_SUBMIT_TOKEN: _bindgen_ty_2 = 186;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX: _bindgen_ty_2 = 187;
-pub const NV_VGPU_MSG_FUNCTION_PMA_SCRUBBER_SHARED_BUFFER_GUEST_PAGES_OPERATION: _bindgen_ty_2 =
- 188;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_MASTER_GET_VIRTUAL_FUNCTION_ERROR_CONT_INTR_MASK:
- _bindgen_ty_2 = 189;
-pub const NV_VGPU_MSG_FUNCTION_SET_SYSMEM_DIRTY_PAGE_TRACKING_BUFFER: _bindgen_ty_2 = 190;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_P2P_CAPS: _bindgen_ty_2 = 191;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_BUS_SET_P2P_MAPPING: _bindgen_ty_2 = 192;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_BUS_UNSET_P2P_MAPPING: _bindgen_ty_2 = 193;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_FLA_SETUP_INSTANCE_MEM_BLOCK: _bindgen_ty_2 = 194;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_MIGRATABLE_OPS: _bindgen_ty_2 = 195;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_TOTAL_HS_CREDITS: _bindgen_ty_2 = 196;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_HS_CREDITS: _bindgen_ty_2 = 197;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_HS_CREDITS: _bindgen_ty_2 = 198;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_PM_AREA_PC_SAMPLER: _bindgen_ty_2 = 199;
-pub const NV_VGPU_MSG_FUNCTION_INVALIDATE_TLB: _bindgen_ty_2 = 200;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_QUERY_ECC_STATUS: _bindgen_ty_2 = 201;
-pub const NV_VGPU_MSG_FUNCTION_ECC_NOTIFIER_WRITE_ACK: _bindgen_ty_2 = 202;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_GET_MODE_MMU_DEBUG: _bindgen_ty_2 = 203;
-pub const NV_VGPU_MSG_FUNCTION_RM_API_CONTROL: _bindgen_ty_2 = 204;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_GPU_START_FABRIC_PROBE: _bindgen_ty_2 = 205;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_NVLINK_GET_INBAND_RECEIVED_DATA: _bindgen_ty_2 = 206;
-pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_DATA: _bindgen_ty_2 = 207;
-pub const NV_VGPU_MSG_FUNCTION_RESERVED_208: _bindgen_ty_2 = 208;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_GET_INFO_V2: _bindgen_ty_2 = 209;
-pub const NV_VGPU_MSG_FUNCTION_GET_BRAND_CAPS: _bindgen_ty_2 = 210;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_NVLINK_INBAND_SEND_DATA: _bindgen_ty_2 = 211;
-pub const NV_VGPU_MSG_FUNCTION_UPDATE_GPM_GUEST_BUFFER_INFO: _bindgen_ty_2 = 212;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_CONTROL_GSP_TRACE: _bindgen_ty_2 = 213;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_STENCIL_CLEAR: _bindgen_ty_2 = 214;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_VGPU_HEAP_STATS: _bindgen_ty_2 = 215;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_LIBOS_HEAP_STATS: _bindgen_ty_2 = 216;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_MMU_GCC_DEBUG: _bindgen_ty_2 = 217;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_GET_MODE_MMU_GCC_DEBUG: _bindgen_ty_2 = 218;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_HES: _bindgen_ty_2 = 219;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_RELEASE_HES: _bindgen_ty_2 = 220;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_CCU_PROF: _bindgen_ty_2 = 221;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_RELEASE_CCU_PROF: _bindgen_ty_2 = 222;
-pub const NV_VGPU_MSG_FUNCTION_RESERVED: _bindgen_ty_2 = 223;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_GET_CHIPLET_HS_CREDIT_POOL: _bindgen_ty_2 = 224;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_GET_HS_CREDITS_MAPPING: _bindgen_ty_2 = 225;
-pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_EXPORT: _bindgen_ty_2 = 226;
-pub const NV_VGPU_MSG_FUNCTION_NUM_FUNCTIONS: _bindgen_ty_2 = 227;
-pub type _bindgen_ty_2 = ffi::c_uint;
-pub const NV_VGPU_MSG_EVENT_FIRST_EVENT: _bindgen_ty_3 = 4096;
-pub const NV_VGPU_MSG_EVENT_GSP_INIT_DONE: _bindgen_ty_3 = 4097;
-pub const NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER: _bindgen_ty_3 = 4098;
-pub const NV_VGPU_MSG_EVENT_POST_EVENT: _bindgen_ty_3 = 4099;
-pub const NV_VGPU_MSG_EVENT_RC_TRIGGERED: _bindgen_ty_3 = 4100;
-pub const NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED: _bindgen_ty_3 = 4101;
-pub const NV_VGPU_MSG_EVENT_OS_ERROR_LOG: _bindgen_ty_3 = 4102;
-pub const NV_VGPU_MSG_EVENT_RG_LINE_INTR: _bindgen_ty_3 = 4103;
-pub const NV_VGPU_MSG_EVENT_GPUACCT_PERFMON_UTIL_SAMPLES: _bindgen_ty_3 = 4104;
-pub const NV_VGPU_MSG_EVENT_SIM_READ: _bindgen_ty_3 = 4105;
-pub const NV_VGPU_MSG_EVENT_SIM_WRITE: _bindgen_ty_3 = 4106;
-pub const NV_VGPU_MSG_EVENT_SEMAPHORE_SCHEDULE_CALLBACK: _bindgen_ty_3 = 4107;
-pub const NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT: _bindgen_ty_3 = 4108;
-pub const NV_VGPU_MSG_EVENT_VGPU_GSP_PLUGIN_TRIGGERED: _bindgen_ty_3 = 4109;
-pub const NV_VGPU_MSG_EVENT_PERF_GPU_BOOST_SYNC_LIMITS_CALLBACK: _bindgen_ty_3 = 4110;
-pub const NV_VGPU_MSG_EVENT_PERF_BRIDGELESS_INFO_UPDATE: _bindgen_ty_3 = 4111;
-pub const NV_VGPU_MSG_EVENT_VGPU_CONFIG: _bindgen_ty_3 = 4112;
-pub const NV_VGPU_MSG_EVENT_DISPLAY_MODESET: _bindgen_ty_3 = 4113;
-pub const NV_VGPU_MSG_EVENT_EXTDEV_INTR_SERVICE: _bindgen_ty_3 = 4114;
-pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_256: _bindgen_ty_3 = 4115;
-pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_512: _bindgen_ty_3 = 4116;
-pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_1024: _bindgen_ty_3 = 4117;
-pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_2048: _bindgen_ty_3 = 4118;
-pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_4096: _bindgen_ty_3 = 4119;
-pub const NV_VGPU_MSG_EVENT_TIMED_SEMAPHORE_RELEASE: _bindgen_ty_3 = 4120;
-pub const NV_VGPU_MSG_EVENT_NVLINK_IS_GPU_DEGRADED: _bindgen_ty_3 = 4121;
-pub const NV_VGPU_MSG_EVENT_PFM_REQ_HNDLR_STATE_SYNC_CALLBACK: _bindgen_ty_3 = 4122;
-pub const NV_VGPU_MSG_EVENT_NVLINK_FAULT_UP: _bindgen_ty_3 = 4123;
-pub const NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE: _bindgen_ty_3 = 4124;
-pub const NV_VGPU_MSG_EVENT_MIG_CI_CONFIG_UPDATE: _bindgen_ty_3 = 4125;
-pub const NV_VGPU_MSG_EVENT_UPDATE_GSP_TRACE: _bindgen_ty_3 = 4126;
-pub const NV_VGPU_MSG_EVENT_NVLINK_FATAL_ERROR_RECOVERY: _bindgen_ty_3 = 4127;
-pub const NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD: _bindgen_ty_3 = 4128;
-pub const NV_VGPU_MSG_EVENT_FECS_ERROR: _bindgen_ty_3 = 4129;
-pub const NV_VGPU_MSG_EVENT_RECOVERY_ACTION: _bindgen_ty_3 = 4130;
-pub const NV_VGPU_MSG_EVENT_NUM_EVENTS: _bindgen_ty_3 = 4131;
-pub type _bindgen_ty_3 = ffi::c_uint;
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS {
- pub totalVFs: u32_,
- pub firstVfOffset: u32_,
- pub vfFeatureMask: u32_,
- pub __bindgen_padding_0: [u8; 4usize],
- pub FirstVFBar0Address: u64_,
- pub FirstVFBar1Address: u64_,
- pub FirstVFBar2Address: u64_,
- pub bar0Size: u64_,
- pub bar1Size: u64_,
- pub bar2Size: u64_,
- pub b64bitBar0: u8_,
- pub b64bitBar1: u8_,
- pub b64bitBar2: u8_,
- pub bSriovEnabled: u8_,
- pub bSriovHeavyEnabled: u8_,
- pub bEmulateVFBar0TlbInvalidationRegister: u8_,
- pub bClientRmAllocatedCtxBuffer: u8_,
- pub bNonPowerOf2ChannelCountSupported: u8_,
- pub bVfResizableBAR1Supported: u8_,
- pub __bindgen_padding_1: [u8; 7usize],
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS {
- pub BoardID: u32_,
- pub chipSKU: [ffi::c_char; 9usize],
- pub chipSKUMod: [ffi::c_char; 5usize],
- pub __bindgen_padding_0: [u8; 2usize],
- pub skuConfigVersion: u32_,
- pub project: [ffi::c_char; 5usize],
- pub projectSKU: [ffi::c_char; 5usize],
- pub CDP: [ffi::c_char; 6usize],
- pub projectSKUMod: [ffi::c_char; 2usize],
- pub __bindgen_padding_1: [u8; 2usize],
- pub businessCycle: u32_,
-}
-pub type NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG = [u8_; 17usize];
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO {
- pub base: u64_,
- pub limit: u64_,
- pub reserved: u64_,
- pub performance: u32_,
- pub supportCompressed: u8_,
- pub supportISO: u8_,
- pub bProtected: u8_,
- pub blackList: NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS {
- pub numFBRegions: u32_,
- pub __bindgen_padding_0: [u8; 4usize],
- pub fbRegion: [NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO; 16usize],
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
-pub struct NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
- pub index: u32_,
- pub flags: u32_,
- pub length: u32_,
- pub data: [u8_; 256usize],
-}
-impl Default for NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct DOD_METHOD_DATA {
- pub status: u32_,
- pub acpiIdListLen: u32_,
- pub acpiIdList: [u32_; 16usize],
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct JT_METHOD_DATA {
- pub status: u32_,
- pub jtCaps: u32_,
- pub jtRevId: u16_,
- pub bSBIOSCaps: u8_,
- pub __bindgen_padding_0: u8,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct MUX_METHOD_DATA_ELEMENT {
- pub acpiId: u32_,
- pub mode: u32_,
- pub status: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct MUX_METHOD_DATA {
- pub tableLen: u32_,
- pub acpiIdMuxModeTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
- pub acpiIdMuxPartTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
- pub acpiIdMuxStateTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct CAPS_METHOD_DATA {
- pub status: u32_,
- pub optimusCaps: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct ACPI_METHOD_DATA {
- pub bValid: u8_,
- pub __bindgen_padding_0: [u8; 3usize],
- pub dodMethodData: DOD_METHOD_DATA,
- pub jtMethodData: JT_METHOD_DATA,
- pub muxMethodData: MUX_METHOD_DATA,
- pub capsMethodData: CAPS_METHOD_DATA,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS {
- pub headIndex: u32_,
- pub maxHResolution: u32_,
- pub maxVResolution: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS {
- pub numHeads: u32_,
- pub maxNumHeads: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct BUSINFO {
- pub deviceID: u16_,
- pub vendorID: u16_,
- pub subdeviceID: u16_,
- pub subvendorID: u16_,
- pub revisionID: u8_,
- pub __bindgen_padding_0: u8,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_VF_INFO {
- pub totalVFs: u32_,
- pub firstVFOffset: u32_,
- pub FirstVFBar0Address: u64_,
- pub FirstVFBar1Address: u64_,
- pub FirstVFBar2Address: u64_,
- pub b64bitBar0: u8_,
- pub b64bitBar1: u8_,
- pub b64bitBar2: u8_,
- pub __bindgen_padding_0: [u8; 5usize],
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_PCIE_CONFIG_REG {
- pub linkCap: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct EcidManufacturingInfo {
- pub ecidLow: u32_,
- pub ecidHigh: u32_,
- pub ecidExtended: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct FW_WPR_LAYOUT_OFFSET {
- pub nonWprHeapOffset: u64_,
- pub frtsOffset: u64_,
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
-pub struct GspStaticConfigInfo_t {
- pub grCapsBits: [u8_; 23usize],
- pub __bindgen_padding_0: u8,
- pub gidInfo: NV2080_CTRL_GPU_GET_GID_INFO_PARAMS,
- pub SKUInfo: NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS,
- pub __bindgen_padding_1: [u8; 4usize],
- pub fbRegionInfoParams: NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS,
- pub sriovCaps: NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS,
- pub sriovMaxGfid: u32_,
- pub engineCaps: [u32_; 3usize],
- pub poisonFuseEnabled: u8_,
- pub __bindgen_padding_2: [u8; 7usize],
- pub fb_length: u64_,
- pub fbio_mask: u64_,
- pub fb_bus_width: u32_,
- pub fb_ram_type: u32_,
- pub fbp_mask: u64_,
- pub l2_cache_size: u32_,
- pub gpuNameString: [u8_; 64usize],
- pub gpuShortNameString: [u8_; 64usize],
- pub gpuNameString_Unicode: [u16_; 64usize],
- pub bGpuInternalSku: u8_,
- pub bIsQuadroGeneric: u8_,
- pub bIsQuadroAd: u8_,
- pub bIsNvidiaNvs: u8_,
- pub bIsVgx: u8_,
- pub bGeforceSmb: u8_,
- pub bIsTitan: u8_,
- pub bIsTesla: u8_,
- pub bIsMobile: u8_,
- pub bIsGc6Rtd3Allowed: u8_,
- pub bIsGc8Rtd3Allowed: u8_,
- pub bIsGcOffRtd3Allowed: u8_,
- pub bIsGcoffLegacyAllowed: u8_,
- pub bIsMigSupported: u8_,
- pub RTD3GC6TotalBoardPower: u16_,
- pub RTD3GC6PerstDelay: u16_,
- pub __bindgen_padding_3: [u8; 2usize],
- pub bar1PdeBase: u64_,
- pub bar2PdeBase: u64_,
- pub bVbiosValid: u8_,
- pub __bindgen_padding_4: [u8; 3usize],
- pub vbiosSubVendor: u32_,
- pub vbiosSubDevice: u32_,
- pub bPageRetirementSupported: u8_,
- pub bSplitVasBetweenServerClientRm: u8_,
- pub bClRootportNeedsNosnoopWAR: u8_,
- pub __bindgen_padding_5: u8,
- pub displaylessMaxHeads: VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS,
- pub displaylessMaxResolution: VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS,
- pub __bindgen_padding_6: [u8; 4usize],
- pub displaylessMaxPixels: u64_,
- pub hInternalClient: u32_,
- pub hInternalDevice: u32_,
- pub hInternalSubdevice: u32_,
- pub bSelfHostedMode: u8_,
- pub bAtsSupported: u8_,
- pub bIsGpuUefi: u8_,
- pub bIsEfiInit: u8_,
- pub ecidInfo: [EcidManufacturingInfo; 2usize],
- pub fwWprLayoutOffset: FW_WPR_LAYOUT_OFFSET,
-}
-impl Default for GspStaticConfigInfo_t {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GspSystemInfo {
- pub gpuPhysAddr: u64_,
- pub gpuPhysFbAddr: u64_,
- pub gpuPhysInstAddr: u64_,
- pub gpuPhysIoAddr: u64_,
- pub nvDomainBusDeviceFunc: u64_,
- pub simAccessBufPhysAddr: u64_,
- pub notifyOpSharedSurfacePhysAddr: u64_,
- pub pcieAtomicsOpMask: u64_,
- pub consoleMemSize: u64_,
- pub maxUserVa: u64_,
- pub pciConfigMirrorBase: u32_,
- pub pciConfigMirrorSize: u32_,
- pub PCIDeviceID: u32_,
- pub PCISubDeviceID: u32_,
- pub PCIRevisionID: u32_,
- pub pcieAtomicsCplDeviceCapMask: u32_,
- pub oorArch: u8_,
- pub __bindgen_padding_0: [u8; 7usize],
- pub clPdbProperties: u64_,
- pub Chipset: u32_,
- pub bGpuBehindBridge: u8_,
- pub bFlrSupported: u8_,
- pub b64bBar0Supported: u8_,
- pub bMnocAvailable: u8_,
- pub chipsetL1ssEnable: u32_,
- pub bUpstreamL0sUnsupported: u8_,
- pub bUpstreamL1Unsupported: u8_,
- pub bUpstreamL1PorSupported: u8_,
- pub bUpstreamL1PorMobileOnly: u8_,
- pub bSystemHasMux: u8_,
- pub upstreamAddressValid: u8_,
- pub FHBBusInfo: BUSINFO,
- pub chipsetIDInfo: BUSINFO,
- pub __bindgen_padding_1: [u8; 2usize],
- pub acpiMethodData: ACPI_METHOD_DATA,
- pub hypervisorType: u32_,
- pub bIsPassthru: u8_,
- pub __bindgen_padding_2: [u8; 7usize],
- pub sysTimerOffsetNs: u64_,
- pub gspVFInfo: GSP_VF_INFO,
- pub bIsPrimary: u8_,
- pub isGridBuild: u8_,
- pub __bindgen_padding_3: [u8; 2usize],
- pub pcieConfigReg: GSP_PCIE_CONFIG_REG,
- pub gridBuildCsp: u32_,
- pub bPreserveVideoMemoryAllocations: u8_,
- pub bTdrEventSupported: u8_,
- pub bFeatureStretchVblankCapable: u8_,
- pub bEnableDynamicGranularityPageArrays: u8_,
- pub bClockBoostSupported: u8_,
- pub bRouteDispIntrsToCPU: u8_,
- pub __bindgen_padding_4: [u8; 6usize],
- pub hostPageSize: u64_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct MESSAGE_QUEUE_INIT_ARGUMENTS {
- pub sharedMemPhysAddr: u64_,
- pub pageTableEntryCount: u32_,
- pub __bindgen_padding_0: [u8; 4usize],
- pub cmdQueueOffset: u64_,
- pub statQueueOffset: u64_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_SR_INIT_ARGUMENTS {
- pub oldLevel: u32_,
- pub flags: u32_,
- pub bInPMTransition: u8_,
- pub __bindgen_padding_0: [u8; 3usize],
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_ARGUMENTS_CACHED {
- pub messageQueueInitArguments: MESSAGE_QUEUE_INIT_ARGUMENTS,
- pub srInitArguments: GSP_SR_INIT_ARGUMENTS,
- pub gpuInstance: u32_,
- pub bDmemStack: u8_,
- pub __bindgen_padding_0: [u8; 7usize],
- pub profilerArgs: GSP_ARGUMENTS_CACHED__bindgen_ty_1,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_1 {
- pub pa: u64_,
- pub size: u64_,
-}
-#[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
-pub union rpc_message_rpc_union_field_v03_00 {
- pub spare: u32_,
- pub cpuRmGfid: u32_,
-}
-impl Default for rpc_message_rpc_union_field_v03_00 {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-pub type rpc_message_rpc_union_field_v = rpc_message_rpc_union_field_v03_00;
-#[repr(C)]
-#[derive(MaybeZeroable)]
-pub struct rpc_message_header_v03_00 {
- pub header_version: u32_,
- pub signature: u32_,
- pub length: u32_,
- pub function: u32_,
- pub rpc_result: u32_,
- pub rpc_result_private: u32_,
- pub sequence: u32_,
- pub u: rpc_message_rpc_union_field_v,
- pub rpc_message_data: __IncompleteArrayField<u8_>,
-}
-impl Default for rpc_message_header_v03_00 {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-pub type rpc_message_header_v = rpc_message_header_v03_00;
-#[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
-pub struct GspFwWprMeta {
- pub magic: u64_,
- pub revision: u64_,
- pub sysmemAddrOfRadix3Elf: u64_,
- pub sizeOfRadix3Elf: u64_,
- pub sysmemAddrOfBootloader: u64_,
- pub sizeOfBootloader: u64_,
- pub bootloaderCodeOffset: u64_,
- pub bootloaderDataOffset: u64_,
- pub bootloaderManifestOffset: u64_,
- pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1,
- pub gspFwRsvdStart: u64_,
- pub nonWprHeapOffset: u64_,
- pub nonWprHeapSize: u64_,
- pub gspFwWprStart: u64_,
- pub gspFwHeapOffset: u64_,
- pub gspFwHeapSize: u64_,
- pub gspFwOffset: u64_,
- pub bootBinOffset: u64_,
- pub frtsOffset: u64_,
- pub frtsSize: u64_,
- pub gspFwWprEnd: u64_,
- pub fbSize: u64_,
- pub vgaWorkspaceOffset: u64_,
- pub vgaWorkspaceSize: u64_,
- pub bootCount: u64_,
- pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2,
- pub gspFwHeapVfPartitionCount: u8_,
- pub flags: u8_,
- pub padding: [u8_; 2usize],
- pub pmuReservedSize: u32_,
- pub verified: u64_,
-}
-#[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
-pub union GspFwWprMeta__bindgen_ty_1 {
- pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1__bindgen_ty_1,
- pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_1__bindgen_ty_2,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_1 {
- pub sysmemAddrOfSignature: u64_,
- pub sizeOfSignature: u64_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_2 {
- pub gspFwHeapFreeListWprOffset: u32_,
- pub unused0: u32_,
- pub unused1: u64_,
-}
-impl Default for GspFwWprMeta__bindgen_ty_1 {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-#[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
-pub union GspFwWprMeta__bindgen_ty_2 {
- pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_2__bindgen_ty_1,
- pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2__bindgen_ty_2,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 {
- pub partitionRpcAddr: u64_,
- pub partitionRpcRequestOffset: u16_,
- pub partitionRpcReplyOffset: u16_,
- pub elfCodeOffset: u32_,
- pub elfDataOffset: u32_,
- pub elfCodeSize: u32_,
- pub elfDataSize: u32_,
- pub lsUcodeVersion: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_2 {
- pub partitionRpcPadding: [u32_; 4usize],
- pub sysmemAddrOfCrashReportQueue: u64_,
- pub sizeOfCrashReportQueue: u32_,
- pub lsUcodeVersionPadding: [u32_; 1usize],
-}
-impl Default for GspFwWprMeta__bindgen_ty_2 {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-impl Default for GspFwWprMeta {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-pub type LibosAddress = u64_;
-pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_NONE: LibosMemoryRegionKind = 0;
-pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS: LibosMemoryRegionKind = 1;
-pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_RADIX3: LibosMemoryRegionKind = 2;
-pub type LibosMemoryRegionKind = ffi::c_uint;
-pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_NONE: LibosMemoryRegionLoc = 0;
-pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM: LibosMemoryRegionLoc = 1;
-pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_FB: LibosMemoryRegionLoc = 2;
-pub type LibosMemoryRegionLoc = ffi::c_uint;
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct LibosMemoryRegionInitArgument {
- pub id8: LibosAddress,
- pub pa: LibosAddress,
- pub size: LibosAddress,
- pub kind: u8_,
- pub loc: u8_,
- pub __bindgen_padding_0: [u8; 6usize],
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct PACKED_REGISTRY_ENTRY {
- pub nameOffset: u32_,
- pub type_: u8_,
- pub __bindgen_padding_0: [u8; 3usize],
- pub data: u32_,
- pub length: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, MaybeZeroable)]
-pub struct PACKED_REGISTRY_TABLE {
- pub size: u32_,
- pub numEntries: u32_,
- pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct msgqTxHeader {
- pub version: u32_,
- pub size: u32_,
- pub msgSize: u32_,
- pub msgCount: u32_,
- pub writePtr: u32_,
- pub flags: u32_,
- pub rxHdrOff: u32_,
- pub entryOff: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct msgqRxHeader {
- pub readPtr: u32_,
-}
-#[repr(C)]
-#[repr(align(8))]
-#[derive(MaybeZeroable)]
-pub struct GSP_MSG_QUEUE_ELEMENT {
- pub authTagBuffer: [u8_; 16usize],
- pub aadBuffer: [u8_; 16usize],
- pub checkSum: u32_,
- pub seqNum: u32_,
- pub elemCount: u32_,
- pub __bindgen_padding_0: [u8; 4usize],
- pub rpc: rpc_message_header_v,
-}
-impl Default for GSP_MSG_QUEUE_ELEMENT {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-pub const GSP_DMA_TARGET_GSP_DMA_TARGET_LOCAL_FB: GSP_DMA_TARGET = 0;
-pub const GSP_DMA_TARGET_GSP_DMA_TARGET_COHERENT_SYSTEM: GSP_DMA_TARGET = 1;
-pub const GSP_DMA_TARGET_GSP_DMA_TARGET_NONCOHERENT_SYSTEM: GSP_DMA_TARGET = 2;
-pub const GSP_DMA_TARGET_GSP_DMA_TARGET_COUNT: GSP_DMA_TARGET = 3;
-pub type GSP_DMA_TARGET = ffi::c_uint;
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_FMC_INIT_PARAMS {
- pub regkeys: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
-pub struct GSP_ACR_BOOT_GSP_RM_PARAMS {
- pub target: GSP_DMA_TARGET,
- pub gspRmDescSize: u32_,
- pub gspRmDescOffset: u64_,
- pub wprCarveoutOffset: u64_,
- pub wprCarveoutSize: u32_,
- pub bIsGspRmBoot: u8_,
- pub __bindgen_padding_0: [u8; 3usize],
-}
-impl Default for GSP_ACR_BOOT_GSP_RM_PARAMS {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
-pub struct GSP_RM_PARAMS {
- pub target: GSP_DMA_TARGET,
- pub __bindgen_padding_0: [u8; 4usize],
- pub bootArgsOffset: u64_,
-}
-impl Default for GSP_RM_PARAMS {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
-pub struct GSP_SPDM_PARAMS {
- pub target: GSP_DMA_TARGET,
- pub __bindgen_padding_0: [u8; 4usize],
- pub payloadBufferOffset: u64_,
- pub payloadBufferSize: u32_,
- pub __bindgen_padding_1: [u8; 4usize],
-}
-impl Default for GSP_SPDM_PARAMS {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone, MaybeZeroable)]
-pub struct GSP_FMC_BOOT_PARAMS {
- pub initParams: GSP_FMC_INIT_PARAMS,
- pub __bindgen_padding_0: [u8; 4usize],
- pub bootGspRmParams: GSP_ACR_BOOT_GSP_RM_PARAMS,
- pub gspRmParams: GSP_RM_PARAMS,
- pub gspSpdmParams: GSP_SPDM_PARAMS,
-}
-impl Default for GSP_FMC_BOOT_PARAMS {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct rpc_unloading_guest_driver_v1F_07 {
- pub bInPMTransition: u8_,
- pub bGc6Entering: u8_,
- pub __bindgen_padding_0: [u8; 2usize],
- pub newLevel: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, MaybeZeroable)]
-pub struct rpc_run_cpu_sequencer_v17_00 {
- pub bufferSizeDWord: u32_,
- pub cmdIndex: u32_,
- pub regSaveArea: [u32_; 8usize],
- pub commandBuffer: __IncompleteArrayField<u32_>,
-}
-pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE: GSP_SEQ_BUF_OPCODE = 0;
-pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY: GSP_SEQ_BUF_OPCODE = 1;
-pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL: GSP_SEQ_BUF_OPCODE = 2;
-pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US: GSP_SEQ_BUF_OPCODE = 3;
-pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE: GSP_SEQ_BUF_OPCODE = 4;
-pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET: GSP_SEQ_BUF_OPCODE = 5;
-pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START: GSP_SEQ_BUF_OPCODE = 6;
-pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT: GSP_SEQ_BUF_OPCODE = 7;
-pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME: GSP_SEQ_BUF_OPCODE = 8;
-pub type GSP_SEQ_BUF_OPCODE = ffi::c_uint;
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_SEQ_BUF_PAYLOAD_REG_WRITE {
- pub addr: u32_,
- pub val: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_SEQ_BUF_PAYLOAD_REG_MODIFY {
- pub addr: u32_,
- pub mask: u32_,
- pub val: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_SEQ_BUF_PAYLOAD_REG_POLL {
- pub addr: u32_,
- pub mask: u32_,
- pub val: u32_,
- pub timeout: u32_,
- pub error: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_SEQ_BUF_PAYLOAD_DELAY_US {
- pub val: u32_,
-}
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, MaybeZeroable)]
-pub struct GSP_SEQ_BUF_PAYLOAD_REG_STORE {
- pub addr: u32_,
- pub index: u32_,
-}
-#[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
-pub struct GSP_SEQUENCER_BUFFER_CMD {
- pub opCode: GSP_SEQ_BUF_OPCODE,
- pub payload: GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1,
-}
-#[repr(C)]
-#[derive(Copy, Clone, MaybeZeroable)]
-pub union GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1 {
- pub regWrite: GSP_SEQ_BUF_PAYLOAD_REG_WRITE,
- pub regModify: GSP_SEQ_BUF_PAYLOAD_REG_MODIFY,
- pub regPoll: GSP_SEQ_BUF_PAYLOAD_REG_POLL,
- pub delayUs: GSP_SEQ_BUF_PAYLOAD_DELAY_US,
- pub regStore: GSP_SEQ_BUF_PAYLOAD_REG_STORE,
-}
-impl Default for GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1 {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
-impl Default for GSP_SEQUENCER_BUFFER_CMD {
- fn default() -> Self {
- let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
- s.assume_init()
- }
- }
-}
--
2.55.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH 02/27] gpu: nova-core: extract radix3 page table into its own module
2026-08-19 3:51 ` [PATCH 02/27] gpu: nova-core: extract radix3 page table into its own module John Hubbard
@ 2026-08-19 17:37 ` Timur Tabi
2026-08-20 1:05 ` John Hubbard
0 siblings, 1 reply; 57+ messages in thread
From: Timur Tabi @ 2026-08-19 17:37 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On Tue, 2026-08-18 at 20:51 -0700, John Hubbard wrote:
>
> let elf = {
> const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
> - let elf_size = u64::from_safe_cast(gsp_fw.size);
> + let elf_size = u64::from_safe_cast(gsp_fw.size());
> let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
Just FYI, the GSP images are no longer ELFs. With the transition to TLV, the actual GSP image
section is extracted from the original ELF, and that image is not an ELF on GA102+.
$ file /lib/firmware/nvidia/ga102/gsp/gsp.bin
/lib/firmware/nvidia/ga102/gsp/gsp.bin: OpenPGP Secret Key
It's not a PGP key either. This is just `file` being confused.
I probably should have changed this when I removed all the ELF code in my TLV patchset, but I
didn't notice it.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 05/27] gpu: nova-core: zero-pad radix3 page table levels to page boundary
2026-08-19 3:51 ` [PATCH 05/27] gpu: nova-core: zero-pad radix3 page table levels to page boundary John Hubbard
@ 2026-08-19 17:41 ` Timur Tabi
2026-08-20 2:18 ` John Hubbard
0 siblings, 1 reply; 57+ messages in thread
From: Timur Tabi @ 2026-08-19 17:41 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On Tue, 2026-08-18 at 20:51 -0700, John Hubbard wrote:
> diff --git a/drivers/gpu/nova-core/firmware/radix3.rs b/drivers/gpu/nova-
> core/firmware/radix3.rs
> index b60611c7bea0..f14ad4e82d3d 100644
> --- a/drivers/gpu/nova-core/firmware/radix3.rs
> +++ b/drivers/gpu/nova-core/firmware/radix3.rs
> @@ -67,22 +67,18 @@ pub(crate) fn new<'a>(
> Ok(try_pin_init!(Self {
> data <- SGTable::new(dev, data, DataDirection::ToDevice, GFP_KERNEL),
> level2 <- {
> - VVec::<u8>::with_capacity(
> - data.iter().count() * core::mem::size_of::<u64>(),
> - GFP_KERNEL,
> - )
> - .map_err(|_| ENOMEM)
> - .and_then(|level2| map_into_lvl(&data, level2))
> - .map(|level2| SGTable::new(dev, level2, DataDirection::ToDevice,
> GFP_KERNEL))?
> + let level2 = VVec::<u8>::with_capacity(lvl_size(&data), GFP_KERNEL)
> + .map_err(|_| ENOMEM)
> + .and_then(|level2| map_into_lvl(&data, level2))?;
> +
> + SGTable::new(dev, level2, DataDirection::ToDevice, GFP_KERNEL)
Could you use the new Vec::zeroed() to get a buffer that's already all zeroed-out?
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 07/27] gpu: nova-core: add optional ucodes firmware loading
2026-08-19 3:52 ` [PATCH 07/27] gpu: nova-core: add optional ucodes firmware loading John Hubbard
@ 2026-08-19 17:55 ` Timur Tabi
2026-08-20 2:22 ` John Hubbard
2026-08-19 18:41 ` Timur Tabi
1 sibling, 1 reply; 57+ messages in thread
From: Timur Tabi @ 2026-08-19 17:55 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
> + if file.is_empty()
> + || matches!(file, "." | "..")
> + || file
> + .bytes()
> + .any(|b| b == b'/' || b == b'\\' || b.is_ascii_control())
> + {
> + return Err(EINVAL);
> + }
I'm not sure if this is needed because request_into_buf() will reject any crazy filename that
would result from an invalid FILE tag.
> +
> + let size = usize::from_safe_cast(self.get_u32(b"SIZE")?);
> + if size == 0 {
> + return Err(EINVAL); // TODO: Use ENODATA once available.
> + }
FYI, ENODATA is already in linux-next.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 08/27] gpu: nova-core: add LIBOS3 log buffers and state monitor buffer
2026-08-19 3:52 ` [PATCH 08/27] gpu: nova-core: add LIBOS3 log buffers and state monitor buffer John Hubbard
@ 2026-08-19 18:12 ` Timur Tabi
2026-08-20 2:26 ` John Hubbard
0 siblings, 1 reply; 57+ messages in thread
From: Timur Tabi @ 2026-08-19 18:12 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
> +/// Matches the registry defaults for the init, interrupt, RM, and MNOC tasks
> +/// (`NV_REG_STR_RM_GSP_LOG_BUFFER_SIZE_TASK_*_DEFAULT`).
> +type TaskLogBuffer = LogBuffer<LOG_BUFFER_SIZE, RM_LOG_BUFFER_NUM_PAGES>;
> +
> +/// Log buffer for a task that GSP-RM gives a single page.
> +///
> +/// Matches the size GSP-RM hardcodes for the root and RM state monitor tasks.
> +type SmallLogBuffer = LogBuffer<GSP_PAGE_SIZE, 1>;
>
> -impl LogBuffer {
> +impl<const SIZE: usize, const NUM_PAGES: usize> LogBuffer<SIZE, NUM_PAGES> {
> /// Creates a new `LogBuffer` mapped on `dev`.
> fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
> + build_assert!(SIZE == NUM_PAGES * GSP_PAGE_SIZE);
Why do we need to specify both NUM_PAGES and SIZE? We can always derive one from the other.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 09/27] gpu: nova-core: add build ID headers to debugfs log buffer dumps
2026-08-19 3:52 ` [PATCH 09/27] gpu: nova-core: add build ID headers to debugfs log buffer dumps John Hubbard
@ 2026-08-19 18:20 ` Timur Tabi
2026-08-20 1:00 ` John Hubbard
0 siblings, 1 reply; 57+ messages in thread
From: Timur Tabi @ 2026-08-19 18:20 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
> diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
> index dfd41364cc77..e0befe84aa3e 100644
> --- a/drivers/gpu/nova-core/firmware.rs
> +++ b/drivers/gpu/nova-core/firmware.rs
> @@ -31,6 +31,40 @@
> pub(crate) mod riscv;
> pub(crate) mod tlv;
>
> +/// Maximum length of a build ID, matching Open RM's `BUILD_ID_MAX_LENGTH`.
> +const BUILD_ID_MAX_LENGTH: usize = 32;
Um, I recently made a change to the script that expands the build ID from a 32-byte hex
[3] tag="BLID" length=20
00000000 b5 9b b5 6c 76 7e 1e 34 ef 86 45 c6 73 31 f6 a6 |...lv~.4..E.s1..|
00000010 5b 8a 5c 3e |[.\>|
to a 64-byte string:
[7] tag="BLID" length=40
00000000 39 65 37 64 39 35 66 35 32 62 63 30 36 66 37 63 |9e7d95f52bc06f7c|
00000010 66 31 61 34 36 37 37 35 61 64 34 35 30 63 63 63 |f1a46775ad450ccc|
00000020 65 66 66 34 35 35 65 34 |eff455e4|
I'm assuming you want me to change that back? I didn't realize the build ID was actually going
to be used by the driver.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 10/27] gpu: nova-core: rename the FbRanges elf field to fw_image
2026-08-19 3:52 ` [PATCH 10/27] gpu: nova-core: rename the FbRanges elf field to fw_image John Hubbard
@ 2026-08-19 18:21 ` Timur Tabi
2026-08-20 1:01 ` John Hubbard
0 siblings, 1 reply; 57+ messages in thread
From: Timur Tabi @ 2026-08-19 18:21 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
> The framebuffer region this field describes holds the GSP firmware
> image. It is an ELF under 570.144 and is not one under the firmware this
> series moves to, so name the field for what it holds rather than for its
> current format.
It's not an ELF under the 570 either. It's only an ELF with non-TLV.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 07/27] gpu: nova-core: add optional ucodes firmware loading
2026-08-19 3:52 ` [PATCH 07/27] gpu: nova-core: add optional ucodes firmware loading John Hubbard
2026-08-19 17:55 ` Timur Tabi
@ 2026-08-19 18:41 ` Timur Tabi
2026-08-20 2:31 ` John Hubbard
1 sibling, 1 reply; 57+ messages in thread
From: Timur Tabi @ 2026-08-19 18:41 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
> + /// Loads the payload described by this TLV into owned memory.
> + ///
> + /// The generic firmware TLV representation is either an inline `BLOB`, or a `FILE`
> basename
> + /// together with its `SIZE`. Mixing the representations or specifying only half of the
> file
> + /// representation is invalid. Referenced files are constrained to the metadata file's
> + /// directory.
> + pub(crate) fn load_blob_or_file(
> + &self,
> + dev: &device::Device,
> + chipset: gpu::Chipset,
> + ) -> Result<VVec<u8>> {
> + let has_blob = self.contains(b"BLOB");
> + let has_file = self.contains(b"FILE");
> + let has_size = self.contains(b"SIZE");
The problem with this approach is that it allocates a buffer for the BLOB and memcpys the data,
even though that's not necessary if you expect the TLV to have a BLOB tag. It's highly unlikely
that the driver will ever not know in advance whether a given TLV has a BLOB or a FILE tag.
We should not encourage the driver to use this function for all images. Instead, I would like
to see a comment that stipulates that this function should be used in only two cases:
1) We will want to keep the image data after the TLV goes out of scope, so we want the BLOB
copied into a VVec anyway.
2) We expect the TLV to have a FILE tag, but obviously it shouldn't fail if it doesn't.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 25/27] gpu: nova-core: switch to the r000 GSP firmware
[not found] ` <20260819035221.336390-26-jhubbard@nvidia.com>
@ 2026-08-19 18:58 ` Timur Tabi
2026-08-20 1:02 ` John Hubbard
0 siblings, 1 reply; 57+ messages in thread
From: Timur Tabi @ 2026-08-19 18:58 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
> // Perform the chipset-specific boot sequence, and retrieve the unload bundle.
> let unload_bundle = hal.boot(&self, &mut ctx, &gsp_fw)?.or_else(|| {
> dev_warn!(dev, "The GSP won't be able to unload properly on unbind.\n");
> @@ -96,17 +122,41 @@ pub(crate) fn boot(
>
> dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(),);
>
> - self.cmdq
> - .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
> - self.cmdq
> - .send_command_no_wait(bar, commands::SetRegistry::new(ctx.vgpu.state())?)?;
> -
> - hal.post_boot(&self, ctx, &gsp_fw)?;
FYI, my Turing fix moves the send_command_no_wait() calls to above the hal.boot() line, where it
belongs, so you'll need to rebase on top of it.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
` (26 preceding siblings ...)
[not found] ` <20260819035221.336390-26-jhubbard@nvidia.com>
@ 2026-08-19 19:25 ` Timur Tabi
27 siblings, 0 replies; 57+ messages in thread
From: Timur Tabi @ 2026-08-19 19:25 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On Tue, 2026-08-18 at 20:51 -0700, John Hubbard wrote:
> Nova-core boots the 570.144 GSP firmware today. This series moves the
> driver to the r000 firmware ABI, which is stable across releases, so
> firmware file names no longer carry a version number.
>
> The actual firmware is not yet publicly available, so this series is for
> early review, and should not be merged until the corresponding firmware
> shows up.
I'm not familiar with the new boot process for "r000", so FWIW:
Reviewed-by: Timur Tabi <ttabi@nvidia.com>
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 09/27] gpu: nova-core: add build ID headers to debugfs log buffer dumps
2026-08-19 18:20 ` Timur Tabi
@ 2026-08-20 1:00 ` John Hubbard
2026-08-20 1:30 ` Timur Tabi
0 siblings, 1 reply; 57+ messages in thread
From: John Hubbard @ 2026-08-20 1:00 UTC (permalink / raw)
To: Timur Tabi, Alexandre Courbot, dakr@kernel.org
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On 8/19/26 11:20 AM, Timur Tabi wrote:
> On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
>
>
>> diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
>> index dfd41364cc77..e0befe84aa3e 100644
>> --- a/drivers/gpu/nova-core/firmware.rs
>> +++ b/drivers/gpu/nova-core/firmware.rs
>> @@ -31,6 +31,40 @@
>> pub(crate) mod riscv;
>> pub(crate) mod tlv;
>>
>> +/// Maximum length of a build ID, matching Open RM's `BUILD_ID_MAX_LENGTH`.
>> +const BUILD_ID_MAX_LENGTH: usize = 32;
>
> Um, I recently made a change to the script that expands the build ID from a 32-byte hex
>
> [3] tag="BLID" length=20
> 00000000 b5 9b b5 6c 76 7e 1e 34 ef 86 45 c6 73 31 f6 a6 |...lv~.4..E.s1..|
> 00000010 5b 8a 5c 3e |[.\>|
>
> to a 64-byte string:
>
> [7] tag="BLID" length=40
> 00000000 39 65 37 64 39 35 66 35 32 62 63 30 36 66 37 63 |9e7d95f52bc06f7c|
> 00000010 66 31 61 34 36 37 37 35 61 64 34 35 30 63 63 63 |f1a46775ad450ccc|
> 00000020 65 66 66 34 35 35 65 34 |eff455e4|
>
> I'm assuming you want me to change that back? I didn't realize the build ID was actually going
> to be used by the driver.
Well, let's do whatever is best. If you really want/need BLID to be 64 bytes
then I can update this series to match.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 10/27] gpu: nova-core: rename the FbRanges elf field to fw_image
2026-08-19 18:21 ` Timur Tabi
@ 2026-08-20 1:01 ` John Hubbard
0 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-20 1:01 UTC (permalink / raw)
To: Timur Tabi, Alexandre Courbot, dakr@kernel.org
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On 8/19/26 11:21 AM, Timur Tabi wrote:
> On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
>> The framebuffer region this field describes holds the GSP firmware
>> image. It is an ELF under 570.144 and is not one under the firmware this
>> series moves to, so name the field for what it holds rather than for its
>> current format.
>
> It's not an ELF under the 570 either. It's only an ELF with non-TLV.
Yes. I'll fix that to reflect that.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 25/27] gpu: nova-core: switch to the r000 GSP firmware
2026-08-19 18:58 ` [PATCH 25/27] gpu: nova-core: switch to the r000 GSP firmware Timur Tabi
@ 2026-08-20 1:02 ` John Hubbard
0 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-20 1:02 UTC (permalink / raw)
To: Timur Tabi, Alexandre Courbot, dakr@kernel.org
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On 8/19/26 11:58 AM, Timur Tabi wrote:
> On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
...>> @@ -96,17 +122,41 @@ pub(crate) fn boot(
>>
>> dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(),);
>>
>> - self.cmdq
>> - .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
>> - self.cmdq
>> - .send_command_no_wait(bar, commands::SetRegistry::new(ctx.vgpu.state())?)?;
>> -
>> - hal.post_boot(&self, ctx, &gsp_fw)?;
>
> FYI, my Turing fix moves the send_command_no_wait() calls to above the hal.boot() line, where it
> belongs, so you'll need to rebase on top of it.
>
Thanks for the heads up on that!
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 02/27] gpu: nova-core: extract radix3 page table into its own module
2026-08-19 17:37 ` Timur Tabi
@ 2026-08-20 1:05 ` John Hubbard
0 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-20 1:05 UTC (permalink / raw)
To: Timur Tabi, Alexandre Courbot, dakr@kernel.org
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On 8/19/26 10:37 AM, Timur Tabi wrote:
> On Tue, 2026-08-18 at 20:51 -0700, John Hubbard wrote:
>>
>> let elf = {
>> const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
>> - let elf_size = u64::from_safe_cast(gsp_fw.size);
>> + let elf_size = u64::from_safe_cast(gsp_fw.size());
>> let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
>
> Just FYI, the GSP images are no longer ELFs. With the transition to TLV, the actual GSP image
> section is extracted from the original ELF, and that image is not an ELF on GA102+.
>
Yes. In fact I removed all mention of ELF and elf, by the end of this
series.
> $ file /lib/firmware/nvidia/ga102/gsp/gsp.bin
> /lib/firmware/nvidia/ga102/gsp/gsp.bin: OpenPGP Secret Key
>
> It's not a PGP key either. This is just `file` being confused.
lol
>
> I probably should have changed this when I removed all the ELF code in my TLV patchset, but I
> didn't notice it.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 09/27] gpu: nova-core: add build ID headers to debugfs log buffer dumps
2026-08-20 1:00 ` John Hubbard
@ 2026-08-20 1:30 ` Timur Tabi
2026-08-20 2:03 ` John Hubbard
0 siblings, 1 reply; 57+ messages in thread
From: Timur Tabi @ 2026-08-20 1:30 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On Wed, 2026-08-19 at 18:00 -0700, John Hubbard wrote:
> > I'm assuming you want me to change that back? I didn't realize the build ID was actually
> > going
> > to be used by the driver.
>
> Well, let's do whatever is best. If you really want/need BLID to be 64 bytes
> then I can update this series to match.
You would need to run some ascii_to_hex command on the string to turn it back to 32 bytes, which
is silly. I made it 64 bytes because I thought the only use case for it was to display it.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 09/27] gpu: nova-core: add build ID headers to debugfs log buffer dumps
2026-08-20 1:30 ` Timur Tabi
@ 2026-08-20 2:03 ` John Hubbard
0 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-20 2:03 UTC (permalink / raw)
To: Timur Tabi, Alexandre Courbot, dakr@kernel.org
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On 8/19/26 6:30 PM, Timur Tabi wrote:
> On Wed, 2026-08-19 at 18:00 -0700, John Hubbard wrote:
>>> I'm assuming you want me to change that back? I didn't realize the build ID was actually
>>> going
>>> to be used by the driver.
>>
>> Well, let's do whatever is best. If you really want/need BLID to be 64 bytes
>> then I can update this series to match.
>
> You would need to run some ascii_to_hex command on the string to turn it back to 32 bytes, which
> is silly. I made it 64 bytes because I thought the only use case for it was to display it.
OK, yes, please change it back, then. Having BLID here is of course
incredibly convenient, because we can avoid all kinds of difficulty
in decoding the logs. The decoder really prefers having this, and
then it does the right thing.
I've tested log decoding with this series and it is working nicely,
btw.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 05/27] gpu: nova-core: zero-pad radix3 page table levels to page boundary
2026-08-19 17:41 ` Timur Tabi
@ 2026-08-20 2:18 ` John Hubbard
0 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-20 2:18 UTC (permalink / raw)
To: Timur Tabi, Alexandre Courbot, dakr@kernel.org
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On 8/19/26 10:41 AM, Timur Tabi wrote:
> On Tue, 2026-08-18 at 20:51 -0700, John Hubbard wrote:
>> diff --git a/drivers/gpu/nova-core/firmware/radix3.rs b/drivers/gpu/nova-
>> core/firmware/radix3.rs
>> index b60611c7bea0..f14ad4e82d3d 100644
>> --- a/drivers/gpu/nova-core/firmware/radix3.rs
>> +++ b/drivers/gpu/nova-core/firmware/radix3.rs
>> @@ -67,22 +67,18 @@ pub(crate) fn new<'a>(
>> Ok(try_pin_init!(Self {
>> data <- SGTable::new(dev, data, DataDirection::ToDevice, GFP_KERNEL),
>> level2 <- {
>> - VVec::<u8>::with_capacity(
>> - data.iter().count() * core::mem::size_of::<u64>(),
>> - GFP_KERNEL,
>> - )
>> - .map_err(|_| ENOMEM)
>> - .and_then(|level2| map_into_lvl(&data, level2))
>> - .map(|level2| SGTable::new(dev, level2, DataDirection::ToDevice,
>> GFP_KERNEL))?
>> + let level2 = VVec::<u8>::with_capacity(lvl_size(&data), GFP_KERNEL)
>> + .map_err(|_| ENOMEM)
>> + .and_then(|level2| map_into_lvl(&data, level2))?;
>> +
>> + SGTable::new(dev, level2, DataDirection::ToDevice, GFP_KERNEL)
>
> Could you use the new Vec::zeroed() to get a buffer that's already all zeroed-out?
Yes. The incremental diff below does that, and is passing my runtime tests:
<blueforge> linux-github (nova-core-run-on-r615-or-later-v2)$ git d -- drivers/gpu/nova-core/firmware/radix3.rs
diff --git a/drivers/gpu/nova-core/firmware/radix3.rs b/drivers/gpu/nova-core/firmware/radix3.rs
index f14ad4e82d3d..b0630fd96c01 100644
--- a/drivers/gpu/nova-core/firmware/radix3.rs
+++ b/drivers/gpu/nova-core/firmware/radix3.rs
@@ -67,16 +67,12 @@ pub(crate) fn new<'a>(
Ok(try_pin_init!(Self {
data <- SGTable::new(dev, data, DataDirection::ToDevice, GFP_KERNEL),
level2 <- {
- let level2 = VVec::<u8>::with_capacity(lvl_size(&data), GFP_KERNEL)
- .map_err(|_| ENOMEM)
- .and_then(|level2| map_into_lvl(&data, level2))?;
+ let level2 = build_lvl(&data)?;
SGTable::new(dev, level2, DataDirection::ToDevice, GFP_KERNEL)
},
level1 <- {
- let level1 = VVec::<u8>::with_capacity(lvl_size(&level2), GFP_KERNEL)
- .map_err(|_| ENOMEM)
- .and_then(|level1| map_into_lvl(&level2, level1))?;
+ let level1 = build_lvl(&level2)?;
SGTable::new(dev, level1, DataDirection::ToDevice, GFP_KERNEL)
},
@@ -110,7 +106,7 @@ pub(crate) fn size(&self) -> usize {
}
/// Returns the size, in bytes, of the page table level that maps `sg_table`: one `u64` entry per
-/// 4KB page it spans, rounded up to the page boundary that [`map_into_lvl`] pads to.
+/// 4KB page it spans, rounded up to a whole number of `GSP_PAGE_SIZE` pages.
fn lvl_size(sg_table: &SGTable<Owned<VVec<u8>>>) -> usize {
let entries: usize = sg_table
.iter()
@@ -120,26 +116,34 @@ fn lvl_size(sg_table: &SGTable<Owned<VVec<u8>>>) -> usize {
(entries * size_of::<u64>()).next_multiple_of(GSP_PAGE_SIZE)
}
-/// Build a page table from a scatter-gather list.
+/// Builds a page table level from a scatter-gather list.
///
/// Takes each DMA-mapped region from `sg_table` and writes page table entries
/// for all 4KB pages within that region. For example, a 16KB SG entry becomes
/// 4 consecutive page table entries.
-fn map_into_lvl(sg_table: &SGTable<Owned<VVec<u8>>>, mut dst: VVec<u8>) -> Result<VVec<u8>> {
+///
+/// The returned buffer spans a whole number of `GSP_PAGE_SIZE` pages, and every byte past the
+/// last entry is zero. The booter DMAs each level a whole page at a time.
+///
+/// Returns `ENOMEM` if the level cannot be allocated, and `EINVAL` if `sg_table` spans more
+/// pages than [`lvl_size`] accounted for.
+fn build_lvl(sg_table: &SGTable<Owned<VVec<u8>>>) -> Result<VVec<u8>> {
+ let mut dst = VVec::<u8>::zeroed(lvl_size(sg_table), GFP_KERNEL).map_err(|_| ENOMEM)?;
+ let mut entries = dst.chunks_exact_mut(size_of::<u64>());
+
for sg_entry in sg_table.iter() {
let num_pages = usize::from_safe_cast(sg_entry.dma_len()).div_ceil(GSP_PAGE_SIZE);
for i in 0..num_pages {
let entry = sg_entry.dma_address()
+ (u64::from_safe_cast(i) * u64::from_safe_cast(GSP_PAGE_SIZE));
- dst.extend_from_slice(&entry.to_le_bytes(), GFP_KERNEL)?;
+
+ entries
+ .next()
+ .ok_or(EINVAL)?
+ .copy_from_slice(&entry.to_le_bytes());
}
}
- // The last page of a level is only partly filled, and the booter DMAs each level a
- // whole page at a time, so no entry past the last valid one may hold a stale address.
- let padded = dst.len().next_multiple_of(GSP_PAGE_SIZE);
- dst.resize(padded, 0, GFP_KERNEL)?;
-
Ok(dst)
}
thanks,
--
John Hubbard
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH 07/27] gpu: nova-core: add optional ucodes firmware loading
2026-08-19 17:55 ` Timur Tabi
@ 2026-08-20 2:22 ` John Hubbard
0 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-20 2:22 UTC (permalink / raw)
To: Timur Tabi, Alexandre Courbot, dakr@kernel.org
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On 8/19/26 10:55 AM, Timur Tabi wrote:
> On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
>> + if file.is_empty()
>> + || matches!(file, "." | "..")
>> + || file
>> + .bytes()
>> + .any(|b| b == b'/' || b == b'\\' || b.is_ascii_control())
>> + {
>> + return Err(EINVAL);
>> + }
>
> I'm not sure if this is needed because request_into_buf() will reject any crazy filename that
> would result from an invalid FILE tag.
OK, mostly yes. I'll simplify the checks down to just looking for empty,
".", "..", or "/".
>
>> +
>> + let size = usize::from_safe_cast(self.get_u32(b"SIZE")?);
>> + if size == 0 {
>> + return Err(EINVAL); // TODO: Use ENODATA once available.
>> + }
>
> FYI, ENODATA is already in linux-next.
>
OK, I'll just cherry pick it into my branch as a prerequisite, for v2,
and use ENODATA here and get rid of that TODO.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 08/27] gpu: nova-core: add LIBOS3 log buffers and state monitor buffer
2026-08-19 18:12 ` Timur Tabi
@ 2026-08-20 2:26 ` John Hubbard
0 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-20 2:26 UTC (permalink / raw)
To: Timur Tabi, Alexandre Courbot, dakr@kernel.org
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On 8/19/26 11:12 AM, Timur Tabi wrote:
> On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
>> +/// Matches the registry defaults for the init, interrupt, RM, and MNOC tasks
>> +/// (`NV_REG_STR_RM_GSP_LOG_BUFFER_SIZE_TASK_*_DEFAULT`).
>> +type TaskLogBuffer = LogBuffer<LOG_BUFFER_SIZE, RM_LOG_BUFFER_NUM_PAGES>;
>> +
>> +/// Log buffer for a task that GSP-RM gives a single page.
>> +///
>> +/// Matches the size GSP-RM hardcodes for the root and RM state monitor tasks.
>> +type SmallLogBuffer = LogBuffer<GSP_PAGE_SIZE, 1>;
>>
>> -impl LogBuffer {
>> +impl<const SIZE: usize, const NUM_PAGES: usize> LogBuffer<SIZE, NUM_PAGES> {
>> /// Creates a new `LogBuffer` mapped on `dev`.
>> fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
>> + build_assert!(SIZE == NUM_PAGES * GSP_PAGE_SIZE);
>
> Why do we need to specify both NUM_PAGES and SIZE? We can always derive one from the other.
OK yes. As long as we use NUM_PAGES, it works. I'll do that.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 07/27] gpu: nova-core: add optional ucodes firmware loading
2026-08-19 18:41 ` Timur Tabi
@ 2026-08-20 2:31 ` John Hubbard
0 siblings, 0 replies; 57+ messages in thread
From: John Hubbard @ 2026-08-20 2:31 UTC (permalink / raw)
To: Timur Tabi, Alexandre Courbot, dakr@kernel.org
Cc: lossin@kernel.org, a.hindborg@kernel.org, boqun.feng@gmail.com,
Zhi Wang, simona@ffwll.ch, tmgross@umich.edu,
alex.gaynor@gmail.com, nova-gpu@lists.linux.dev,
linux-kernel@vger.kernel.org, ojeda@kernel.org,
bjorn3_gh@protonmail.com, Eliot Courtney, airlied@gmail.com,
aliceryhl@google.com, bhelgaas@google.com, gary@garyguo.net,
Alistair Popple
On 8/19/26 11:41 AM, Timur Tabi wrote:
> On Tue, 2026-08-18 at 20:52 -0700, John Hubbard wrote:
>> + /// Loads the payload described by this TLV into owned memory.
>> + ///
>> + /// The generic firmware TLV representation is either an inline `BLOB`, or a `FILE`
>> basename
>> + /// together with its `SIZE`. Mixing the representations or specifying only half of the
>> file
>> + /// representation is invalid. Referenced files are constrained to the metadata file's
>> + /// directory.
>> + pub(crate) fn load_blob_or_file(
>> + &self,
>> + dev: &device::Device,
>> + chipset: gpu::Chipset,
>> + ) -> Result<VVec<u8>> {
>> + let has_blob = self.contains(b"BLOB");
>> + let has_file = self.contains(b"FILE");
>> + let has_size = self.contains(b"SIZE");
>
> The problem with this approach is that it allocates a buffer for the BLOB and memcpys the data,
> even though that's not necessary if you expect the TLV to have a BLOB tag. It's highly unlikely
> that the driver will ever not know in advance whether a given TLV has a BLOB or a FILE tag.
>
> We should not encourage the driver to use this function for all images. Instead, I would like
> to see a comment that stipulates that this function should be used in only two cases:
>
> 1) We will want to keep the image data after the TLV goes out of scope, so we want the BLOB
> copied into a VVec anyway.
>
> 2) We expect the TLV to have a FILE tag, but obviously it shouldn't fail if it doesn't.
>
OK, let me go tighten this up accordingly, yes.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 03/27] gpu: nova-core: set MCTP transport header version to 1
2026-08-19 3:51 ` [PATCH 03/27] gpu: nova-core: set MCTP transport header version to 1 John Hubbard
@ 2026-08-20 9:38 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 9:38 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:51:56 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
One nit: might be better to update the doc as well.
Reviewed-by: Zhi Wang <zhiw@nvidia.com>
diff --git a/Documentation/gpu/nova/core/fsp.rst
b/Documentation/gpu/nova/core/fsp.rst index 52d618d22bb8..60c647181da0
100644 --- a/Documentation/gpu/nova/core/fsp.rst
+++ b/Documentation/gpu/nova/core/fsp.rst
@@ -106,6 +106,8 @@ All FSP messages share a common header format
consisting of two 32-bit words:
- Bit 30: EOM (End of Message)
- Bits 29:28: Packet sequence number
- Bits 23:16: Source Endpoint ID
+- Bits 15:8: Destination Endpoint ID
+- Bits 3:0: MCTP header version (1)
**NVDM header** (NVIDIA Vendor Defined Message):
> FSP requires MCTP transport header version 1. Nova-core leaves version
> bits 3:0 at zero, so every CoT and PRC request carries an invalid
> transport header.
>
> Define the version and destination endpoint ID fields. Set the version
> to 1 for single-packet requests, and leave the unused destination
> endpoint ID at zero.
>
> Assisted-by: Cursor:claude-opus-5
> Signed-off-by: John Hubbard <jhubbard@nvidia.com>
> ---
> drivers/gpu/nova-core/mctp.rs | 14 ++++++++++++--
> 1 file changed, 12 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/nova-core/mctp.rs
> b/drivers/gpu/nova-core/mctp.rs index 90c642c91a72..03546213da28
> 100644 --- a/drivers/gpu/nova-core/mctp.rs
> +++ b/drivers/gpu/nova-core/mctp.rs
> @@ -42,13 +42,23 @@ pub(crate) struct MctpHeader(u32) {
> 29:28 seq;
> /// Source endpoint ID.
> 23:16 seid;
> + /// Destination endpoint ID.
> + 15:8 deid;
> + /// MCTP header version.
> + 3:0 version;
> }
> }
>
> impl MctpHeader {
> - /// Builds a single-packet MCTP header (`SOM=1`, `EOM=1`,
> `SEQ=0`, `SEID=0`).
> + /// MCTP specification version 1.0.
> + const VERSION: u32 = 1;
> +
> + /// Builds a single-packet MCTP header (`SOM=1`, `EOM=1`,
> `SEQ=0`, `SEID=0`, `VERSION=1`). pub(crate) fn single_packet() ->
> Self {
> - Self::zeroed().with_som(true).with_eom(true)
> + Self::zeroed()
> + .with_const_version::<{ Self::VERSION }>()
> + .with_som(true)
> + .with_eom(true)
> }
>
> /// Returns whether this is a complete single-packet message
> (`SOM=1` and `EOM=1`).
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 04/27] gpu: nova-core: add Falcon helpers for r000 LOAD_EXEC events
2026-08-19 3:51 ` [PATCH 04/27] gpu: nova-core: add Falcon helpers for r000 LOAD_EXEC events John Hubbard
@ 2026-08-20 9:53 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 9:53 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:51:57 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
Looking good to me (double checking the flows aligned with r000
firmware) with one nit below.
Reviewed-by: Zhi Wang <zhiw@nvidia.com>
> The r000 LOAD_EXEC events provide addresses for images that the driver
> must copy into GSP Falcon IMEM and DMEM. The existing Falcon DMA path
> creates its source buffer from a FalconFirmware object, so it cannot
> use those addresses. The protocol also reports GSP RISC-V suspension
> through MAILBOX0 bit 31 rather than CPUCTL.halted.
>
> Add Falcon operations for DMA from event-provided addresses and for
> the RISC-V suspend indication.
>
snip
> +
> regs::NV_PFALCON_FALCON_DMATRFMOFFS::zeroed().try_with_offs(dst_offset
> + pos)?,
> + );
> + self.bar.write(
> + WithBase::of::<E>(),
> +
> regs::NV_PFALCON_FALCON_DMATRFFBOFFS::zeroed().with_offs(src_offset +
> pos),
> + );
> +
Should we think of using check_add() here?
Z.
> + self.bar.write(WithBase::of::<E>(), cmd);
> +
> + read_poll_timeout(
> + ||
> Ok(self.bar.read(regs::NV_PFALCON_FALCON_DMATRFCMD::of::<E>())),
> + |r| r.idle(),
> + Delta::ZERO,
> + Delta::from_secs(2),
> + )?;
> + }
> +
> + Ok(())
> + }
> +
> /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and
> prepare the falcon to run it. fn dma_load<F: FalconFirmware<Target =
> E> + FalconDmaLoadable>(&self, fw: &F) -> Result { // DMA object with
> E> firmware content as the source of the DMA engine.
> @@ -647,6 +763,24 @@ pub(crate) fn wait_till_halted(&self) ->
> Result<()> { Ok(())
> }
>
> + /// Wait until the GSP processor has suspended.
> + ///
> + /// The RISC-V GSP signals suspension by setting bit 31
> (`0x8000_0000`) in `MAILBOX0`, rather
> + /// than through `CPUCTL.halted`.
> + #[expect(dead_code)]
> + pub(crate) fn wait_for_processor_suspend(&self) -> Result<()> {
> + const INTERRUPT_PROCESSOR_SUSPENDED: u32 = 0x8000_0000;
> +
> + read_poll_timeout(
> + || Ok(self.read_mailbox0()),
> + |val| (*val & INTERRUPT_PROCESSOR_SUSPENDED) != 0,
> + Delta::ZERO,
> + Delta::from_secs(2),
> + )?;
> +
> + Ok(())
> + }
> +
> /// Start the falcon CPU.
> pub(crate) fn start(&self) -> Result<()> {
> match self
> diff --git a/drivers/gpu/nova-core/regs.rs
> b/drivers/gpu/nova-core/regs.rs index 01fde2c5e5a6..5d265b5788e3
> 100644 --- a/drivers/gpu/nova-core/regs.rs
> +++ b/drivers/gpu/nova-core/regs.rs
> @@ -264,7 +264,7 @@ pub(crate) fn usable_fb_size(self) -> u64 {
> }
>
> pub(crate) NV_PFALCON_FALCON_DMATRFCMD(u32) @ PFalconBase +
> 0x00000118 {
> - 16:16 set_dmtag;
> + 16:16 set_dmtag => bool;
> 14:12 ctxdma;
> 10:8 size ?=> DmaTrfCmdSize;
> 5:5 is_write => bool;
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 06/27] gpu: nova-core: distinguish async GSP RPC traffic in debug logs
2026-08-19 3:51 ` [PATCH 06/27] gpu: nova-core: distinguish async GSP RPC traffic in debug logs John Hubbard
@ 2026-08-20 10:26 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 10:26 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:51:59 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
Looks good to me with one nit: should we use wrapping_add(1) for
tx_async_seq/rx_event_seq?
Reviewed-by: Zhi Wang <zhiw@nvidia.com>
Z.
> The receive path logged every message at the transport layer and did
> not distinguish events from command replies. Async sends shared the
> RPC sequence with waited-for commands, and GSP-initiated events
> arrived with that field unset.
>
> Print a distinct debug line for async send, sync send, event, and
> command reply. Number async sends and events with driver-local
> counters.
>
> IS_ASYNC selects only the send log line. Every command still carries
> the next RPC sequence on the wire.
>
> Assisted-by: Cursor:claude-opus-5
> Signed-off-by: John Hubbard <jhubbard@nvidia.com>
> ---
> drivers/gpu/nova-core/gsp/cmdq.rs | 111
> ++++++++++++++---- .../gpu/nova-core/gsp/cmdq/continuation.rs |
> 1 + drivers/gpu/nova-core/gsp/commands.rs | 2 +
> drivers/gpu/nova-core/gsp/fw.rs | 19 +++
> 4 files changed, 107 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs
> b/drivers/gpu/nova-core/gsp/cmdq.rs index ac3e6642031a..d3afe3f9bb37
> 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs
> +++ b/drivers/gpu/nova-core/gsp/cmdq.rs
> @@ -87,6 +87,12 @@ pub(crate) trait CommandToGsp {
> /// Function identifying this command to the GSP.
> const FUNCTION: MsgFunction;
>
> + /// Classifies the send-side debug log as an async send.
> + ///
> + /// Default is `false`. Set this only on commands that are sent
> without waiting for a reply. A
> + /// [`NoReply`] continuation of a waited-for command stays
> `false`.
> + const IS_ASYNC: bool = false;
> +
> /// Type generated by [`CommandToGsp::init`], to be written into
> the command queue buffer. type Command: FromBytes + AsBytes;
>
> @@ -528,6 +534,8 @@ pub(crate) fn new(dev:
> &device::Device<device::Bound>) -> impl PinInit<Self, Err gsp_mem,
> elem_seq: 0,
> rpc_seq: 0,
> + tx_async_seq: 0,
> + rx_event_seq: 0,
> poisoned: Cell::new(false),
> }),
> }))
> @@ -672,6 +680,12 @@ struct CmdqInner {
> /// [`CmdqInner::receive_msg`] match that reply to the awaiting
> command. Advances once per /// logical command.
> rpc_seq: u32,
> + /// Debug-log sequence for async sends. Those commands do not
> wait for a reply, so this
> + /// counter is the number printed in the send log.
> + tx_async_seq: u32,
> + /// Debug-log sequence for GSP-initiated events. The GSP leaves
> the RPC sequence unset on
> + /// those messages, so the driver numbers them itself.
> + rx_event_seq: u32,
> /// Set once a message with corrupt framing or a bad checksum is
> seen. Such a message has an /// untrusted length, so the queue cannot
> be advanced past it, and every later receive fails /// until the
> queue is torn down and reset. @@ -738,13 +752,24 @@ fn
> send_single_command<M>(&mut self, bar: Bar0<'_>, command: M, rpc_seq:
> u32) -> dst.contents.1, ])));
>
> - dev_dbg!(
> - &self.dev,
> - "GSP RPC: send: seq# {}, function={:?}, length=0x{:x}\n",
> - rpc_seq,
> - M::FUNCTION,
> - dst.header.length(),
> - );
> + if M::IS_ASYNC {
> + dev_dbg!(
> + &self.dev,
> + "GSP RPC: async send: seq# {}, function={:?},
> length=0x{:x}\n",
> + self.tx_async_seq,
> + M::FUNCTION,
> + dst.header.length(),
> + );
> + self.tx_async_seq += 1;
> + } else {
> + dev_dbg!(
> + &self.dev,
> + "GSP RPC: send: seq# {}, function={:?},
> length=0x{:x}\n",
> + rpc_seq,
> + M::FUNCTION,
> + dst.header.length(),
> + );
> + }
>
> // All set - update the write pointer and inform the GSP of
> the new command. let elem_count = dst.header.element_count();
> @@ -826,14 +851,6 @@ fn wait_for_msg(&self, timeout: Delta) ->
> Result<GspMessage<'_>> { return Err(EIO);
> };
>
> - dev_dbg!(
> - &self.dev,
> - "GSP RPC: receive: seq# {}, function={:?},
> length=0x{:x}\n",
> - header.sequence(),
> - header.function(),
> - header.length(),
> - );
> -
> let payload_length = header.payload_length();
>
> // Check that the driver read area is large enough for the
> message. @@ -877,6 +894,41 @@ fn wait_for_msg(&self, timeout: Delta)
> -> Result<GspMessage<'_>> { })
> }
>
> + /// Writes the classified receive debug line for a decoded GSP
> message.
> + ///
> + /// Events print [`Self::rx_event_seq`]. Command replies print
> `seq`. An unrecognized function
> + /// code prints nothing here.
> + fn log_received(&self, function: Result<MsgFunction, u32>, seq:
> u32, length: usize) {
> + match function {
> + Ok(f) if f.is_event() => {
> + dev_dbg!(
> + &self.dev,
> + "GSP RPC: async received: seq# {},
> function={:?}, length=0x{:x}\n",
> + self.rx_event_seq,
> + f,
> + length,
> + );
> + }
> + Ok(f) => {
> + dev_dbg!(
> + &self.dev,
> + "GSP RPC: response received: seq# {},
> function={:?}, length=0x{:x}\n",
> + seq,
> + f,
> + length,
> + );
> + }
> + Err(_) => {}
> + }
> + }
> +
> + /// Advances [`Self::rx_event_seq`] after a GSP-initiated event
> has been consumed.
> + fn advance_rx_event_seq(&mut self, function: Result<MsgFunction,
> u32>) {
> + if matches!(function, Ok(f) if f.is_event()) {
> + self.rx_event_seq += 1;
> + }
> + }
> +
> /// Receive a message from the GSP.
> ///
> /// The expected message type is given by the `M` generic
> parameter. With `expected_seq` set, @@ -910,9 +962,12 @@ fn
> receive_msg<M: MessageFromGsp>( let message =
> self.wait_for_msg(timeout)?; let function = message.header.function();
> let seq = message.header.sequence();
> + let length = message.header.length();
> let func_matches = matches!(function, Ok(f) if f ==
> M::FUNCTION); let matched = func_matches &&
> expected_seq.is_none_or(|expected| seq == expected);
> + self.log_received(function, seq, length);
> +
> // Every path must advance the read pointer past this
> message, including a failed decode. let result = if matched {
> match M::Message::from_bytes_prefix(message.contents.0) {
> @@ -938,9 +993,10 @@ fn receive_msg<M: MessageFromGsp>(
> };
>
> // 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(length.div_ceil(GSP_PAGE_SIZE))?);
> +
> + self.advance_rx_event_seq(function);
>
> if !matched {
> if func_matches {
> @@ -962,8 +1018,8 @@ fn receive_msg<M: MessageFromGsp>(
> /// Routes a GSP message that is not the reply a caller is
> waiting for. ///
> /// GSP-reported errors are logged at error level and
> unrecognized function codes at warning
> - /// level. Every other known function code is consumed without a
> log line, because the RPC
> - /// receive trace in [`Self::wait_for_msg`] already records its
> arrival.
> + /// level. Every other known function code is consumed without a
> log line, because
> + /// [`Self::log_received`] already records its arrival.
> fn dispatch_event(&self, function: Result<MsgFunction, u32>,
> seq: u32) { match function {
> Ok(MsgFunction::OsErrorLog) => {
> @@ -1004,16 +1060,19 @@ fn drain(&mut self) -> Result {
> while !self.gsp_mem.driver_read_area().0.is_empty() {
> // A message is available, so this returns without
> waiting. let msg = self.wait_for_msg(Delta::ZERO)?;
> -
> - let pages =
> -
> u32::try_from(msg.header.length().div_ceil(GSP_PAGE_SIZE)).map_err(|_|
> {
> - dev_err!(&self.dev, "GSP drain: message length
> overflow\n");
> - EIO
> - })?;
> let function = msg.header.function();
> let seq = msg.header.sequence();
> + let length = msg.header.length();
> +
> + self.log_received(function, seq, length);
> +
> + let pages =
> u32::try_from(length.div_ceil(GSP_PAGE_SIZE)).map_err(|_| {
> + dev_err!(&self.dev, "GSP drain: message length
> overflow\n");
> + EIO
> + })?;
>
> self.gsp_mem.advance_cpu_read_ptr(pages);
> + self.advance_rx_event_seq(function);
> self.dispatch_event(function, seq);
> }
>
> diff --git a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
> b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs index
> 05e904f18097..a606c7be52a7 100644 ---
> a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs +++
> b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs @@ -147,6 +147,7 @@
> fn new(command: C, payload: KVVec<u8>) -> Self {
> impl<C: CommandToGsp> CommandToGsp for SplitCommand<C> {
> const FUNCTION: MsgFunction = C::FUNCTION;
> + const IS_ASYNC: bool = C::IS_ASYNC;
> type Command = C::Command;
> type Reply = C::Reply;
> type InitError = C::InitError;
> diff --git a/drivers/gpu/nova-core/gsp/commands.rs
> b/drivers/gpu/nova-core/gsp/commands.rs index
> 61fe93db9e7e..d5575c036eb9 100644 ---
> a/drivers/gpu/nova-core/gsp/commands.rs +++
> b/drivers/gpu/nova-core/gsp/commands.rs @@ -52,6 +52,7 @@ pub(crate)
> fn new(pdev: &'a pci::Device<device::Bound>, chipset: Chipset) -> Sel
> impl<'a> CommandToGsp for SetSystemInfo<'a> {
> const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo;
> + const IS_ASYNC: bool = true;
> type Command = fw::commands::GspSetSystemInfo;
> type Reply = NoReply;
> type InitError = Error;
> @@ -122,6 +123,7 @@ pub(crate) fn new(vgpu_state: VgpuState) ->
> Result<Self> {
> impl CommandToGsp for SetRegistry {
> const FUNCTION: MsgFunction = MsgFunction::SetRegistry;
> + const IS_ASYNC: bool = true;
> type Command = fw::commands::PackedRegistryTable;
> type Reply = NoReply;
> type InitError = Infallible;
> diff --git a/drivers/gpu/nova-core/gsp/fw.rs
> b/drivers/gpu/nova-core/gsp/fw.rs index d3678f16750a..857d53c77297
> 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs
> +++ b/drivers/gpu/nova-core/gsp/fw.rs
> @@ -359,6 +359,25 @@ fn try_from(value: u32) -> Result<MsgFunction> {
> }
> }
>
> +impl MsgFunction {
> + /// Returns true if this is a GSP-initiated async event
> (`NV_VGPU_MSG_EVENT_*`), as opposed to
> + /// a command response (`NV_VGPU_MSG_FUNCTION_*`).
> + pub(crate) fn is_event(&self) -> bool {
> + matches!(
> + self,
> + Self::GspInitDone
> + | Self::GspRunCpuSequencer
> + | Self::PostEvent
> + | Self::RcTriggered
> + | Self::MmuFaultQueued
> + | Self::OsErrorLog
> + | Self::GspPostNoCat
> + | Self::GspLockdownNotice
> + | Self::UcodeLibOsPrint //
> + )
> + }
> +}
> +
> impl From<MsgFunction> for u32 {
> fn from(value: MsgFunction) -> Self {
> // CAST: `MsgFunction` is `repr(u32)` and can thus be cast
> losslessly.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 11/27] gpu: nova-core: regs: add msgq v2 BAR0 register declarations
2026-08-19 3:52 ` [PATCH 11/27] gpu: nova-core: regs: add msgq v2 BAR0 register declarations John Hubbard
@ 2026-08-20 10:39 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 10:39 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:52:04 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
Looking good to me.
Reviewed-by: Zhi Wang <zhiw@nvidia.com>
Z.
> Msgq v2 moves the four ring pointers out of the in-memory headers and
> into BAR0. NV_PGSP_QUEUE_HEAD already exists as the v0 doorbell, so
> add NV_PGSP_QUEUE_TAIL, NV_PGSP_MSGQ_HEAD, and NV_PGSP_MSGQ_TAIL at
> the queue-0 offsets.
>
> Nova only uses queue 0, so the registers are scalars rather than
> 8-element arrays. A comment records the full hardware layout for
> future multi-queue work.
>
> Assisted-by: Cursor:claude-opus-5
> Signed-off-by: John Hubbard <jhubbard@nvidia.com>
> ---
> drivers/gpu/nova-core/gsp/regs.rs | 30 ++++++++++++++++++++++++++++++
> 1 file changed, 30 insertions(+)
>
> diff --git a/drivers/gpu/nova-core/gsp/regs.rs
> b/drivers/gpu/nova-core/gsp/regs.rs index 9a48aa87e7fb..d2e40ed13123
> 100644 --- a/drivers/gpu/nova-core/gsp/regs.rs
> +++ b/drivers/gpu/nova-core/gsp/regs.rs
> @@ -5,6 +5,18 @@
> use crate::regs::NV_PBUS_SW_SCRATCH;
>
> // PGSP
> +//
> +// The msgq v2 wire protocol replaces the in-memory ring pointers
> with four +// BAR0 registers per queue:
> +//
> +// NV_PGSP_QUEUE_HEAD[i] @ 0x00110c00 + i*8 (CPU TX write,
> doorbell) +// NV_PGSP_QUEUE_TAIL[i] @ 0x00110c04 + i*8 (GSP TX
> read) +// NV_PGSP_MSGQ_HEAD[i] @ 0x00110c80 + i*8 (GSP RX write)
> +// NV_PGSP_MSGQ_TAIL[i] @ 0x00110c84 + i*8 (CPU RX read)
> +//
> +// Nova only uses queue 0, so the four registers are declared as
> single +// scalars at the i=0 offsets. NV_PGSP_QUEUE_HEAD is also
> used as the v0 +// doorbell, which is why it predates the others.
>
> register! {
> pub(super) NV_PGSP_QUEUE_HEAD(u32) @ 0x00110c00 {
> @@ -12,6 +24,24 @@
> }
> }
>
> +register! {
> + pub(super) NV_PGSP_QUEUE_TAIL(u32) @ 0x00110c04 {
> + 31:0 address;
> + }
> +}
> +
> +register! {
> + pub(super) NV_PGSP_MSGQ_HEAD(u32) @ 0x00110c80 {
> + 31:0 address;
> + }
> +}
> +
> +register! {
> + pub(super) NV_PGSP_MSGQ_TAIL(u32) @ 0x00110c84 {
> + 31:0 address;
> + }
> +}
> +
> // PBUS
>
> register! {
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 14/27] gpu: nova-core: add GMC API message types
2026-08-19 3:52 ` [PATCH 14/27] gpu: nova-core: add GMC API message types John Hubbard
@ 2026-08-20 10:53 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 10:53 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:52:07 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
snip
> +/// GMC API message header.
> +///
> +/// Matches the `GMCAPI_HEADER` struct from Open RM. The `command`
> field carries +/// flags in the high byte and a 24-bit command ID in
> the low bits. `size` is the +/// payload size for both requests and
> responses, while the union at offset 16 +/// carries the maximum
> response size for requests and the status for responses. +#[repr(C)]
> +#[derive(Zeroable)]
> +pub(crate) struct GmcApiHeader {
> + /// GMC command identifier with flags in the high byte.
> + pub(crate) command: u32,
> + /// Payload size in bytes.
> + pub(crate) size: u32,
> + /// Sequence number for matching requests to responses.
> + pub(crate) sequence: u64,
> + /// Request: maximum response size. Response: `NV_STATUS` code.
> + pub(crate) max_resp_or_status: u32,
> + reserved: [u32; 5],
> +}
> +
> +static_assert!(size_of::<GmcApiHeader>() == 40);
> +
Should we use the GMCAPI_HEADER in the r000 bindings?
Z.
> +// SAFETY: All fields are integer types with no uninitialized
> padding bytes. +unsafe impl AsBytes for GmcApiHeader {}
> +
> +// SAFETY: All fields are integer types for which all bit patterns
> are valid. +unsafe impl FromBytes for GmcApiHeader {}
> +
> +/// Header for an unencrypted GMC API message in the GSP command
> queue. +///
> +/// The fields before `gmc` match `GSP_MSG_QUEUE_ELEMENT` through its
> +/// `noEncryption` union member. `gmc` occupies the start of that
> member's +/// flexible payload array, and the GMC command payload
> follows this structure. +#[repr(C)]
> +pub(crate) struct GspGmcMsgElement {
> + mctp_magic: u32,
> + mctp_payload_size: u32,
> + mctp_header: MctpHeader,
> + nvdm_header: NvdmHeader,
> + nvdm_payload_size: u32,
> + reserved: u32,
> + pub(crate) gmc: GmcApiHeader,
> +}
> +
> +static_assert!(
> + core::mem::offset_of!(GspGmcMsgElement, mctp_magic)
> + == core::mem::offset_of!(r000_00::GSP_MSG_QUEUE_ELEMENT,
> mctpMagic) +);
> +static_assert!(
> + core::mem::offset_of!(GspGmcMsgElement, mctp_payload_size)
> + == core::mem::offset_of!(r000_00::GSP_MSG_QUEUE_ELEMENT,
> mctpPayloadSize) +);
> +static_assert!(
> + core::mem::offset_of!(GspGmcMsgElement, mctp_header)
> + == core::mem::offset_of!(r000_00::GSP_MSG_QUEUE_ELEMENT,
> mctpHeader) +);
> +static_assert!(
> + core::mem::offset_of!(GspGmcMsgElement, nvdm_header)
> + == core::mem::offset_of!(r000_00::GSP_MSG_QUEUE_ELEMENT,
> nvdmHeader) +);
> +
> +#[expect(dead_code)]
> +impl GspGmcMsgElement {
> + /// Creates a new GMC message element.
> + ///
> + /// `payload_size` is the size of the command payload following
> this header.
> + /// `command_id` is the GMC command identifier.
> + /// `sequence` is the sequence number for request/response
> matching.
> + /// `max_response_size` is the maximum expected response payload
> size.
> + pub(crate) fn init(
> + command_id: u32,
> + sequence: u64,
> + payload_size: usize,
> + max_response_size: u32,
> + ) -> impl Init<Self, Error> {
> + try_init!(GspGmcMsgElement {
> + mctp_magic: MCTP_MAGIC,
> + // Despite the name, this counts the element header as
> well as the payload.
> + mctp_payload_size: size_of::<Self>()
> + .checked_add(payload_size)
> + .ok_or(EOVERFLOW)?
> + .try_into()
> + .map_err(|_| EOVERFLOW)?,
> + mctp_header: MctpHeader::single_packet(),
> + nvdm_header: NvdmHeader::new(NvdmType::GmcApi),
> + nvdm_payload_size: size_of::<GmcApiHeader>()
> + .checked_add(payload_size)
> + .ok_or(EOVERFLOW)?
> + .try_into()
> + .map_err(|_| EOVERFLOW)?,
> + reserved: 0u32,
> + gmc: GmcApiHeader {
> + command: command_id,
> + size: payload_size.try_into().map_err(|_|
> EOVERFLOW)?,
> + sequence,
> + max_resp_or_status: max_response_size,
> + reserved: [0; 5],
> + },
> + })
> + }
> +
> + /// Returns the total length of the message, transport and GMC
> headers included.
> + pub(crate) fn length(&self) -> usize {
> + num::u32_as_usize(self.mctp_payload_size)
> + }
> +
> + /// Returns the number of elements (i.e. memory pages) used by
> this message.
> + pub(crate) fn element_count(&self) -> u32 {
> + self.mctp_payload_size
> + .div_ceil(num::usize_into_u32::<GSP_PAGE_SIZE>())
> + }
> +}
> +
> +// SAFETY: All fields are integer types with no uninitialized
> padding bytes. +unsafe impl AsBytes for GspGmcMsgElement {}
> +
> +// SAFETY: All fields are integer types for which all bit patterns
> are valid. +unsafe impl FromBytes for GspGmcMsgElement {}
> +
> /// Arguments for GSP startup.
> #[repr(transparent)]
> #[derive(Zeroable)]
> diff --git a/drivers/gpu/nova-core/mctp.rs
> b/drivers/gpu/nova-core/mctp.rs index 03546213da28..0ae88bef2a05
> 100644 --- a/drivers/gpu/nova-core/mctp.rs
> +++ b/drivers/gpu/nova-core/mctp.rs
> @@ -28,6 +28,8 @@ pub(crate) enum NvdmType with TryFrom<Bounded<u32,
> 8>> { Cot = 0x14,
> /// FSP command response.
> FspResponse = 0x15,
> + /// GMC API message (GSP command queue).
> + GmcApi = 0x26,
> }
> }
>
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 15/27] gpu: nova-core: add GMC send path
2026-08-19 3:52 ` [PATCH 15/27] gpu: nova-core: add GMC send path John Hubbard
@ 2026-08-20 11:12 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 11:12 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:52:08 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
Looking good to me.
Reviewed-by: Zhi Wang <zhiw@nvidia.com>
> The r000 GSP-RM boot protocol requires the driver to send GSP_INIT as
> a GMC command. GMC and RPC commands share the command queue and
> sequence counter. GMC queue elements carry a GMC API header and no
> checksum.
>
> Add the GMC send path needed for GSP_INIT.
>
> Assisted-by: Cursor:claude-opus-5
> Signed-off-by: John Hubbard <jhubbard@nvidia.com>
> ---
> drivers/gpu/nova-core/gsp/cmdq.rs | 59
> +++++++++++++++++++++++++++++++ drivers/gpu/nova-core/gsp/fw.rs |
> 1 - 2 files changed, 59 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs
> b/drivers/gpu/nova-core/gsp/cmdq.rs index 0dab3d279426..88b143c6a7b7
> 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs
> +++ b/drivers/gpu/nova-core/gsp/cmdq.rs
> @@ -52,6 +52,7 @@
> driver::Bar0,
> gsp::{
> fw::{
> + GspGmcMsgElement,
> GspMsgElement,
> MsgFunction,
> MsgqRxHeader,
> @@ -963,6 +964,64 @@ fn send_command<M>(&mut self, bar: Bar0<'_>,
> command: M) -> Result<u32> Ok(rpc_seq)
> }
>
> + /// Sends a GMC API command to the GSP.
> + ///
> + /// `command_id` is the GMC command identifier (from
> `GMCAPI_COMMANDS`).
> + /// `payload` is the command-specific data following the
> [`super::fw::GmcApiHeader`].
> + /// `max_response_size` is the maximum expected response payload
> size.
> + ///
> + /// The command carries the next RPC sequence number, which the
> GSP echoes in its response.
> + /// The number is consumed whether or not the send succeeds.
> + ///
> + /// # Errors
> + ///
> + /// - `EMSGSIZE` if the command exceeds the maximum queue
> element size.
> + /// - `ETIMEDOUT` if space does not become available within the
> timeout.
> + /// - `EIO` if the command header is not properly aligned.
> + #[expect(dead_code)]
> + fn send_gmc(
> + &mut self,
> + bar: Bar0<'_>,
> + command_id: u32,
> + payload: &[u8],
> + max_response_size: u32,
> + ) -> Result {
> + let rpc_seq = self.rpc_seq;
> + self.rpc_seq = self.rpc_seq.wrapping_add(1);
> +
> + let dst = self
> + .gsp_mem
> + .allocate_command::<GspGmcMsgElement>(payload.len(),
> Self::ALLOCATE_TIMEOUT)?; +
> + let msg_element = GspGmcMsgElement::init(
> + command_id,
> + u64::from(rpc_seq),
> + payload.len(),
> + max_response_size,
> + );
> + // SAFETY: `dst.header` points to a valid, writable
> `GspGmcMsgElement` region.
> + unsafe {
> + msg_element.__init(core::ptr::from_mut(dst.header))?;
> + }
> +
> + SBufferIter::new_writer([&mut dst.contents.0[..], &mut
> dst.contents.1[..]])
> + .write_all(payload)?;
> +
> + dev_dbg!(
> + &self.dev,
> + "GSP GMC: send: seq# {}, command_id=0x{:x},
> length=0x{:x}\n",
> + rpc_seq,
> + command_id,
> + dst.header.length(),
> + );
> +
> + let elem_count = dst.header.element_count();
> + self.gsp_mem.advance_cpu_write_ptr(elem_count);
> + Cmdq::notify_gsp(bar);
> +
> + Ok(())
> + }
> +
> /// Wait for a message to become available on the message queue.
> ///
> /// This works purely at the transport layer and does not
> interpret or validate the message diff --git
> a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
> index 050a6af8c0c7..56f255a3d49c 100644 ---
> a/drivers/gpu/nova-core/gsp/fw.rs +++
> b/drivers/gpu/nova-core/gsp/fw.rs @@ -1016,7 +1016,6 @@ pub(crate)
> struct GspGmcMsgElement { ==
> core::mem::offset_of!(r000_00::GSP_MSG_QUEUE_ELEMENT, nvdmHeader) );
>
> -#[expect(dead_code)]
> impl GspGmcMsgElement {
> /// Creates a new GMC message element.
> ///
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 16/27] gpu: nova-core: add GMC transport receive path
2026-08-19 3:52 ` [PATCH 16/27] gpu: nova-core: add GMC transport receive path John Hubbard
@ 2026-08-20 12:00 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 12:00 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:52:09 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
> GSP-RM posts GMC and RPC messages on the same message queue. Both use
> the same MCTP and NVDM transport headers, but RPC messages continue
> with rpc_message_header_v and GMC messages continue with GmcApiHeader.
> Existing RPC receive code cannot parse the GMC layout.
>
snip
> + fn wait_for_gmc_msg(&self, timeout: Delta) ->
> Result<GmcMessage<'_>> {
> + if self.poisoned.get() {
> + return Err(EIO);
> + }
> +
> + let (slice_1, slice_2) = read_poll_timeout(
> + || Ok(self.gsp_mem.driver_read_area()),
> + |driver_area| !driver_area.0.is_empty(),
> + Delta::from_millis(1),
> + timeout,
> + )
> + .map(|(slice_1, slice_2)| (slice_1.as_flattened(),
> slice_2.as_flattened()))?; +
> + let Some((header, slice_1)) =
> GspGmcMsgElement::from_bytes_prefix(slice_1) else {
> + self.poisoned.set(true);
> + return Err(EIO);
> + };
> +
> + // Checked before any length field is read, since a bad
> magic leaves them untrusted.
> + if !header.has_valid_magic() {
> + dev_err!(&self.dev, "GSP GMC: receive: bad MCTP
> magic\n");
> + self.poisoned.set(true);
> + return Err(EIO);
> + }
> +
IMO, should we also check the MCTP version here?
> + let payload_length = header.payload_length();
> +
> + // Check that the driver read area is large enough for the
> message.
> + if slice_1.len() + slice_2.len() < payload_length {
> + self.poisoned.set(true);
> + return Err(EIO);
> + }
> +
Do we need more checks here? Here we only checked the nvdm payload
size, while in PATCH 17, the queue cursor is advanced by mctp payload
size. It would be better we can confirm the frame correctness here in
PATCH 16, e.g. check the mctp payload size with various constraints.
(size > min size, size < QUEUE MAX SIZE, size < available space,
etc...r000 firmware does check these)
> + // Cut the message slices down to the actual length of the
> message.
> + let (slice_1, slice_2) = if slice_1.len() > payload_length {
> + // PANIC: we checked above that `slice_1` is at least as
> long as `payload_length`.
> + (slice_1.split_at(payload_length).0, &slice_2[0..0])
> + } else {
> + (
> + slice_1,
> + // PANIC: we checked above that `slice_1.len() +
> slice_2.len()` is at least as
> + // large as `payload_length`.
> + slice_2.split_at(payload_length - slice_1.len()).0,
> + )
> + };
> +
> + Ok(GmcMessage {
> + header,
> + contents: (slice_1, slice_2),
> + })
> + }
> }
> diff --git a/drivers/gpu/nova-core/gsp/fw.rs
> b/drivers/gpu/nova-core/gsp/fw.rs index 56f255a3d49c..9e6b5ec6aadb
> 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs
> +++ b/drivers/gpu/nova-core/gsp/fw.rs
> @@ -1055,11 +1055,21 @@ pub(crate) fn init(
> })
> }
>
> + /// Returns the length of the response payload (data after the
> [`GmcApiHeader`]).
> + pub(crate) fn payload_length(&self) -> usize {
> +
> num::u32_as_usize(self.nvdm_payload_size).saturating_sub(size_of::<GmcApiHeader>())
> + }
> +
> /// Returns the total length of the message, transport and GMC
> headers included. pub(crate) fn length(&self) -> usize {
> num::u32_as_usize(self.mctp_payload_size)
> }
>
> + /// Returns `true` if the MCTP magic field contains the expected
> value.
> + pub(crate) fn has_valid_magic(&self) -> bool {
> + self.mctp_magic == MCTP_MAGIC
> + }
> +
> /// Returns the number of elements (i.e. memory pages) used by
> this message. pub(crate) fn element_count(&self) -> u32 {
> self.mctp_payload_size
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 17/27] gpu: nova-core: gsp: add GMC dispatch on receive
2026-08-19 3:52 ` [PATCH 17/27] gpu: nova-core: gsp: add GMC dispatch on receive John Hubbard
@ 2026-08-20 12:30 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 12:30 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:52:10 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
> The r000 boot path delivers the load-and-execute steps as GMC events
> keyed by command id, where the RPC path keys on a function code, so
> the boot loop needs its own dispatch.
snip
> + fn receive_gmc_and_dispatch<R>(
> + &mut self,
> + timeout: Delta,
> + handler: impl FnOnce(u32, &[u8], &[u8]) -> Option<R>,
> + ) -> Result<Option<R>> {
> + let message = self.wait_for_gmc_msg(timeout)?;
> + let header = message.header;
> + let length = header.length();
> +
> + // The RPC and GMC elements share every field through
> `nvdm_header`, so `gmc` holds an
> + // RPC header rather than a GMC one unless the NVDM type
> says otherwise.
> + let result = if header.is_gmc_api() {
> + let command_id = header.gmc.command_id();
> +
IMO, should we check header.gmc.size == payload_length()? as now we
know it is a GMC message now. Not sure if gmc.size < payload_length()
is a valid use case.
> + dev_dbg!(
> + &self.dev,
> + "GSP GMC: event: seq# {}, command_id=0x{:x},
> length=0x{:x}\n",
> + header.gmc.sequence,
> + command_id,
> + length,
> + );
> +
> + handler(command_id, message.contents.0,
> message.contents.1)
> + } else {
> + dev_warn!(&self.dev, "GSP GMC: dropping non-GMC queue
> element\n");
> + None
> + };
> +
> + self.gsp_mem
> +
> .advance_cpu_read_ptr(u32::try_from(length.div_ceil(GSP_PAGE_SIZE))?);
> +
> + Ok(result)
> + }
> }
> diff --git a/drivers/gpu/nova-core/gsp/fw.rs
> b/drivers/gpu/nova-core/gsp/fw.rs index 9e6b5ec6aadb..83f7d2042aa1
> 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs
> +++ b/drivers/gpu/nova-core/gsp/fw.rs
> @@ -975,8 +975,19 @@ pub(crate) struct GmcApiHeader {
> reserved: [u32; 5],
> }
>
> +/// Command identifier bits of [`GmcApiHeader::command`], matching
> Open RM's +/// `GMCAPI_HEADER_COMMAND_ID_MASK`. The remaining byte
> carries flags. +const GMCAPI_COMMAND_ID_MASK: u32 = 0x00ff_ffff;
> +
> static_assert!(size_of::<GmcApiHeader>() == 40);
>
> +impl GmcApiHeader {
> + /// Returns the command identifier, without the flag byte.
> + pub(crate) fn command_id(&self) -> u32 {
> + self.command & GMCAPI_COMMAND_ID_MASK
> + }
> +}
> +
> // SAFETY: All fields are integer types with no uninitialized
> padding bytes. unsafe impl AsBytes for GmcApiHeader {}
>
> @@ -1070,6 +1081,15 @@ pub(crate) fn has_valid_magic(&self) -> bool {
> self.mctp_magic == MCTP_MAGIC
> }
>
> + /// Returns `true` if the NVDM header routes this element to the
> GSP's GMC dispatch.
> + ///
> + /// A [`GspMsgElement`] and a [`GspGmcMsgElement`] share every
> field through `nvdm_header`,
> + /// so the NVDM type is what distinguishes the two on the queue,
> and `gmc` holds an RPC
> + /// header rather than a GMC one when this returns `false`.
> + pub(crate) fn is_gmc_api(&self) -> bool {
> + self.nvdm_header.validate(NvdmType::GmcApi)
> + }
> +
> /// Returns the number of elements (i.e. memory pages) used by
> this message. pub(crate) fn element_count(&self) -> u32 {
> self.mctp_payload_size
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 13/27] gpu: nova-core: generalize allocate_command() for variable headers
2026-08-19 3:52 ` [PATCH 13/27] gpu: nova-core: generalize allocate_command() for variable headers John Hubbard
@ 2026-08-20 12:47 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 12:47 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:52:06 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
Looking good to me.
Reviewed-by: Zhi Wang <zhiw@nvidia.com>
> The GMC command path uses a different message element header from the
> VGPU-style RPC path, but the two share the queue, the space
> accounting, and the wrapping rules. Make allocate_command() and
> GspCommand generic over the header type, defaulting to GspMsgElement
> so the existing RPC callers are unchanged.
>
> No functional changes.
>
> Assisted-by: Cursor:claude-opus-5
> Signed-off-by: John Hubbard <jhubbard@nvidia.com>
> ---
> drivers/gpu/nova-core/gsp/cmdq.rs | 22 +++++++++++++++-------
> 1 file changed, 15 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs
> b/drivers/gpu/nova-core/gsp/cmdq.rs index 1d86d2927a94..0dab3d279426
> 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs
> +++ b/drivers/gpu/nova-core/gsp/cmdq.rs
> @@ -365,6 +365,8 @@ fn driver_write_area_size(&self) -> usize {
> /// Allocates a region on the command queue that is large enough
> to send a command of `size` /// bytes, waiting for space to become
> available based on the provided timeout. ///
> + /// The type parameter `H` selects the message element header,
> [`GspMsgElement`] for RM RPC.
> + ///
> /// This returns a [`GspCommand`] ready to be written to by the
> caller. ///
> /// # Errors
> @@ -372,13 +374,17 @@ fn driver_write_area_size(&self) -> usize {
> /// - `EMSGSIZE` if the command is larger than
> [`GSP_MSG_QUEUE_ELEMENT_SIZE_MAX`]. /// - `ETIMEDOUT` if space does
> not become available within the timeout. /// - `EIO` if the command
> header is not properly aligned.
> - fn allocate_command(&mut self, size: usize, timeout: Delta) ->
> Result<GspCommand<'_>> {
> - if size_of::<GspMsgElement>() + size >
> GSP_MSG_QUEUE_ELEMENT_SIZE_MAX {
> + fn allocate_command<H: FromBytes + AsBytes>(
> + &mut self,
> + size: usize,
> + timeout: Delta,
> + ) -> Result<GspCommand<'_, H>> {
> + if size_of::<H>() + size > GSP_MSG_QUEUE_ELEMENT_SIZE_MAX {
> return Err(EMSGSIZE);
> }
> read_poll_timeout(
> || Ok(self.driver_write_area_size()),
> - |available_bytes| *available_bytes >=
> size_of::<GspMsgElement>() + size,
> + |available_bytes| *available_bytes >= size_of::<H>() +
> size, Delta::from_micros(1),
> timeout,
> )?;
> @@ -390,8 +396,8 @@ fn allocate_command(&mut self, size: usize,
> timeout: Delta) -> Result<GspCommand (slice_1.as_flattened_mut(),
> slice_2.as_flattened_mut()) };
>
> - // Extract area for the `GspMsgElement`.
> - let (header, slice_1) =
> GspMsgElement::from_bytes_mut_prefix(slice_1).ok_or(EIO)?;
> + // Extract area for the message element header.
> + let (header, slice_1) =
> H::from_bytes_mut_prefix(slice_1).ok_or(EIO)?;
> // Create the contents area.
> let (slice_1, slice_2) = if slice_1.len() > size {
> @@ -609,10 +615,12 @@ fn driver_read_area_v2(
>
> /// A command ready to be sent on the command queue.
> ///
> +/// The type parameter `H` is the message element header type,
> [`GspMsgElement`] for RM RPC. +///
> /// This is the type returned by [`DmaGspMem::allocate_command`].
> -struct GspCommand<'a> {
> +struct GspCommand<'a, H = GspMsgElement> {
> // Writable reference to the header of the command.
> - header: &'a mut GspMsgElement,
> + header: &'a mut H,
> // Writable slices to the contents of the command. The second
> slice is zero unless the command // loops over the command queue.
> contents: (&'a mut [u8], &'a mut [u8]),
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 18/27] gpu: nova-core: separate the generic falcon bootloader from FWSEC
2026-08-19 3:52 ` [PATCH 18/27] gpu: nova-core: separate the generic falcon bootloader from FWSEC John Hubbard
@ 2026-08-20 13:02 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 13:02 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:52:11 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
Looking good to me.
Reviewed-by: Zhi Wang <zhiw@nvidia.com>
> The r000 GSP boot protocol sends a load-and-execute event for the
> generic falcon bootloader.
>
> Nova-core stored the image and its load parameters in the FWSEC
> firmware wrapper, which kept the r000 boot path from loading it
> independently.
>
> Separate the bootloader image and load parameters from the FWSEC
> wrapper. Place the bootloader at the end of the target falcon's
> HWCFG-reported IMEM rather than below a fixed 64 KiB ceiling, so its
> placement remains within bounds on falcons with less IMEM.
>
> Assisted-by: Cursor:claude-opus-5
> Signed-off-by: John Hubbard <jhubbard@nvidia.com>
> ---
> drivers/gpu/nova-core/falcon.rs | 15 +++
> drivers/gpu/nova-core/firmware.rs | 1 +
> .../nova-core/firmware/fwsec/bootloader.rs | 64 ++---------
> .../gpu/nova-core/firmware/gen_bootloader.rs | 102
> ++++++++++++++++++ drivers/gpu/nova-core/gsp/hal/tu102.rs |
> 6 +- drivers/gpu/nova-core/regs.rs | 5 +
> 6 files changed, 135 insertions(+), 58 deletions(-)
> create mode 100644 drivers/gpu/nova-core/firmware/gen_bootloader.rs
>
> diff --git a/drivers/gpu/nova-core/falcon.rs
> b/drivers/gpu/nova-core/falcon.rs index 20a288050c37..b8013d2eb582
> 100644 --- a/drivers/gpu/nova-core/falcon.rs
> +++ b/drivers/gpu/nova-core/falcon.rs
> @@ -387,6 +387,21 @@ pub(crate) fn new(
> })
> }
>
> + /// Returns the size of this falcon's IMEM, in bytes.
> + ///
> + /// `NV_PFALCON_FALCON_IMEMC` addresses IMEM with a 16-bit byte
> offset, so the result never
> + /// exceeds 64KiB.
> + pub(crate) fn imem_size(&self) -> usize {
> + let blocks = usize::from_safe_cast(
> + *self
> + .bar
> + .read(regs::NV_PFALCON_FALCON_HWCFG::of::<E>())
> + .imem_size(),
> + );
> +
> + blocks * MEM_BLOCK_ALIGNMENT
> + }
> +
> /// Resets DMA-related registers.
> pub(crate) fn dma_reset(&self) {
> self.bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| {
> diff --git a/drivers/gpu/nova-core/firmware.rs
> b/drivers/gpu/nova-core/firmware.rs index e0befe84aa3e..9ff764930bcd
> 100644 --- a/drivers/gpu/nova-core/firmware.rs
> +++ b/drivers/gpu/nova-core/firmware.rs
> @@ -26,6 +26,7 @@
> pub(crate) mod booter;
> pub(crate) mod fsp;
> pub(crate) mod fwsec;
> +pub(crate) mod gen_bootloader;
> pub(crate) mod gsp;
> pub(crate) mod radix3;
> pub(crate) mod riscv;
> diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
> b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs index
> ec4d92317a93..6670d17b4eeb 100644 ---
> a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs +++
> b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs @@ -14,18 +14,12
> @@ dma::Coherent,
> io::{register::WithBase, Io},
> prelude::*,
> - ptr::{
> - Alignable,
> - Alignment, //
> - },
> - sizes,
> transmute::AsBytes,
> };
>
> use crate::{
> driver::Bar0,
> falcon::{
> - self,
> gsp::Gsp,
> Falcon,
> FalconBromParams,
> @@ -39,10 +33,7 @@
> },
> firmware::{
> fwsec::FwsecFirmware,
> - tlv::{
> - request_tlv, //
> - Tlv,
> - },
> + gen_bootloader::GenericBootloader, //
> },
> gpu::Chipset,
> num::FromSafeCast, //
> @@ -101,16 +92,12 @@ unsafe impl AsBytes for BootloaderDmemDescV2 {}
> pub(crate) struct FwsecFirmwareWithBl {
> /// DMA object the bootloader will copy the firmware from.
> _firmware_dma: Coherent<[u8]>,
> - /// Code of the bootloader to be loaded into non-secure IMEM.
> - ucode: KVec<u8>,
> + /// Bootloader that performs the load.
> + bootloader: GenericBootloader,
> /// Descriptor to be loaded into DMEM for the bootloader to read.
> dmem_desc: BootloaderDmemDescV2,
> - /// Range-validated start offset of the firmware code in IMEM.
> - imem_dst_start: u16,
> /// BROM parameters of the loaded firmware.
> brom_params: FalconBromParams,
> - /// Range-validated `desc.start_tag`.
> - start_tag: u16,
> }
>
> impl FwsecFirmwareWithBl {
> @@ -120,29 +107,9 @@ pub(crate) fn new(
> firmware: FwsecFirmware,
> dev: &Device<device::Bound>,
> chipset: Chipset,
> + falcon: &Falcon<'_, Gsp>,
> ) -> Result<Self> {
> - let fw = request_tlv(dev, chipset, "gen_bootloader")?;
> - let tlv = Tlv::new(fw.data())?;
> - dev_dbg!(
> - dev,
> - "loaded generic bootloader firmware v{}\n",
> - tlv.get_string(b"VERS")?
> - );
> -
> - let ucode = {
> - let blob = tlv.get_bytes(b"BLOB")?;
> - let code_size =
> usize::from_safe_cast(tlv.get_u32(b"CDSZ")?);
> - let code = blob.get(..code_size).ok_or(EINVAL)?;
> - let aligned_code_size = code_size
> - .align_up(Alignment::new::<{
> falcon::MEM_BLOCK_ALIGNMENT }>())
> - .ok_or(EINVAL)?;
> -
> - let mut ucode = KVec::with_capacity(aligned_code_size,
> GFP_KERNEL)?;
> - ucode.extend_from_slice(code, GFP_KERNEL)?;
> - ucode.resize(aligned_code_size, 0, GFP_KERNEL)?;
> -
> - ucode
> - };
> + let bootloader = GenericBootloader::new(dev, chipset,
> falcon)?;
> // `BootloaderDmemDescV2` expects the source to be a mirror
> image of the destination and // uses the same offset parameter for
> both. @@ -213,21 +180,11 @@ pub(crate) fn new(
> }
> };
>
> - // The bootloader's code must be loaded in the area right
> below the first 64K of IMEM.
> - const BOOTLOADER_LOAD_CEILING: usize = sizes::SZ_64K;
> - let imem_dst_start = BOOTLOADER_LOAD_CEILING
> - .checked_sub(ucode.len())
> - .ok_or(EOVERFLOW)?;
> -
> - let start_tag = u16::try_from(tlv.get_u32(b"STRT")?)?;
> -
> Ok(Self {
> _firmware_dma: firmware_dma,
> - ucode,
> + bootloader,
> dmem_desc,
> brom_params: firmware.brom_params(),
> - imem_dst_start: u16::try_from(imem_dst_start)?,
> - start_tag,
> })
> }
>
> @@ -282,7 +239,7 @@ fn brom_params(&self) -> FalconBromParams {
> fn boot_addr(&self) -> u32 {
> // On V2 platforms, the boot address is extracted from the
> generic bootloader, because the // gbl is what actually copies FWSEC
> into memory, so that is what needs to be booted.
> - u32::from(self.start_tag) << 8
> + self.bootloader.boot_addr()
> }
> }
>
> @@ -292,12 +249,7 @@ fn imem_sec_load_params(&self) ->
> Option<FalconPioImemLoadTarget<'_>> { }
>
> fn imem_ns_load_params(&self) ->
> Option<FalconPioImemLoadTarget<'_>> {
> - Some(FalconPioImemLoadTarget {
> - data: self.ucode.as_ref(),
> - dst_start: self.imem_dst_start,
> - secure: false,
> - start_tag: self.start_tag,
> - })
> + Some(self.bootloader.imem_load_params())
> }
>
> fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> {
> diff --git a/drivers/gpu/nova-core/firmware/gen_bootloader.rs
> b/drivers/gpu/nova-core/firmware/gen_bootloader.rs new file mode
> 100644 index 000000000000..f949223af2d0
> --- /dev/null
> +++ b/drivers/gpu/nova-core/firmware/gen_bootloader.rs
> @@ -0,0 +1,102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! The generic falcon bootloader.
> +//!
> +//! A small program loaded into falcon IMEM using PIO, which then
> DMAs a larger image into IMEM and +//! DMEM from a descriptor the
> driver leaves in DMEM at offset 0. Open RM's +//!
> `ksec2GetGenericBlUcode` supplies the same image for both SEC2 and
> GSP. + +use kernel::{
> + device,
> + prelude::*,
> + ptr::{
> + Alignable,
> + Alignment, //
> + },
> +};
> +
> +use crate::{
> + falcon::{
> + self,
> + Falcon,
> + FalconEngine,
> + FalconPioImemLoadTarget, //
> + },
> + firmware::tlv::{
> + request_tlv, //
> + Tlv,
> + },
> + gpu::Chipset,
> + num::FromSafeCast, //
> +};
> +
> +/// The generic falcon bootloader image and the IMEM placement it
> was loaded for. +pub(crate) struct GenericBootloader {
> + /// Bootloader code, zero-padded to a whole number of falcon
> memory blocks.
> + ucode: KVec<u8>,
> + /// Byte offset in IMEM the code is loaded at.
> + imem_dst_start: u16,
> + /// Tag the first code block is loaded under.
> + start_tag: u16,
> +}
> +
> +impl GenericBootloader {
> + /// Loads the generic bootloader image for `chipset`, placed in
> the last blocks of `falcon`'s
> + /// IMEM so the image it goes on to load has the rest to itself.
> + ///
> + /// # Errors
> + ///
> + /// - `EINVAL` if a required TLV field is absent or the image
> does not fit in IMEM.
> + /// - `ENOMEM` if the padded copy of the code cannot be
> allocated.
> + pub(crate) fn new<E: FalconEngine + 'static>(
> + dev: &device::Device<device::Bound>,
> + chipset: Chipset,
> + falcon: &Falcon<'_, E>,
> + ) -> Result<Self> {
> + let fw = request_tlv(dev, chipset, "gen_bootloader")?;
> + let tlv = Tlv::new(fw.data())?;
> + dev_dbg!(
> + dev,
> + "loaded generic bootloader firmware v{}\n",
> + tlv.get_string(b"VERS")?
> + );
> +
> + let ucode = {
> + let blob = tlv.get_bytes(b"BLOB")?;
> + let code_size =
> usize::from_safe_cast(tlv.get_u32(b"CDSZ")?);
> + let code = blob.get(..code_size).ok_or(EINVAL)?;
> + let aligned_code_size = code_size
> + .align_up(Alignment::new::<{
> falcon::MEM_BLOCK_ALIGNMENT }>())
> + .ok_or(EINVAL)?;
> +
> + let mut ucode = KVec::with_capacity(aligned_code_size,
> GFP_KERNEL)?;
> + ucode.extend_from_slice(code, GFP_KERNEL)?;
> + ucode.resize(aligned_code_size, 0, GFP_KERNEL)?;
> +
> + ucode
> + };
> +
> + let imem_dst_start =
> falcon.imem_size().checked_sub(ucode.len()).ok_or(EINVAL)?; +
> + Ok(Self {
> + ucode,
> + imem_dst_start: u16::try_from(imem_dst_start)?,
> + start_tag: u16::try_from(tlv.get_u32(b"STRT")?)?,
> + })
> + }
> +
> + /// Returns the address the falcon must boot from to run this
> bootloader.
> + pub(crate) fn boot_addr(&self) -> u32 {
> + u32::from(self.start_tag) << 8
> + }
> +
> + /// Returns the PIO parameters that place this bootloader in
> non-secure IMEM.
> + pub(crate) fn imem_load_params(&self) ->
> FalconPioImemLoadTarget<'_> {
> + FalconPioImemLoadTarget {
> + data: self.ucode.as_ref(),
> + dst_start: self.imem_dst_start,
> + secure: false,
> + start_tag: self.start_tag,
> + }
> + }
> +}
> diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs
> b/drivers/gpu/nova-core/gsp/hal/tu102.rs index
> a5c0ca355493..68a48c882c0f 100644 ---
> a/drivers/gpu/nova-core/gsp/hal/tu102.rs +++
> b/drivers/gpu/nova-core/gsp/hal/tu102.rs @@ -166,7 +166,7 @@ fn
> run_fwsec_frts( )?;
>
> if self.needs_fwsec_bootloader {
> - let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts,
> dev, chipset)?;
> + let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts,
> dev, chipset, falcon)?; // Load and run the bootloader, which will
> load FWSEC-FRTS and run it. fwsec_frts_bl.run(dev, falcon, bar)?;
> } else {
> @@ -224,7 +224,9 @@ fn build_unload_bundle(
> // Load the FWSEC SB firmware, as well as its bootloader if
> required. let fwsec_sb = FwsecFirmware::new(dev, gsp_falcon, bios,
> FwsecCommand::Sb)?; let fwsec_sb = if self.needs_fwsec_bootloader {
> -
> FwsecUnloadFirmware::WithBl(FwsecFirmwareWithBl::new(fwsec_sb, dev,
> chipset)?)
> + FwsecUnloadFirmware::WithBl(FwsecFirmwareWithBl::new(
> + fwsec_sb, dev, chipset, gsp_falcon,
> + )?)
> } else {
> FwsecUnloadFirmware::WithoutBl(fwsec_sb)
> };
> diff --git a/drivers/gpu/nova-core/regs.rs
> b/drivers/gpu/nova-core/regs.rs index 5d265b5788e3..5501c36a56af
> 100644 --- a/drivers/gpu/nova-core/regs.rs
> +++ b/drivers/gpu/nova-core/regs.rs
> @@ -247,6 +247,11 @@ pub(crate) fn usable_fb_size(self) -> u64 {
> 31:0 value => u32;
> }
>
> + pub(crate) NV_PFALCON_FALCON_HWCFG(u32) @ PFalconBase +
> 0x00000108 {
> + /// Size of this falcon's IMEM, in blocks of
> [`crate::falcon::MEM_BLOCK_ALIGNMENT`] bytes.
> + 8:0 imem_size;
> + }
> +
> pub(crate) NV_PFALCON_FALCON_DMACTL(u32) @ PFalconBase +
> 0x0000010c { 7:7 secure_stat => bool;
> 6:3 dmaq_num;
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH 20/27] gpu: nova-core: handle the r000 load-and-execute bootloader event
2026-08-19 3:52 ` [PATCH 20/27] gpu: nova-core: handle the r000 load-and-execute bootloader event John Hubbard
@ 2026-08-20 16:24 ` Zhi Wang
0 siblings, 0 replies; 57+ messages in thread
From: Zhi Wang @ 2026-08-20 16:24 UTC (permalink / raw)
To: John Hubbard
Cc: Danilo Krummrich, Alexandre Courbot, Timur Tabi, Alistair Popple,
Eliot Courtney, David Airlie, Simona Vetter, Bjorn Helgaas,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, nova-gpu, LKML
On Tue, 18 Aug 2026 20:52:13 -0700
John Hubbard <jhubbard@nvidia.com> wrote:
snip
> + /// Handle a `GSP_LOAD_EXEC_GENERIC_BOOTLOADER` event.
> + ///
> + /// The driver does not copy the image the GSP asks for. It
> writes the descriptor the event
> + /// carries to DMEM offset 0, places the generic bootloader in
> IMEM, points the requested FBIF
> + /// aperture at wherever the image lives, and runs the
> bootloader, which does the copy from
> + /// the descriptor and jumps to the image. The aperture is
> restored afterwards.
> + ///
> + /// # Errors
> + ///
> + /// - `EINVAL` if the payload is shorter than the parameter
> block, the descriptor is not the
> + /// size this driver mirrors, or the event names a context DMA
> slot or an aperture that does
> + /// not exist.
> + /// - `ETIMEDOUT` if the GSP does not suspend, or the image does
> not halt, in time.
> + #[expect(dead_code)]
> + #[allow(clippy::too_many_arguments)]
> + fn handle_load_exec_bootloader(
> + payload: &[u8],
> + bootloader: &GenericBootloader,
> + gsp_falcon: &Falcon<'_, Gsp>,
> + sec2_falcon: &Falcon<'_, Sec2>,
> + bar: Bar0<'_>,
> + dev: &device::Device,
> + bootloader_app_version: u32,
> + libos_dma_handle: u64,
> + ) -> Result {
> + let params =
> LoadExecGenericBootloaderParams::from_bytes_prefix(payload)
> + .ok_or(EINVAL)?
> + .0;
> +
> + let desc_size =
> +
> u32::try_from(core::mem::size_of::<BootloaderDmemDescV2>()).map_err(|_|
> EOVERFLOW)?;
> + if params.dmem_desc_size != desc_size {
> + dev_err!(
> + dev,
> + "Load-exec descriptor is {} bytes, expected {}\n",
> + params.dmem_desc_size,
> + desc_size
> + );
> + return Err(EINVAL);
> + }
> +
> + let ctx_dma = params.ctx_dma()?;
> + let fbif_target = params.fbif_target()?;
> + let transcfg = || {
> + regs::NV_PFALCON_FBIF_TRANSCFG::of::<Gsp>()
> + .try_at(usize::from(ctx_dma))
> + .ok_or(EINVAL)
> + };
> +
> + gsp_falcon.wait_for_processor_suspend().inspect_err(|_| {
> + dev_err!(
> + dev,
> + "Timeout waiting for GSP suspend (mbox0={:#x})\n",
> + gsp_falcon.read_mailbox0()
> + );
> + })?;
> +
> + gsp_falcon.reset()?;
> + gsp_falcon.dma_reset();
> +
> + let saved_transcfg = bar.read(transcfg()?);
> + bar.update(transcfg()?, |v| {
> + v.with_target(fbif_target)
> + .with_mem_type(FalconFbifMemType::Physical)
> + });
> +
> + let run = (|| -> Result {
> +
> gsp_falcon.pio_load(&bootloader.with_descriptor(¶ms.dmem_desc))?;
> +
> + // Also clears the suspend bit that
> `wait_for_processor_suspend` polls, so the next
> + // load-and-execute event does not read this one's
> suspension.
> +
> gsp_falcon.write_mailboxes(Some(FLCN_ERR_BINARY_NOT_STARTED), None); +
> + gsp_falcon.start()?;
> + gsp_falcon.wait_till_halted().inspect_err(|_| {
> + dev_err!(
> + dev,
> + "Timeout waiting for the loaded image to halt
> (mbox0={:#x})\n",
> + gsp_falcon.read_mailbox0()
> + );
> + })
> + })();
> +
> + bar.update(transcfg()?, |_| saved_transcfg);
> + run?;
TRANSCFG was restored only when the transfer is successful in OpenRM [1]
(also r000 firmware), I think that is reasonable since if the HALT is
not received, the controller is in stale and might still be active on
memory read/write until it got reset.
I guess we should follow OpenRM's scheme, e.g.
run?;
bar.update(xxxx);
[1]
https://github.com/NVIDIA/open-gpu-kernel-modules/blob/main/src/nvidia/src/kernel/gpu/gsp/arch/turing/kernel_gsp_falcon_tu102.c#L556
> +
> + Self::core_resume(
> + gsp_falcon,
> + sec2_falcon,
> + dev,
> + bootloader_app_version,
> + libos_dma_handle,
> + )
> + }
> +
> /// Handle a `GSP_LOAD_EXEC_HS_BINARY` event.
> ///
> /// The GSP asks the driver to run a high-security binary that
> it has already placed in the @@ -347,6 +448,64 @@ pub(crate) fn
> unload( /// points it at local framebuffer.
> const HS_BINARY_CTX_DMA: u8 = 0;
>
> +/// Number of FBIF context DMA slots a falcon has.
> +const NUM_CTX_DMA: usize = 8;
> +
> +/// Parameters for loading and executing the generic bootloader.
> +///
> +/// Sent by GSP-RM as the payload of
> `GSP_LOAD_EXEC_GENERIC_BOOTLOADER`. The descriptor carries +/// the
> code and data addresses, while `addr_space` and `cpu_cache_attrib`
> say which FBIF aperture +/// reaches them. +#[repr(C)]
> +struct LoadExecGenericBootloaderParams {
> + dmem_desc: BootloaderDmemDescV2,
> + dmem_desc_size: u32,
> + addr_space: u32,
> + cpu_cache_attrib: u32,
> + _reserved: [u32; 4],
> +}
> +
> +impl LoadExecGenericBootloaderParams {
> + const ADDR_SYSMEM: u32 = 1;
> + const ADDR_FBMEM: u32 = 2;
> + const NV_MEMORY_CACHED: u32 = 0;
> + const NV_MEMORY_UNCACHED: u32 = 1;
> +
> + /// Returns the context DMA slot the bootloader is to fetch the
> image through.
> + ///
> + /// # Errors
> + ///
> + /// - `EINVAL` if the slot is outside the FBIF `TRANSCFG` array.
> + fn ctx_dma(&self) -> Result<u8> {
> + let ctx_dma = self.dmem_desc.ctx_dma;
> +
> + u8::try_from(ctx_dma)
> + .ok()
> + .filter(|slot| usize::from(*slot) < NUM_CTX_DMA)
> + .ok_or(EINVAL)
> + }
> +
> + /// Returns the FBIF aperture that reaches the image.
> + ///
> + /// # Errors
> + ///
> + /// - `EINVAL` if the address space and cache attribute pair is
> not one this driver maps.
> + fn fbif_target(&self) -> Result<FalconFbifTarget> {
> + match (self.addr_space, self.cpu_cache_attrib) {
> + (Self::ADDR_FBMEM, _) => Ok(FalconFbifTarget::LocalFb),
> + (Self::ADDR_SYSMEM, Self::NV_MEMORY_CACHED) =>
> Ok(FalconFbifTarget::CoherentSysmem),
> + (Self::ADDR_SYSMEM, Self::NV_MEMORY_UNCACHED) => {
> + Ok(FalconFbifTarget::NoncoherentSysmem)
> + }
> + _ => Err(EINVAL),
> + }
> + }
> +}
> +
> +// SAFETY: The nested descriptor is `FromBytes`, and every other
> field is an integer type for +// which all bit patterns are valid.
> +unsafe impl FromBytes for LoadExecGenericBootloaderParams {}
> +
> /// Parameters for loading and executing an HS (High-Security)
> binary. ///
> /// Sent by GSP-RM as the payload of `GSP_LOAD_EXEC_HS_BINARY`. The
> firmware
^ permalink raw reply [flat|nested] 57+ messages in thread
end of thread, other threads:[~2026-08-20 16:25 UTC | newest]
Thread overview: 57+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-19 3:51 [PATCH 00/27] gpu: nova-core: boot on the r000 GSP firmware John Hubbard
2026-08-19 3:51 ` [PATCH 01/27] gpu: nova-core: firmware: add r000 bindings John Hubbard
2026-08-19 3:51 ` [PATCH 02/27] gpu: nova-core: extract radix3 page table into its own module John Hubbard
2026-08-19 17:37 ` Timur Tabi
2026-08-20 1:05 ` John Hubbard
2026-08-19 3:51 ` [PATCH 03/27] gpu: nova-core: set MCTP transport header version to 1 John Hubbard
2026-08-20 9:38 ` Zhi Wang
2026-08-19 3:51 ` [PATCH 04/27] gpu: nova-core: add Falcon helpers for r000 LOAD_EXEC events John Hubbard
2026-08-20 9:53 ` Zhi Wang
2026-08-19 3:51 ` [PATCH 05/27] gpu: nova-core: zero-pad radix3 page table levels to page boundary John Hubbard
2026-08-19 17:41 ` Timur Tabi
2026-08-20 2:18 ` John Hubbard
2026-08-19 3:51 ` [PATCH 06/27] gpu: nova-core: distinguish async GSP RPC traffic in debug logs John Hubbard
2026-08-20 10:26 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 07/27] gpu: nova-core: add optional ucodes firmware loading John Hubbard
2026-08-19 17:55 ` Timur Tabi
2026-08-20 2:22 ` John Hubbard
2026-08-19 18:41 ` Timur Tabi
2026-08-20 2:31 ` John Hubbard
2026-08-19 3:52 ` [PATCH 08/27] gpu: nova-core: add LIBOS3 log buffers and state monitor buffer John Hubbard
2026-08-19 18:12 ` Timur Tabi
2026-08-20 2:26 ` John Hubbard
2026-08-19 3:52 ` [PATCH 09/27] gpu: nova-core: add build ID headers to debugfs log buffer dumps John Hubbard
2026-08-19 18:20 ` Timur Tabi
2026-08-20 1:00 ` John Hubbard
2026-08-20 1:30 ` Timur Tabi
2026-08-20 2:03 ` John Hubbard
2026-08-19 3:52 ` [PATCH 10/27] gpu: nova-core: rename the FbRanges elf field to fw_image John Hubbard
2026-08-19 18:21 ` Timur Tabi
2026-08-20 1:01 ` John Hubbard
2026-08-19 3:52 ` [PATCH 11/27] gpu: nova-core: regs: add msgq v2 BAR0 register declarations John Hubbard
2026-08-20 10:39 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 12/27] gpu: nova-core: gsp: add msgq v2 internals John Hubbard
2026-08-19 3:52 ` [PATCH 13/27] gpu: nova-core: generalize allocate_command() for variable headers John Hubbard
2026-08-20 12:47 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 14/27] gpu: nova-core: add GMC API message types John Hubbard
2026-08-20 10:53 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 15/27] gpu: nova-core: add GMC send path John Hubbard
2026-08-20 11:12 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 16/27] gpu: nova-core: add GMC transport receive path John Hubbard
2026-08-20 12:00 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 17/27] gpu: nova-core: gsp: add GMC dispatch on receive John Hubbard
2026-08-20 12:30 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 18/27] gpu: nova-core: separate the generic falcon bootloader from FWSEC John Hubbard
2026-08-20 13:02 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 19/27] gpu: nova-core: handle the r000 load-and-execute HS binary event John Hubbard
2026-08-19 3:52 ` [PATCH 20/27] gpu: nova-core: handle the r000 load-and-execute bootloader event John Hubbard
2026-08-20 16:24 ` Zhi Wang
2026-08-19 3:52 ` [PATCH 21/27] gpu: nova-core: gsp: add the GMC boot event dispatcher John Hubbard
2026-08-19 3:52 ` [PATCH 22/27] gpu: nova-core: gsp: add the GSP_INIT request builder John Hubbard
2026-08-19 3:52 ` [PATCH 23/27] gpu: nova-core: gsp: send GSP_INIT and decode its reply John Hubbard
2026-08-19 3:52 ` [PATCH 24/27] gpu: nova-core: gsp: pass the remaining log buffers to GSP-RM John Hubbard
2026-08-19 3:52 ` [PATCH 26/27] gpu: nova-core: gsp: remove the retired system-info and static-info RPCs John Hubbard
2026-08-19 3:52 ` [PATCH 27/27] gpu: nova-core: firmware: delete the r570 bindings John Hubbard
[not found] ` <20260819035221.336390-26-jhubbard@nvidia.com>
2026-08-19 18:58 ` [PATCH 25/27] gpu: nova-core: switch to the r000 GSP firmware Timur Tabi
2026-08-20 1:02 ` John Hubbard
2026-08-19 19:25 ` [PATCH 00/27] gpu: nova-core: boot on " Timur Tabi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox