* [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active
@ 2025-08-27 8:19 Alistair Popple
2025-08-27 8:19 ` [PATCH 01/10] gpu: nova-core: Set correct DMA mask Alistair Popple
` (10 more replies)
0 siblings, 11 replies; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:19 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
This series builds on top of Alex's series[1] to continue initialising the GSP
into a state where it becomes active and it starts communicating with the host.
It includes patches to initialise several important data structures required to
boot the GSP. The biggest change is the implementation of the command/message
circular queue used to establish communication between GSP and host.
This is required to configure and boot the GSP. However this series does not
get the GSP to a fully active state. Instead it gets it to a state where the GSP
sends a message to the host with a sequence of instructions which need running
to get to the active state. A subsequent series will implement processing of
this message and allow the GSP to get to the fully active state.
A full tree including the prerequisites for this patch series is available at
https://github.com/apopple-nvidia/linux/tree/nova-core-for-upstream.
[1] - https://lore.kernel.org/rust-for-linux/dc18894e-09d3-4088-9be0-22c2070b61f4@nvidia.com/T/
To: dri-devel@lists.freedesktop.org
To: Danilo Krummrich <dakr@kernel.org>
To: Alexandre Courbot <acourbot@nvidia.com>
Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Alex Gaynor <alex.gaynor@gmail.com>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Gary Guo <gary@garyguo.net>
Cc: Björn Roy Baron <bjorn3_gh@protonmail.com>
Cc: Benno Lossin <lossin@kernel.org>
Cc: Andreas Hindborg <a.hindborg@kernel.org>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Trevor Gross <tmgross@umich.edu>
Cc: David Airlie <airlied@gmail.com>
Cc: Simona Vetter <simona@ffwll.ch>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Joel Fernandes <joelagnelf@nvidia.com>
Cc: Timur Tabi <ttabi@nvidia.com>
Cc: linux-kernel@vger.kernel.org
Cc: nouveau@lists.freedesktop.org
Alistair Popple (7):
gpu: nova-core: Set correct DMA mask
gpu: nova-core: Create initial GspSharedMemObjects
gpu: nova-core: gsp: Create wpr metadata
gpu: nova-core: gsp: Add GSP command queue handling
gpu: nova-core: gsp: Create rmargs
gpu: nova-core: gsp: Create RM registry and sysinfo commands
gpu: nova-core: gsp: Boot GSP
Joel Fernandes (3):
gpu: nova-core: Add a slice-buffer (sbuffer) datastructure
gpu: nova-core: falcon: Add support to check if RISC-V is active
gpu: nova-core: falcon: Add support to write firmware version
drivers/gpu/nova-core/driver.rs | 8 +-
drivers/gpu/nova-core/falcon.rs | 16 +
drivers/gpu/nova-core/fb.rs | 1 -
drivers/gpu/nova-core/firmware.rs | 6 +-
drivers/gpu/nova-core/firmware/gsp.rs | 1 -
drivers/gpu/nova-core/firmware/riscv.rs | 9 +-
drivers/gpu/nova-core/gpu.rs | 60 +-
drivers/gpu/nova-core/gsp.rs | 237 +++++-
drivers/gpu/nova-core/gsp/cmdq.rs | 701 ++++++++++++++++++
drivers/gpu/nova-core/gsp/commands.rs | 201 +++++
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/nvfw.rs | 59 ++
.../gpu/nova-core/nvfw/r570_144_bindings.rs | 501 +++++++++++++
drivers/gpu/nova-core/regs.rs | 15 +
drivers/gpu/nova-core/sbuffer.rs | 188 +++++
15 files changed, 1991 insertions(+), 13 deletions(-)
create mode 100644 drivers/gpu/nova-core/gsp/cmdq.rs
create mode 100644 drivers/gpu/nova-core/gsp/commands.rs
create mode 100644 drivers/gpu/nova-core/sbuffer.rs
--
2.47.2
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 01/10] gpu: nova-core: Set correct DMA mask
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
@ 2025-08-27 8:19 ` Alistair Popple
2025-08-29 23:55 ` John Hubbard
2025-08-27 8:19 ` [PATCH 02/10] gpu: nova-core: Create initial GspSharedMemObjects Alistair Popple
` (9 subsequent siblings)
10 siblings, 1 reply; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:19 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
Set the correct DMA mask. Without this DMA will fail on some setups.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/driver.rs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 274989ea1fb4a..3e154ffb6be4b 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
-use kernel::{auxiliary, bindings, c_str, device::Core, pci, prelude::*, sizes::SZ_16M, sync::Arc};
+use kernel::{
+ auxiliary, bindings, c_str, device::Core, dma::Device, dma::DmaMask, pci, prelude::*,
+ sizes::SZ_16M, sync::Arc,
+};
use crate::gpu::Gpu;
@@ -34,6 +37,9 @@ fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self
pdev.enable_device_mem()?;
pdev.set_master();
+ // SAFETY: No DMA allocations have been made yet
+ unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<48>())? };
+
let bar = Arc::pin_init(
pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0")),
GFP_KERNEL,
--
2.47.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 02/10] gpu: nova-core: Create initial GspSharedMemObjects
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
2025-08-27 8:19 ` [PATCH 01/10] gpu: nova-core: Set correct DMA mask Alistair Popple
@ 2025-08-27 8:19 ` Alistair Popple
2025-08-27 8:20 ` [PATCH 03/10] gpu: nova-core: gsp: Create wpr metadata Alistair Popple
` (8 subsequent siblings)
10 siblings, 0 replies; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:19 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
The GSP requires several areas of memory to operate. Each of these have
their own simple embedded page tables. Set these up and map them for DMA
to/from GSP using CoherentAllocation's. Return the DMA handle describing
where each of these regions are for future use when booting GSP.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 6 +
drivers/gpu/nova-core/gsp.rs | 113 ++++++++++++++++++
drivers/gpu/nova-core/nvfw.rs | 7 ++
.../gpu/nova-core/nvfw/r570_144_bindings.rs | 19 +++
4 files changed, 145 insertions(+)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 5c1c88086cb0d..6190199e055c2 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -9,6 +9,7 @@
use crate::firmware::fwsec::{FwsecCommand, FwsecFirmware};
use crate::firmware::{Firmware, FIRMWARE_VERSION};
use crate::gfw;
+use crate::gsp;
use crate::regs;
use crate::util;
use crate::vbios::Vbios;
@@ -172,6 +173,7 @@ pub(crate) struct Gpu {
/// System memory page required for flushing all pending GPU-side memory writes done through
/// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
sysmem_flush: SysmemFlush,
+ libos: gsp::GspMemObjects,
}
#[pinned_drop]
@@ -309,11 +311,15 @@ pub(crate) fn new(
Self::run_fwsec_frts(pdev.as_ref(), &gsp_falcon, bar, &bios, &fb_layout)?;
+ let libos = gsp::GspMemObjects::new(pdev)?;
+ let _libos_handle = libos.libos_dma_handle();
+
Ok(pin_init!(Self {
spec,
bar: devres_bar,
fw,
sysmem_flush,
+ libos,
}))
}
}
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index ead471746ccad..161c057350622 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -1,7 +1,120 @@
// SPDX-License-Identifier: GPL-2.0
+use kernel::bindings;
+use kernel::device;
+use kernel::dma::CoherentAllocation;
+use kernel::dma_write;
+use kernel::pci;
+use kernel::prelude::*;
use kernel::ptr::Alignment;
+use kernel::transmute::{AsBytes, FromBytes};
+
+use crate::nvfw::{
+ LibosMemoryRegionInitArgument, LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
+ LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM,
+};
pub(crate) const GSP_PAGE_SHIFT: usize = 12;
pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT;
pub(crate) const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new(1 << 20);
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for LibosMemoryRegionInitArgument {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for LibosMemoryRegionInitArgument {}
+
+#[allow(unused)]
+pub(crate) struct GspMemObjects {
+ libos: CoherentAllocation<LibosMemoryRegionInitArgument>,
+ pub loginit: CoherentAllocation<u8>,
+ pub logintr: CoherentAllocation<u8>,
+ pub logrm: CoherentAllocation<u8>,
+}
+
+/// Generates the `ID8` identifier required for some GSP objects.
+fn id8(name: &str) -> u64 {
+ let mut bytes = [0u8; core::mem::size_of::<u64>()];
+
+ for (c, b) in name.bytes().rev().zip(&mut bytes) {
+ *b = c;
+ }
+
+ u64::from_ne_bytes(bytes)
+}
+
+/// Creates a self-mapping page table for `obj` at its beginning.
+fn create_pte_array(obj: &mut CoherentAllocation<u8>) {
+ let num_pages = obj.size().div_ceil(GSP_PAGE_SIZE);
+ let handle = obj.dma_handle();
+
+ // SAFETY:
+ // - By the invariants of the CoherentAllocation ptr is non-NULL.
+ // - CoherentAllocation CPU addresses are always aligned to a
+ // page-boundary, satisfying the alignment requirements for
+ // from_raw_parts_mut()
+ // - The allocation size is at least as long as 8 * num_pages as
+ // GSP_PAGE_SIZE is larger than 8 bytes.
+ let ptes = unsafe {
+ let ptr = obj.start_ptr_mut().cast::<u64>().add(1);
+ core::slice::from_raw_parts_mut(ptr, num_pages)
+ };
+
+ for (i, pte) in ptes.iter_mut().enumerate() {
+ *pte = handle + ((i as u64) << GSP_PAGE_SHIFT);
+ }
+}
+
+/// Creates a new `CoherentAllocation<A>` with `name` of `size` elements, and
+/// register it into the `libos` object at argument position `libos_arg_nr`.
+fn create_coherent_dma_object<A: AsBytes + FromBytes>(
+ dev: &device::Device<device::Bound>,
+ name: &'static str,
+ size: usize,
+ libos: &mut CoherentAllocation<LibosMemoryRegionInitArgument>,
+ libos_arg_nr: usize,
+) -> Result<CoherentAllocation<A>> {
+ let obj = CoherentAllocation::<A>::alloc_coherent(dev, size, GFP_KERNEL | __GFP_ZERO)?;
+
+ dma_write!(
+ libos[libos_arg_nr] = LibosMemoryRegionInitArgument {
+ id8: id8(name),
+ pa: obj.dma_handle(),
+ size: obj.size() as u64,
+ kind: LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS as u8,
+ loc: LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM as u8,
+ ..Default::default()
+ }
+ )?;
+
+ Ok(obj)
+}
+
+impl GspMemObjects {
+ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<Self> {
+ let dev = pdev.as_ref();
+ let mut libos = CoherentAllocation::<LibosMemoryRegionInitArgument>::alloc_coherent(
+ dev,
+ GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(),
+ GFP_KERNEL | __GFP_ZERO,
+ )?;
+ let mut loginit = create_coherent_dma_object::<u8>(dev, "LOGINIT", 0x10000, &mut libos, 0)?;
+ create_pte_array(&mut loginit);
+ let mut logintr = create_coherent_dma_object::<u8>(dev, "LOGINTR", 0x10000, &mut libos, 1)?;
+ create_pte_array(&mut logintr);
+ let mut logrm = create_coherent_dma_object::<u8>(dev, "LOGRM", 0x10000, &mut libos, 2)?;
+ create_pte_array(&mut logrm);
+
+ Ok(GspMemObjects {
+ libos,
+ loginit,
+ logintr,
+ logrm,
+ })
+ }
+
+ pub(crate) fn libos_dma_handle(&self) -> bindings::dma_addr_t {
+ self.libos.dma_handle()
+ }
+}
diff --git a/drivers/gpu/nova-core/nvfw.rs b/drivers/gpu/nova-core/nvfw.rs
index 11a63c3710b1a..9a2f0c84ab103 100644
--- a/drivers/gpu/nova-core/nvfw.rs
+++ b/drivers/gpu/nova-core/nvfw.rs
@@ -40,3 +40,10 @@ pub(crate) struct LibosParams {
/// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA
/// addresses of the GSP bootloader and firmware.
pub(crate) use r570_144::GspFwWprMeta;
+
+pub(crate) use r570_144::{
+ // LibOS memory structures
+ LibosMemoryRegionInitArgument,
+ LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
+ LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM,
+};
diff --git a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
index 0407000cca229..6a14cc3243918 100644
--- a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
+++ b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
@@ -124,3 +124,22 @@ fn default() -> Self {
}
}
}
+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)]
+pub struct LibosMemoryRegionInitArgument {
+ pub id8: LibosAddress,
+ pub pa: LibosAddress,
+ pub size: LibosAddress,
+ pub kind: u8_,
+ pub loc: u8_,
+ pub __bindgen_padding_0: [u8; 6usize],
+}
--
2.47.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 03/10] gpu: nova-core: gsp: Create wpr metadata
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
2025-08-27 8:19 ` [PATCH 01/10] gpu: nova-core: Set correct DMA mask Alistair Popple
2025-08-27 8:19 ` [PATCH 02/10] gpu: nova-core: Create initial GspSharedMemObjects Alistair Popple
@ 2025-08-27 8:20 ` Alistair Popple
2025-09-01 7:46 ` Alexandre Courbot
2025-08-27 8:20 ` [PATCH 04/10] gpu: nova-core: Add a slice-buffer (sbuffer) datastructure Alistair Popple
` (7 subsequent siblings)
10 siblings, 1 reply; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:20 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
The GSP requires some pieces of metadata to boot. These are passed in a
struct which the GSP transfers via DMA. Create this struct and get a
handle to it for future use when booting the GSP.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/fb.rs | 1 -
drivers/gpu/nova-core/firmware.rs | 2 +-
drivers/gpu/nova-core/firmware/gsp.rs | 1 -
drivers/gpu/nova-core/firmware/riscv.rs | 6 +-
drivers/gpu/nova-core/gpu.rs | 4 +-
drivers/gpu/nova-core/gsp.rs | 77 ++++++++++++++++++-
drivers/gpu/nova-core/nvfw.rs | 7 ++
.../gpu/nova-core/nvfw/r570_144_bindings.rs | 2 +
8 files changed, 89 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index a3eb063f86b3a..b1131a94389c6 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -130,7 +130,6 @@ pub(crate) fn wpr_heap_size(&self, fb_size: u64) -> Result<u64> {
///
/// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process.
#[derive(Debug)]
-#[expect(dead_code)]
pub(crate) struct FbLayout {
/// Range of the framebuffer. Starts at `0`.
pub(crate) fb: Range<u64>,
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 05e57730a3c6f..6c210e668d541 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -126,7 +126,7 @@ pub(crate) struct Firmware {
/// GSP firmware.
pub gsp: Pin<KBox<GspFirmware>>,
/// GSP signatures, to be passed as parameter to the bootloader for validation.
- gsp_sigs: DmaObject,
+ pub gsp_sigs: DmaObject,
}
impl Firmware {
diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index f37bd619bfb71..69322fa7c1466 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -94,7 +94,6 @@ pub(crate) fn new<'a>(
})
}
- #[expect(unused)]
/// Returns the DMA handle of the level 0 page table.
pub(crate) fn lvl0_dma_handle(&self) -> DmaAddress {
self.lvl0.dma_handle()
diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs
index b2f646c1f02c6..81bb348055031 100644
--- a/drivers/gpu/nova-core/firmware/riscv.rs
+++ b/drivers/gpu/nova-core/firmware/riscv.rs
@@ -53,11 +53,11 @@ fn new(bin_fw: &BinFirmware<'_>) -> Result<Self> {
#[expect(unused)]
pub(crate) struct RiscvFirmware {
/// Offset at which the code starts in the firmware image.
- code_offset: u32,
+ pub code_offset: u32,
/// Offset at which the data starts in the firmware image.
- data_offset: u32,
+ pub data_offset: u32,
/// Offset at which the manifest starts in the firmware image.
- manifest_offset: u32,
+ pub manifest_offset: u32,
/// Application version.
app_version: u32,
/// Device-mapped firmware image.
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 6190199e055c2..bf762353f1d91 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -13,6 +13,7 @@
use crate::regs;
use crate::util;
use crate::vbios::Vbios;
+
use core::fmt;
macro_rules! define_chipset {
@@ -311,8 +312,9 @@ pub(crate) fn new(
Self::run_fwsec_frts(pdev.as_ref(), &gsp_falcon, bar, &bios, &fb_layout)?;
- let libos = gsp::GspMemObjects::new(pdev)?;
+ let libos = gsp::GspMemObjects::new(pdev, &fw, &fb_layout)?;
let _libos_handle = libos.libos_dma_handle();
+ let _wpr_handle = libos.wpr_meta.dma_handle();
Ok(pin_init!(Self {
spec,
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 161c057350622..1f51e354b9569 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -6,12 +6,17 @@
use kernel::dma_write;
use kernel::pci;
use kernel::prelude::*;
-use kernel::ptr::Alignment;
+use kernel::ptr::{Alignable, Alignment};
+use kernel::sizes::SZ_128K;
use kernel::transmute::{AsBytes, FromBytes};
+use crate::fb::FbLayout;
+use crate::firmware::Firmware;
use crate::nvfw::{
- LibosMemoryRegionInitArgument, LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
- LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM,
+ GspFwWprMeta, GspFwWprMetaBootInfo, GspFwWprMetaBootResumeInfo, LibosMemoryRegionInitArgument,
+ LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
+ LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM, GSP_FW_WPR_META_MAGIC,
+ GSP_FW_WPR_META_REVISION,
};
pub(crate) const GSP_PAGE_SHIFT: usize = 12;
@@ -25,12 +30,69 @@ unsafe impl AsBytes for LibosMemoryRegionInitArgument {}
// are valid.
unsafe impl FromBytes for LibosMemoryRegionInitArgument {}
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for GspFwWprMeta {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for GspFwWprMeta {}
+
#[allow(unused)]
pub(crate) struct GspMemObjects {
libos: CoherentAllocation<LibosMemoryRegionInitArgument>,
pub loginit: CoherentAllocation<u8>,
pub logintr: CoherentAllocation<u8>,
pub logrm: CoherentAllocation<u8>,
+ pub wpr_meta: CoherentAllocation<GspFwWprMeta>,
+}
+
+pub(crate) fn build_wpr_meta(
+ dev: &device::Device<device::Bound>,
+ fw: &Firmware,
+ fb_layout: &FbLayout,
+) -> Result<CoherentAllocation<GspFwWprMeta>> {
+ let wpr_meta =
+ CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
+ dma_write!(
+ wpr_meta[0] = GspFwWprMeta {
+ magic: GSP_FW_WPR_META_MAGIC as u64,
+ revision: u64::from(GSP_FW_WPR_META_REVISION),
+ sysmemAddrOfRadix3Elf: fw.gsp.lvl0_dma_handle(),
+ sizeOfRadix3Elf: fw.gsp.size as u64,
+ sysmemAddrOfBootloader: fw.gsp_bootloader.ucode.dma_handle(),
+ sizeOfBootloader: fw.gsp_bootloader.ucode.size() as u64,
+ bootloaderCodeOffset: u64::from(fw.gsp_bootloader.code_offset),
+ bootloaderDataOffset: u64::from(fw.gsp_bootloader.data_offset),
+ bootloaderManifestOffset: u64::from(fw.gsp_bootloader.manifest_offset),
+ __bindgen_anon_1: GspFwWprMetaBootResumeInfo {
+ __bindgen_anon_1: GspFwWprMetaBootInfo {
+ sysmemAddrOfSignature: fw.gsp_sigs.dma_handle(),
+ sizeOfSignature: fw.gsp_sigs.size() as u64,
+ }
+ },
+ gspFwRsvdStart: fb_layout.heap.start,
+ nonWprHeapOffset: fb_layout.heap.start,
+ nonWprHeapSize: fb_layout.heap.end - fb_layout.heap.start,
+ gspFwWprStart: fb_layout.wpr2.start,
+ gspFwHeapOffset: fb_layout.wpr2_heap.start,
+ gspFwHeapSize: fb_layout.wpr2_heap.end - fb_layout.wpr2_heap.start,
+ gspFwOffset: fb_layout.elf.start,
+ bootBinOffset: fb_layout.boot.start,
+ frtsOffset: fb_layout.frts.start,
+ frtsSize: fb_layout.frts.end - fb_layout.frts.start,
+ gspFwWprEnd: fb_layout
+ .vga_workspace
+ .start
+ .align_down(Alignment::new(SZ_128K)),
+ gspFwHeapVfPartitionCount: fb_layout.vf_partition_count,
+ fbSize: fb_layout.fb.end - fb_layout.fb.start,
+ vgaWorkspaceOffset: fb_layout.vga_workspace.start,
+ vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start,
+ ..Default::default()
+ }
+ )?;
+
+ Ok(wpr_meta)
}
/// Generates the `ID8` identifier required for some GSP objects.
@@ -92,7 +154,11 @@ fn create_coherent_dma_object<A: AsBytes + FromBytes>(
}
impl GspMemObjects {
- pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<Self> {
+ pub(crate) fn new(
+ pdev: &pci::Device<device::Bound>,
+ fw: &Firmware,
+ fb_layout: &FbLayout,
+ ) -> Result<Self> {
let dev = pdev.as_ref();
let mut libos = CoherentAllocation::<LibosMemoryRegionInitArgument>::alloc_coherent(
dev,
@@ -106,11 +172,14 @@ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<Self> {
let mut logrm = create_coherent_dma_object::<u8>(dev, "LOGRM", 0x10000, &mut libos, 2)?;
create_pte_array(&mut logrm);
+ let wpr_meta = build_wpr_meta(dev, fw, fb_layout)?;
+
Ok(GspMemObjects {
libos,
loginit,
logintr,
logrm,
+ wpr_meta,
})
}
diff --git a/drivers/gpu/nova-core/nvfw.rs b/drivers/gpu/nova-core/nvfw.rs
index 9a2f0c84ab103..c04b8e218758b 100644
--- a/drivers/gpu/nova-core/nvfw.rs
+++ b/drivers/gpu/nova-core/nvfw.rs
@@ -46,4 +46,11 @@ pub(crate) struct LibosParams {
LibosMemoryRegionInitArgument,
LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM,
+
+ // GSP firmware constants
+ GSP_FW_WPR_META_MAGIC,
+ GSP_FW_WPR_META_REVISION,
};
+
+pub(crate) type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1;
+pub(crate) type GspFwWprMetaBootInfo = r570_144::GspFwWprMeta__bindgen_ty_1__bindgen_ty_1;
diff --git a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
index 6a14cc3243918..392b25dc6991a 100644
--- a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
+++ b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
@@ -9,6 +9,8 @@
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 type __u8 = ffi::c_uchar;
pub type __u16 = ffi::c_ushort;
pub type __u32 = ffi::c_uint;
--
2.47.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 04/10] gpu: nova-core: Add a slice-buffer (sbuffer) datastructure
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
` (2 preceding siblings ...)
2025-08-27 8:20 ` [PATCH 03/10] gpu: nova-core: gsp: Create wpr metadata Alistair Popple
@ 2025-08-27 8:20 ` Alistair Popple
2025-08-27 8:20 ` [PATCH 05/10] gpu: nova-core: gsp: Add GSP command queue handling Alistair Popple
` (6 subsequent siblings)
10 siblings, 0 replies; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:20 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
From: Joel Fernandes <joelagnelf@nvidia.com>
A data structure that can be used to write across multiple slices which
may be out of order in memory. This lets SBuffer user correctly and
safely write out of memory order, without error-prone tracking of
pointers/offsets.
let mut buf1 = [0u8; 3];
let mut buf2 = [0u8; 5];
let mut sbuffer = SBuffer::new([&mut buf1[..], &mut buf2[..]]);
let data = b"hellowo";
let result = sbuffer.write(data);
An internal conversion of gsp.rs to use this resulted in a nice -ve delta:
gsp.rs: 37 insertions(+), 99 deletions(-)
Co-developed-by: Alistair Popple <apopple@nvidia.com>
Signed-off-by: Alistair Popple <apopple@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/sbuffer.rs | 188 +++++++++++++++++++++++++++++
2 files changed, 189 insertions(+)
create mode 100644 drivers/gpu/nova-core/sbuffer.rs
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index db197498b0b7b..4dbc7e5daae33 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -12,6 +12,7 @@
mod gsp;
mod nvfw;
mod regs;
+mod sbuffer;
mod util;
mod vbios;
diff --git a/drivers/gpu/nova-core/sbuffer.rs b/drivers/gpu/nova-core/sbuffer.rs
new file mode 100644
index 0000000000000..b1b8c4536d2f6
--- /dev/null
+++ b/drivers/gpu/nova-core/sbuffer.rs
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use core::ops::Deref;
+
+use kernel::alloc::KVec;
+use kernel::error::code::*;
+use kernel::prelude::*;
+
+/// A buffer abstraction for discontiguous byte slices.
+///
+/// This allows you to treat multiple non-contiguous `&mut [u8]` slices
+/// as a single stream-like read/write buffer.
+///
+/// Example:
+///
+/// let mut buf1 = [0u8; 3];
+/// let mut buf2 = [0u8; 5];
+/// let mut sbuffer = SWriteBuffer::new([&buf1, &buf2]);
+///
+/// let data = b"hellowo";
+/// let result = sbuffer.write_all(0, data);
+///
+/// A sliding window of slices to proceed.
+///
+/// Both read and write buffers are implemented in terms of operating on slices of a requested
+/// size. This base class implements logic that can be shared between the two to support that.
+///
+/// `S` is a slice type, `I` is an iterator yielding `S`.
+pub(crate) struct SBuffer<I: Iterator> {
+ /// `Some` if we are not at the end of the data yet.
+ cur_slice: Option<I::Item>,
+ /// All the slices remaining after `cur_slice`.
+ slices: I,
+}
+
+impl<'a, I> SBuffer<I>
+where
+ I: Iterator,
+{
+ pub(crate) fn new_reader(slices: impl IntoIterator<IntoIter = I>) -> Self
+ where
+ I: Iterator<Item = &'a [u8]>,
+ {
+ Self::new(slices)
+ }
+
+ pub(crate) fn new_writer(slices: impl IntoIterator<IntoIter = I>) -> Self
+ where
+ I: Iterator<Item = &'a mut [u8]>,
+ {
+ Self::new(slices)
+ }
+
+ fn new(slices: impl IntoIterator<IntoIter = I>) -> Self
+ where
+ I::Item: Deref<Target = [u8]>,
+ {
+ let mut slices = slices.into_iter();
+
+ Self {
+ // Skip empty slices to avoid trouble down the road.
+ cur_slice: slices.find(|s| !s.deref().is_empty()),
+ slices,
+ }
+ }
+
+ fn get_slice_internal(
+ &mut self,
+ len: usize,
+ mut f: impl FnMut(I::Item, usize) -> (I::Item, I::Item),
+ ) -> Option<I::Item>
+ where
+ I::Item: Deref<Target = [u8]>,
+ {
+ match self.cur_slice.take() {
+ None => None,
+ Some(cur_slice) => {
+ if len >= cur_slice.len() {
+ // Caller requested more data than is in the current slice, return it entirely
+ // and prepare the following slice for being used. Skip empty slices to avoid
+ // trouble.
+ self.cur_slice = self.slices.find(|s| !s.deref().is_empty());
+
+ Some(cur_slice)
+ } else {
+ // The current slice can satisfy the request, split it and return a slice of
+ // the requested size.
+ let (ret, next) = f(cur_slice, len);
+ self.cur_slice = Some(next);
+
+ Some(ret)
+ }
+ }
+ }
+ }
+}
+
+/// Provides a way to get non-mutable slices of data to read from.
+impl<'a, I> SBuffer<I>
+where
+ I: Iterator<Item = &'a [u8]>,
+{
+ /// Returns a slice of at most `len` bytes, or `None` if we are at the end of the data.
+ ///
+ /// If a slice shorter than `len` bytes has been returned, the caller can call this method
+ /// again until it returns `None` to try and obtain the remainder of the data.
+ fn get_slice(&mut self, len: usize) -> Option<&'a [u8]> {
+ self.get_slice_internal(len, |s, pos| s.split_at(pos))
+ }
+
+ /// Ideally we would implement `Read`, but it is not available in `core`.
+ /// So mimic `std::io::Read::read_exact`.
+ #[expect(unused)]
+ pub(crate) fn read_exact(&mut self, mut dst: &mut [u8]) -> Result {
+ while !dst.is_empty() {
+ match self.get_slice(dst.len()) {
+ None => return Err(ETOOSMALL),
+ Some(src) => {
+ let dst_slice;
+ (dst_slice, dst) = dst.split_at_mut(src.len());
+ dst_slice.copy_from_slice(src);
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Read all the remaining data into a `KVec`.
+ ///
+ /// `self` will be empty after this operation.
+ #[expect(unused)]
+ pub(crate) fn read_into_kvec(&mut self, flags: kernel::alloc::Flags) -> Result<KVec<u8>> {
+ let mut buf = KVec::<u8>::new();
+
+ if let Some(slice) = core::mem::take(&mut self.cur_slice) {
+ buf.extend_from_slice(slice, flags)?;
+ }
+ for slice in &mut self.slices {
+ buf.extend_from_slice(slice, flags)?;
+ }
+
+ Ok(buf)
+ }
+}
+
+/// Provides a way to get mutable slices of data to write into.
+impl<'a, I> SBuffer<I>
+where
+ I: Iterator<Item = &'a mut [u8]>,
+{
+ /// Returns a mutable slice of at most `len` bytes, or `None` if we are at the end of the data.
+ ///
+ /// If a slice shorter than `len` bytes has been returned, the caller can call this method
+ /// again until it returns `None` to try and obtain the remainder of the data.
+ fn get_slice_mut(&mut self, len: usize) -> Option<&'a mut [u8]> {
+ self.get_slice_internal(len, |s, pos| s.split_at_mut(pos))
+ }
+
+ /// Ideally we would implement `Write`, but it is not available in `core`.
+ /// So mimic `std::io::Write::write_all`.
+ pub(crate) fn write_all(&mut self, mut src: &[u8]) -> Result {
+ while !src.is_empty() {
+ match self.get_slice_mut(src.len()) {
+ None => return Err(ETOOSMALL),
+ Some(dst) => {
+ let src_slice;
+ (src_slice, src) = src.split_at(dst.len());
+ dst.copy_from_slice(src_slice);
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl<'a, I> Iterator for SBuffer<I>
+where
+ I: Iterator<Item = &'a [u8]>,
+{
+ type Item = u8;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ // Returned slices are guaranteed to not be empty so we can safely index the first entry.
+ self.get_slice(1).map(|s| s[0])
+ }
+}
--
2.47.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 05/10] gpu: nova-core: gsp: Add GSP command queue handling
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
` (3 preceding siblings ...)
2025-08-27 8:20 ` [PATCH 04/10] gpu: nova-core: Add a slice-buffer (sbuffer) datastructure Alistair Popple
@ 2025-08-27 8:20 ` Alistair Popple
2025-08-27 20:35 ` John Hubbard
2025-08-27 8:20 ` [PATCH 06/10] gpu: nova-core: gsp: Create rmargs Alistair Popple
` (5 subsequent siblings)
10 siblings, 1 reply; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:20 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
This commit introduces core infrastructure for handling GSP command and
message queues in the nova-core driver. The command queue system enables
bidirectional communication between the host driver and GSP firmware
through a remote message passing interface.
The interface is based on passing serialised data structures over a ring
buffer with separate transmit and receive queues. Commands are sent by
writing to the CPU transmit queue and waiting for completion via the
receive queue.
To ensure safety mutable or immutable (depending on whether it is a send
or receive operation) references are taken on the command queue when
allocating the message to write/read to. This ensures message memory
remains valid and the command queue can't be mutated whilst an operation
is in progress.
Currently this is only used by the probe() routine and therefore can
only used by a single thread of execution. Locking to enable safe access
from multiple threads will be introduced in a future series when that
becomes necessary.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/gsp.rs | 20 +-
drivers/gpu/nova-core/gsp/cmdq.rs | 695 ++++++++++++++++++
drivers/gpu/nova-core/nvfw.rs | 31 +
.../gpu/nova-core/nvfw/r570_144_bindings.rs | 268 +++++++
drivers/gpu/nova-core/regs.rs | 4 +
5 files changed, 1012 insertions(+), 6 deletions(-)
create mode 100644 drivers/gpu/nova-core/gsp/cmdq.rs
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 1f51e354b9569..41a88087d9baa 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
+use kernel::alloc::flags::GFP_KERNEL;
use kernel::bindings;
use kernel::device;
use kernel::dma::CoherentAllocation;
@@ -12,6 +13,7 @@
use crate::fb::FbLayout;
use crate::firmware::Firmware;
+use crate::gsp::cmdq::GspCmdq;
use crate::nvfw::{
GspFwWprMeta, GspFwWprMetaBootInfo, GspFwWprMetaBootResumeInfo, LibosMemoryRegionInitArgument,
LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
@@ -19,6 +21,8 @@
GSP_FW_WPR_META_REVISION,
};
+pub(crate) mod cmdq;
+
pub(crate) const GSP_PAGE_SHIFT: usize = 12;
pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT;
pub(crate) const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new(1 << 20);
@@ -44,6 +48,7 @@ pub(crate) struct GspMemObjects {
pub logintr: CoherentAllocation<u8>,
pub logrm: CoherentAllocation<u8>,
pub wpr_meta: CoherentAllocation<GspFwWprMeta>,
+ pub cmdq: GspCmdq,
}
pub(crate) fn build_wpr_meta(
@@ -107,7 +112,7 @@ fn id8(name: &str) -> u64 {
}
/// Creates a self-mapping page table for `obj` at its beginning.
-fn create_pte_array(obj: &mut CoherentAllocation<u8>) {
+fn create_pte_array<T: AsBytes + FromBytes>(obj: &mut CoherentAllocation<T>, skip: usize) {
let num_pages = obj.size().div_ceil(GSP_PAGE_SIZE);
let handle = obj.dma_handle();
@@ -119,7 +124,7 @@ fn create_pte_array(obj: &mut CoherentAllocation<u8>) {
// - The allocation size is at least as long as 8 * num_pages as
// GSP_PAGE_SIZE is larger than 8 bytes.
let ptes = unsafe {
- let ptr = obj.start_ptr_mut().cast::<u64>().add(1);
+ let ptr = obj.start_ptr_mut().cast::<u64>().add(skip);
core::slice::from_raw_parts_mut(ptr, num_pages)
};
@@ -166,20 +171,23 @@ pub(crate) fn new(
GFP_KERNEL | __GFP_ZERO,
)?;
let mut loginit = create_coherent_dma_object::<u8>(dev, "LOGINIT", 0x10000, &mut libos, 0)?;
- create_pte_array(&mut loginit);
+ create_pte_array(&mut loginit, 1);
let mut logintr = create_coherent_dma_object::<u8>(dev, "LOGINTR", 0x10000, &mut libos, 1)?;
- create_pte_array(&mut logintr);
+ create_pte_array(&mut logintr, 1);
let mut logrm = create_coherent_dma_object::<u8>(dev, "LOGRM", 0x10000, &mut libos, 2)?;
- create_pte_array(&mut logrm);
-
+ create_pte_array(&mut logrm, 1);
let wpr_meta = build_wpr_meta(dev, fw, fb_layout)?;
+ // Creates its own PTE array
+ let cmdq = GspCmdq::new(dev)?;
+
Ok(GspMemObjects {
libos,
loginit,
logintr,
logrm,
wpr_meta,
+ cmdq,
})
}
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
new file mode 100644
index 0000000000000..3f5d31c8e68f2
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -0,0 +1,695 @@
+// SPDX-License-Identifier: GPL-2.0
+use core::mem::offset_of;
+use core::ptr;
+use core::sync::atomic::{fence, Ordering};
+
+use kernel::alloc::flags::GFP_KERNEL;
+use kernel::device;
+use kernel::dma::CoherentAllocation;
+use kernel::prelude::*;
+use kernel::sync::aref::ARef;
+use kernel::time::Delta;
+use kernel::transmute::{AsBytes, FromBytes};
+use kernel::{dma_read, dma_write};
+
+use crate::driver::Bar0;
+use crate::gsp::create_pte_array;
+use crate::gsp::{GSP_PAGE_SHIFT, GSP_PAGE_SIZE};
+use crate::nvfw::{
+ NV_VGPU_MSG_EVENT_GSP_INIT_DONE, NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE,
+ NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD, NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER,
+ NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED, NV_VGPU_MSG_EVENT_OS_ERROR_LOG,
+ NV_VGPU_MSG_EVENT_POST_EVENT, NV_VGPU_MSG_EVENT_RC_TRIGGERED,
+ NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT, NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA,
+ NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA, NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE,
+ NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY, NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT,
+ NV_VGPU_MSG_FUNCTION_ALLOC_ROOT, NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA, NV_VGPU_MSG_FUNCTION_FREE,
+ NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO, NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO,
+ NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU, NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL,
+ NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO, NV_VGPU_MSG_FUNCTION_LOG,
+ NV_VGPU_MSG_FUNCTION_MAP_MEMORY, NV_VGPU_MSG_FUNCTION_NOP,
+ NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO, NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
+};
+use crate::regs::NV_PGSP_QUEUE_HEAD;
+use crate::sbuffer::SBuffer;
+use crate::util::wait_on;
+
+const GSP_COMMAND_TIMEOUT: i64 = 5;
+
+pub(crate) trait GspCommandToGsp: Sized {
+ const FUNCTION: u32;
+}
+
+pub(crate) trait GspMessageFromGsp: Sized {
+ const FUNCTION: u32;
+}
+
+// This next section contains constants and structures hand-coded from the GSP
+// headers We could replace these with bindgen versions, but that's a bit of a
+// pain because they basically end up pulling in the world (ie. definitions for
+// every rpc method). So for now the hand-coded ones are fine. They are just
+// structs so we can easily move to bindgen generated ones if/when we want to.
+
+// A GSP RPC header
+#[repr(C)]
+#[derive(Debug, Clone)]
+struct GspRpcHeader {
+ header_version: u32,
+ signature: u32,
+ length: u32,
+ function: u32,
+ rpc_result: u32,
+ rpc_result_private: u32,
+ sequence: u32,
+ cpu_rm_gfid: u32,
+}
+
+// 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 GspRpcHeader {}
+
+// 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 GspRpcHeader {}
+
+// A GSP message element header
+#[repr(C)]
+#[derive(Debug, Clone)]
+struct GspMsgHeader {
+ auth_tag_buffer: [u8; 16],
+ aad_buffer: [u8; 16],
+ checksum: u32,
+ sequence: u32,
+ elem_count: u32,
+ pad: u32,
+}
+
+// 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 GspMsgHeader {}
+
+// 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 GspMsgHeader {}
+
+// These next two structs come from msgq_priv.h. Hopefully the will never
+// need updating once the ABI is stabalised.
+#[repr(C)]
+#[derive(Debug)]
+struct MsgqTxHeader {
+ version: u32, // queue version
+ size: u32, // bytes, page aligned
+ msg_size: u32, // entry size, bytes, must be power-of-2, 16 is minimum
+ msg_count: u32, // number of entries in queue
+ write_ptr: u32, // message id of next slot
+ flags: u32, // if set it means "i want to swap RX"
+ rx_hdr_off: u32, // Offset of msgqRxHeader from start of backing store
+ entry_off: u32, // Offset of entries from start of backing store
+}
+
+// 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 MsgqTxHeader {}
+
+#[repr(C)]
+#[derive(Debug)]
+struct MsgqRxHeader {
+ read_ptr: u32, // message id of last message read
+}
+
+/// Number of GSP pages making the Msgq.
+const MSGQ_NUM_PAGES: usize = 0x3f;
+
+#[repr(C, align(0x1000))]
+#[derive(Debug)]
+struct MsgqData {
+ data: [[u8; GSP_PAGE_SIZE]; MSGQ_NUM_PAGES],
+}
+
+// Annoyingly there is no real equivalent of #define so we're forced to use a
+// literal to specify the alignment above. So check that against the actual GSP
+// page size here.
+static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);
+
+// There is no struct defined for this in the open-gpu-kernel-source headers.
+// Instead it is defined by code in GspMsgQueuesInit().
+#[repr(C)]
+#[derive(Debug)]
+struct Msgq {
+ tx: MsgqTxHeader,
+ rx: MsgqRxHeader,
+ msgq: MsgqData,
+}
+
+#[repr(C)]
+#[derive(Debug)]
+struct GspMem {
+ ptes: [u8; GSP_PAGE_SIZE],
+ cpuq: Msgq,
+ gspq: Msgq,
+}
+
+// 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 GspMem {}
+
+// 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 GspMem {}
+
+pub(crate) struct GspCmdq {
+ dev: ARef<device::Device>,
+ msg_count: u32,
+ seq: u32,
+ gsp_mem: CoherentAllocation<GspMem>,
+ pub _nr_ptes: u32,
+}
+
+// A reference to a message currently sitting in the GSP command queue. May
+// contain two slices as the command queue is a circular buffer which may have
+// wrapped.
+//
+// INVARIANT: The underlying message data cannot change because the struct holds
+// a reference to the command queue which prevents command queue manipulation
+// until the GspQueueMessage is dropped.
+pub(crate) struct GspQueueMessage<'a> {
+ cmdq: &'a mut GspCmdq,
+ rpc_header: &'a GspRpcHeader,
+ slice_1: &'a [u8],
+ slice_2: Option<&'a [u8]>,
+}
+
+type GspQueueMessageData<'a, M> = (&'a M, Option<SBuffer<core::array::IntoIter<&'a [u8], 2>>>);
+
+impl<'a> GspQueueMessage<'a> {
+ #[expect(unused)]
+ pub(crate) fn try_as<M: GspMessageFromGsp>(&'a self) -> Result<GspQueueMessageData<'a, M>> {
+ if self.rpc_header.function != M::FUNCTION {
+ return Err(ERANGE);
+ }
+
+ // SAFETY: The slice references the cmdq message memory which is
+ // guaranteed to outlive the returned GspQueueMessageData by the
+ // invariants of GspQueueMessage and the lifetime 'a.
+ let msg = unsafe { &*(self.slice_1.as_ptr().cast::<M>()) };
+ let data = &self.slice_1[size_of::<M>()..];
+ let data_size =
+ self.rpc_header.length as usize - size_of::<GspRpcHeader>() - size_of::<M>();
+ let sbuf = if data_size > 0 {
+ Some(SBuffer::new_reader([data, self.slice_2.unwrap_or(&[])]))
+ } else {
+ None
+ };
+
+ Ok((msg, sbuf))
+ }
+
+ #[expect(unused)]
+ pub(crate) fn ack(self) -> Result {
+ self.cmdq.ack_msg(self.rpc_header.length)?;
+
+ Ok(())
+ }
+}
+
+// The same as GspQueueMessage except the fields are mutable for constructing a
+// message to the GSP.
+pub(crate) struct GspQueueCommand<'a> {
+ cmdq: &'a mut GspCmdq,
+ msg_header: &'a mut GspMsgHeader,
+ rpc_header: &'a mut GspRpcHeader,
+ slice_1: &'a mut [u8],
+ slice_2: &'a mut [u8],
+}
+
+type GspQueueCommandData<'a, M> = (
+ &'a mut M,
+ Option<SBuffer<core::array::IntoIter<&'a mut [u8], 2>>>,
+);
+
+impl<'a> GspQueueCommand<'a> {
+ #[expect(unused)]
+ pub(crate) fn try_as<'b, M: GspCommandToGsp>(&'b mut self) -> GspQueueCommandData<'b, M> {
+ // SAFETY: The slice references the cmdq message memory which is
+ // guaranteed to outlive the returned GspQueueCommandData by the
+ // invariants of GspQueueCommand and the lifetime 'a.
+ let msg = unsafe { &mut *(self.slice_1.as_mut_ptr().cast::<M>()) };
+ let data = &mut self.slice_1[size_of::<M>()..];
+ let data_size =
+ self.rpc_header.length as usize - size_of::<GspRpcHeader>() - size_of::<M>();
+ let sbuf = if data_size > 0 {
+ Some(SBuffer::new_writer([data, self.slice_2]))
+ } else {
+ None
+ };
+ self.rpc_header.function = M::FUNCTION;
+
+ (msg, sbuf)
+ }
+
+ #[expect(unused)]
+ pub(crate) fn send_to_gsp(self, bar: &Bar0) -> Result {
+ self.cmdq.wait_for_free_cmd_to_gsp(
+ Delta::from_secs(GSP_COMMAND_TIMEOUT),
+ self.rpc_header.length as usize + size_of::<GspMsgHeader>(),
+ )?;
+ GspCmdq::send_cmd_to_gsp(self, bar)?;
+ Ok(())
+ }
+}
+
+impl GspCmdq {
+ pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<GspCmdq> {
+ let mut gsp_mem =
+ CoherentAllocation::<GspMem>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
+
+ let nr_ptes = size_of::<GspMem>() >> GSP_PAGE_SHIFT;
+ build_assert!(nr_ptes * size_of::<u64>() <= GSP_PAGE_SIZE);
+
+ create_pte_array(&mut gsp_mem, 0);
+
+ const MSGQ_SIZE: u32 = size_of::<Msgq>() as u32;
+ const MSG_COUNT: u32 = ((MSGQ_SIZE as usize - GSP_PAGE_SIZE) / GSP_PAGE_SIZE) as u32;
+ const RX_HDR_OFF: u32 = offset_of!(Msgq, rx) as u32;
+ dma_write!(
+ gsp_mem[0].cpuq.tx = MsgqTxHeader {
+ version: 0,
+ size: MSGQ_SIZE,
+ entry_off: GSP_PAGE_SIZE as u32,
+ msg_size: GSP_PAGE_SIZE as u32,
+ msg_count: MSG_COUNT,
+ write_ptr: 0,
+ flags: 1,
+ rx_hdr_off: RX_HDR_OFF,
+ }
+ )?;
+
+ Ok(GspCmdq {
+ dev: dev.into(),
+ msg_count: MSG_COUNT,
+ seq: 0,
+ gsp_mem,
+ _nr_ptes: nr_ptes as u32,
+ })
+ }
+
+ fn cpu_wptr(&self) -> u32 {
+ // SAFETY: index `0` is valid as `gsp_mem` has been allocated accordingly, thus the access
+ // cannot fail.
+ unsafe { dma_read!(self.gsp_mem[0].cpuq.tx.write_ptr).unwrap_unchecked() }
+ }
+
+ fn gsp_rptr(&self) -> u32 {
+ // SAFETY: index `0` is valid as `gsp_mem` has been allocated accordingly, thus the access
+ // cannot fail.
+ unsafe { dma_read!(self.gsp_mem[0].gspq.rx.read_ptr).unwrap_unchecked() }
+ }
+
+ fn cpu_rptr(&self) -> u32 {
+ // SAFETY: index `0` is valid as `gsp_mem` has been allocated accordingly, thus the access
+ // cannot fail.
+ unsafe { dma_read!(self.gsp_mem[0].cpuq.rx.read_ptr).unwrap_unchecked() }
+ }
+
+ fn gsp_wptr(&self) -> u32 {
+ // SAFETY: index `0` is valid as `gsp_mem` has been allocated accordingly, thus the access
+ // cannot fail.
+ unsafe { dma_read!(self.gsp_mem[0].gspq.tx.write_ptr).unwrap_unchecked() }
+ }
+
+ // Returns the numbers of pages free for sending an RPC to GSP.
+ fn free_tx_pages(&self) -> u32 {
+ let wptr = self.cpu_wptr();
+ let rptr = self.gsp_rptr();
+ let mut free = rptr + self.msg_count - wptr - 1;
+
+ if free >= self.msg_count {
+ free -= self.msg_count;
+ }
+
+ free
+ }
+
+ // Returns the number of pages the GSP has written to the queue.
+ fn used_rx_pages(&self) -> u32 {
+ let rptr = self.cpu_rptr();
+ let wptr = self.gsp_wptr();
+ let mut used = wptr + self.msg_count - rptr;
+ if used >= self.msg_count {
+ used -= self.msg_count;
+ }
+
+ used
+ }
+
+ fn calculate_checksum<T: Iterator<Item = u8>>(it: T) -> u32 {
+ let sum64 = it
+ .enumerate()
+ .map(|(idx, byte)| (((idx % 8) * 8) as u32, byte))
+ .fold(0, |acc, (rol, byte)| acc ^ u64::from(byte).rotate_left(rol));
+
+ ((sum64 >> 32) as u32) ^ (sum64 as u32)
+ }
+
+ pub(crate) fn wait_for_free_cmd_to_gsp(&self, timeout: Delta, size: usize) -> Result {
+ wait_on(timeout, || {
+ if self.free_tx_pages() < size.div_ceil(GSP_PAGE_SIZE) as u32 {
+ None
+ } else {
+ Some(())
+ }
+ })
+ }
+
+ #[expect(unused)]
+ pub(crate) fn alloc_gsp_queue_command<'a>(
+ &'a mut self,
+ cmd_size: usize,
+ ) -> Result<GspQueueCommand<'a>> {
+ const HEADER_SIZE: usize = size_of::<GspMsgHeader>() + size_of::<GspRpcHeader>();
+ let msg_size = size_of::<GspMsgHeader>() + size_of::<GspRpcHeader>() + cmd_size;
+ if self.free_tx_pages() < msg_size.div_ceil(GSP_PAGE_SIZE) as u32 {
+ return Err(EAGAIN);
+ }
+ let wptr = self.cpu_wptr() as usize;
+
+ // SAFETY: By the invariants of CoherentAllocation gsp_mem.start_ptr_mut() is valid.
+ let ptr = unsafe {
+ core::ptr::addr_of_mut!((*self.gsp_mem.start_ptr_mut()).cpuq.msgq.data[wptr])
+ };
+
+ // SAFETY: ptr points to at least one GSP_PAGE_SIZE bytes of contiguous
+ // memory which is larger than GspMsgHeader.
+ let msg_header_slice: &mut [u8] =
+ unsafe { core::slice::from_raw_parts_mut(ptr.cast::<u8>(), size_of::<GspMsgHeader>()) };
+ msg_header_slice.fill(0);
+ let msg_header = GspMsgHeader::from_bytes_mut(msg_header_slice).ok_or(EINVAL)?;
+ msg_header.auth_tag_buffer = [0; 16];
+ msg_header.aad_buffer = [0; 16];
+ msg_header.checksum = 0;
+ msg_header.sequence = self.seq;
+ msg_header.elem_count = (HEADER_SIZE + cmd_size).div_ceil(GSP_PAGE_SIZE) as u32;
+ msg_header.pad = 0;
+ self.seq += 1;
+
+ // SAFETY: ptr points to GSP_PAGE_SIZE bytes of memory which is larger
+ // than both GspMsgHeader and GspRpcHeader combined.
+ let rpc_header_slice: &mut [u8] = unsafe {
+ core::slice::from_raw_parts_mut(
+ ptr.cast::<u8>().add(size_of::<GspMsgHeader>()),
+ size_of::<GspRpcHeader>(),
+ )
+ };
+ rpc_header_slice.fill(0);
+ let rpc_header = GspRpcHeader::from_bytes_mut(rpc_header_slice).ok_or(EINVAL)?;
+ rpc_header.header_version = 0x03000000;
+ rpc_header.signature = 0x43505256;
+ rpc_header.length = (size_of::<GspRpcHeader>() + cmd_size) as u32;
+ rpc_header.rpc_result = 0xffffffff;
+ rpc_header.rpc_result_private = 0xffffffff;
+ rpc_header.sequence = 0;
+ rpc_header.cpu_rm_gfid = 0;
+
+ // Number of bytes left before we have to wrap the buffer
+ let remaining = ((self.msg_count as usize - wptr) << GSP_PAGE_SHIFT) - HEADER_SIZE;
+
+ let (slice_1, slice_2) = if cmd_size <= remaining {
+ // SAFETY: ptr points to a region of contiguous memory at least
+ // cmd_size + HEADER_SIZE long.
+ let slice_1: &mut [u8] = unsafe {
+ core::slice::from_raw_parts_mut(ptr.cast::<u8>().add(HEADER_SIZE), cmd_size)
+ };
+ slice_1.fill(0);
+ (slice_1, &mut [] as &mut [u8])
+ } else {
+ // SAFETY: ptr points to a region of contiguous memory remaining +
+ // HEADER_SIZE bytes long.
+ let slice_1: &mut [u8] = unsafe {
+ core::slice::from_raw_parts_mut(ptr.cast::<u8>().add(HEADER_SIZE), remaining)
+ };
+ // SAFETY: By the invariants of CoherentAllocation gsp_mem.start_ptr_mut() is valid.
+ let ptr = unsafe {
+ core::ptr::addr_of_mut!((*self.gsp_mem.start_ptr_mut()).gspq.msgq.data[0])
+ };
+ // SAFETY: ptr points to a region of contiguous memory
+ // self.msg_count GSP_PAGE_SIZE pages long.
+ let slice_2: &mut [u8] =
+ unsafe { core::slice::from_raw_parts_mut(ptr.cast::<u8>(), remaining - cmd_size) };
+ slice_1.fill(0);
+ (slice_1, slice_2)
+ };
+
+ Ok(GspQueueCommand {
+ cmdq: self,
+ msg_header,
+ rpc_header,
+ slice_1,
+ slice_2,
+ })
+ }
+
+ pub(crate) fn send_cmd_to_gsp(cmd: GspQueueCommand<'_>, bar: &Bar0) -> Result {
+ // Find the start of the message. We could also re-read the HW pointer.
+ // SAFETY: The command was previously allocated and initialised on the
+ // queue and is therefore not-NULL and aligned.
+ let slice_1: &[u8] = unsafe {
+ core::slice::from_raw_parts(
+ ptr::from_ref(cmd.msg_header).cast::<u8>(),
+ size_of::<GspMsgHeader>() + size_of::<GspRpcHeader>() + cmd.slice_1.len(),
+ )
+ };
+
+ dev_info!(
+ &cmd.cmdq.dev,
+ "GSP RPC: send: seq# {}, function=0x{:x} ({}), length=0x{:x}\n",
+ cmd.cmdq.seq - 1,
+ cmd.rpc_header.function,
+ decode_gsp_function(cmd.rpc_header.function),
+ cmd.rpc_header.length,
+ );
+
+ // Calculate checksum over the entire message
+ cmd.msg_header.checksum =
+ GspCmdq::calculate_checksum(SBuffer::new_reader([slice_1, &cmd.slice_2[..]]));
+
+ let mut wptr = cmd.cmdq.cpu_wptr();
+ wptr += cmd.msg_header.elem_count;
+ wptr %= MSGQ_NUM_PAGES as u32;
+
+ // SAFETY: index `0` is valid as `gsp_mem` has been allocated accordingly, thus the access
+ // cannot fail.
+ unsafe { dma_write!(cmd.cmdq.gsp_mem[0].cpuq.tx.write_ptr = wptr).unwrap_unchecked() };
+
+ // Ensure all command data is visible before triggering the GSP read
+ fence(Ordering::SeqCst);
+
+ NV_PGSP_QUEUE_HEAD::default().set_address(0).write(bar);
+
+ Ok(())
+ }
+
+ pub(crate) fn msg_from_gsp_available(&self) -> bool {
+ const HEADER_SIZE: u32 = (size_of::<GspMsgHeader>() + size_of::<GspRpcHeader>()) as u32;
+
+ // Used pages contains the total number of pages available to consume
+ let used_pages = self.used_rx_pages();
+ if used_pages < HEADER_SIZE.div_ceil(GSP_PAGE_SIZE as u32) {
+ return false;
+ }
+
+ let rptr = self.cpu_rptr();
+ // SAFETY: By the invariants of CoherentAllocation gsp_mem.start_ptr() is valid.
+ let ptr = unsafe {
+ core::ptr::addr_of!((*self.gsp_mem.start_ptr()).gspq.msgq.data[rptr as usize])
+ };
+
+ // SAFETY: ptr points to at least GSP_PAGE_SIZE bytes of memory which is
+ // larger than GspRpcHeader.
+ let rpc = unsafe {
+ &*(ptr
+ .cast::<u8>()
+ .add(size_of::<GspMsgHeader>())
+ .cast::<GspRpcHeader>())
+ };
+
+ // Not all pages of the message have made it to the queue so bail and
+ // let the caller retry. Note rpc.length includes the rpc header size
+ // but not the message header size.
+ if used_pages << GSP_PAGE_SHIFT < size_of::<GspMsgHeader>() as u32 + rpc.length {
+ return false;
+ }
+
+ true
+ }
+
+ #[expect(unused)]
+ pub(crate) fn wait_for_msg_from_gsp(&self, timeout: Delta) -> Result {
+ wait_on(timeout, || {
+ if self.msg_from_gsp_available() {
+ Some(())
+ } else {
+ None
+ }
+ })
+ }
+
+ #[expect(unused)]
+ pub(crate) fn receive_msg_from_gsp<'a>(&'a mut self) -> Result<GspQueueMessage<'a>> {
+ const HEADER_SIZE: u32 = (size_of::<GspMsgHeader>() + size_of::<GspRpcHeader>()) as u32;
+
+ // Used pages contains the total number of pages available to consume
+ let used_pages = self.used_rx_pages();
+ if used_pages < HEADER_SIZE.div_ceil(GSP_PAGE_SIZE as u32) {
+ return Err(EAGAIN);
+ }
+
+ let rptr = self.cpu_rptr();
+
+ // Remaining number of bytes left before we have to wrap
+ let remaining = if rptr + used_pages > self.msg_count {
+ (self.msg_count - rptr) << GSP_PAGE_SHIFT
+ } else {
+ used_pages << GSP_PAGE_SHIFT
+ };
+
+ // SAFETY: By the invariants of CoherentAllocation gsp_mem.start_ptr_mut() is valid.
+ let ptr = unsafe {
+ core::ptr::addr_of_mut!((*self.gsp_mem.start_ptr_mut()).gspq.msgq.data[rptr as usize])
+ };
+
+ // SAFETY: ptr points to a region of memory remaining bytes long.
+ let msg_slice =
+ unsafe { core::slice::from_raw_parts(ptr as *const u8, remaining as usize) };
+
+ let msg_header =
+ GspMsgHeader::from_bytes(&msg_slice[0..size_of::<GspMsgHeader>()]).ok_or(EINVAL)?;
+ let rpc_header = GspRpcHeader::from_bytes(
+ &msg_slice
+ [size_of::<GspMsgHeader>()..size_of::<GspMsgHeader>() + size_of::<GspRpcHeader>()],
+ )
+ .ok_or(EINVAL)?;
+
+ if rpc_header.length >= self.msg_count << GSP_PAGE_SHIFT {
+ return Err(E2BIG);
+ }
+
+ // rpc.length includes the size of the GspRpcHeader. Remove it to make
+ // the rest of the code a bit easier to follow.
+ let rpc_data_length = rpc_header.length - size_of::<GspRpcHeader>() as u32;
+
+ // Log RPC receive with message type decoding
+ dev_info!(
+ self.dev,
+ "GSP RPC: receive: seq# {}, function=0x{:x} ({}), length=0x{:x}\n",
+ rpc_header.sequence,
+ rpc_header.function,
+ decode_gsp_function(rpc_header.function),
+ rpc_header.length,
+ );
+
+ // Should never happen if `wait_on_message()` has been called but we need to check.
+ if used_pages << GSP_PAGE_SHIFT < HEADER_SIZE + rpc_data_length {
+ return Err(EAGAIN);
+ }
+
+ let (slice_1, slice_2) = if rpc_data_length + HEADER_SIZE < remaining {
+ (
+ &msg_slice[(HEADER_SIZE as usize)..(HEADER_SIZE + rpc_data_length) as usize],
+ None,
+ )
+ } else {
+ let slice_1 = &msg_slice[(HEADER_SIZE as usize)..(HEADER_SIZE + remaining) as usize];
+ // SAFETY: By the invariants of CoherentAllocation gsp_mem.start_ptr_mut() is valid and
+ // large enough to hold gsp_mem.
+ let ptr =
+ unsafe { core::ptr::addr_of!((*self.gsp_mem.start_ptr_mut()).gspq.msgq.data[0]) };
+ // SAFETY: ptr pointers to self.msg_count GSP_PAGE_SIZE bytes of memory which by the
+ // earlier check is greater than rpc_data_length.
+ let slice_2 = unsafe {
+ core::slice::from_raw_parts(
+ ptr.cast::<u8>(),
+ rpc_data_length as usize - slice_1.len(),
+ )
+ };
+ (slice_1, Some(slice_2))
+ };
+
+ if GspCmdq::calculate_checksum(SBuffer::new_reader([
+ msg_header.as_bytes(),
+ rpc_header.as_bytes(),
+ slice_1,
+ slice_2.unwrap_or(&[]),
+ ])) != 0
+ {
+ dev_err!(
+ self.dev,
+ "GSP RPC: receive: Call {} - bad checksum",
+ rpc_header.sequence
+ );
+ return Err(EIO);
+ }
+
+ let gspq_msg = GspQueueMessage {
+ cmdq: self,
+ slice_1,
+ slice_2,
+ rpc_header,
+ };
+
+ Ok(gspq_msg)
+ }
+
+ fn ack_msg(&mut self, length: u32) -> Result {
+ const HEADER_SIZE: u32 = (size_of::<GspMsgHeader>() + size_of::<GspRpcHeader>()) as u32;
+ let mut rptr = self.cpu_rptr();
+ rptr += (HEADER_SIZE + length).div_ceil(GSP_PAGE_SIZE as u32);
+ rptr %= MSGQ_NUM_PAGES as u32;
+
+ // Ensure read pointer is properly ordered
+ fence(Ordering::SeqCst);
+
+ // SAFETY: index `0` is valid as `gsp_mem` has been allocated accordingly, thus the access
+ // cannot fail.
+ unsafe { dma_write!(self.gsp_mem[0].cpuq.rx.read_ptr = rptr).unwrap_unchecked() };
+
+ Ok(())
+ }
+}
+
+fn decode_gsp_function(function: u32) -> &'static str {
+ match function {
+ // Common function codes
+ NV_VGPU_MSG_FUNCTION_NOP => "NOP",
+ NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO => "SET_GUEST_SYSTEM_INFO",
+ NV_VGPU_MSG_FUNCTION_ALLOC_ROOT => "ALLOC_ROOT",
+ NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE => "ALLOC_DEVICE",
+ NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY => "ALLOC_MEMORY",
+ NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA => "ALLOC_CTX_DMA",
+ NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA => "ALLOC_CHANNEL_DMA",
+ NV_VGPU_MSG_FUNCTION_MAP_MEMORY => "MAP_MEMORY",
+ NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA => "BIND_CTX_DMA",
+ NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT => "ALLOC_OBJECT",
+ NV_VGPU_MSG_FUNCTION_FREE => "FREE",
+ NV_VGPU_MSG_FUNCTION_LOG => "LOG",
+ NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO => "GET_GSP_STATIC_INFO",
+ NV_VGPU_MSG_FUNCTION_SET_REGISTRY => "SET_REGISTRY",
+ NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO => "GSP_SET_SYSTEM_INFO",
+ NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU => "GSP_INIT_POST_OBJGPU",
+ NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL => "GSP_RM_CONTROL",
+ NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO => "GET_STATIC_INFO",
+
+ // Event codes
+ NV_VGPU_MSG_EVENT_GSP_INIT_DONE => "INIT_DONE",
+ NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER => "RUN_CPU_SEQUENCER",
+ NV_VGPU_MSG_EVENT_POST_EVENT => "POST_EVENT",
+ NV_VGPU_MSG_EVENT_RC_TRIGGERED => "RC_TRIGGERED",
+ NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED => "MMU_FAULT_QUEUED",
+ NV_VGPU_MSG_EVENT_OS_ERROR_LOG => "OS_ERROR_LOG",
+ NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD => "NOCAT",
+ NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE => "LOCKDOWN_NOTICE",
+ NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT => "LIBOS_PRINT",
+
+ // Default for unknown codes
+ _ => "UNKNOWN",
+ }
+}
diff --git a/drivers/gpu/nova-core/nvfw.rs b/drivers/gpu/nova-core/nvfw.rs
index c04b8e218758b..0db4e18f7dc97 100644
--- a/drivers/gpu/nova-core/nvfw.rs
+++ b/drivers/gpu/nova-core/nvfw.rs
@@ -50,6 +50,37 @@ pub(crate) struct LibosParams {
// GSP firmware constants
GSP_FW_WPR_META_MAGIC,
GSP_FW_WPR_META_REVISION,
+
+ // GSP events
+ NV_VGPU_MSG_EVENT_GSP_INIT_DONE,
+ NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE,
+ NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD,
+ NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER,
+ NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED,
+ NV_VGPU_MSG_EVENT_OS_ERROR_LOG,
+ NV_VGPU_MSG_EVENT_POST_EVENT,
+ NV_VGPU_MSG_EVENT_RC_TRIGGERED,
+ NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT,
+
+ // GSP function calls
+ NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA,
+ NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA,
+ NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE,
+ NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY,
+ NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT,
+ NV_VGPU_MSG_FUNCTION_ALLOC_ROOT,
+ NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA,
+ NV_VGPU_MSG_FUNCTION_FREE,
+ NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO,
+ NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO,
+ NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU,
+ NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL,
+ NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO,
+ NV_VGPU_MSG_FUNCTION_LOG,
+ NV_VGPU_MSG_FUNCTION_MAP_MEMORY,
+ NV_VGPU_MSG_FUNCTION_NOP,
+ NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO,
+ NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
};
pub(crate) type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1;
diff --git a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
index 392b25dc6991a..8820c488cd25f 100644
--- a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
+++ b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
@@ -19,6 +19,274 @@
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(Copy, Clone)]
pub struct GspFwWprMeta {
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 206dab2e13351..0585699ae9511 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -71,6 +71,10 @@ pub(crate) fn chipset(self) -> Result<Chipset> {
30:30 ecc_mode_enabled as bool;
});
+register!(NV_PGSP_QUEUE_HEAD @ 0x00110c00 {
+ 31:0 address as u32;
+});
+
impl NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE {
/// Returns the usable framebuffer size, in bytes.
pub(crate) fn usable_fb_size(self) -> u64 {
--
2.47.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 06/10] gpu: nova-core: gsp: Create rmargs
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
` (4 preceding siblings ...)
2025-08-27 8:20 ` [PATCH 05/10] gpu: nova-core: gsp: Add GSP command queue handling Alistair Popple
@ 2025-08-27 8:20 ` Alistair Popple
2025-08-27 8:20 ` [PATCH 07/10] gpu: nova-core: gsp: Create RM registry and sysinfo commands Alistair Popple
` (4 subsequent siblings)
10 siblings, 0 replies; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:20 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
Initialise the GSP resource manager arguments (rmargs) which provide
initialisation parameters to the GSP firmware during boot. The rmargs
structure contains arguments to configure the GSP message/command queue
location.
These are mapped for coherent DMA and added to the libos data structure
for access when booting GSP.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/gsp.rs | 44 +++++++++++++++++--
drivers/gpu/nova-core/gsp/cmdq.rs | 13 +++++-
drivers/gpu/nova-core/nvfw.rs | 6 +++
.../gpu/nova-core/nvfw/r570_144_bindings.rs | 33 ++++++++++++++
4 files changed, 91 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 41a88087d9baa..56a6be7b9eb15 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -17,8 +17,9 @@
use crate::nvfw::{
GspFwWprMeta, GspFwWprMetaBootInfo, GspFwWprMetaBootResumeInfo, LibosMemoryRegionInitArgument,
LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
- LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM, GSP_FW_WPR_META_MAGIC,
- GSP_FW_WPR_META_REVISION,
+ LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM, GSP_ARGUMENTS_CACHED,
+ GSP_FW_WPR_META_MAGIC, GSP_FW_WPR_META_REVISION, GSP_SR_INIT_ARGUMENTS,
+ MESSAGE_QUEUE_INIT_ARGUMENTS,
};
pub(crate) mod cmdq;
@@ -41,6 +42,19 @@ unsafe impl AsBytes for GspFwWprMeta {}
// are valid.
unsafe impl FromBytes for GspFwWprMeta {}
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for GSP_ARGUMENTS_CACHED {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for GSP_ARGUMENTS_CACHED {}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for MESSAGE_QUEUE_INIT_ARGUMENTS {}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for GSP_SR_INIT_ARGUMENTS {}
+
#[allow(unused)]
pub(crate) struct GspMemObjects {
libos: CoherentAllocation<LibosMemoryRegionInitArgument>,
@@ -49,6 +63,7 @@ pub(crate) struct GspMemObjects {
pub logrm: CoherentAllocation<u8>,
pub wpr_meta: CoherentAllocation<GspFwWprMeta>,
pub cmdq: GspCmdq,
+ rmargs: CoherentAllocation<GSP_ARGUMENTS_CACHED>,
}
pub(crate) fn build_wpr_meta(
@@ -179,13 +194,36 @@ pub(crate) fn new(
let wpr_meta = build_wpr_meta(dev, fw, fb_layout)?;
// Creates its own PTE array
- let cmdq = GspCmdq::new(dev)?;
+ let mut cmdq = GspCmdq::new(dev)?;
+ let rmargs =
+ create_coherent_dma_object::<GSP_ARGUMENTS_CACHED>(dev, "RMARGS", 1, &mut libos, 3)?;
+ let (shared_mem_phys_addr, cmd_queue_offset, stat_queue_offset) = cmdq.get_cmdq_offsets();
+
+ dma_write!(
+ rmargs[0].messageQueueInitArguments = MESSAGE_QUEUE_INIT_ARGUMENTS {
+ sharedMemPhysAddr: shared_mem_phys_addr,
+ pageTableEntryCount: cmdq.nr_ptes,
+ cmdQueueOffset: cmd_queue_offset,
+ statQueueOffset: stat_queue_offset,
+ ..Default::default()
+ }
+ )?;
+ dma_write!(
+ rmargs[0].srInitArguments = GSP_SR_INIT_ARGUMENTS {
+ oldLevel: 0,
+ flags: 0,
+ bInPMTransition: 0,
+ ..Default::default()
+ }
+ )?;
+ dma_write!(rmargs[0].bDmemStack = 1)?;
Ok(GspMemObjects {
libos,
loginit,
logintr,
logrm,
+ rmargs,
wpr_meta,
cmdq,
})
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 3f5d31c8e68f2..134ed0e20d9e3 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -162,7 +162,7 @@ pub(crate) struct GspCmdq {
msg_count: u32,
seq: u32,
gsp_mem: CoherentAllocation<GspMem>,
- pub _nr_ptes: u32,
+ pub nr_ptes: u32,
}
// A reference to a message currently sitting in the GSP command queue. May
@@ -289,7 +289,7 @@ pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<GspCmdq> {
msg_count: MSG_COUNT,
seq: 0,
gsp_mem,
- _nr_ptes: nr_ptes as u32,
+ nr_ptes: nr_ptes as u32,
})
}
@@ -639,6 +639,15 @@ pub(crate) fn receive_msg_from_gsp<'a>(&'a mut self) -> Result<GspQueueMessage<'
Ok(gspq_msg)
}
+ pub(crate) fn get_cmdq_offsets(&self) -> (u64, u64, u64) {
+ (
+ self.gsp_mem.dma_handle(),
+ core::mem::offset_of!(Msgq, msgq) as u64,
+ (core::mem::offset_of!(GspMem, gspq) - core::mem::offset_of!(GspMem, cpuq)
+ + core::mem::offset_of!(Msgq, msgq)) as u64,
+ )
+ }
+
fn ack_msg(&mut self, length: u32) -> Result {
const HEADER_SIZE: u32 = (size_of::<GspMsgHeader>() + size_of::<GspRpcHeader>()) as u32;
let mut rptr = self.cpu_rptr();
diff --git a/drivers/gpu/nova-core/nvfw.rs b/drivers/gpu/nova-core/nvfw.rs
index 0db4e18f7dc97..3d934cc95feb0 100644
--- a/drivers/gpu/nova-core/nvfw.rs
+++ b/drivers/gpu/nova-core/nvfw.rs
@@ -47,9 +47,15 @@ pub(crate) struct LibosParams {
LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM,
+ GSP_ARGUMENTS_CACHED,
+
// GSP firmware constants
GSP_FW_WPR_META_MAGIC,
GSP_FW_WPR_META_REVISION,
+ GSP_SR_INIT_ARGUMENTS,
+
+ // RM message queue parameters
+ MESSAGE_QUEUE_INIT_ARGUMENTS,
// GSP events
NV_VGPU_MSG_EVENT_GSP_INIT_DONE,
diff --git a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
index 8820c488cd25f..ab331fe6b1c81 100644
--- a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
+++ b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
@@ -288,6 +288,39 @@
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)]
+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)]
+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)]
+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)]
+pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_1 {
+ pub pa: u64_,
+ pub size: u64_,
+}
+#[repr(C)]
#[derive(Copy, Clone)]
pub struct GspFwWprMeta {
pub magic: u64_,
--
2.47.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 07/10] gpu: nova-core: gsp: Create RM registry and sysinfo commands
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
` (5 preceding siblings ...)
2025-08-27 8:20 ` [PATCH 06/10] gpu: nova-core: gsp: Create rmargs Alistair Popple
@ 2025-08-27 8:20 ` Alistair Popple
2025-08-29 6:02 ` Alistair Popple
2025-08-27 8:20 ` [PATCH 08/10] gpu: nova-core: falcon: Add support to check if RISC-V is active Alistair Popple
` (3 subsequent siblings)
10 siblings, 1 reply; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:20 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
Add the RM registry and system information commands that enable the host
driver to configure GSP firmware parameters during initialization.
The RM registry is serialized into a packed format and sent via the
command queue. For now only two parameters which are required to boot
GSP are hardcoded. In future a kernel module parameter will be added to
enable other parameters to be added.
Also add the system info command, which provides required hardware
information to the GSP. These commands use the GSP command queue
infrastructure to issue commands to the GSP which is read during GSP
boot.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 2 +-
drivers/gpu/nova-core/gsp.rs | 7 +
drivers/gpu/nova-core/gsp/cmdq.rs | 3 -
drivers/gpu/nova-core/gsp/commands.rs | 201 ++++++++++++++++++
drivers/gpu/nova-core/nvfw.rs | 8 +
.../gpu/nova-core/nvfw/r570_144_bindings.rs | 179 ++++++++++++++++
6 files changed, 396 insertions(+), 4 deletions(-)
create mode 100644 drivers/gpu/nova-core/gsp/commands.rs
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index bf762353f1d91..c070bd581e2c9 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -312,7 +312,7 @@ pub(crate) fn new(
Self::run_fwsec_frts(pdev.as_ref(), &gsp_falcon, bar, &bios, &fb_layout)?;
- let libos = gsp::GspMemObjects::new(pdev, &fw, &fb_layout)?;
+ let libos = gsp::GspMemObjects::new(pdev, bar, &fw, &fb_layout)?;
let _libos_handle = libos.libos_dma_handle();
let _wpr_handle = libos.wpr_meta.dma_handle();
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 56a6be7b9eb15..9776c643f5276 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -11,9 +11,11 @@
use kernel::sizes::SZ_128K;
use kernel::transmute::{AsBytes, FromBytes};
+use crate::driver::Bar0;
use crate::fb::FbLayout;
use crate::firmware::Firmware;
use crate::gsp::cmdq::GspCmdq;
+use crate::gsp::commands::{build_registry, set_system_info};
use crate::nvfw::{
GspFwWprMeta, GspFwWprMetaBootInfo, GspFwWprMetaBootResumeInfo, LibosMemoryRegionInitArgument,
LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
@@ -23,6 +25,7 @@
};
pub(crate) mod cmdq;
+pub(crate) mod commands;
pub(crate) const GSP_PAGE_SHIFT: usize = 12;
pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT;
@@ -176,6 +179,7 @@ fn create_coherent_dma_object<A: AsBytes + FromBytes>(
impl GspMemObjects {
pub(crate) fn new(
pdev: &pci::Device<device::Bound>,
+ bar: &Bar0,
fw: &Firmware,
fb_layout: &FbLayout,
) -> Result<Self> {
@@ -218,6 +222,9 @@ pub(crate) fn new(
)?;
dma_write!(rmargs[0].bDmemStack = 1)?;
+ set_system_info(&mut cmdq, pdev, bar)?;
+ build_registry(&mut cmdq, bar)?;
+
Ok(GspMemObjects {
libos,
loginit,
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 134ed0e20d9e3..4e4fbaa81e8e7 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -228,7 +228,6 @@ pub(crate) struct GspQueueCommand<'a> {
);
impl<'a> GspQueueCommand<'a> {
- #[expect(unused)]
pub(crate) fn try_as<'b, M: GspCommandToGsp>(&'b mut self) -> GspQueueCommandData<'b, M> {
// SAFETY: The slice references the cmdq message memory which is
// guaranteed to outlive the returned GspQueueCommandData by the
@@ -247,7 +246,6 @@ pub(crate) fn try_as<'b, M: GspCommandToGsp>(&'b mut self) -> GspQueueCommandDat
(msg, sbuf)
}
- #[expect(unused)]
pub(crate) fn send_to_gsp(self, bar: &Bar0) -> Result {
self.cmdq.wait_for_free_cmd_to_gsp(
Delta::from_secs(GSP_COMMAND_TIMEOUT),
@@ -361,7 +359,6 @@ pub(crate) fn wait_for_free_cmd_to_gsp(&self, timeout: Delta, size: usize) -> Re
})
}
- #[expect(unused)]
pub(crate) fn alloc_gsp_queue_command<'a>(
&'a mut self,
cmd_size: usize,
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
new file mode 100644
index 0000000000000..12ea8cdec21db
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use core::alloc::Layout;
+
+use kernel::alloc::allocator::Kmalloc;
+use kernel::alloc::Allocator;
+use kernel::build_assert;
+use kernel::device;
+use kernel::pci;
+use kernel::prelude::*;
+use kernel::transmute::{AsBytes, FromBytes};
+
+use crate::driver::Bar0;
+use crate::gsp::cmdq::GspCommandToGsp;
+use crate::gsp::cmdq::GspCmdq;
+use crate::gsp::GSP_PAGE_SIZE;
+use crate::nvfw::{
+ NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
+ NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO,
+ GspSystemInfo,
+ PACKED_REGISTRY_TABLE,
+ PACKED_REGISTRY_ENTRY,
+ REGISTRY_TABLE_ENTRY_TYPE_DWORD,
+};
+use crate::sbuffer::SBuffer;
+
+// 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 GspSystemInfo {}
+
+// 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 GspSystemInfo {}
+
+const GSP_REGISTRY_NUM_ENTRIES: usize = 2;
+struct RegistryEntry {
+ key: &'static str,
+ value: u32,
+}
+
+struct RegistryTable {
+ entries: [RegistryEntry; GSP_REGISTRY_NUM_ENTRIES],
+}
+
+struct GspRegistryTable;
+impl GspCommandToGsp for GspRegistryTable {
+ const FUNCTION: u32 = NV_VGPU_MSG_FUNCTION_SET_REGISTRY;
+}
+
+impl RegistryTable {
+ fn serialize_registry_table(&self) -> Result<KVec<u8>> {
+ let entries = &self.entries;
+ let total_size = self.size();
+ let align = core::mem::align_of::<PACKED_REGISTRY_TABLE>();
+ let layout = Layout::from_size_align(total_size, align).map_err(|_| ENOMEM)?;
+ debug_assert_eq!(layout.size(), total_size);
+ let mut string_data_offset = size_of::<PACKED_REGISTRY_TABLE>()
+ + GSP_REGISTRY_NUM_ENTRIES * size_of::<PACKED_REGISTRY_ENTRY>();
+ let allocation = Kmalloc::alloc(layout, GFP_KERNEL)?;
+ let ptr = allocation.as_ptr().cast::<u8>();
+
+ // We allocate the memory for the vector ourselves to ensure it has the
+ // correct layout to cast to a PACKED_REGISTRY_TABLE and subsequent
+ // fw:PACKED_REGISTRY_ENTRIES.
+ //
+ // SAFETY:
+ // - ptr was allocated with Kmalloc as required for KVec.
+ // - ptr trivally meets the alignment requirements for u8.
+ // - No elements have been initialised so this is zero length.
+ // - The capacity matches the total size of the allocation.
+ let mut table_vec = unsafe { KVec::<u8>::from_raw_parts(ptr, 0, layout.size()) };
+ let table_slice = table_vec.spare_capacity_mut();
+ let table = table_slice.as_mut_ptr().cast::<PACKED_REGISTRY_TABLE>();
+
+ // SAFETY: We ensured the alignment was correct when allocating the vector.
+ unsafe {
+ // Set the table header
+ (*table).numEntries = GSP_REGISTRY_NUM_ENTRIES as u32;
+ (*table).size = total_size as u32;
+ }
+
+ for (i, entry) in entries.iter().enumerate().take(GSP_REGISTRY_NUM_ENTRIES) {
+ // SAFETY: The allocation meets the alignment requirements for
+ // fw::PACKED_REGISTRY_TABLE which includes a zero length array for the entries.
+ unsafe {
+ let entry_ptr = table_slice
+ .as_mut_ptr()
+ .add(
+ size_of::<PACKED_REGISTRY_TABLE>()
+ + i * size_of::<PACKED_REGISTRY_ENTRY>(),
+ )
+ .cast::<PACKED_REGISTRY_ENTRY>();
+
+ // Set entry metadata
+ (*entry_ptr).nameOffset = string_data_offset as u32;
+ (*entry_ptr).type_ = REGISTRY_TABLE_ENTRY_TYPE_DWORD as u8;
+ (*entry_ptr).data = entry.value;
+ (*entry_ptr).length = 0;
+ }
+
+ let key_bytes = entry.key.as_bytes();
+ let string_dest_slice =
+ &mut table_slice[string_data_offset..string_data_offset + key_bytes.len() + 1];
+
+ // Can't use copy_from_slice() because string_dest_slice is MaybeUninit<u8>.
+ for (i, &byte) in key_bytes.iter().enumerate() {
+ string_dest_slice[i].write(byte);
+ }
+
+ // Add null terminator
+ string_dest_slice[key_bytes.len()].write(0);
+
+ // Update offset for next string
+ string_data_offset += string_dest_slice.len();
+ }
+
+ debug_assert_eq!(total_size, string_data_offset);
+
+ // SAFETY: All data has been written to as asserted above and the
+ // capacity matches the original allocation.
+ unsafe { table_vec.inc_len(layout.size()) };
+
+ Ok(table_vec)
+ }
+
+ fn copy_to_sbuf_iter(&self, mut sbuf: SBuffer<core::array::IntoIter<&mut [u8], 2>>) -> Result {
+ let table_vec = self.serialize_registry_table()?;
+ sbuf.write_all(&table_vec)?;
+ Ok(())
+ }
+
+ fn size(&self) -> usize {
+ let mut key_size = 0;
+ for i in 0..GSP_REGISTRY_NUM_ENTRIES {
+ key_size += self.entries[i].key.len() + 1; // +1 for NULL terminator
+ }
+ size_of::<PACKED_REGISTRY_TABLE>()
+ + GSP_REGISTRY_NUM_ENTRIES * size_of::<PACKED_REGISTRY_ENTRY>()
+ + key_size
+ }
+}
+
+pub(crate) fn build_registry(cmdq: &mut GspCmdq, bar: &Bar0) -> Result {
+ let registry = RegistryTable {
+ entries: [
+ RegistryEntry {
+ key: "RMSecBusResetEnable",
+ value: 1,
+ },
+ RegistryEntry {
+ key: "RMForcePcieConfigSave",
+ value: 1,
+ },
+ ],
+ };
+ let mut msg = cmdq.alloc_gsp_queue_command(registry.size())?;
+ {
+ let (_, some_sbuf) = msg.try_as::<GspRegistryTable>();
+ let sbuf = some_sbuf.ok_or(ENOMEM)?;
+ registry.copy_to_sbuf_iter(sbuf)?;
+ }
+ msg.send_to_gsp(bar)?;
+
+ Ok(())
+}
+
+impl GspCommandToGsp for GspSystemInfo {
+ const FUNCTION: u32 = NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO;
+}
+
+pub(crate) fn set_system_info(
+ cmdq: &mut GspCmdq,
+ dev: &pci::Device<device::Bound>,
+ bar: &Bar0,
+) -> Result {
+ build_assert!(size_of::<GspSystemInfo>() < GSP_PAGE_SIZE);
+ let mut msg = cmdq.alloc_gsp_queue_command(size_of::<GspSystemInfo>())?;
+ {
+ let (info, _) = msg.try_as::<GspSystemInfo>();
+
+ info.gpuPhysAddr = dev.resource_start(0)?;
+ info.gpuPhysFbAddr = dev.resource_start(1)?;
+ info.gpuPhysInstAddr = dev.resource_start(3)?;
+ info.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.
+ info.maxUserVa = (1 << 47) - 4096;
+ info.pciConfigMirrorBase = 0x088000;
+ info.pciConfigMirrorSize = 0x001000;
+
+ info.PCIDeviceID = (u32::from(dev.device_id()) << 16) | u32::from(dev.vendor_id());
+ info.PCISubDeviceID =
+ (u32::from(dev.subsystem_device_id()) << 16) | u32::from(dev.subsystem_vendor_id());
+ info.PCIRevisionID = u32::from(dev.revision_id());
+ info.bIsPrimary = 0;
+ info.bPreserveVideoMemoryAllocations = 0;
+ }
+ msg.send_to_gsp(bar)?;
+ Ok(())
+}
diff --git a/drivers/gpu/nova-core/nvfw.rs b/drivers/gpu/nova-core/nvfw.rs
index 3d934cc95feb0..aa883d4588388 100644
--- a/drivers/gpu/nova-core/nvfw.rs
+++ b/drivers/gpu/nova-core/nvfw.rs
@@ -42,6 +42,9 @@ pub(crate) struct LibosParams {
pub(crate) use r570_144::GspFwWprMeta;
pub(crate) use r570_144::{
+ // Core GSP structures
+ GspSystemInfo,
+
// LibOS memory structures
LibosMemoryRegionInitArgument,
LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
@@ -87,6 +90,11 @@ pub(crate) struct LibosParams {
NV_VGPU_MSG_FUNCTION_NOP,
NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO,
NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
+
+ // RM registry structures
+ PACKED_REGISTRY_ENTRY,
+ PACKED_REGISTRY_TABLE,
+ REGISTRY_TABLE_ENTRY_TYPE_DWORD,
};
pub(crate) type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1;
diff --git a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
index ab331fe6b1c81..3832d0b7a0b92 100644
--- a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
+++ b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
@@ -1,5 +1,35 @@
// 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 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;
@@ -11,6 +41,7 @@
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 type __u8 = ffi::c_uchar;
pub type __u16 = ffi::c_ushort;
pub type __u32 = ffi::c_uint;
@@ -289,6 +320,138 @@
pub type _bindgen_ty_3 = ffi::c_uint;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
+pub struct DOD_METHOD_DATA {
+ pub status: u32_,
+ pub acpiIdListLen: u32_,
+ pub acpiIdList: [u32_; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+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)]
+pub struct MUX_METHOD_DATA_ELEMENT {
+ pub acpiId: u32_,
+ pub mode: u32_,
+ pub status: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+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)]
+pub struct CAPS_METHOD_DATA {
+ pub status: u32_,
+ pub optimusCaps: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+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)]
+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)]
+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)]
+pub struct GSP_PCIE_CONFIG_REG {
+ pub linkCap: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+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)]
pub struct MESSAGE_QUEUE_INIT_ARGUMENTS {
pub sharedMemPhysAddr: u64_,
pub pageTableEntryCount: u32_,
@@ -446,3 +609,19 @@ pub struct LibosMemoryRegionInitArgument {
pub loc: u8_,
pub __bindgen_padding_0: [u8; 6usize],
}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+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)]
+pub struct PACKED_REGISTRY_TABLE {
+ pub size: u32_,
+ pub numEntries: u32_,
+ pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>,
+}
--
2.47.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 08/10] gpu: nova-core: falcon: Add support to check if RISC-V is active
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
` (6 preceding siblings ...)
2025-08-27 8:20 ` [PATCH 07/10] gpu: nova-core: gsp: Create RM registry and sysinfo commands Alistair Popple
@ 2025-08-27 8:20 ` Alistair Popple
2025-08-29 18:48 ` Timur Tabi
2025-08-27 8:20 ` [PATCH 09/10] gpu: nova-core: falcon: Add support to write firmware version Alistair Popple
` (2 subsequent siblings)
10 siblings, 1 reply; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:20 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
From: Joel Fernandes <joelagnelf@nvidia.com>
Add definition for RISCV_CPUCTL register and use it in a new falcon API
to check if the RISC-V core of a Falcon is active. It is required by
the sequencer to know if the GSP's RISCV processor is active.
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 8 ++++++++
drivers/gpu/nova-core/regs.rs | 5 +++++
2 files changed, 13 insertions(+)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 7bd13481a6a37..8c4faff043455 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -610,4 +610,12 @@ pub(crate) fn signature_reg_fuse_version(
self.hal
.signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
}
+
+ /// Check if the RISC-V core is active.
+ ///
+ /// Returns `true` if the RISC-V core is active, `false` otherwise.
+ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> Result<bool> {
+ let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID);
+ Ok(cpuctl.active_stat())
+ }
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 0585699ae9511..5df6a2bf42ad9 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -324,6 +324,11 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
// PRISCV
+register!(NV_PRISCV_RISCV_CPUCTL @ PFalconBase[0x00001388] {
+ 7:7 active_stat as bool;
+ 0:0 halted as bool;
+});
+
register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalconBase[0x00001668] {
0:0 valid as bool;
4:4 core_select as bool => PeregrineCoreSelect;
--
2.47.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 09/10] gpu: nova-core: falcon: Add support to write firmware version
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
` (7 preceding siblings ...)
2025-08-27 8:20 ` [PATCH 08/10] gpu: nova-core: falcon: Add support to check if RISC-V is active Alistair Popple
@ 2025-08-27 8:20 ` Alistair Popple
2025-08-27 8:20 ` [PATCH 10/10] gpu: nova-core: gsp: Boot GSP Alistair Popple
2025-08-28 8:37 ` [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Miguel Ojeda
10 siblings, 0 replies; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:20 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
From: Joel Fernandes <joelagnelf@nvidia.com>
This will be needed by both the GSP boot code as well as GSP resume code
in the sequencer.
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 9 +++++++++
drivers/gpu/nova-core/regs.rs | 6 ++++++
2 files changed, 15 insertions(+)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 8c4faff043455..875804499b077 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -618,4 +618,13 @@ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> Result<bool> {
let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID);
Ok(cpuctl.active_stat())
}
+
+ /// Write the application version to the OS register.
+ #[expect(dead_code)]
+ pub(crate) fn write_os_version(&self, bar: &Bar0, app_version: u32) -> Result<()> {
+ regs::NV_PFALCON_FALCON_OS::default()
+ .set_value(app_version)
+ .write(bar, &E::ID);
+ Ok(())
+ }
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 5df6a2bf42ad9..d9212fa501974 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -215,6 +215,12 @@ pub(crate) fn vga_workspace_addr(self) -> Option<u64> {
31:0 value as u32;
});
+// Used to store version information about the firmware running
+// on the Falcon processor.
+register!(NV_PFALCON_FALCON_OS @ PFalconBase[0x00000080] {
+ 31:0 value as u32;
+});
+
register!(NV_PFALCON_FALCON_RM @ PFalconBase[0x00000084] {
31:0 value as u32;
});
--
2.47.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 10/10] gpu: nova-core: gsp: Boot GSP
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
` (8 preceding siblings ...)
2025-08-27 8:20 ` [PATCH 09/10] gpu: nova-core: falcon: Add support to write firmware version Alistair Popple
@ 2025-08-27 8:20 ` Alistair Popple
2025-08-28 8:37 ` [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Miguel Ojeda
10 siblings, 0 replies; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 8:20 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
Boot the GSP to the RISC-V active state. Completing the boot requires
running the CPU sequencer which will be added in a future commit.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 1 -
drivers/gpu/nova-core/firmware.rs | 4 +-
drivers/gpu/nova-core/firmware/riscv.rs | 3 +-
drivers/gpu/nova-core/gpu.rs | 56 +++++++++++++++++++++++--
4 files changed, 56 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 875804499b077..0c8ee7761f705 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -620,7 +620,6 @@ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> Result<bool> {
}
/// Write the application version to the OS register.
- #[expect(dead_code)]
pub(crate) fn write_os_version(&self, bar: &Bar0, app_version: u32) -> Result<()> {
regs::NV_PFALCON_FALCON_OS::default()
.set_value(app_version)
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 6c210e668d541..d35f1affaa28d 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -115,11 +115,11 @@ pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a
}
/// Structure encapsulating the firmware blobs required for the GPU to operate.
-#[expect(dead_code)]
pub(crate) struct Firmware {
/// Runs on the sec2 falcon engine to load and start the GSP bootloader.
- booter_loader: BooterFirmware,
+ pub booter_loader: BooterFirmware,
/// Runs on the sec2 falcon engine to stop and unload a running GSP firmware.
+ #[expect(unused)]
booter_unloader: BooterFirmware,
/// GSP bootloader, verifies the GSP firmware before loading and running it.
pub gsp_bootloader: RiscvFirmware,
diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs
index 81bb348055031..b67e130e06cd6 100644
--- a/drivers/gpu/nova-core/firmware/riscv.rs
+++ b/drivers/gpu/nova-core/firmware/riscv.rs
@@ -50,7 +50,6 @@ fn new(bin_fw: &BinFirmware<'_>) -> Result<Self> {
}
/// A parsed firmware for a RISC-V core, ready to be loaded and run.
-#[expect(unused)]
pub(crate) struct RiscvFirmware {
/// Offset at which the code starts in the firmware image.
pub code_offset: u32,
@@ -59,7 +58,7 @@ pub(crate) struct RiscvFirmware {
/// Offset at which the manifest starts in the firmware image.
pub manifest_offset: u32,
/// Application version.
- app_version: u32,
+ pub app_version: u32,
/// Device-mapped firmware image.
pub ucode: DmaObject,
}
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index c070bd581e2c9..f86221a681e27 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-use kernel::{device, devres::Devres, error::code::*, pci, prelude::*, sync::Arc};
+use kernel::{device, devres::Devres, error::code::*, pci, prelude::*, sync::Arc, time::Delta};
use crate::driver::Bar0;
use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon};
@@ -313,8 +313,58 @@ pub(crate) fn new(
Self::run_fwsec_frts(pdev.as_ref(), &gsp_falcon, bar, &bios, &fb_layout)?;
let libos = gsp::GspMemObjects::new(pdev, bar, &fw, &fb_layout)?;
- let _libos_handle = libos.libos_dma_handle();
- let _wpr_handle = libos.wpr_meta.dma_handle();
+ let libos_handle = libos.libos_dma_handle();
+ let wpr_handle = libos.wpr_meta.dma_handle();
+
+ gsp_falcon.reset(bar)?;
+ let (mbox0, mbox1) = gsp_falcon.boot(
+ bar,
+ Some(libos_handle as u32),
+ Some((libos_handle >> 32) as u32),
+ )?;
+ dev_dbg!(
+ pdev.as_ref(),
+ "GSP MBOX0: {:#x}, MBOX1: {:#x}\n",
+ mbox0,
+ mbox1
+ );
+
+ dev_dbg!(
+ pdev.as_ref(),
+ "Using SEC2 to load and run the booter_load firmware...\n"
+ );
+
+ sec2_falcon.reset(bar)?;
+ sec2_falcon.dma_load(bar, &fw.booter_loader)?;
+ let (mbox0, mbox1) = sec2_falcon.boot(
+ bar,
+ Some(wpr_handle as u32),
+ Some((wpr_handle >> 32) as u32),
+ )?;
+ dev_dbg!(
+ pdev.as_ref(),
+ "SEC2 MBOX0: {:#x}, MBOX1{:#x}\n",
+ mbox0,
+ mbox1
+ );
+
+ // Match what Nouveau does here:
+ gsp_falcon.write_os_version(bar, fw.gsp_bootloader.app_version)?;
+
+ // Poll for RISC-V to become active before running sequencer
+ util::wait_on(Delta::from_secs(5), || {
+ if gsp_falcon.is_riscv_active(bar).unwrap_or(false) {
+ Some(())
+ } else {
+ None
+ }
+ })?;
+
+ dev_dbg!(
+ pdev.as_ref(),
+ "RISC-V active? {}\n",
+ gsp_falcon.is_riscv_active(bar)?,
+ );
Ok(pin_init!(Self {
spec,
--
2.47.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH 05/10] gpu: nova-core: gsp: Add GSP command queue handling
2025-08-27 8:20 ` [PATCH 05/10] gpu: nova-core: gsp: Add GSP command queue handling Alistair Popple
@ 2025-08-27 20:35 ` John Hubbard
2025-08-27 23:42 ` Alistair Popple
0 siblings, 1 reply; 28+ messages in thread
From: John Hubbard @ 2025-08-27 20:35 UTC (permalink / raw)
To: Alistair Popple, dri-devel, dakr, acourbot
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Joel Fernandes, Timur Tabi,
linux-kernel, nouveau
On 8/27/25 1:20 AM, Alistair Popple wrote:
...
Hi Alistair,
Not a real review yet, but one thing I noticed on a quick first pass:
> + pub(crate) fn send_cmd_to_gsp(cmd: GspQueueCommand<'_>, bar: &Bar0) -> Result {
> + // Find the start of the message. We could also re-read the HW pointer.
> + // SAFETY: The command was previously allocated and initialised on the
> + // queue and is therefore not-NULL and aligned.
> + let slice_1: &[u8] = unsafe {
> + core::slice::from_raw_parts(
> + ptr::from_ref(cmd.msg_header).cast::<u8>(),
> + size_of::<GspMsgHeader>() + size_of::<GspRpcHeader>() + cmd.slice_1.len(),
> + )
> + };
> +
> + dev_info!(
> + &cmd.cmdq.dev,
> + "GSP RPC: send: seq# {}, function=0x{:x} ({}), length=0x{:x}\n",
> + cmd.cmdq.seq - 1,
> + cmd.rpc_header.function,
> + decode_gsp_function(cmd.rpc_header.function),
> + cmd.rpc_header.length,
> + );
Let's please make this (and the corresponding receive) a dev_dbg!().
Otherwise the driver is too chatty at INFO log levels.
I suspect that I'm to blame here, because I recall pretty-ing up the
output of these, and I probably set dev_info!() at the same time. doh!
...
> + // Log RPC receive with message type decoding
> + dev_info!(
> + self.dev,
> + "GSP RPC: receive: seq# {}, function=0x{:x} ({}), length=0x{:x}\n",
> + rpc_header.sequence,
> + rpc_header.function,
> + decode_gsp_function(rpc_header.function),
> + rpc_header.length,
> + );
Here also: please use dev_dbg!() for this one.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 05/10] gpu: nova-core: gsp: Add GSP command queue handling
2025-08-27 20:35 ` John Hubbard
@ 2025-08-27 23:42 ` Alistair Popple
0 siblings, 0 replies; 28+ messages in thread
From: Alistair Popple @ 2025-08-27 23:42 UTC (permalink / raw)
To: John Hubbard
Cc: dri-devel, dakr, acourbot, Miguel Ojeda, Alex Gaynor, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Joel Fernandes, Timur Tabi, linux-kernel, nouveau
On 2025-08-28 at 06:35 +1000, John Hubbard <jhubbard@nvidia.com> wrote...
> On 8/27/25 1:20 AM, Alistair Popple wrote:
> ...
>
> Hi Alistair,
>
> Not a real review yet, but one thing I noticed on a quick first pass:
>
> > + pub(crate) fn send_cmd_to_gsp(cmd: GspQueueCommand<'_>, bar: &Bar0) -> Result {
> > + // Find the start of the message. We could also re-read the HW pointer.
> > + // SAFETY: The command was previously allocated and initialised on the
> > + // queue and is therefore not-NULL and aligned.
> > + let slice_1: &[u8] = unsafe {
> > + core::slice::from_raw_parts(
> > + ptr::from_ref(cmd.msg_header).cast::<u8>(),
> > + size_of::<GspMsgHeader>() + size_of::<GspRpcHeader>() + cmd.slice_1.len(),
> > + )
> > + };
> > +
> > + dev_info!(
> > + &cmd.cmdq.dev,
> > + "GSP RPC: send: seq# {}, function=0x{:x} ({}), length=0x{:x}\n",
> > + cmd.cmdq.seq - 1,
> > + cmd.rpc_header.function,
> > + decode_gsp_function(cmd.rpc_header.function),
> > + cmd.rpc_header.length,
> > + );
>
> Let's please make this (and the corresponding receive) a dev_dbg!().
> Otherwise the driver is too chatty at INFO log levels.
>
> I suspect that I'm to blame here, because I recall pretty-ing up the
> output of these, and I probably set dev_info!() at the same time. doh!
You probably took "inspiration" from my original pr_info though! So all good,
I'm sure there will be a v2 so will clean these up then.
> ...
> > + // Log RPC receive with message type decoding
> > + dev_info!(
> > + self.dev,
> > + "GSP RPC: receive: seq# {}, function=0x{:x} ({}), length=0x{:x}\n",
> > + rpc_header.sequence,
> > + rpc_header.function,
> > + decode_gsp_function(rpc_header.function),
> > + rpc_header.length,
> > + );
>
> Here also: please use dev_dbg!() for this one.
>
>
> thanks,
> --
> John Hubbard
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
` (9 preceding siblings ...)
2025-08-27 8:20 ` [PATCH 10/10] gpu: nova-core: gsp: Boot GSP Alistair Popple
@ 2025-08-28 8:37 ` Miguel Ojeda
2025-08-29 3:03 ` Alexandre Courbot
10 siblings, 1 reply; 28+ messages in thread
From: Miguel Ojeda @ 2025-08-28 8:37 UTC (permalink / raw)
To: apopple
Cc: a.hindborg, acourbot, airlied, alex.gaynor, aliceryhl, bjorn3_gh,
boqun.feng, dakr, dri-devel, gary, jhubbard, joelagnelf,
linux-kernel, lossin, maarten.lankhorst, mripard, nouveau, ojeda,
simona, tmgross, ttabi, tzimmermann
On Wed, 27 Aug 2025 18:19:57 +1000 Alistair Popple <apopple@nvidia.com> wrote:
>
> This series builds on top of Alex's series[1] to continue initialising the GSP
> into a state where it becomes active and it starts communicating with the host.
No big deal, but in case it helps since probably it was not intentional given
the rest of the people is there: the rust-for-linux Cc is missing.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active
2025-08-28 8:37 ` [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Miguel Ojeda
@ 2025-08-29 3:03 ` Alexandre Courbot
2025-08-29 7:40 ` Danilo Krummrich
0 siblings, 1 reply; 28+ messages in thread
From: Alexandre Courbot @ 2025-08-29 3:03 UTC (permalink / raw)
To: Miguel Ojeda, apopple
Cc: a.hindborg, airlied, alex.gaynor, aliceryhl, bjorn3_gh,
boqun.feng, dakr, dri-devel, gary, jhubbard, joelagnelf,
linux-kernel, lossin, maarten.lankhorst, mripard, nouveau, simona,
tmgross, ttabi, tzimmermann, Nouveau
On Thu Aug 28, 2025 at 5:37 PM JST, Miguel Ojeda wrote:
> On Wed, 27 Aug 2025 18:19:57 +1000 Alistair Popple <apopple@nvidia.com> wrote:
>>
>> This series builds on top of Alex's series[1] to continue initialising the GSP
>> into a state where it becomes active and it starts communicating with the host.
>
> No big deal, but in case it helps since probably it was not intentional given
> the rest of the people is there: the rust-for-linux Cc is missing.
This did happen to me once as well - should we add the rust-for-linux
list to the Nova MAINTAINERS entry to protect against this?
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 07/10] gpu: nova-core: gsp: Create RM registry and sysinfo commands
2025-08-27 8:20 ` [PATCH 07/10] gpu: nova-core: gsp: Create RM registry and sysinfo commands Alistair Popple
@ 2025-08-29 6:02 ` Alistair Popple
0 siblings, 0 replies; 28+ messages in thread
From: Alistair Popple @ 2025-08-29 6:02 UTC (permalink / raw)
To: dri-devel, dakr, acourbot
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau
On 2025-08-27 at 18:20 +1000, Alistair Popple <apopple@nvidia.com> wrote...
> Add the RM registry and system information commands that enable the host
> driver to configure GSP firmware parameters during initialization.
>
> The RM registry is serialized into a packed format and sent via the
> command queue. For now only two parameters which are required to boot
> GSP are hardcoded. In future a kernel module parameter will be added to
> enable other parameters to be added.
>
> Also add the system info command, which provides required hardware
> information to the GSP. These commands use the GSP command queue
> infrastructure to issue commands to the GSP which is read during GSP
> boot.
>
> Signed-off-by: Alistair Popple <apopple@nvidia.com>
> ---
> drivers/gpu/nova-core/gpu.rs | 2 +-
> drivers/gpu/nova-core/gsp.rs | 7 +
> drivers/gpu/nova-core/gsp/cmdq.rs | 3 -
> drivers/gpu/nova-core/gsp/commands.rs | 201 ++++++++++++++++++
> drivers/gpu/nova-core/nvfw.rs | 8 +
> .../gpu/nova-core/nvfw/r570_144_bindings.rs | 179 ++++++++++++++++
> 6 files changed, 396 insertions(+), 4 deletions(-)
> create mode 100644 drivers/gpu/nova-core/gsp/commands.rs
>
> diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
> index bf762353f1d91..c070bd581e2c9 100644
> --- a/drivers/gpu/nova-core/gpu.rs
> +++ b/drivers/gpu/nova-core/gpu.rs
> @@ -312,7 +312,7 @@ pub(crate) fn new(
>
> Self::run_fwsec_frts(pdev.as_ref(), &gsp_falcon, bar, &bios, &fb_layout)?;
>
> - let libos = gsp::GspMemObjects::new(pdev, &fw, &fb_layout)?;
> + let libos = gsp::GspMemObjects::new(pdev, bar, &fw, &fb_layout)?;
> let _libos_handle = libos.libos_dma_handle();
> let _wpr_handle = libos.wpr_meta.dma_handle();
>
> diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
> index 56a6be7b9eb15..9776c643f5276 100644
> --- a/drivers/gpu/nova-core/gsp.rs
> +++ b/drivers/gpu/nova-core/gsp.rs
> @@ -11,9 +11,11 @@
> use kernel::sizes::SZ_128K;
> use kernel::transmute::{AsBytes, FromBytes};
>
> +use crate::driver::Bar0;
> use crate::fb::FbLayout;
> use crate::firmware::Firmware;
> use crate::gsp::cmdq::GspCmdq;
> +use crate::gsp::commands::{build_registry, set_system_info};
> use crate::nvfw::{
> GspFwWprMeta, GspFwWprMetaBootInfo, GspFwWprMetaBootResumeInfo, LibosMemoryRegionInitArgument,
> LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
> @@ -23,6 +25,7 @@
> };
>
> pub(crate) mod cmdq;
> +pub(crate) mod commands;
>
> pub(crate) const GSP_PAGE_SHIFT: usize = 12;
> pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT;
> @@ -176,6 +179,7 @@ fn create_coherent_dma_object<A: AsBytes + FromBytes>(
> impl GspMemObjects {
> pub(crate) fn new(
> pdev: &pci::Device<device::Bound>,
> + bar: &Bar0,
> fw: &Firmware,
> fb_layout: &FbLayout,
> ) -> Result<Self> {
> @@ -218,6 +222,9 @@ pub(crate) fn new(
> )?;
> dma_write!(rmargs[0].bDmemStack = 1)?;
>
> + set_system_info(&mut cmdq, pdev, bar)?;
> + build_registry(&mut cmdq, bar)?;
> +
> Ok(GspMemObjects {
> libos,
> loginit,
> diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
> index 134ed0e20d9e3..4e4fbaa81e8e7 100644
> --- a/drivers/gpu/nova-core/gsp/cmdq.rs
> +++ b/drivers/gpu/nova-core/gsp/cmdq.rs
> @@ -228,7 +228,6 @@ pub(crate) struct GspQueueCommand<'a> {
> );
>
> impl<'a> GspQueueCommand<'a> {
> - #[expect(unused)]
> pub(crate) fn try_as<'b, M: GspCommandToGsp>(&'b mut self) -> GspQueueCommandData<'b, M> {
> // SAFETY: The slice references the cmdq message memory which is
> // guaranteed to outlive the returned GspQueueCommandData by the
> @@ -247,7 +246,6 @@ pub(crate) fn try_as<'b, M: GspCommandToGsp>(&'b mut self) -> GspQueueCommandDat
> (msg, sbuf)
> }
>
> - #[expect(unused)]
> pub(crate) fn send_to_gsp(self, bar: &Bar0) -> Result {
> self.cmdq.wait_for_free_cmd_to_gsp(
> Delta::from_secs(GSP_COMMAND_TIMEOUT),
> @@ -361,7 +359,6 @@ pub(crate) fn wait_for_free_cmd_to_gsp(&self, timeout: Delta, size: usize) -> Re
> })
> }
>
> - #[expect(unused)]
> pub(crate) fn alloc_gsp_queue_command<'a>(
> &'a mut self,
> cmd_size: usize,
> diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
> new file mode 100644
> index 0000000000000..12ea8cdec21db
> --- /dev/null
> +++ b/drivers/gpu/nova-core/gsp/commands.rs
> @@ -0,0 +1,201 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use core::alloc::Layout;
> +
> +use kernel::alloc::allocator::Kmalloc;
> +use kernel::alloc::Allocator;
> +use kernel::build_assert;
> +use kernel::device;
> +use kernel::pci;
> +use kernel::prelude::*;
> +use kernel::transmute::{AsBytes, FromBytes};
> +
> +use crate::driver::Bar0;
> +use crate::gsp::cmdq::GspCommandToGsp;
> +use crate::gsp::cmdq::GspCmdq;
> +use crate::gsp::GSP_PAGE_SIZE;
> +use crate::nvfw::{
> + NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
> + NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO,
> + GspSystemInfo,
> + PACKED_REGISTRY_TABLE,
> + PACKED_REGISTRY_ENTRY,
> + REGISTRY_TABLE_ENTRY_TYPE_DWORD,
I see I must have somehow missed running rustfmt on this file (probably due to
resolving a merge conflict). I will make sure that happens for v2.
> +};
> +use crate::sbuffer::SBuffer;
> +
> +// 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 GspSystemInfo {}
> +
> +// 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 GspSystemInfo {}
> +
> +const GSP_REGISTRY_NUM_ENTRIES: usize = 2;
> +struct RegistryEntry {
> + key: &'static str,
> + value: u32,
> +}
> +
> +struct RegistryTable {
> + entries: [RegistryEntry; GSP_REGISTRY_NUM_ENTRIES],
> +}
> +
> +struct GspRegistryTable;
> +impl GspCommandToGsp for GspRegistryTable {
> + const FUNCTION: u32 = NV_VGPU_MSG_FUNCTION_SET_REGISTRY;
> +}
> +
> +impl RegistryTable {
> + fn serialize_registry_table(&self) -> Result<KVec<u8>> {
> + let entries = &self.entries;
> + let total_size = self.size();
> + let align = core::mem::align_of::<PACKED_REGISTRY_TABLE>();
> + let layout = Layout::from_size_align(total_size, align).map_err(|_| ENOMEM)?;
> + debug_assert_eq!(layout.size(), total_size);
> + let mut string_data_offset = size_of::<PACKED_REGISTRY_TABLE>()
> + + GSP_REGISTRY_NUM_ENTRIES * size_of::<PACKED_REGISTRY_ENTRY>();
> + let allocation = Kmalloc::alloc(layout, GFP_KERNEL)?;
> + let ptr = allocation.as_ptr().cast::<u8>();
> +
> + // We allocate the memory for the vector ourselves to ensure it has the
> + // correct layout to cast to a PACKED_REGISTRY_TABLE and subsequent
> + // fw:PACKED_REGISTRY_ENTRIES.
> + //
> + // SAFETY:
> + // - ptr was allocated with Kmalloc as required for KVec.
> + // - ptr trivally meets the alignment requirements for u8.
> + // - No elements have been initialised so this is zero length.
> + // - The capacity matches the total size of the allocation.
> + let mut table_vec = unsafe { KVec::<u8>::from_raw_parts(ptr, 0, layout.size()) };
> + let table_slice = table_vec.spare_capacity_mut();
> + let table = table_slice.as_mut_ptr().cast::<PACKED_REGISTRY_TABLE>();
> +
> + // SAFETY: We ensured the alignment was correct when allocating the vector.
> + unsafe {
> + // Set the table header
> + (*table).numEntries = GSP_REGISTRY_NUM_ENTRIES as u32;
> + (*table).size = total_size as u32;
> + }
> +
> + for (i, entry) in entries.iter().enumerate().take(GSP_REGISTRY_NUM_ENTRIES) {
> + // SAFETY: The allocation meets the alignment requirements for
> + // fw::PACKED_REGISTRY_TABLE which includes a zero length array for the entries.
> + unsafe {
> + let entry_ptr = table_slice
> + .as_mut_ptr()
> + .add(
> + size_of::<PACKED_REGISTRY_TABLE>()
> + + i * size_of::<PACKED_REGISTRY_ENTRY>(),
> + )
> + .cast::<PACKED_REGISTRY_ENTRY>();
> +
> + // Set entry metadata
> + (*entry_ptr).nameOffset = string_data_offset as u32;
> + (*entry_ptr).type_ = REGISTRY_TABLE_ENTRY_TYPE_DWORD as u8;
> + (*entry_ptr).data = entry.value;
> + (*entry_ptr).length = 0;
> + }
> +
> + let key_bytes = entry.key.as_bytes();
> + let string_dest_slice =
> + &mut table_slice[string_data_offset..string_data_offset + key_bytes.len() + 1];
> +
> + // Can't use copy_from_slice() because string_dest_slice is MaybeUninit<u8>.
> + for (i, &byte) in key_bytes.iter().enumerate() {
> + string_dest_slice[i].write(byte);
> + }
> +
> + // Add null terminator
> + string_dest_slice[key_bytes.len()].write(0);
> +
> + // Update offset for next string
> + string_data_offset += string_dest_slice.len();
> + }
> +
> + debug_assert_eq!(total_size, string_data_offset);
> +
> + // SAFETY: All data has been written to as asserted above and the
> + // capacity matches the original allocation.
> + unsafe { table_vec.inc_len(layout.size()) };
> +
> + Ok(table_vec)
> + }
> +
> + fn copy_to_sbuf_iter(&self, mut sbuf: SBuffer<core::array::IntoIter<&mut [u8], 2>>) -> Result {
> + let table_vec = self.serialize_registry_table()?;
> + sbuf.write_all(&table_vec)?;
> + Ok(())
> + }
> +
> + fn size(&self) -> usize {
> + let mut key_size = 0;
> + for i in 0..GSP_REGISTRY_NUM_ENTRIES {
> + key_size += self.entries[i].key.len() + 1; // +1 for NULL terminator
> + }
> + size_of::<PACKED_REGISTRY_TABLE>()
> + + GSP_REGISTRY_NUM_ENTRIES * size_of::<PACKED_REGISTRY_ENTRY>()
> + + key_size
> + }
> +}
> +
> +pub(crate) fn build_registry(cmdq: &mut GspCmdq, bar: &Bar0) -> Result {
> + let registry = RegistryTable {
> + entries: [
> + RegistryEntry {
> + key: "RMSecBusResetEnable",
> + value: 1,
> + },
> + RegistryEntry {
> + key: "RMForcePcieConfigSave",
> + value: 1,
> + },
> + ],
> + };
> + let mut msg = cmdq.alloc_gsp_queue_command(registry.size())?;
> + {
> + let (_, some_sbuf) = msg.try_as::<GspRegistryTable>();
> + let sbuf = some_sbuf.ok_or(ENOMEM)?;
> + registry.copy_to_sbuf_iter(sbuf)?;
> + }
> + msg.send_to_gsp(bar)?;
> +
> + Ok(())
> +}
> +
> +impl GspCommandToGsp for GspSystemInfo {
> + const FUNCTION: u32 = NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO;
> +}
> +
> +pub(crate) fn set_system_info(
> + cmdq: &mut GspCmdq,
> + dev: &pci::Device<device::Bound>,
> + bar: &Bar0,
> +) -> Result {
> + build_assert!(size_of::<GspSystemInfo>() < GSP_PAGE_SIZE);
> + let mut msg = cmdq.alloc_gsp_queue_command(size_of::<GspSystemInfo>())?;
> + {
> + let (info, _) = msg.try_as::<GspSystemInfo>();
> +
> + info.gpuPhysAddr = dev.resource_start(0)?;
> + info.gpuPhysFbAddr = dev.resource_start(1)?;
> + info.gpuPhysInstAddr = dev.resource_start(3)?;
> + info.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.
> + info.maxUserVa = (1 << 47) - 4096;
> + info.pciConfigMirrorBase = 0x088000;
> + info.pciConfigMirrorSize = 0x001000;
> +
> + info.PCIDeviceID = (u32::from(dev.device_id()) << 16) | u32::from(dev.vendor_id());
> + info.PCISubDeviceID =
> + (u32::from(dev.subsystem_device_id()) << 16) | u32::from(dev.subsystem_vendor_id());
> + info.PCIRevisionID = u32::from(dev.revision_id());
> + info.bIsPrimary = 0;
> + info.bPreserveVideoMemoryAllocations = 0;
> + }
> + msg.send_to_gsp(bar)?;
> + Ok(())
> +}
> diff --git a/drivers/gpu/nova-core/nvfw.rs b/drivers/gpu/nova-core/nvfw.rs
> index 3d934cc95feb0..aa883d4588388 100644
> --- a/drivers/gpu/nova-core/nvfw.rs
> +++ b/drivers/gpu/nova-core/nvfw.rs
> @@ -42,6 +42,9 @@ pub(crate) struct LibosParams {
> pub(crate) use r570_144::GspFwWprMeta;
>
> pub(crate) use r570_144::{
> + // Core GSP structures
> + GspSystemInfo,
> +
> // LibOS memory structures
> LibosMemoryRegionInitArgument,
> LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
> @@ -87,6 +90,11 @@ pub(crate) struct LibosParams {
> NV_VGPU_MSG_FUNCTION_NOP,
> NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO,
> NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
> +
> + // RM registry structures
> + PACKED_REGISTRY_ENTRY,
> + PACKED_REGISTRY_TABLE,
> + REGISTRY_TABLE_ENTRY_TYPE_DWORD,
> };
>
> pub(crate) type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1;
> diff --git a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
> index ab331fe6b1c81..3832d0b7a0b92 100644
> --- a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
> +++ b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
> @@ -1,5 +1,35 @@
> // 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 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;
> @@ -11,6 +41,7 @@
> 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 type __u8 = ffi::c_uchar;
> pub type __u16 = ffi::c_ushort;
> pub type __u32 = ffi::c_uint;
> @@ -289,6 +320,138 @@
> pub type _bindgen_ty_3 = ffi::c_uint;
> #[repr(C)]
> #[derive(Debug, Default, Copy, Clone)]
> +pub struct DOD_METHOD_DATA {
> + pub status: u32_,
> + pub acpiIdListLen: u32_,
> + pub acpiIdList: [u32_; 16usize],
> +}
> +#[repr(C)]
> +#[derive(Debug, Default, Copy, Clone)]
> +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)]
> +pub struct MUX_METHOD_DATA_ELEMENT {
> + pub acpiId: u32_,
> + pub mode: u32_,
> + pub status: u32_,
> +}
> +#[repr(C)]
> +#[derive(Debug, Default, Copy, Clone)]
> +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)]
> +pub struct CAPS_METHOD_DATA {
> + pub status: u32_,
> + pub optimusCaps: u32_,
> +}
> +#[repr(C)]
> +#[derive(Debug, Default, Copy, Clone)]
> +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)]
> +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)]
> +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)]
> +pub struct GSP_PCIE_CONFIG_REG {
> + pub linkCap: u32_,
> +}
> +#[repr(C)]
> +#[derive(Debug, Default, Copy, Clone)]
> +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)]
> pub struct MESSAGE_QUEUE_INIT_ARGUMENTS {
> pub sharedMemPhysAddr: u64_,
> pub pageTableEntryCount: u32_,
> @@ -446,3 +609,19 @@ pub struct LibosMemoryRegionInitArgument {
> pub loc: u8_,
> pub __bindgen_padding_0: [u8; 6usize],
> }
> +#[repr(C)]
> +#[derive(Debug, Default, Copy, Clone)]
> +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)]
> +pub struct PACKED_REGISTRY_TABLE {
> + pub size: u32_,
> + pub numEntries: u32_,
> + pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>,
> +}
> --
> 2.47.2
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active
2025-08-29 3:03 ` Alexandre Courbot
@ 2025-08-29 7:40 ` Danilo Krummrich
2025-08-29 10:01 ` Miguel Ojeda
0 siblings, 1 reply; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-29 7:40 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Miguel Ojeda, apopple, a.hindborg, airlied, alex.gaynor,
aliceryhl, bjorn3_gh, boqun.feng, dri-devel, gary, jhubbard,
joelagnelf, linux-kernel, lossin, maarten.lankhorst, mripard,
nouveau, simona, tmgross, ttabi, tzimmermann, Nouveau
On 8/29/25 5:03 AM, Alexandre Courbot wrote:
> On Thu Aug 28, 2025 at 5:37 PM JST, Miguel Ojeda wrote:
>> On Wed, 27 Aug 2025 18:19:57 +1000 Alistair Popple <apopple@nvidia.com> wrote:
>>>
>>> This series builds on top of Alex's series[1] to continue initialising the GSP
>>> into a state where it becomes active and it starts communicating with the host.
>>
>> No big deal, but in case it helps since probably it was not intentional given
>> the rest of the people is there: the rust-for-linux Cc is missing.
>
> This did happen to me once as well - should we add the rust-for-linux
> list to the Nova MAINTAINERS entry to protect against this?
I'm happy about every potential additional reviewer for Nova, but I'm not sure
it scales very well for the rust-for-linux if we get more drivers. :)
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active
2025-08-29 7:40 ` Danilo Krummrich
@ 2025-08-29 10:01 ` Miguel Ojeda
2025-08-29 13:47 ` Alexandre Courbot
0 siblings, 1 reply; 28+ messages in thread
From: Miguel Ojeda @ 2025-08-29 10:01 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alexandre Courbot, Miguel Ojeda, apopple, a.hindborg, airlied,
alex.gaynor, aliceryhl, bjorn3_gh, boqun.feng, dri-devel, gary,
jhubbard, joelagnelf, linux-kernel, lossin, maarten.lankhorst,
mripard, nouveau, simona, tmgross, ttabi, tzimmermann, Nouveau
On Fri, Aug 29, 2025 at 9:40 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> I'm happy about every potential additional reviewer for Nova, but I'm not sure
> it scales very well for the rust-for-linux if we get more drivers. :)
Yeah, it is an informal rule I/we added back then so that people
interested in Rust in the early days can follow everything in that
list.
The expectation was that eventually it would go away organically as
time passed if Rust grew -- I mentioned it here since I saw people
mentioned explicitly otherwise.
@Alexandre As for your `MAINTAINERS` suggestion, up to all of you --
other entries do that currently, but it is not a requirement, and you
are big enough already (have several people looking at patches etc.).
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active
2025-08-29 10:01 ` Miguel Ojeda
@ 2025-08-29 13:47 ` Alexandre Courbot
0 siblings, 0 replies; 28+ messages in thread
From: Alexandre Courbot @ 2025-08-29 13:47 UTC (permalink / raw)
To: Miguel Ojeda, Danilo Krummrich
Cc: Miguel Ojeda, apopple, a.hindborg, airlied, alex.gaynor,
aliceryhl, bjorn3_gh, boqun.feng, dri-devel, gary, jhubbard,
joelagnelf, linux-kernel, lossin, maarten.lankhorst, mripard,
nouveau, simona, tmgross, ttabi, tzimmermann, Nouveau
On Fri Aug 29, 2025 at 7:01 PM JST, Miguel Ojeda wrote:
> On Fri, Aug 29, 2025 at 9:40 AM Danilo Krummrich <dakr@kernel.org> wrote:
>>
>> I'm happy about every potential additional reviewer for Nova, but I'm not sure
>> it scales very well for the rust-for-linux if we get more drivers. :)
>
> Yeah, it is an informal rule I/we added back then so that people
> interested in Rust in the early days can follow everything in that
> list.
>
> The expectation was that eventually it would go away organically as
> time passed if Rust grew -- I mentioned it here since I saw people
> mentioned explicitly otherwise.
>
> @Alexandre As for your `MAINTAINERS` suggestion, up to all of you --
> other entries do that currently, but it is not a requirement, and you
> are big enough already (have several people looking at patches etc.).
No, Danilo is right, it might somehow make sense now that the driver is
still basic, but this probably won't scale too well after the basic
bring up and not everyone on R4L is necessarily interested in GPU
drivers.
We'll add "don't forget rust-for-linux" to our individual pre-send
checklists for the time being. ;)
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 08/10] gpu: nova-core: falcon: Add support to check if RISC-V is active
2025-08-27 8:20 ` [PATCH 08/10] gpu: nova-core: falcon: Add support to check if RISC-V is active Alistair Popple
@ 2025-08-29 18:48 ` Timur Tabi
2025-09-02 0:08 ` Alistair Popple
0 siblings, 1 reply; 28+ messages in thread
From: Timur Tabi @ 2025-08-29 18:48 UTC (permalink / raw)
To: dri-devel@lists.freedesktop.org, Alistair Popple,
Alexandre Courbot, dakr@kernel.org
Cc: lossin@kernel.org, ojeda@kernel.org, boqun.feng@gmail.com,
a.hindborg@kernel.org, tzimmermann@suse.de, tmgross@umich.edu,
alex.gaynor@gmail.com, simona@ffwll.ch, mripard@kernel.org,
maarten.lankhorst@linux.intel.com, linux-kernel@vger.kernel.org,
John Hubbard, nouveau@lists.freedesktop.org,
bjorn3_gh@protonmail.com, airlied@gmail.com, aliceryhl@google.com,
Joel Fernandes, gary@garyguo.net
On Wed, 2025-08-27 at 18:20 +1000, Alistair Popple wrote:
> + pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> Result<bool> {
> + let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID);
> + Ok(cpuctl.active_stat())
> + }
I think this should return just bool instead of Result<bool>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 01/10] gpu: nova-core: Set correct DMA mask
2025-08-27 8:19 ` [PATCH 01/10] gpu: nova-core: Set correct DMA mask Alistair Popple
@ 2025-08-29 23:55 ` John Hubbard
2025-09-01 23:55 ` Alistair Popple
0 siblings, 1 reply; 28+ messages in thread
From: John Hubbard @ 2025-08-29 23:55 UTC (permalink / raw)
To: Alistair Popple, dri-devel, dakr, acourbot
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Joel Fernandes, Timur Tabi,
linux-kernel, nouveau
On 8/27/25 1:19 AM, Alistair Popple wrote:
> Set the correct DMA mask. Without this DMA will fail on some setups.
>
> Signed-off-by: Alistair Popple <apopple@nvidia.com>
> ---
> drivers/gpu/nova-core/driver.rs | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
> index 274989ea1fb4a..3e154ffb6be4b 100644
> --- a/drivers/gpu/nova-core/driver.rs
> +++ b/drivers/gpu/nova-core/driver.rs
> @@ -1,6 +1,9 @@
> // SPDX-License-Identifier: GPL-2.0
>
> -use kernel::{auxiliary, bindings, c_str, device::Core, pci, prelude::*, sizes::SZ_16M, sync::Arc};
> +use kernel::{
> + auxiliary, bindings, c_str, device::Core, dma::Device, dma::DmaMask, pci, prelude::*,
> + sizes::SZ_16M, sync::Arc,
> +};
>
> use crate::gpu::Gpu;
>
> @@ -34,6 +37,9 @@ fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self
> pdev.enable_device_mem()?;
> pdev.set_master();
>
> + // SAFETY: No DMA allocations have been made yet
> + unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<48>())? };
Eventually, should be 52 bits wide, rather than 48. Or so I believe from
looking at various drivers, including Nouveau (which uses 52-bit for
Blackwell) and mlx* (which use a 64-bit mask).
However, it works for the current series, because this series only supports
Ampere GPUs, and 48 bits suffices for those.
So, you could leave this patch as-is, and I'll change 48 --> 52 in the
upcoming Hopper/Blackwell series. Or you can change it now.
Either way is fine, so:
Reviewed-by: John Hubbard <jhubbard@nvidia.com>
thanks,
--
John Hubbard
> +
> let bar = Arc::pin_init(
> pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0")),
> GFP_KERNEL,
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 03/10] gpu: nova-core: gsp: Create wpr metadata
2025-08-27 8:20 ` [PATCH 03/10] gpu: nova-core: gsp: Create wpr metadata Alistair Popple
@ 2025-09-01 7:46 ` Alexandre Courbot
2025-09-03 8:57 ` Alistair Popple
0 siblings, 1 reply; 28+ messages in thread
From: Alexandre Courbot @ 2025-09-01 7:46 UTC (permalink / raw)
To: Alistair Popple, dri-devel, dakr
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau, Nouveau
Hi Alistair,
On Wed Aug 27, 2025 at 5:20 PM JST, Alistair Popple wrote:
<snip>
> index 161c057350622..1f51e354b9569 100644
> --- a/drivers/gpu/nova-core/gsp.rs
> +++ b/drivers/gpu/nova-core/gsp.rs
> @@ -6,12 +6,17 @@
> use kernel::dma_write;
> use kernel::pci;
> use kernel::prelude::*;
> -use kernel::ptr::Alignment;
> +use kernel::ptr::{Alignable, Alignment};
> +use kernel::sizes::SZ_128K;
> use kernel::transmute::{AsBytes, FromBytes};
>
> +use crate::fb::FbLayout;
> +use crate::firmware::Firmware;
> use crate::nvfw::{
> - LibosMemoryRegionInitArgument, LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
> - LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM,
> + GspFwWprMeta, GspFwWprMetaBootInfo, GspFwWprMetaBootResumeInfo, LibosMemoryRegionInitArgument,
> + LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
> + LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM, GSP_FW_WPR_META_MAGIC,
> + GSP_FW_WPR_META_REVISION,
> };
>
> pub(crate) const GSP_PAGE_SHIFT: usize = 12;
> @@ -25,12 +30,69 @@ unsafe impl AsBytes for LibosMemoryRegionInitArgument {}
> // are valid.
> unsafe impl FromBytes for LibosMemoryRegionInitArgument {}
>
> +// SAFETY: Padding is explicit and will not contain uninitialized data.
> +unsafe impl AsBytes for GspFwWprMeta {}
> +
> +// SAFETY: This struct only contains integer types for which all bit patterns
> +// are valid.
> +unsafe impl FromBytes for GspFwWprMeta {}
> +
> #[allow(unused)]
> pub(crate) struct GspMemObjects {
> libos: CoherentAllocation<LibosMemoryRegionInitArgument>,
> pub loginit: CoherentAllocation<u8>,
> pub logintr: CoherentAllocation<u8>,
> pub logrm: CoherentAllocation<u8>,
> + pub wpr_meta: CoherentAllocation<GspFwWprMeta>,
> +}
I think `wpr_meta` is a bit out-of-place in this structure. There are
several reason for this:
- All the other members of this structure (including `cmdq` which is
added later) are referenced by `libos` and constitute the GSP runtime:
they are used as long as the GSP is active. `wpr_meta`, OTOH, does not
reference any of the other objects, nor is it referenced by them.
- `wpr_meta` is never used by the GSP, but needed as a parameter of
Booter on SEC2 to load the GSP firmware. It can actually be discarded
once this step is completed. This is very different from the rest of
this structure, which is used by the GSP.
So I think it doesn't really belong here, and would probably fit better
in `Firmware`. Now the fault lies in my own series, which doesn't let
you build `wpr_meta` easily from there. I'll try to fix that in the v3.
And with the removal of `wpr_meta`, this structure ends up strictly
containing the GSP runtime, including the command queue... Maybe it can
simply be named `Gsp` then? It is even already in the right module! :)
Loosely related, but looking at this series made me realize there is a
very logical split of our firmware into two "bundles":
- The GSP bundle includes the GSP runtime data, which is this
`GspMemObjects` structure minus `wpr_meta`. We pass it as an input
parameter to the GSP firmware using the GSP's falcon mbox registers.
It must live as long as the GSP is running.
- The SEC2 bundle includes Booter, `wpr_meta`, the GSP firmware binary,
bootloader and its signatures (which are all referenced by
`wpr_meta`). All this data is consumed by SEC2, and crucially can be
dropped once the GSP is booted.
This separation is important as currently we are stuffing anything
firmware-related into the `Firmware` struct and keep it there forever,
consuming dozens of megabytes of host memory that we could free. Booting
the GSP is typically a one-time operation in the life of the GPU device,
and even if we ever need to do it again, we can very well build the SEC2
bundle from scratch again.
I will try to reflect the separation better in the v3 of my patchset -
then we can just build `wpr_meta` as a local variable of the method that
runs `Booter`, and drop it (alongside the rest of the SEC2 bundle) upon
return.
> +
> +pub(crate) fn build_wpr_meta(
> + dev: &device::Device<device::Bound>,
> + fw: &Firmware,
> + fb_layout: &FbLayout,
> +) -> Result<CoherentAllocation<GspFwWprMeta>> {
> + let wpr_meta =
> + CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
> + dma_write!(
> + wpr_meta[0] = GspFwWprMeta {
> + magic: GSP_FW_WPR_META_MAGIC as u64,
> + revision: u64::from(GSP_FW_WPR_META_REVISION),
> + sysmemAddrOfRadix3Elf: fw.gsp.lvl0_dma_handle(),
> + sizeOfRadix3Elf: fw.gsp.size as u64,
> + sysmemAddrOfBootloader: fw.gsp_bootloader.ucode.dma_handle(),
> + sizeOfBootloader: fw.gsp_bootloader.ucode.size() as u64,
> + bootloaderCodeOffset: u64::from(fw.gsp_bootloader.code_offset),
> + bootloaderDataOffset: u64::from(fw.gsp_bootloader.data_offset),
> + bootloaderManifestOffset: u64::from(fw.gsp_bootloader.manifest_offset),
> + __bindgen_anon_1: GspFwWprMetaBootResumeInfo {
> + __bindgen_anon_1: GspFwWprMetaBootInfo {
> + sysmemAddrOfSignature: fw.gsp_sigs.dma_handle(),
> + sizeOfSignature: fw.gsp_sigs.size() as u64,
> + }
> + },
> + gspFwRsvdStart: fb_layout.heap.start,
> + nonWprHeapOffset: fb_layout.heap.start,
> + nonWprHeapSize: fb_layout.heap.end - fb_layout.heap.start,
> + gspFwWprStart: fb_layout.wpr2.start,
> + gspFwHeapOffset: fb_layout.wpr2_heap.start,
> + gspFwHeapSize: fb_layout.wpr2_heap.end - fb_layout.wpr2_heap.start,
> + gspFwOffset: fb_layout.elf.start,
> + bootBinOffset: fb_layout.boot.start,
> + frtsOffset: fb_layout.frts.start,
> + frtsSize: fb_layout.frts.end - fb_layout.frts.start,
> + gspFwWprEnd: fb_layout
> + .vga_workspace
> + .start
> + .align_down(Alignment::new(SZ_128K)),
> + gspFwHeapVfPartitionCount: fb_layout.vf_partition_count,
> + fbSize: fb_layout.fb.end - fb_layout.fb.start,
> + vgaWorkspaceOffset: fb_layout.vga_workspace.start,
> + vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start,
> + ..Default::default()
> + }
> + )?;
> +
> + Ok(wpr_meta)
I've discussed the bindings abstractions with Danilo last week. We
agreed that no layout information should ever escape the `nvfw` module.
I.e. the fields of `GspFwWprMeta` should not even be visible here.
Instead, `GspFwWprMeta` should be wrapped privately into another
structure inside `nvfw`:
/// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA
/// addresses of the GSP bootloader and firmware.
#[repr(transparent)]
pub(crate) struct GspFwWprMeta(r570_144::GspFwWprMeta);
All its implementations should also be there:
// SAFETY: Padding is explicit and will not contain uninitialized data.
unsafe impl AsBytes for GspFwWprMeta {}
// SAFETY: This struct only contains integer types for which all bit patterns
// are valid.
unsafe impl FromBytes for GspFwWprMeta {}
And lastly, this `new` method can also be moved into `nvfw`, as an impl
block for the wrapping `GspFwWprMeta` type. That way no layout detail
escapes that module, and it will be easier to adapt the code to
potential layout chances with new firmware versions.
(note that my series is the one carelessly re-exporting `GspFwWprMeta`
as-is - I'll fix that too in v3)
The same applies to `LibosMemoryRegionInitArgument` of the previous
patch, and other types introduced in subsequent patches. Usually there
is little more work to do than moving the implentations into `nvfw` as
everything is already abstracted correctly - just not where we
eventually want it.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 01/10] gpu: nova-core: Set correct DMA mask
2025-08-29 23:55 ` John Hubbard
@ 2025-09-01 23:55 ` Alistair Popple
2025-09-03 19:45 ` John Hubbard
0 siblings, 1 reply; 28+ messages in thread
From: Alistair Popple @ 2025-09-01 23:55 UTC (permalink / raw)
To: John Hubbard
Cc: dri-devel, dakr, acourbot, Miguel Ojeda, Alex Gaynor, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Joel Fernandes, Timur Tabi, linux-kernel, nouveau
On 2025-08-30 at 09:55 +1000, John Hubbard <jhubbard@nvidia.com> wrote...
> On 8/27/25 1:19 AM, Alistair Popple wrote:
> > Set the correct DMA mask. Without this DMA will fail on some setups.
> >
> > Signed-off-by: Alistair Popple <apopple@nvidia.com>
> > ---
> > drivers/gpu/nova-core/driver.rs | 8 +++++++-
> > 1 file changed, 7 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
> > index 274989ea1fb4a..3e154ffb6be4b 100644
> > --- a/drivers/gpu/nova-core/driver.rs
> > +++ b/drivers/gpu/nova-core/driver.rs
> > @@ -1,6 +1,9 @@
> > // SPDX-License-Identifier: GPL-2.0
> >
> > -use kernel::{auxiliary, bindings, c_str, device::Core, pci, prelude::*, sizes::SZ_16M, sync::Arc};
> > +use kernel::{
> > + auxiliary, bindings, c_str, device::Core, dma::Device, dma::DmaMask, pci, prelude::*,
> > + sizes::SZ_16M, sync::Arc,
> > +};
> >
> > use crate::gpu::Gpu;
> >
> > @@ -34,6 +37,9 @@ fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self
> > pdev.enable_device_mem()?;
> > pdev.set_master();
> >
> > + // SAFETY: No DMA allocations have been made yet
> > + unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<48>())? };
>
> Eventually, should be 52 bits wide, rather than 48. Or so I believe from
> looking at various drivers, including Nouveau (which uses 52-bit for
> Blackwell) and mlx* (which use a 64-bit mask).
>
> However, it works for the current series, because this series only supports
> Ampere GPUs, and 48 bits suffices for those.
Actually based on both Nouveau and our internal docs this should be 47-bits. I
suspect I just chose 48 during initial bring-up because that's what most CPUs
support but neglected to add the TODO to actually go and check this. So will fix
for v2.
> So, you could leave this patch as-is, and I'll change 48 --> 52 in the
> upcoming Hopper/Blackwell series. Or you can change it now.
We can't of course just change this to 52 bits because this needs to reflect
what the GPU HW supports. So ideally this needs to come from the HAL. I left
this hard-coded because in the short-term leaving it as 47 bits even for
Blackwell won't cause any issues. It may force usage of an IOMMU to address
physical addresses greater than 47-bits when it otherwise wouldn't for
Hopper/Blackwell (it would always have to for Ampere/Turing), but short-term I
doubt many systems actually have physical memory above 47-bits anyway.
In other words you could leave this as 47 bits in the upcoming Hopper/Blackwell
series or use the HAL we have come up with (if that is available) to obtain the
optimal value.
> Either way is fine, so:
>
>
> Reviewed-by: John Hubbard <jhubbard@nvidia.com>
Thanks.
> thanks,
> --
> John Hubbard
>
> > +
> > let bar = Arc::pin_init(
> > pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0")),
> > GFP_KERNEL,
>
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 08/10] gpu: nova-core: falcon: Add support to check if RISC-V is active
2025-08-29 18:48 ` Timur Tabi
@ 2025-09-02 0:08 ` Alistair Popple
0 siblings, 0 replies; 28+ messages in thread
From: Alistair Popple @ 2025-09-02 0:08 UTC (permalink / raw)
To: Timur Tabi
Cc: dri-devel@lists.freedesktop.org, Alexandre Courbot,
dakr@kernel.org, lossin@kernel.org, ojeda@kernel.org,
boqun.feng@gmail.com, a.hindborg@kernel.org, tzimmermann@suse.de,
tmgross@umich.edu, alex.gaynor@gmail.com, simona@ffwll.ch,
mripard@kernel.org, maarten.lankhorst@linux.intel.com,
linux-kernel@vger.kernel.org, John Hubbard,
nouveau@lists.freedesktop.org, bjorn3_gh@protonmail.com,
airlied@gmail.com, aliceryhl@google.com, Joel Fernandes,
gary@garyguo.net
On 2025-08-30 at 04:48 +1000, Timur Tabi <ttabi@nvidia.com> wrote...
> On Wed, 2025-08-27 at 18:20 +1000, Alistair Popple wrote:
> > + pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> Result<bool> {
> > + let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID);
> > + Ok(cpuctl.active_stat())
> > + }
>
> I think this should return just bool instead of Result<bool>
Agreed. And looking at some of the other sequencer/falcon code in Joel's series
I believe similar comments apply there. I assume something changed with the
register reading code to make this possible.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 03/10] gpu: nova-core: gsp: Create wpr metadata
2025-09-01 7:46 ` Alexandre Courbot
@ 2025-09-03 8:57 ` Alistair Popple
2025-09-03 12:51 ` Alexandre Courbot
0 siblings, 1 reply; 28+ messages in thread
From: Alistair Popple @ 2025-09-03 8:57 UTC (permalink / raw)
To: Alexandre Courbot
Cc: dri-devel, dakr, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau, Nouveau
On 2025-09-01 at 17:46 +1000, Alexandre Courbot <acourbot@nvidia.com> wrote...
> Hi Alistair,
>
> On Wed Aug 27, 2025 at 5:20 PM JST, Alistair Popple wrote:
> <snip>
> > index 161c057350622..1f51e354b9569 100644
> > --- a/drivers/gpu/nova-core/gsp.rs
> > +++ b/drivers/gpu/nova-core/gsp.rs
> > @@ -6,12 +6,17 @@
> > use kernel::dma_write;
> > use kernel::pci;
> > use kernel::prelude::*;
> > -use kernel::ptr::Alignment;
> > +use kernel::ptr::{Alignable, Alignment};
> > +use kernel::sizes::SZ_128K;
> > use kernel::transmute::{AsBytes, FromBytes};
> >
> > +use crate::fb::FbLayout;
> > +use crate::firmware::Firmware;
> > use crate::nvfw::{
> > - LibosMemoryRegionInitArgument, LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
> > - LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM,
> > + GspFwWprMeta, GspFwWprMetaBootInfo, GspFwWprMetaBootResumeInfo, LibosMemoryRegionInitArgument,
> > + LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS,
> > + LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM, GSP_FW_WPR_META_MAGIC,
> > + GSP_FW_WPR_META_REVISION,
> > };
> >
> > pub(crate) const GSP_PAGE_SHIFT: usize = 12;
> > @@ -25,12 +30,69 @@ unsafe impl AsBytes for LibosMemoryRegionInitArgument {}
> > // are valid.
> > unsafe impl FromBytes for LibosMemoryRegionInitArgument {}
> >
> > +// SAFETY: Padding is explicit and will not contain uninitialized data.
> > +unsafe impl AsBytes for GspFwWprMeta {}
> > +
> > +// SAFETY: This struct only contains integer types for which all bit patterns
> > +// are valid.
> > +unsafe impl FromBytes for GspFwWprMeta {}
> > +
> > #[allow(unused)]
> > pub(crate) struct GspMemObjects {
> > libos: CoherentAllocation<LibosMemoryRegionInitArgument>,
> > pub loginit: CoherentAllocation<u8>,
> > pub logintr: CoherentAllocation<u8>,
> > pub logrm: CoherentAllocation<u8>,
> > + pub wpr_meta: CoherentAllocation<GspFwWprMeta>,
> > +}
>
> I think `wpr_meta` is a bit out-of-place in this structure. There are
> several reason for this:
>
> - All the other members of this structure (including `cmdq` which is
> added later) are referenced by `libos` and constitute the GSP runtime:
> they are used as long as the GSP is active. `wpr_meta`, OTOH, does not
> reference any of the other objects, nor is it referenced by them.
> - `wpr_meta` is never used by the GSP, but needed as a parameter of
> Booter on SEC2 to load the GSP firmware. It can actually be discarded
> once this step is completed. This is very different from the rest of
> this structure, which is used by the GSP.
Yes, I had noticed that too and had tried to remove it previously. But as you
mention below that was a little bit tricky but if you fix it for v3 I think this
all makes perfect sense.
> So I think it doesn't really belong here, and would probably fit better
> in `Firmware`. Now the fault lies in my own series, which doesn't let
> you build `wpr_meta` easily from there. I'll try to fix that in the v3.
>
> And with the removal of `wpr_meta`, this structure ends up strictly
> containing the GSP runtime, including the command queue... Maybe it can
> simply be named `Gsp` then? It is even already in the right module! :)
Agreed - I noticed this right after I renamed this struct last time so wanted
to let things settle down a bit before doing another rename. But I think `Gsp`
makes a whole lot more sense, especially if we remove the wpr_meta data.
> Loosely related, but looking at this series made me realize there is a
> very logical split of our firmware into two "bundles":
>
> - The GSP bundle includes the GSP runtime data, which is this
> `GspMemObjects` structure minus `wpr_meta`. We pass it as an input
> parameter to the GSP firmware using the GSP's falcon mbox registers.
> It must live as long as the GSP is running.
> - The SEC2 bundle includes Booter, `wpr_meta`, the GSP firmware binary,
> bootloader and its signatures (which are all referenced by
> `wpr_meta`). All this data is consumed by SEC2, and crucially can be
> dropped once the GSP is booted.
>
> This separation is important as currently we are stuffing anything
> firmware-related into the `Firmware` struct and keep it there forever,
> consuming dozens of megabytes of host memory that we could free. Booting
> the GSP is typically a one-time operation in the life of the GPU device,
> and even if we ever need to do it again, we can very well build the SEC2
> bundle from scratch again.
>
> I will try to reflect the separation better in the v3 of my patchset -
> then we can just build `wpr_meta` as a local variable of the method that
> runs `Booter`, and drop it (alongside the rest of the SEC2 bundle) upon
> return.
>
> > +
> > +pub(crate) fn build_wpr_meta(
> > + dev: &device::Device<device::Bound>,
> > + fw: &Firmware,
> > + fb_layout: &FbLayout,
> > +) -> Result<CoherentAllocation<GspFwWprMeta>> {
> > + let wpr_meta =
> > + CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
> > + dma_write!(
> > + wpr_meta[0] = GspFwWprMeta {
> > + magic: GSP_FW_WPR_META_MAGIC as u64,
> > + revision: u64::from(GSP_FW_WPR_META_REVISION),
> > + sysmemAddrOfRadix3Elf: fw.gsp.lvl0_dma_handle(),
> > + sizeOfRadix3Elf: fw.gsp.size as u64,
> > + sysmemAddrOfBootloader: fw.gsp_bootloader.ucode.dma_handle(),
> > + sizeOfBootloader: fw.gsp_bootloader.ucode.size() as u64,
> > + bootloaderCodeOffset: u64::from(fw.gsp_bootloader.code_offset),
> > + bootloaderDataOffset: u64::from(fw.gsp_bootloader.data_offset),
> > + bootloaderManifestOffset: u64::from(fw.gsp_bootloader.manifest_offset),
> > + __bindgen_anon_1: GspFwWprMetaBootResumeInfo {
> > + __bindgen_anon_1: GspFwWprMetaBootInfo {
> > + sysmemAddrOfSignature: fw.gsp_sigs.dma_handle(),
> > + sizeOfSignature: fw.gsp_sigs.size() as u64,
> > + }
> > + },
> > + gspFwRsvdStart: fb_layout.heap.start,
> > + nonWprHeapOffset: fb_layout.heap.start,
> > + nonWprHeapSize: fb_layout.heap.end - fb_layout.heap.start,
> > + gspFwWprStart: fb_layout.wpr2.start,
> > + gspFwHeapOffset: fb_layout.wpr2_heap.start,
> > + gspFwHeapSize: fb_layout.wpr2_heap.end - fb_layout.wpr2_heap.start,
> > + gspFwOffset: fb_layout.elf.start,
> > + bootBinOffset: fb_layout.boot.start,
> > + frtsOffset: fb_layout.frts.start,
> > + frtsSize: fb_layout.frts.end - fb_layout.frts.start,
> > + gspFwWprEnd: fb_layout
> > + .vga_workspace
> > + .start
> > + .align_down(Alignment::new(SZ_128K)),
> > + gspFwHeapVfPartitionCount: fb_layout.vf_partition_count,
> > + fbSize: fb_layout.fb.end - fb_layout.fb.start,
> > + vgaWorkspaceOffset: fb_layout.vga_workspace.start,
> > + vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start,
> > + ..Default::default()
> > + }
> > + )?;
> > +
> > + Ok(wpr_meta)
>
> I've discussed the bindings abstractions with Danilo last week. We
> agreed that no layout information should ever escape the `nvfw` module.
> I.e. the fields of `GspFwWprMeta` should not even be visible here.
>
> Instead, `GspFwWprMeta` should be wrapped privately into another
> structure inside `nvfw`:
>
> /// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA
> /// addresses of the GSP bootloader and firmware.
> #[repr(transparent)]
> pub(crate) struct GspFwWprMeta(r570_144::GspFwWprMeta);
I'm a little bit unsure what the advantage of this is? Admittedly I'm not sure
I've seen the discussion from last week so I may have missed something but it's
not obvious how creating another layer of abstraction is better. How would it
help contain any layout changes to nvfw? Supporting any new struct fields for
example would almost certainly still require code changes outside nvfw.
My thinking here was that the bindings (at least for GSP) probably want to live
in the Gsp crate/module, and the rest of the driver would be isolated from Gsp
changes by the public API provided by the Gsp crate/module rather than trying to
do that at the binding level. For example the get_gsp_info() command implemented
in [1] provides a separate public struct representing what the rest of the
driver needs, thus ensuring the implementation specific details of Gsp (such as
struct layout) do not leak into the wider nova-core driver.
> All its implementations should also be there:
>
> // SAFETY: Padding is explicit and will not contain uninitialized data.
> unsafe impl AsBytes for GspFwWprMeta {}
>
> // SAFETY: This struct only contains integer types for which all bit patterns
> // are valid.
> unsafe impl FromBytes for GspFwWprMeta {}
Makes sense.
> And lastly, this `new` method can also be moved into `nvfw`, as an impl
> block for the wrapping `GspFwWprMeta` type. That way no layout detail
> escapes that module, and it will be easier to adapt the code to
> potential layout chances with new firmware versions.
>
> (note that my series is the one carelessly re-exporting `GspFwWprMeta`
> as-is - I'll fix that too in v3)
>
> The same applies to `LibosMemoryRegionInitArgument` of the previous
> patch, and other types introduced in subsequent patches. Usually there
> is little more work to do than moving the implentations into `nvfw` as
> everything is already abstracted correctly - just not where we
> eventually want it.
This is where I get a little bit uncomfortable - this doesn't feel right to me.
It seems to me moving all these implementations to the bindings would just end
up with a significant amount of Gsp code in nvfw.rs rather than in the places
that actually use it, making nvfw.rs large and unwieldy and the code more
distributed and harder to follow.
And it's all tightly coupled anyway - for example the Gsp boot arguments require some
command queue offsets which are all pretty specific to the Gsp implementation.
Ie. we can't define some nice public API in the Gsp crate for "getting arguments
required for booting Gsp" without that just being "here is a struct containing
all the fields that must be packed into the Gsp arguments for this version",
which at that point may as well just be the actual struct itself right?
- Alistair
[1] - https://lore.kernel.org/rust-for-linux/20250829173254.2068763-18-joelagnelf@nvidia.com/
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 03/10] gpu: nova-core: gsp: Create wpr metadata
2025-09-03 8:57 ` Alistair Popple
@ 2025-09-03 12:51 ` Alexandre Courbot
2025-09-03 13:10 ` Alexandre Courbot
0 siblings, 1 reply; 28+ messages in thread
From: Alexandre Courbot @ 2025-09-03 12:51 UTC (permalink / raw)
To: Alistair Popple
Cc: dri-devel, dakr, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau, Nouveau
On Wed Sep 3, 2025 at 5:57 PM JST, Alistair Popple wrote:
<snip>
>> I've discussed the bindings abstractions with Danilo last week. We
>> agreed that no layout information should ever escape the `nvfw` module.
>> I.e. the fields of `GspFwWprMeta` should not even be visible here.
>>
>> Instead, `GspFwWprMeta` should be wrapped privately into another
>> structure inside `nvfw`:
>>
>> /// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA
>> /// addresses of the GSP bootloader and firmware.
>> #[repr(transparent)]
>> pub(crate) struct GspFwWprMeta(r570_144::GspFwWprMeta);
>
> I'm a little bit unsure what the advantage of this is? Admittedly I'm not sure
> I've seen the discussion from last week so I may have missed something but it's
> not obvious how creating another layer of abstraction is better. How would it
> help contain any layout changes to nvfw? Supporting any new struct fields for
> example would almost certainly still require code changes outside nvfw.
It is not as much creating a new abstraction layer as it is controlling
where it resides - nicely contained in `nvfw` or all over the place.
This is particularly relevant if we consider that binding abstractions
are more likely to require `unsafe` code, that we will then be able to
confine to the `nvfw` module. As I got reminded in my own series, we
don't want `unsafe` code in regular driver modules.
Even if a new field is added to `GspFwWprMeta`, there is a good chance
that the parameters of its current constructor will cover what we need
to initialize it, so the calling code outside of `nvfw` won't need to
change. Of course we cannot guarantee this will be true all the time,
but it still covers us better than the alternative.
And then there is the question of if/when we need to support several
firmware versions. If we start having code in `gsp` that is specific to
a given firmware version, this is already a non-starter. Whereas having
all the abstractions in a single module leaves us in a better position
to use trait objects and virtual calls, or apply proc-macro magic.
>
> My thinking here was that the bindings (at least for GSP) probably want to live
> in the Gsp crate/module, and the rest of the driver would be isolated from Gsp
> changes by the public API provided by the Gsp crate/module rather than trying to
> do that at the binding level. For example the get_gsp_info() command implemented
> in [1] provides a separate public struct representing what the rest of the
> driver needs, thus ensuring the implementation specific details of Gsp (such as
> struct layout) do not leak into the wider nova-core driver.
>
>> All its implementations should also be there:
>>
>> // SAFETY: Padding is explicit and will not contain uninitialized data.
>> unsafe impl AsBytes for GspFwWprMeta {}
>>
>> // SAFETY: This struct only contains integer types for which all bit patterns
>> // are valid.
>> unsafe impl FromBytes for GspFwWprMeta {}
>
> Makes sense.
>
>> And lastly, this `new` method can also be moved into `nvfw`, as an impl
>> block for the wrapping `GspFwWprMeta` type. That way no layout detail
>> escapes that module, and it will be easier to adapt the code to
>> potential layout chances with new firmware versions.
>>
>> (note that my series is the one carelessly re-exporting `GspFwWprMeta`
>> as-is - I'll fix that too in v3)
>>
>> The same applies to `LibosMemoryRegionInitArgument` of the previous
>> patch, and other types introduced in subsequent patches. Usually there
>> is little more work to do than moving the implentations into `nvfw` as
>> everything is already abstracted correctly - just not where we
>> eventually want it.
>
> This is where I get a little bit uncomfortable - this doesn't feel right to me.
> It seems to me moving all these implementations to the bindings would just end
> up with a significant amount of Gsp code in nvfw.rs rather than in the places
> that actually use it, making nvfw.rs large and unwieldy and the code more
> distributed and harder to follow.
If we want to split things more logically, I think it's perfectly fine
to have e.g. a `nvfw/gsp` module that contains all the GSP abstractions,
another one for the sequencer, etc. As long as all the version-specific
bits are contained below `nvfw`.
>
> And it's all tightly coupled anyway - for example the Gsp boot arguments require some
> command queue offsets which are all pretty specific to the Gsp implementation.
> Ie. we can't define some nice public API in the Gsp crate for "getting arguments
> required for booting Gsp" without that just being "here is a struct containing
> all the fields that must be packed into the Gsp arguments for this version",
> which at that point may as well just be the actual struct itself right?
Which particular structure are you refering to?
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 03/10] gpu: nova-core: gsp: Create wpr metadata
2025-09-03 12:51 ` Alexandre Courbot
@ 2025-09-03 13:10 ` Alexandre Courbot
0 siblings, 0 replies; 28+ messages in thread
From: Alexandre Courbot @ 2025-09-03 13:10 UTC (permalink / raw)
To: Alexandre Courbot, Alistair Popple
Cc: dri-devel, dakr, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, linux-kernel, nouveau, Nouveau
On Wed Sep 3, 2025 at 9:51 PM JST, Alexandre Courbot wrote:
>> And it's all tightly coupled anyway - for example the Gsp boot arguments require some
>> command queue offsets which are all pretty specific to the Gsp implementation.
>> Ie. we can't define some nice public API in the Gsp crate for "getting arguments
>> required for booting Gsp" without that just being "here is a struct containing
>> all the fields that must be packed into the Gsp arguments for this version",
>> which at that point may as well just be the actual struct itself right?
>
> Which particular structure are you refering to?
Ah, I guess that was about `GspArgumentsCached` and the message queue's
`get_cmdq_offsets` method. For this I guess we can just have a
fn new(cmdq: &GspCmdq) -> Self
constructor for `GspArgumentsCached` which grabs the information it
needs from the queue and initializes itself. You would need to have this
code anyway, it's just a matter of where we put it - inside a function
of `gsp.rs`, or as a reusable (and easily replacable) constructor.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 01/10] gpu: nova-core: Set correct DMA mask
2025-09-01 23:55 ` Alistair Popple
@ 2025-09-03 19:45 ` John Hubbard
0 siblings, 0 replies; 28+ messages in thread
From: John Hubbard @ 2025-09-03 19:45 UTC (permalink / raw)
To: Alistair Popple
Cc: dri-devel, dakr, acourbot, Miguel Ojeda, Alex Gaynor, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Joel Fernandes, Timur Tabi, linux-kernel, nouveau
On 9/1/25 4:55 PM, Alistair Popple wrote:
> On 2025-08-30 at 09:55 +1000, John Hubbard <jhubbard@nvidia.com> wrote...
>> On 8/27/25 1:19 AM, Alistair Popple wrote:
...
>>>
>>> + // SAFETY: No DMA allocations have been made yet
>>> + unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<48>())? };
>>
>> Eventually, should be 52 bits wide, rather than 48. Or so I believe from
>> looking at various drivers, including Nouveau (which uses 52-bit for
>> Blackwell) and mlx* (which use a 64-bit mask).
>>
>> However, it works for the current series, because this series only supports
>> Ampere GPUs, and 48 bits suffices for those.
>
> Actually based on both Nouveau and our internal docs this should be 47-bits. I
Yes. Which is why I wrote "48 bits suffices".
> suspect I just chose 48 during initial bring-up because that's what most CPUs
> support but neglected to add the TODO to actually go and check this. So will fix
> for v2.
>
>> So, you could leave this patch as-is, and I'll change 48 --> 52 in the
>> upcoming Hopper/Blackwell series. Or you can change it now.
>
> We can't of course just change this to 52 bits because this needs to reflect
> what the GPU HW supports. So ideally this needs to come from the HAL. I left
I should have been more precise. I meant, "use 52 bits, via HAL, just
like Nouveau does".
> this hard-coded because in the short-term leaving it as 47 bits even for
> Blackwell won't cause any issues. It may force usage of an IOMMU to address
> physical addresses greater than 47-bits when it otherwise wouldn't for
> Hopper/Blackwell (it would always have to for Ampere/Turing), but short-term I
> doubt many systems actually have physical memory above 47-bits anyway.
>
> In other words you could leave this as 47 bits in the upcoming Hopper/Blackwell
> series or use the HAL we have come up with (if that is available) to obtain the
> optimal value.
Yes. I'm planning to match Nouveau's HAL approach for this, in the
upcoming Hopper/Blackwell series.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2025-09-03 19:45 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-27 8:19 [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Alistair Popple
2025-08-27 8:19 ` [PATCH 01/10] gpu: nova-core: Set correct DMA mask Alistair Popple
2025-08-29 23:55 ` John Hubbard
2025-09-01 23:55 ` Alistair Popple
2025-09-03 19:45 ` John Hubbard
2025-08-27 8:19 ` [PATCH 02/10] gpu: nova-core: Create initial GspSharedMemObjects Alistair Popple
2025-08-27 8:20 ` [PATCH 03/10] gpu: nova-core: gsp: Create wpr metadata Alistair Popple
2025-09-01 7:46 ` Alexandre Courbot
2025-09-03 8:57 ` Alistair Popple
2025-09-03 12:51 ` Alexandre Courbot
2025-09-03 13:10 ` Alexandre Courbot
2025-08-27 8:20 ` [PATCH 04/10] gpu: nova-core: Add a slice-buffer (sbuffer) datastructure Alistair Popple
2025-08-27 8:20 ` [PATCH 05/10] gpu: nova-core: gsp: Add GSP command queue handling Alistair Popple
2025-08-27 20:35 ` John Hubbard
2025-08-27 23:42 ` Alistair Popple
2025-08-27 8:20 ` [PATCH 06/10] gpu: nova-core: gsp: Create rmargs Alistair Popple
2025-08-27 8:20 ` [PATCH 07/10] gpu: nova-core: gsp: Create RM registry and sysinfo commands Alistair Popple
2025-08-29 6:02 ` Alistair Popple
2025-08-27 8:20 ` [PATCH 08/10] gpu: nova-core: falcon: Add support to check if RISC-V is active Alistair Popple
2025-08-29 18:48 ` Timur Tabi
2025-09-02 0:08 ` Alistair Popple
2025-08-27 8:20 ` [PATCH 09/10] gpu: nova-core: falcon: Add support to write firmware version Alistair Popple
2025-08-27 8:20 ` [PATCH 10/10] gpu: nova-core: gsp: Boot GSP Alistair Popple
2025-08-28 8:37 ` [PATCH 00/10] gpu: nova-core: Boot GSP to RISC-V active Miguel Ojeda
2025-08-29 3:03 ` Alexandre Courbot
2025-08-29 7:40 ` Danilo Krummrich
2025-08-29 10:01 ` Miguel Ojeda
2025-08-29 13:47 ` Alexandre Courbot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).