* [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active
@ 2025-11-07 23:43 Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 01/16] gpu: nova-core: compute layout of more framebuffer regions required for GSP Alexandre Courbot
` (16 more replies)
0 siblings, 17 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot, Lyude Paul, Alexandre Courbot
Hopefully a close-to-mergeable revision. Lots of documentation added,
making the process of understanding the code hopefully easier, and I
have finally given more attention to the top patches.
This resulted in another refactor/simplification of the GSP command
sending, moving it closer to Alistair's original idea of separating the
commands from the objects representing them. This also means there is a
single trait for commands, and a single method to send them. A couple
Reviewed-by tags have been removed as that part of the code changed
quite a bit.
The message receive method in its current form won't be adequate as-is
for the long-term, but it is fit to boot GSP.
The hope is to merge this or a v9 before -rc6, so it can make it into
6.19. If there are reasons not to, please scream within a week or so. :)
A branch including this series and its dependencies is available at [1].
[1] https://github.com/Gnurou/linux/tree/b4/gsp_boot
Changes in v8:
- Add lots of doccomments.
- Refactor and simplify GSP command sending - now uses a single trait
and a single method.
- Leverage the new `from_bytes_prefix` family of methods to simplify GSP
command queue code a bit.
- Simplify RM arguments.
- Split the `GspSetSystemInfo` and `SetRegistry` commands into their own
patch.
- Add a `is_empty` method to `SBufferIter` and use it to detect when a
command's variable length payload has not been entirely written.
- Harmonize imports according to new format rules.
- Link to v7: https://lore.kernel.org/r/20251029-gsp_boot-v7-0-34227afad347@nvidia.com
Changes in v7:
- Remove `as` conversions by using the features of the `num` module.
- Add build-time conversion of constant integer values to smaller types.
- Thanks to the two items above, make more functions infallible.
- Remove unneeded `nr_ptes` member of the `Cmdq`.
- Use `repr(u32)` for `MsgFunction` to simplify it.
- Use `from_ref` instead of casting references into pointers with `as`.
- Add message header version type to remove use of magic number.
- Switch some parameters to `usize` to limit type conversions.
- Add comments for undocumented functions.
- Remove `function_number` method of `GspMsgElement` and return invalid
function numbers as the error value of `function` instead.
- Work around the renaming of `slice::flatten` to
`slice::as_flattened` in Rust 1.80 (thanks Miguel!).
- Fix clippy warning with Rust 1.78.
- Link to v6: https://lore.kernel.org/r/20251017054736.2984332-1-apopple@nvidia.com/
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
Alexandre Courbot (5):
gpu: nova-core: compute layout of more framebuffer regions required for GSP
gpu: nova-core: num: add functions to safely convert a const value to a smaller type
rust: enable slice_flatten feature and provide it through an extension trait
gpu: nova-core: gsp: Add SetRegistry command
bitfields RANGE doc - not great
Alistair Popple (8):
gpu: nova-core: Set correct DMA mask
gpu: nova-core: Create initial Gsp
gpu: nova-core: gsp: Create wpr metadata
gpu: nova-core: Add zeroable trait to bindings
gpu: nova-core: gsp: Add GSP command queue bindings and handling
gpu: nova-core: gsp: Create rmargs
gpu: nova-core: gsp: Add SetSystemInfo command
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/bitfield.rs | 16 +-
drivers/gpu/nova-core/driver.rs | 16 +
drivers/gpu/nova-core/falcon.rs | 15 +
drivers/gpu/nova-core/fb.rs | 72 ++-
drivers/gpu/nova-core/firmware/gsp.rs | 7 +-
drivers/gpu/nova-core/firmware/riscv.rs | 11 +-
drivers/gpu/nova-core/gpu.rs | 2 +-
drivers/gpu/nova-core/gsp.rs | 145 ++++-
drivers/gpu/nova-core/gsp/boot.rs | 86 ++-
drivers/gpu/nova-core/gsp/cmdq.rs | 607 +++++++++++++++++++
drivers/gpu/nova-core/gsp/commands.rs | 122 ++++
drivers/gpu/nova-core/gsp/fw.rs | 608 ++++++++++++++++++-
drivers/gpu/nova-core/gsp/fw/commands.rs | 106 ++++
drivers/gpu/nova-core/gsp/fw/r570_144.rs | 2 +-
drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 703 ++++++++++++++++++++++
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/num.rs | 51 ++
drivers/gpu/nova-core/regs.rs | 17 +-
drivers/gpu/nova-core/sbuffer.rs | 227 +++++++
init/Kconfig | 3 +
rust/kernel/lib.rs | 4 +
rust/kernel/prelude.rs | 3 +
rust/kernel/slice.rs | 49 ++
23 files changed, 2839 insertions(+), 34 deletions(-)
---
base-commit: 80b3dc0a5a2e51fb2b8f3406f5ee20ad4a652316
change-id: 20251027-gsp_boot-c6bb048a304e
Best regards,
--
Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v8 01/16] gpu: nova-core: compute layout of more framebuffer regions required for GSP
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 02/16] gpu: nova-core: Set correct DMA mask Alexandre Courbot
` (15 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
Compute more of the required FB layout information to boot the GSP
firmware.
This information is dependent on the firmware itself, so first we need
to import and abstract the required firmware bindings in the `nvfw`
module.
Then, a new FB HAL method is introduced in `fb::hal` that uses these
bindings and hardware information to compute the correct layout
information.
This information is then used in `fb` and the result made visible in
`FbLayout`.
These 3 things are grouped into the same patch to avoid lots of unused
warnings that would be tedious to work around. As they happen in
different files, they should not be too difficult to track separately.
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/fb.rs | 71 +++++++++++-
drivers/gpu/nova-core/firmware/gsp.rs | 4 +-
drivers/gpu/nova-core/firmware/riscv.rs | 2 +-
drivers/gpu/nova-core/gsp.rs | 2 +
drivers/gpu/nova-core/gsp/boot.rs | 4 +-
drivers/gpu/nova-core/gsp/fw.rs | 113 ++++++++++++++++++-
drivers/gpu/nova-core/gsp/fw/r570_144.rs | 1 -
drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 125 ++++++++++++++++++++++
8 files changed, 311 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index a99223f73367..156d9bf1f191 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -16,9 +16,14 @@
use crate::{
dma::DmaObject,
driver::Bar0,
+ firmware::gsp::GspFirmware,
gpu::Chipset,
- num::usize_as_u64,
- regs, //
+ gsp,
+ num::{
+ usize_as_u64,
+ FromSafeCast, //
+ },
+ regs,
};
mod hal;
@@ -95,14 +100,27 @@ pub(crate) fn unregister(&self, bar: &Bar0) {
#[derive(Debug)]
#[expect(dead_code)]
pub(crate) struct FbLayout {
+ /// Range of the framebuffer. Starts at `0`.
pub(crate) fb: Range<u64>,
+ /// VGA workspace, small area of reserved memory at the end of the framebuffer.
pub(crate) vga_workspace: Range<u64>,
+ /// FRTS range.
pub(crate) frts: Range<u64>,
+ /// Memory area containing the GSP bootloader image.
+ pub(crate) boot: Range<u64>,
+ /// Memory area containing the GSP firmware image.
+ pub(crate) elf: Range<u64>,
+ /// WPR2 heap.
+ pub(crate) wpr2_heap: Range<u64>,
+ /// WPR2 region range, starting with an instance of `GspFwWprMeta`.
+ pub(crate) wpr2: Range<u64>,
+ pub(crate) heap: Range<u64>,
+ pub(crate) vf_partition_count: u8,
}
impl FbLayout {
- /// Computes the FB layout.
- pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> {
+ /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware.
+ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<Self> {
let hal = hal::fb_hal(chipset);
let fb = {
@@ -146,10 +164,55 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> {
frts_base..frts_base + FRTS_SIZE
};
+ let boot = {
+ const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>();
+ let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size());
+ let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN);
+
+ bootloader_base..bootloader_base + bootloader_size
+ };
+
+ let elf = {
+ const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
+ let elf_size = u64::from_safe_cast(gsp_fw.size);
+ let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
+
+ elf_addr..elf_addr + elf_size
+ };
+
+ let wpr2_heap = {
+ const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
+ let wpr2_heap_size =
+ gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end);
+ let wpr2_heap_addr = (elf.start - wpr2_heap_size).align_down(WPR2_HEAP_DOWN_ALIGN);
+
+ wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN)
+ };
+
+ let wpr2 = {
+ const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
+ let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>()))
+ .align_down(WPR2_DOWN_ALIGN);
+
+ wpr2_addr..frts.end
+ };
+
+ let heap = {
+ const HEAP_SIZE: u64 = usize_as_u64(SZ_1M);
+
+ wpr2.start - HEAP_SIZE..wpr2.start
+ };
+
Ok(Self {
fb,
vga_workspace,
frts,
+ boot,
+ elf,
+ wpr2_heap,
+ wpr2,
+ heap,
+ vf_partition_count: 0,
})
}
}
diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index 72766feae36e..471ace238f62 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -143,11 +143,11 @@ pub(crate) struct GspFirmware {
/// Level 0 page table (single 4KB page) with one entry: DMA address of first level 1 page.
level0: DmaObject,
/// Size in bytes of the firmware contained in [`Self::fw`].
- size: usize,
+ pub(crate) size: usize,
/// Device-mapped GSP signatures matching the GPU's [`Chipset`].
signatures: DmaObject,
/// GSP bootloader, verifies the GSP firmware before loading and running it.
- bootloader: RiscvFirmware,
+ pub(crate) bootloader: RiscvFirmware,
}
impl GspFirmware {
diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs
index 270b2c7dc219..3838fab8f1c0 100644
--- a/drivers/gpu/nova-core/firmware/riscv.rs
+++ b/drivers/gpu/nova-core/firmware/riscv.rs
@@ -68,7 +68,7 @@ pub(crate) struct RiscvFirmware {
/// Application version.
app_version: u32,
/// Device-mapped firmware image.
- ucode: DmaObject,
+ pub(crate) ucode: DmaObject,
}
impl RiscvFirmware {
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 64e472e7a9d3..aa2a9e6654e4 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -6,6 +6,8 @@
mod fw;
+pub(crate) use fw::{GspFwWprMeta, LibosParams};
+
pub(crate) const GSP_PAGE_SHIFT: usize = 12;
pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT;
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 19dddff929da..979d3391e58c 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -127,12 +127,12 @@ pub(crate) fn boot(
let bios = Vbios::new(dev, bar)?;
- let _gsp_fw = KBox::pin_init(
+ let gsp_fw = KBox::pin_init(
GspFirmware::new(dev, chipset, FIRMWARE_VERSION)?,
GFP_KERNEL,
)?;
- let fb_layout = FbLayout::new(chipset, bar)?;
+ let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
dev_dbg!(dev, "{:#x?}\n", fb_layout);
Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?;
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 34226dd00982..436c00d07b16 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -3,5 +3,116 @@
mod r570_144;
// Alias to avoid repeating the version number with every use.
-#[expect(unused)]
use r570_144 as bindings;
+
+use core::ops::Range;
+
+use kernel::{
+ ptr::{
+ Alignable,
+ Alignment, //
+ },
+ sizes::SZ_1M,
+};
+
+use crate::{
+ gpu::Chipset,
+ num::{
+ self,
+ FromSafeCast, //
+ },
+};
+
+/// Empty type to group methods related to heap parameters for running the GSP firmware.
+enum GspFwHeapParams {}
+
+/// Minimum required alignment for the GSP heap.
+const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new::<{ 1 << 20 }>();
+
+impl GspFwHeapParams {
+ /// Returns the amount of GSP-RM heap memory used during GSP-RM boot and initialization (up to
+ /// and including the first client subdevice allocation).
+ fn base_rm_size(_chipset: Chipset) -> u64 {
+ // TODO: this needs to be updated to return the correct value for Hopper+ once support for
+ // them is added:
+ // u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100)
+ u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X)
+ }
+
+ /// Returns the amount of heap memory required to support a single channel allocation.
+ fn client_alloc_size() -> u64 {
+ u64::from(bindings::GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE)
+ .align_up(GSP_HEAP_ALIGNMENT)
+ .unwrap_or(u64::MAX)
+ }
+
+ /// Returns the amount of memory to reserve for management purposes for a framebuffer of size
+ /// `fb_size`.
+ fn management_overhead(fb_size: u64) -> u64 {
+ let fb_size_gb = fb_size.div_ceil(u64::from_safe_cast(kernel::sizes::SZ_1G));
+
+ u64::from(bindings::GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB)
+ .saturating_mul(fb_size_gb)
+ .align_up(GSP_HEAP_ALIGNMENT)
+ .unwrap_or(u64::MAX)
+ }
+}
+
+/// Heap memory requirements and constraints for a given version of the GSP LIBOS.
+pub(crate) struct LibosParams {
+ /// The base amount of heap required by the GSP operating system, in bytes.
+ carveout_size: u64,
+ /// The minimum and maximum sizes allowed for the GSP FW heap, in bytes.
+ allowed_heap_size: Range<u64>,
+}
+
+impl LibosParams {
+ /// Version 2 of the GSP LIBOS (Turing and GA100)
+ const LIBOS2: LibosParams = LibosParams {
+ carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2),
+ allowed_heap_size: num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB)
+ * num::usize_as_u64(SZ_1M)
+ ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB)
+ * num::usize_as_u64(SZ_1M),
+ };
+
+ /// Version 3 of the GSP LIBOS (GA102+)
+ const LIBOS3: LibosParams = LibosParams {
+ carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL),
+ allowed_heap_size: num::u32_as_u64(
+ bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB,
+ ) * num::usize_as_u64(SZ_1M)
+ ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB)
+ * num::usize_as_u64(SZ_1M),
+ };
+
+ /// Returns the libos parameters corresponding to `chipset`.
+ pub(crate) fn from_chipset(chipset: Chipset) -> &'static LibosParams {
+ if chipset < Chipset::GA102 {
+ &Self::LIBOS2
+ } else {
+ &Self::LIBOS3
+ }
+ }
+
+ /// Returns the amount of memory (in bytes) to allocate for the WPR heap for a framebuffer size
+ /// of `fb_size` (in bytes) for `chipset`.
+ pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> u64 {
+ // The WPR heap will contain the following:
+ // LIBOS carveout,
+ self.carveout_size
+ // RM boot working memory,
+ .saturating_add(GspFwHeapParams::base_rm_size(chipset))
+ // One RM client,
+ .saturating_add(GspFwHeapParams::client_alloc_size())
+ // Overhead for memory management.
+ .saturating_add(GspFwHeapParams::management_overhead(fb_size))
+ // Clamp to the supported heap sizes.
+ .clamp(self.allowed_heap_size.start, self.allowed_heap_size.end - 1)
+ }
+}
+
+/// 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(bindings::GspFwWprMeta);
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144.rs b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
index 35cb0370a7c9..82a973cd99c3 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
@@ -12,7 +12,6 @@
#![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))]
#![allow(
dead_code,
- unused_imports,
clippy::all,
clippy::undocumented_unsafe_blocks,
clippy::ptr_as_ptr,
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index cec594032515..0407000cca22 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -1 +1,126 @@
// SPDX-License-Identifier: GPL-2.0
+
+pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2: u32 = 0;
+pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL: u32 = 23068672;
+pub const GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X: u32 = 8388608;
+pub const GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB: u32 = 98304;
+pub const GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE: u32 = 100663296;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB: u32 = 64;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB: u32 = 256;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB: u32 = 88;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB: u32 = 280;
+pub type __u8 = ffi::c_uchar;
+pub type __u16 = ffi::c_ushort;
+pub type __u32 = ffi::c_uint;
+pub type __u64 = ffi::c_ulonglong;
+pub type u8_ = __u8;
+pub type u16_ = __u16;
+pub type u32_ = __u32;
+pub type u64_ = __u64;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct GspFwWprMeta {
+ pub magic: u64_,
+ pub revision: u64_,
+ pub sysmemAddrOfRadix3Elf: u64_,
+ pub sizeOfRadix3Elf: u64_,
+ pub sysmemAddrOfBootloader: u64_,
+ pub sizeOfBootloader: u64_,
+ pub bootloaderCodeOffset: u64_,
+ pub bootloaderDataOffset: u64_,
+ pub bootloaderManifestOffset: u64_,
+ pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1,
+ pub gspFwRsvdStart: u64_,
+ pub nonWprHeapOffset: u64_,
+ pub nonWprHeapSize: u64_,
+ pub gspFwWprStart: u64_,
+ pub gspFwHeapOffset: u64_,
+ pub gspFwHeapSize: u64_,
+ pub gspFwOffset: u64_,
+ pub bootBinOffset: u64_,
+ pub frtsOffset: u64_,
+ pub frtsSize: u64_,
+ pub gspFwWprEnd: u64_,
+ pub fbSize: u64_,
+ pub vgaWorkspaceOffset: u64_,
+ pub vgaWorkspaceSize: u64_,
+ pub bootCount: u64_,
+ pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2,
+ pub gspFwHeapVfPartitionCount: u8_,
+ pub flags: u8_,
+ pub padding: [u8_; 2usize],
+ pub pmuReservedSize: u32_,
+ pub verified: u64_,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union GspFwWprMeta__bindgen_ty_1 {
+ pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1__bindgen_ty_1,
+ pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_1__bindgen_ty_2,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_1 {
+ pub sysmemAddrOfSignature: u64_,
+ pub sizeOfSignature: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_2 {
+ pub gspFwHeapFreeListWprOffset: u32_,
+ pub unused0: u32_,
+ pub unused1: u64_,
+}
+impl Default for GspFwWprMeta__bindgen_ty_1 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union GspFwWprMeta__bindgen_ty_2 {
+ pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_2__bindgen_ty_1,
+ pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2__bindgen_ty_2,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 {
+ pub partitionRpcAddr: u64_,
+ pub partitionRpcRequestOffset: u16_,
+ pub partitionRpcReplyOffset: u16_,
+ pub elfCodeOffset: u32_,
+ pub elfDataOffset: u32_,
+ pub elfCodeSize: u32_,
+ pub elfDataSize: u32_,
+ pub lsUcodeVersion: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_2 {
+ pub partitionRpcPadding: [u32_; 4usize],
+ pub sysmemAddrOfCrashReportQueue: u64_,
+ pub sizeOfCrashReportQueue: u32_,
+ pub lsUcodeVersionPadding: [u32_; 1usize],
+}
+impl Default for GspFwWprMeta__bindgen_ty_2 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+impl Default for GspFwWprMeta {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 02/16] gpu: nova-core: Set correct DMA mask
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 01/16] gpu: nova-core: compute layout of more framebuffer regions required for GSP Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 03/16] gpu: nova-core: num: add functions to safely convert a const value to a smaller type Alexandre Courbot
` (14 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
From: Alistair Popple <apopple@nvidia.com>
Set the correct DMA mask. Without this DMA will fail on some setups.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/driver.rs | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 2509f75eccb9..d91bbc50cde7 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -4,6 +4,8 @@
auxiliary,
c_str,
device::Core,
+ dma::Device,
+ dma::DmaMask,
pci,
pci::{
Class,
@@ -25,6 +27,15 @@ pub(crate) struct NovaCore {
}
const BAR0_SIZE: usize = SZ_16M;
+
+// For now we only support Ampere which can use up to 47-bit DMA addresses.
+//
+// TODO: Add an abstraction for this to support newer GPUs which may support
+// larger DMA addresses. Limiting these GPUs to smaller address widths won't
+// have any adverse affects, unless installed on systems which require larger
+// DMA addresses. These systems should be quite rare.
+const GPU_DMA_BITS: u32 = 47;
+
pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>;
kernel::pci_device_table!(
@@ -62,6 +73,11 @@ fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self
pdev.enable_device_mem()?;
pdev.set_master();
+ // SAFETY: No concurrent DMA allocations or mappings can be made because
+ // the device is still being probed and therefore isn't being used by
+ // other threads of execution.
+ unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<GPU_DMA_BITS>())? };
+
let devres_bar = Arc::pin_init(
pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0")),
GFP_KERNEL,
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 03/16] gpu: nova-core: num: add functions to safely convert a const value to a smaller type
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 01/16] gpu: nova-core: compute layout of more framebuffer regions required for GSP Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 02/16] gpu: nova-core: Set correct DMA mask Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 04/16] gpu: nova-core: Create initial Gsp Alexandre Courbot
` (13 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
There are times where we need to store a constant value defined as a
larger type (e.g. through a binding) into a smaller type, knowing
that the value will fit. Rust, unfortunately, only provides us with the
`as` operator for that purpose, the use of which is discouraged as it
silently strips data.
Extend the `num` module with functions allowing to perform the
conversion infallibly, at compile time.
Example:
const FOO_VALUE: u32 = 1;
// `FOO_VALUE` fits into a `u8`, so the conversion is valid.
let foo = num::u32_to_u8::<{ FOO_VALUE }>();
We are going to use this feature extensively in Nova.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/num.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/drivers/gpu/nova-core/num.rs b/drivers/gpu/nova-core/num.rs
index 92a91b9e30de..f3740ab6cb9d 100644
--- a/drivers/gpu/nova-core/num.rs
+++ b/drivers/gpu/nova-core/num.rs
@@ -163,3 +163,54 @@ fn into_safe_cast(self) -> T {
T::from_safe_cast(self)
}
}
+
+macro_rules! impl_const_into {
+ ($from:ty => { $($into:ty),* }) => {
+ $(
+ paste! {
+ #[doc = ::core::concat!(
+ "Performs a build-time safe conversion of a [`",
+ ::core::stringify!($from),
+ "`] constant value into a [`",
+ ::core::stringify!($into),
+ "`].")]
+ ///
+ /// This checks at compile-time that the conversion is lossless, and triggers a build
+ /// error if it isn't.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num;
+ ///
+ /// // Succeeds because the value of the source fits into the destination's type.
+ #[doc = ::core::concat!(
+ "assert_eq!(num::",
+ ::core::stringify!($from),
+ "_into_",
+ ::core::stringify!($into),
+ "(1",
+ ::core::stringify!($from),
+ "), 1",
+ ::core::stringify!($into),
+ ");")]
+ /// ```
+ #[allow(unused)]
+ pub(crate) const fn [<$from _into_ $into>]<const N: $from>() -> $into {
+ // Make sure that the target type is smaller than the source one.
+ static_assert!($from::BITS >= $into::BITS);
+ // CAST: we statically enforced above that `$from` is larger than `$into`, so the
+ // `as` conversion will be lossless.
+ build_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from);
+
+ N as $into
+ }
+ }
+ )*
+ };
+}
+
+impl_const_into!(usize => { u8, u16, u32 });
+impl_const_into!(u64 => { u8, u16, u32 });
+impl_const_into!(u32 => { u8, u16 });
+impl_const_into!(u16 => { u8 });
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 04/16] gpu: nova-core: Create initial Gsp
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (2 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 03/16] gpu: nova-core: num: add functions to safely convert a const value to a smaller type Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 05/16] gpu: nova-core: gsp: Create wpr metadata Alexandre Courbot
` (12 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
From: Alistair Popple <apopple@nvidia.com>
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.
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 2 +-
drivers/gpu/nova-core/gsp.rs | 122 ++++++++++++++++++++--
drivers/gpu/nova-core/gsp/fw.rs | 60 +++++++++++
drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 19 ++++
4 files changed, 194 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 802e71e4f97d..03dae437bc37 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -231,7 +231,7 @@ pub(crate) fn new<'a>(
sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
- gsp <- Gsp::new(),
+ gsp <- Gsp::new(pdev)?,
_: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index aa2a9e6654e4..ea6a1f053780 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -2,23 +2,129 @@
mod boot;
+use kernel::device;
+use kernel::dma::CoherentAllocation;
+use kernel::dma::DmaAddress;
+use kernel::dma_write;
+use kernel::pci;
use kernel::prelude::*;
+use kernel::transmute::AsBytes;
mod fw;
+use fw::LibosMemoryRegionInitArgument;
+
pub(crate) use fw::{GspFwWprMeta, LibosParams};
+use crate::num;
+
pub(crate) const GSP_PAGE_SHIFT: usize = 12;
pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT;
-/// GSP runtime data.
-///
-/// This is an empty pinned placeholder for now.
-#[pin_data]
-pub(crate) struct Gsp {}
+/// Number of GSP pages to use in a RM log buffer.
+const RM_LOG_BUFFER_NUM_PAGES: usize = 0x10;
-impl Gsp {
- pub(crate) fn new() -> impl PinInit<Self> {
- pin_init!(Self {})
+/// Array of page table entries, as understood by the GSP bootloader.
+#[repr(C)]
+struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]);
+
+/// SAFETY: arrays of `u64` implement `AsBytes` and we are but a wrapper around one.
+unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {}
+
+impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> {
+ /// Creates a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`.
+ fn new(start: DmaAddress) -> Result<Self> {
+ let mut ptes = [0u64; NUM_PAGES];
+ for (i, pte) in ptes.iter_mut().enumerate() {
+ *pte = start
+ .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT)
+ .ok_or(EOVERFLOW)?;
+ }
+
+ Ok(Self(ptes))
+ }
+}
+
+/// The logging buffers are byte queues that contain encoded printf-like
+/// messages from GSP-RM. They need to be decoded by a special application
+/// that can parse the buffers.
+///
+/// The 'loginit' buffer contains logs from early GSP-RM init and
+/// exception dumps. The 'logrm' buffer contains the subsequent logs. Both are
+/// written to directly by GSP-RM and can be any multiple of GSP_PAGE_SIZE.
+///
+/// The physical address map for the log buffer is stored in the buffer
+/// itself, starting with offset 1. Offset 0 contains the "put" pointer (pp).
+/// Initially, pp is equal to 0. If the buffer has valid logging data in it,
+/// then pp points to index into the buffer where the next logging entry will
+/// be written. Therefore, the logging data is valid if:
+/// 1 <= pp < sizeof(buffer)/sizeof(u64)
+struct LogBuffer(CoherentAllocation<u8>);
+
+impl LogBuffer {
+ /// Creates a new `LogBuffer` mapped on `dev`.
+ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
+ const NUM_PAGES: usize = RM_LOG_BUFFER_NUM_PAGES;
+
+ let mut obj = Self(CoherentAllocation::<u8>::alloc_coherent(
+ dev,
+ NUM_PAGES * GSP_PAGE_SIZE,
+ GFP_KERNEL | __GFP_ZERO,
+ )?);
+ let ptes = PteArray::<NUM_PAGES>::new(obj.0.dma_handle())?;
+
+ // SAFETY: `obj` has just been created and we are its sole user.
+ unsafe {
+ // Copy the self-mapping PTE at the expected location.
+ obj.0
+ .as_slice_mut(size_of::<u64>(), size_of_val(&ptes))?
+ .copy_from_slice(ptes.as_bytes())
+ };
+
+ Ok(obj)
+ }
+}
+
+/// GSP runtime data.
+#[pin_data]
+pub(crate) struct Gsp {
+ /// Libos arguments.
+ pub(crate) libos: CoherentAllocation<LibosMemoryRegionInitArgument>,
+ /// Init log buffer.
+ loginit: LogBuffer,
+ /// Interrupts log buffer.
+ logintr: LogBuffer,
+ /// RM log buffer.
+ logrm: LogBuffer,
+}
+
+impl Gsp {
+ // Creates an in-place initializer for a `Gsp` manager for `pdev`.
+ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<impl PinInit<Self, Error>> {
+ let dev = pdev.as_ref();
+ let libos = CoherentAllocation::<LibosMemoryRegionInitArgument>::alloc_coherent(
+ dev,
+ GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(),
+ GFP_KERNEL | __GFP_ZERO,
+ )?;
+
+ // Initialise the logging structures. The OpenRM equivalents are in:
+ // _kgspInitLibosLoggingStructures (allocates memory for buffers)
+ // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array)
+ let loginit = LogBuffer::new(dev)?;
+ dma_write!(libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?;
+
+ let logintr = LogBuffer::new(dev)?;
+ dma_write!(libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?;
+
+ let logrm = LogBuffer::new(dev)?;
+ dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?;
+
+ Ok(try_pin_init!(Self {
+ libos,
+ loginit,
+ logintr,
+ logrm,
+ }))
}
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 436c00d07b16..458b5610061f 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -8,11 +8,16 @@
use core::ops::Range;
use kernel::{
+ dma::CoherentAllocation,
ptr::{
Alignable,
Alignment, //
},
sizes::SZ_1M,
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
};
use crate::{
@@ -116,3 +121,58 @@ pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> u64 {
/// addresses of the GSP bootloader and firmware.
#[repr(transparent)]
pub(crate) struct GspFwWprMeta(bindings::GspFwWprMeta);
+
+/// Struct containing the arguments required to pass a memory buffer to the GSP
+/// for use during initialisation.
+///
+/// The GSP only understands 4K pages (GSP_PAGE_SIZE), so even if the kernel is
+/// configured for a larger page size (e.g. 64K pages), we need to give
+/// the GSP an array of 4K pages. Since we only create physically contiguous
+/// buffers the math to calculate the addresses is simple.
+///
+/// The buffers must be a multiple of GSP_PAGE_SIZE. GSP-RM also currently
+/// ignores the @kind field for LOGINIT, LOGINTR, and LOGRM, but expects the
+/// buffers to be physically contiguous anyway.
+///
+/// The memory allocated for the arguments must remain until the GSP sends the
+/// init_done RPC.
+#[repr(transparent)]
+pub(crate) struct LibosMemoryRegionInitArgument(bindings::LibosMemoryRegionInitArgument);
+
+// SAFETY: Padding is explicit and does 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 {}
+
+impl LibosMemoryRegionInitArgument {
+ pub(crate) fn new<A: AsBytes + FromBytes>(
+ name: &'static str,
+ obj: &CoherentAllocation<A>,
+ ) -> Self {
+ /// 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)
+ }
+
+ Self(bindings::LibosMemoryRegionInitArgument {
+ id8: id8(name),
+ pa: obj.dma_handle(),
+ size: num::usize_as_u64(obj.size()),
+ kind: num::u32_into_u8::<
+ { bindings::LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS },
+ >(),
+ loc: num::u32_into_u8::<
+ { bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM },
+ >(),
+ ..Default::default()
+ })
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index 0407000cca22..6a14cc324391 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/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.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 05/16] gpu: nova-core: gsp: Create wpr metadata
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (3 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 04/16] gpu: nova-core: Create initial Gsp Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 06/16] gpu: nova-core: Add a slice-buffer (sbuffer) datastructure Alexandre Courbot
` (11 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
From: Alistair Popple <apopple@nvidia.com>
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.
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/fb.rs | 1 -
drivers/gpu/nova-core/firmware/gsp.rs | 3 +-
drivers/gpu/nova-core/firmware/riscv.rs | 6 +--
drivers/gpu/nova-core/gsp/boot.rs | 7 +++
drivers/gpu/nova-core/gsp/fw.rs | 61 ++++++++++++++++++++++-
drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 2 +
6 files changed, 73 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index 156d9bf1f191..3c9cf151786c 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -98,7 +98,6 @@ pub(crate) fn unregister(&self, bar: &Bar0) {
///
/// 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/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index 471ace238f62..0549805282ab 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -145,7 +145,7 @@ pub(crate) struct GspFirmware {
/// Size in bytes of the firmware contained in [`Self::fw`].
pub(crate) size: usize,
/// Device-mapped GSP signatures matching the GPU's [`Chipset`].
- signatures: DmaObject,
+ pub(crate) signatures: DmaObject,
/// GSP bootloader, verifies the GSP firmware before loading and running it.
pub(crate) bootloader: RiscvFirmware,
}
@@ -231,7 +231,6 @@ pub(crate) fn new<'a, 'b>(
}))
}
- #[expect(unused)]
/// Returns the DMA handle of the radix3 level 0 page table.
pub(crate) fn radix3_dma_handle(&self) -> DmaAddress {
self.level0.dma_handle()
diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs
index 3838fab8f1c0..7d82fb9876e8 100644
--- a/drivers/gpu/nova-core/firmware/riscv.rs
+++ b/drivers/gpu/nova-core/firmware/riscv.rs
@@ -60,11 +60,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(crate) code_offset: u32,
/// Offset at which the data starts in the firmware image.
- data_offset: u32,
+ pub(crate) data_offset: u32,
/// Offset at which the manifest starts in the firmware image.
- manifest_offset: u32,
+ pub(crate) manifest_offset: u32,
/// Application version.
app_version: u32,
/// Device-mapped firmware image.
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 979d3391e58c..5ea53250bf37 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -2,6 +2,8 @@
use kernel::{
device,
+ dma::CoherentAllocation,
+ dma_write,
pci,
prelude::*, //
};
@@ -27,6 +29,7 @@
FIRMWARE_VERSION, //
},
gpu::Chipset,
+ gsp::GspFwWprMeta,
regs,
vbios::Vbios,
};
@@ -146,6 +149,10 @@ pub(crate) fn boot(
bar,
)?;
+ let wpr_meta =
+ CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
+ dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
+
Ok(())
}
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 458b5610061f..a6ee52475bdb 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -13,7 +13,10 @@
Alignable,
Alignment, //
},
- sizes::SZ_1M,
+ sizes::{
+ SZ_128K,
+ SZ_1M, //
+ },
transmute::{
AsBytes,
FromBytes, //
@@ -21,6 +24,8 @@
};
use crate::{
+ fb::FbLayout,
+ firmware::gsp::GspFirmware,
gpu::Chipset,
num::{
self,
@@ -122,6 +127,60 @@ pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> u64 {
#[repr(transparent)]
pub(crate) struct GspFwWprMeta(bindings::GspFwWprMeta);
+// SAFETY: Padding is explicit and does 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 {}
+
+type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1;
+type GspFwWprMetaBootInfo = r570_144::GspFwWprMeta__bindgen_ty_1__bindgen_ty_1;
+
+impl GspFwWprMeta {
+ /// Fill in and return a `GspFwWprMeta` suitable for booting `gsp_firmware` using the
+ /// `fb_layout` layout.
+ pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self {
+ Self(bindings::GspFwWprMeta {
+ // CAST: we want to store the bits of `GSP_FW_WPR_META_MAGIC` unmodified.
+ magic: r570_144::GSP_FW_WPR_META_MAGIC as u64,
+ revision: u64::from(r570_144::GSP_FW_WPR_META_REVISION),
+ sysmemAddrOfRadix3Elf: gsp_firmware.radix3_dma_handle(),
+ sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size),
+ sysmemAddrOfBootloader: gsp_firmware.bootloader.ucode.dma_handle(),
+ sizeOfBootloader: u64::from_safe_cast(gsp_firmware.bootloader.ucode.size()),
+ bootloaderCodeOffset: u64::from(gsp_firmware.bootloader.code_offset),
+ bootloaderDataOffset: u64::from(gsp_firmware.bootloader.data_offset),
+ bootloaderManifestOffset: u64::from(gsp_firmware.bootloader.manifest_offset),
+ __bindgen_anon_1: GspFwWprMetaBootResumeInfo {
+ __bindgen_anon_1: GspFwWprMetaBootInfo {
+ sysmemAddrOfSignature: gsp_firmware.signatures.dma_handle(),
+ sizeOfSignature: u64::from_safe_cast(gsp_firmware.signatures.size()),
+ },
+ },
+ 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()
+ })
+ }
+}
+
/// Struct containing the arguments required to pass a memory buffer to the GSP
/// for use during initialisation.
///
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index 6a14cc324391..392b25dc6991 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/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.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 06/16] gpu: nova-core: Add a slice-buffer (sbuffer) datastructure
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (4 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 05/16] gpu: nova-core: gsp: Create wpr metadata Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 07/16] gpu: nova-core: Add zeroable trait to bindings Alexandre Courbot
` (10 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot, Lyude Paul
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"hello";
let result = sbuffer.write(data);
Reviewed-by: Lyude Paul <lyude@redhat.com>
Co-developed-by: Alistair Popple <apopple@nvidia.com>
Signed-off-by: Alistair Popple <apopple@nvidia.com>
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/sbuffer.rs | 230 +++++++++++++++++++++++++++++++++++++
2 files changed, 231 insertions(+)
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 9180ec9c27ef..c1121e7c64c5 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -15,6 +15,7 @@
mod gsp;
mod num;
mod regs;
+mod sbuffer;
mod vbios;
pub(crate) const MODULE_NAME: &kernel::str::CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
diff --git a/drivers/gpu/nova-core/sbuffer.rs b/drivers/gpu/nova-core/sbuffer.rs
new file mode 100644
index 000000000000..bfd64fc21bbb
--- /dev/null
+++ b/drivers/gpu/nova-core/sbuffer.rs
@@ -0,0 +1,230 @@
+// 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
+/// of the same length as a single stream-like read/write buffer.
+///
+/// # Examples
+///
+/// ```
+// let mut buf1 = [0u8; 5];
+/// let mut buf2 = [0u8; 5];
+/// let mut sbuffer = SBufferIter::new_writer([&mut buf1[..], &mut buf2[..]]);
+///
+/// let data = b"hi world!";
+/// sbuffer.write_all(data)?;
+/// drop(sbuffer);
+///
+/// assert_eq!(buf1, *b"hi wo");
+/// assert_eq!(buf2, *b"rld!\0");
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub(crate) struct SBufferIter<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> SBufferIter<I>
+where
+ I: Iterator,
+{
+ /// Creates a reader buffer for a discontiguous set of byte slices.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let buf1: [u8; 5] = [0, 1, 2, 3, 4];
+ /// let buf2: [u8; 5] = [5, 6, 7, 8, 9];
+ /// let sbuffer = SBufferIter::new_reader([&buf1[..], &buf2[..]]);
+ /// let sum: u8 = sbuffer.sum();
+ /// assert_eq!(sum, 45);
+ /// ```
+ #[expect(unused)]
+ pub(crate) fn new_reader(slices: impl IntoIterator<IntoIter = I>) -> Self
+ where
+ I: Iterator<Item = &'a [u8]>,
+ {
+ Self::new(slices)
+ }
+
+ /// Creates a writeable buffer for a discontiguous set of byte slices.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let mut buf1 = [0u8; 5];
+ /// let mut buf2 = [0u8; 5];
+ /// let mut sbuffer = SBufferIter::new_writer([&mut buf1[..], &mut buf2[..]]);
+ /// sbuffer.write_all(&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9][..])?;
+ /// drop(sbuffer);
+ /// assert_eq!(buf1, [0, 1, 2, 3, 4]);
+ /// assert_eq!(buf2, [5, 6, 7, 8, 9]);
+ ///
+ /// ```
+ #[expect(unused)]
+ 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.
+ cur_slice: slices.find(|s| !s.deref().is_empty()),
+ slices,
+ }
+ }
+
+ /// 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.
+ ///
+ /// The closure `f` should split the slice received in it's first parameter
+ /// at the position given in the second parameter.
+ 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.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)
+ }
+ }
+ }
+ }
+
+ /// Returns whether this buffer still has data available.
+ pub(crate) fn is_empty(&self) -> bool {
+ self.cur_slice.is_none()
+ }
+}
+
+/// Provides a way to get non-mutable slices of data to read from.
+impl<'a, I> SBufferIter<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(EINVAL),
+ 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 flush_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> SBufferIter<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`.
+ #[expect(unused)]
+ 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 SBufferIter<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.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 07/16] gpu: nova-core: Add zeroable trait to bindings
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (5 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 06/16] gpu: nova-core: Add a slice-buffer (sbuffer) datastructure Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 08/16] rust: enable slice_flatten feature and provide it through an extension trait Alexandre Courbot
` (9 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
From: Alistair Popple <apopple@nvidia.com>
Derive the Zeroable trait for existing bindgen generated bindings. This
is safe because all bindgen generated types are simple integer types for
which any bit pattern, including all zeros, is valid.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gsp/fw/r570_144.rs | 1 +
drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 16 ++++++++--------
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144.rs b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
index 82a973cd99c3..4f5c65ac1eb9 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
@@ -25,4 +25,5 @@
unsafe_op_in_unsafe_fn
)]
use kernel::ffi;
+use kernel::prelude::Zeroable;
include!("r570_144/bindings.rs");
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index 392b25dc6991..f7b38978c5f8 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -20,7 +20,7 @@
pub type u32_ = __u32;
pub type u64_ = __u64;
#[repr(C)]
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Zeroable)]
pub struct GspFwWprMeta {
pub magic: u64_,
pub revision: u64_,
@@ -55,19 +55,19 @@ pub struct GspFwWprMeta {
pub verified: u64_,
}
#[repr(C)]
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Zeroable)]
pub union GspFwWprMeta__bindgen_ty_1 {
pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1__bindgen_ty_1,
pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_1__bindgen_ty_2,
}
#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_1 {
pub sysmemAddrOfSignature: u64_,
pub sizeOfSignature: u64_,
}
#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_2 {
pub gspFwHeapFreeListWprOffset: u32_,
pub unused0: u32_,
@@ -83,13 +83,13 @@ fn default() -> Self {
}
}
#[repr(C)]
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Zeroable)]
pub union GspFwWprMeta__bindgen_ty_2 {
pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_2__bindgen_ty_1,
pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2__bindgen_ty_2,
}
#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 {
pub partitionRpcAddr: u64_,
pub partitionRpcRequestOffset: u16_,
@@ -101,7 +101,7 @@ pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 {
pub lsUcodeVersion: u32_,
}
#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_2 {
pub partitionRpcPadding: [u32_; 4usize],
pub sysmemAddrOfCrashReportQueue: u64_,
@@ -136,7 +136,7 @@ fn default() -> Self {
pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_FB: LibosMemoryRegionLoc = 2;
pub type LibosMemoryRegionLoc = ffi::c_uint;
#[repr(C)]
-#[derive(Debug, Default, Copy, Clone)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
pub struct LibosMemoryRegionInitArgument {
pub id8: LibosAddress,
pub pa: LibosAddress,
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 08/16] rust: enable slice_flatten feature and provide it through an extension trait
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (6 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 07/16] gpu: nova-core: Add zeroable trait to bindings Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 09/16] gpu: nova-core: gsp: Add GSP command queue bindings and handling Alexandre Courbot
` (8 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
In Rust 1.80, the previously unstable `slice::flatten` family of methods
have been stabilized and renamed to `slice::as_flattened`.
This creates an issue as we want to use `as_flattened`, but need to
support the MSRV (which at the moment is Rust 1.78) where it is named
`flatten`.
Solve this by enabling the `slice_flatten` feature, and providing an
`as_flattened` implementation through an extension trait for compiler
versions where it is not available.
The trait is then exported from the prelude, making the `as_flattened`
family of methods transparently available for all supported compiler
versions.
This extension trait can be removed once the MSRV passes 1.80.
Suggested-by: Miguel Ojeda <ojeda@kernel.org>
Link: https://lore.kernel.org/all/CANiq72kK4pG=O35NwxPNoTO17oRcg1yfGcvr3==Fi4edr+sfmw@mail.gmail.com/
Acked-by: Danilo Krummrich <dakr@kernel.org>
Acked-by: Miguel Ojeda <ojeda@kernel.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
init/Kconfig | 3 +++
rust/kernel/lib.rs | 4 ++++
rust/kernel/prelude.rs | 3 +++
rust/kernel/slice.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 59 insertions(+)
diff --git a/init/Kconfig b/init/Kconfig
index cab3ad28ca49..7da93c9cccc3 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -147,6 +147,9 @@ config LD_CAN_USE_KEEP_IN_OVERLAY
# https://github.com/llvm/llvm-project/pull/130661
def_bool LD_IS_BFD || LLD_VERSION >= 210000
+config RUSTC_HAS_SLICE_AS_FLATTENED
+ def_bool RUSTC_VERSION >= 108000
+
config RUSTC_HAS_COERCE_POINTEE
def_bool RUSTC_VERSION >= 108400
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 3dd7bebe7888..2581a356d114 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -21,6 +21,9 @@
#![feature(inline_const)]
#![feature(pointer_is_aligned)]
//
+// Stable since Rust 1.80.0.
+#![feature(slice_flatten)]
+//
// Stable since Rust 1.81.0.
#![feature(lint_reasons)]
//
@@ -128,6 +131,7 @@
pub mod security;
pub mod seq_file;
pub mod sizes;
+pub mod slice;
mod static_assert;
#[doc(hidden)]
pub mod std_vendor;
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 198d09a31449..9ee8acc563de 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -51,3 +51,6 @@
pub use super::current;
pub use super::uaccess::UserPtr;
+
+#[cfg(not(CONFIG_RUSTC_HAS_SLICE_AS_FLATTENED))]
+pub use super::slice::AsFlattened;
diff --git a/rust/kernel/slice.rs b/rust/kernel/slice.rs
new file mode 100644
index 000000000000..6ca91a4fd1f2
--- /dev/null
+++ b/rust/kernel/slice.rs
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Additional (and temporary) slice helpers.
+
+/// Extension trait providing a portable version of [`as_flattened`] and
+/// [`as_flattened_mut`].
+///
+/// In Rust 1.80, the previously unstable `slice::flatten` family of methods
+/// have been stabilized and renamed from `flatten` to `as_flattened`.
+///
+/// This creates an issue for as long as the MSRV is < 1.80, as the same functionality is provided
+/// by different methods depending on the compiler version.
+///
+/// This extension trait solves this by abstracting `as_flatten` and calling the correct method
+/// depending on the Rust version.
+///
+/// This trait can be removed once the MSRV passes 1.80.
+///
+/// [`as_flattened`]: slice::as_flattened
+/// [`as_flattened_mut`]: slice::as_flattened_mut
+#[cfg(not(CONFIG_RUSTC_HAS_SLICE_AS_FLATTENED))]
+pub trait AsFlattened<T> {
+ /// Takes a `&[[T; N]]` and flattens it to a `&[T]`.
+ ///
+ /// This is an portable layer on top of [`as_flattened`]; see its documentation for details.
+ ///
+ /// [`as_flattened`]: slice::as_flattened
+ fn as_flattened(&self) -> &[T];
+
+ /// Takes a `&mut [[T; N]]` and flattens it to a `&mut [T]`.
+ ///
+ /// This is an portable layer on top of [`as_flattened_mut`]; see its documentation for details.
+ ///
+ /// [`as_flattened_mut`]: slice::as_flattened_mut
+ fn as_flattened_mut(&mut self) -> &mut [T];
+}
+
+#[cfg(not(CONFIG_RUSTC_HAS_SLICE_AS_FLATTENED))]
+impl<T, const N: usize> AsFlattened<T> for [[T; N]] {
+ #[allow(clippy::incompatible_msrv)]
+ fn as_flattened(&self) -> &[T] {
+ self.flatten()
+ }
+
+ #[allow(clippy::incompatible_msrv)]
+ fn as_flattened_mut(&mut self) -> &mut [T] {
+ self.flatten_mut()
+ }
+}
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 09/16] gpu: nova-core: gsp: Add GSP command queue bindings and handling
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (7 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 08/16] rust: enable slice_flatten feature and provide it through an extension trait Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 10/16] gpu: nova-core: gsp: Create rmargs Alexandre Courbot
` (7 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
From: Alistair Popple <apopple@nvidia.com>
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.
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/gsp.rs | 8 +
drivers/gpu/nova-core/gsp/cmdq.rs | 584 ++++++++++++++++++++++
drivers/gpu/nova-core/gsp/fw.rs | 335 ++++++++++++-
drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 409 +++++++++++++++
drivers/gpu/nova-core/regs.rs | 4 +
drivers/gpu/nova-core/sbuffer.rs | 2 -
6 files changed, 1339 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index ea6a1f053780..46b3e4819473 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -10,6 +10,9 @@
use kernel::prelude::*;
use kernel::transmute::AsBytes;
+use crate::gsp::cmdq::Cmdq;
+
+pub(crate) mod cmdq;
mod fw;
use fw::LibosMemoryRegionInitArgument;
@@ -96,6 +99,8 @@ pub(crate) struct Gsp {
logintr: LogBuffer,
/// RM log buffer.
logrm: LogBuffer,
+ /// Command queue.
+ pub(crate) cmdq: Cmdq,
}
impl Gsp {
@@ -120,11 +125,14 @@ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<impl PinInit<Self
let logrm = LogBuffer::new(dev)?;
dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?;
+ let cmdq = Cmdq::new(dev)?;
+
Ok(try_pin_init!(Self {
libos,
loginit,
logintr,
logrm,
+ cmdq,
}))
}
}
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
new file mode 100644
index 000000000000..8461738b1110
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -0,0 +1,584 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use core::{
+ cmp,
+ mem,
+ sync::atomic::{
+ fence,
+ Ordering, //
+ }, //
+};
+
+use kernel::{
+ device,
+ dma::CoherentAllocation,
+ dma_write,
+ io::poll::read_poll_timeout,
+ prelude::*,
+ sync::aref::ARef,
+ time::Delta,
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
+};
+
+use crate::{
+ driver::Bar0,
+ gsp::{
+ fw::{
+ GspMsgElement,
+ MsgFunction,
+ MsgqRxHeader,
+ MsgqTxHeader, //
+ },
+ PteArray,
+ GSP_PAGE_SIZE, //
+ },
+ num,
+ regs,
+ sbuffer::SBufferIter, //
+};
+
+/// Trait implemented by types representing a command to send to the GSP.
+///
+/// The main purpose of this trait is to provide [`Cmdq::send_gsp_command`] with the information it
+/// needs to send a given command.
+///
+/// The [`CommandToGsp::init`] in particular is responsible for initializing the command directly
+/// into the space reserved for it in the command queue buffer.
+///
+/// Some commands may be followed by a variable-length payload. For these, the
+/// [`CommandToGsp::variable_payload_len`] and [`CommandToGsp::init_variable_payload`] need to be
+/// defined as well.
+pub(crate) trait CommandToGsp {
+ /// Function identifying this command to the GSP.
+ const FUNCTION: MsgFunction;
+
+ /// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer.
+ type Command: FromBytes + AsBytes;
+
+ /// Error returned by [`CommandToGsp::init`].
+ type InitError;
+
+ /// In-place command initializer responsible for filling the command in the command queue
+ /// buffer.
+ fn init(&self) -> impl Init<Self::Command, Self::InitError>;
+
+ /// Size of the variable-length payload following the command structure generated by
+ /// [`CommandToGsp::init`].
+ ///
+ /// Most commands don't have a variable-length payload, so this is zero by default.
+ fn variable_payload_len(&self) -> usize {
+ 0
+ }
+
+ /// Method initializing the variable-length payload.
+ ///
+ /// The command buffer is circular, which means that we may need to jump back to its beginning
+ /// while in the middle of a command. For this reason, the variable-length payload is
+ /// initialized using a [`SBufferIter`].
+ ///
+ /// This method will receive a buffer of the length returned by
+ /// [`CommandToGsp::variable_payload_len`], and must write every single byte of it. Leaving
+ /// unwritten space will lead to an error.
+ ///
+ /// Most commands don't have a variable-length payload, so this does nothing by default.
+ fn init_variable_payload(
+ &self,
+ _dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
+ ) -> Result {
+ Ok(())
+ }
+}
+
+// Trait for messages received from the GSP.
+pub(crate) trait MessageFromGsp: Sized + FromBytes + AsBytes {
+ /// Function identifying this message from the GSP.
+ const FUNCTION: MsgFunction;
+}
+
+/// Number of GSP pages making the [`Msgq`].
+pub(crate) const MSGQ_NUM_PAGES: u32 = 0x3f;
+
+/// Circular buffer of a [`Msgq`].
+///
+/// This area of memory is to be shared between the driver and the GSP to exchange commands or
+/// messages.
+#[repr(C, align(0x1000))]
+#[derive(Debug)]
+struct MsgqData {
+ data: [[u8; GSP_PAGE_SIZE]; num::u32_as_usize(MSGQ_NUM_PAGES)],
+}
+
+// Annoyingly we are forced to use a literal to specify the alignment of
+// `MsgqData`, so check that it corresponds to the actual GSP page size here.
+static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);
+
+/// Unidirectional message queue.
+///
+/// Contains the data for a message queue, that either the driver or GSP writes to.
+///
+/// Note that while the write pointer of `tx` corresponds to the `msgq` of the same instance, the
+/// read pointer of `rx` actually refers to the `Msgq` owned by the other side.
+/// This design ensures that only the driver or GSP ever writes to a given instance of this struct.
+#[repr(C)]
+// There is no struct defined for this in the open-gpu-kernel-source headers.
+// Instead it is defined by code in `GspMsgQueuesInit()`.
+struct Msgq {
+ /// Header for sending messages, including the write pointer.
+ tx: MsgqTxHeader,
+ /// Header for receiving messages, including the read pointer.
+ rx: MsgqRxHeader,
+ /// The message queue proper.
+ msgq: MsgqData,
+}
+
+/// Structure shared between the driver and the GSP and containing the command and message queues.
+#[repr(C)]
+struct GspMem {
+ /// Self-mapping page table entries.
+ ptes: PteArray<{ GSP_PAGE_SIZE / size_of::<u64>() }>,
+ /// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the
+ /// write and read pointers that the CPU updates.
+ ///
+ /// This member is read-only for the GSP.
+ cpuq: Msgq,
+ /// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the
+ /// write and read pointers that the GSP updates.
+ ///
+ /// This member is read-only for the driver.
+ 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 {}
+
+/// Wrapper around [`GspMem`] to share it with the GPU using a [`CoherentAllocation`].
+///
+/// This provides the low-level functionality to communicate with the GSP, including allocation of
+/// queue space to write messages to and management of read/write pointers.
+///
+/// This is shared with the GSP, with clear ownership rules regarding the command queues:
+///
+/// * The driver owns (i.e. can write to) the part of the CPU message queue between the CPU write
+/// pointer and the GSP read pointer. This region is returned by [`Self::driver_write_area`].
+/// * The driver owns (i.e. can read from) the part of the GSP message queue between the CPU read
+/// pointer and the GSP write pointer. This region is returned by [`Self::driver_read_area`].
+struct DmaGspMem(CoherentAllocation<GspMem>);
+
+impl DmaGspMem {
+ /// Allocate a new instance and map it for `dev`.
+ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
+ const MSGQ_SIZE: u32 = num::usize_into_u32::<{ size_of::<Msgq>() }>();
+ const RX_HDR_OFF: u32 = num::usize_into_u32::<{ mem::offset_of!(Msgq, rx) }>();
+
+ let gsp_mem =
+ CoherentAllocation::<GspMem>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
+ dma_write!(gsp_mem[0].ptes = PteArray::new(gsp_mem.dma_handle())?)?;
+ dma_write!(gsp_mem[0].cpuq.tx = MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES))?;
+ dma_write!(gsp_mem[0].cpuq.rx = MsgqRxHeader::new())?;
+
+ Ok(Self(gsp_mem))
+ }
+
+ /// Returns the region of the CPU message queue that the driver is currently allowed to write
+ /// to.
+ ///
+ /// As the message queue is a circular buffer, the region may be discontiguous in memory. In
+ /// that case the second slice will have a non-zero length.
+ fn driver_write_area(&mut self) -> (&mut [[u8; GSP_PAGE_SIZE]], &mut [[u8; GSP_PAGE_SIZE]]) {
+ let tx = self.cpu_write_ptr() as usize;
+ let rx = self.gsp_read_ptr() as usize;
+
+ // SAFETY:
+ // - The `CoherentAllocation` contains exactly one object.
+ // - We will only access the driver-owned part of the shared memory.
+ // - Per the safety statement of the function, no concurrent access will be performed.
+ let gsp_mem = &mut unsafe { self.0.as_slice_mut(0, 1) }.unwrap()[0];
+ let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx);
+
+ if rx <= tx {
+ // The area from `tx` up to the end of the ring, and from the beginning of the ring up
+ // to `rx`, minus one unit, belongs to the driver.
+ if rx == 0 {
+ let last = after_tx.len() - 1;
+ (&mut after_tx[..last], &mut before_tx[0..0])
+ } else {
+ (after_tx, &mut before_tx[..rx])
+ }
+ } else {
+ // The area from `tx` to `rx`, minus one unit, belongs to the driver.
+ (after_tx.split_at_mut(rx - tx).0, &mut before_tx[0..0])
+ }
+ }
+
+ /// Returns the region of the GSP message queue that the driver is currently allowed to read
+ /// from.
+ ///
+ /// As the message queue is a circular buffer, the region may be discontiguous in memory. In
+ /// that case the second slice will have a non-zero length.
+ fn driver_read_area(&self) -> (&[[u8; GSP_PAGE_SIZE]], &[[u8; GSP_PAGE_SIZE]]) {
+ let tx = self.gsp_write_ptr() as usize;
+ let rx = self.cpu_read_ptr() as usize;
+
+ // SAFETY:
+ // - The `CoherentAllocation` contains exactly one object.
+ // - We will only access the driver-owned part of the shared memory.
+ // - Per the safety statement of the function, no concurrent access will be performed.
+ let gsp_mem = &unsafe { self.0.as_slice(0, 1) }.unwrap()[0];
+ let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx);
+
+ match tx.cmp(&rx) {
+ cmp::Ordering::Equal => (&after_rx[0..0], &after_rx[0..0]),
+ cmp::Ordering::Greater => (&after_rx[..tx], &before_rx[0..0]),
+ cmp::Ordering::Less => (after_rx, &before_rx[..tx]),
+ }
+ }
+
+ /// Allocates a region on the command queue that is large enough to send the command `M`, and a
+ /// payload of size `payload_size`.
+ ///
+ /// This returns a tuple with writable references that the caller must initialize:
+ ///
+ /// * The [`GspMsgElement`] for the command,
+ /// * The `M` command itself,
+ /// * Two byte slices which combined size is `payload_size` for the requested payload. The
+ /// second slice will be empty unless there has been a need to loop on the command queue's
+ /// circular buffer.
+ ///
+ /// # Errors
+ ///
+ /// Returns `EAGAIN` if the driver area is too small to hold the requested command, or `EINVAL`
+ /// if the command cannot be placed due to a bad alignment.
+ fn allocate_command_regions<'a, M: Sized + FromBytes + AsBytes>(
+ &'a mut self,
+ payload_size: usize,
+ ) -> Result<(&'a mut GspMsgElement, &'a mut M, &'a mut [u8], &'a mut [u8])> {
+ // Get the current writable area as an array of bytes.
+ let (slice_1, slice_2) = {
+ let (slice_1, slice_2) = self.driver_write_area();
+
+ #[allow(clippy::incompatible_msrv)]
+ (slice_1.as_flattened_mut(), slice_2.as_flattened_mut())
+ };
+ let msg_size = size_of::<GspMsgElement>() + size_of::<M>() + payload_size;
+
+ // If the GSP is still processing previous messages the shared region
+ // may be full in which case we will have to retry once the GSP has
+ // processed the existing commands.
+ if msg_size > slice_1.len() + slice_2.len() {
+ return Err(EAGAIN);
+ }
+
+ // Extract area for the `GspMsgElement`.
+ let (msg_header, slice_1) = GspMsgElement::from_bytes_mut_prefix(slice_1).ok_or(EINVAL)?;
+
+ // Extract area for the command.
+ let (cmd, payload_1) = M::from_bytes_mut_prefix(slice_1).ok_or(EINVAL)?;
+
+ // Create the payload area.
+ let (payload_1, payload_2) = if payload_1.len() > payload_size {
+ // Payload fits entirely in `payload_1`.
+ (&mut payload_1[..payload_size], &mut slice_2[0..0])
+ } else {
+ // Need all of `payload_1` and some of `payload_2`.
+ let payload_2_len = payload_size - payload_1.len();
+ (payload_1, &mut slice_2[..payload_2_len])
+ };
+
+ Ok((msg_header, cmd, payload_1, payload_2))
+ }
+
+ // Returns the index of the memory page the GSP will write the next message to.
+ fn gsp_write_ptr(&self) -> u32 {
+ let gsp_mem = self.0.start_ptr();
+
+ // SAFETY:
+ // - The 'CoherentAllocation' contains at least one object.
+ // - By the invariants of `CoherentAllocation` the pointer is valid.
+ (unsafe { (*gsp_mem).gspq.tx.write_ptr() } % MSGQ_NUM_PAGES)
+ }
+
+ // Returns the index of the memory page the GSP will read the next command from.
+ fn gsp_read_ptr(&self) -> u32 {
+ let gsp_mem = self.0.start_ptr();
+
+ // SAFETY:
+ // - The 'CoherentAllocation' contains at least one object.
+ // - By the invariants of `CoherentAllocation` the pointer is valid.
+ (unsafe { (*gsp_mem).gspq.rx.read_ptr() } % MSGQ_NUM_PAGES)
+ }
+
+ // Returns the index of the memory page the CPU can read the next message from.
+ fn cpu_read_ptr(&self) -> u32 {
+ let gsp_mem = self.0.start_ptr();
+
+ // SAFETY:
+ // - The ['CoherentAllocation'] contains at least one object.
+ // - By the invariants of CoherentAllocation the pointer is valid.
+ (unsafe { (*gsp_mem).cpuq.rx.read_ptr() } % MSGQ_NUM_PAGES)
+ }
+
+ // Informs the GSP that it can send `elem_count` new pages into the message queue.
+ fn advance_cpu_read_ptr(&mut self, elem_count: u32) {
+ let rptr = self.cpu_read_ptr().wrapping_add(elem_count) % MSGQ_NUM_PAGES;
+
+ // Ensure read pointer is properly ordered.
+ fence(Ordering::SeqCst);
+
+ let gsp_mem = self.0.start_ptr_mut();
+
+ // SAFETY:
+ // - The 'CoherentAllocation' contains at least one object.
+ // - By the invariants of `CoherentAllocation` the pointer is valid.
+ unsafe { (*gsp_mem).cpuq.rx.set_read_ptr(rptr) };
+ }
+
+ // Returns the index of the memory page the CPU can write the next command to.
+ fn cpu_write_ptr(&self) -> u32 {
+ let gsp_mem = self.0.start_ptr();
+
+ // SAFETY:
+ // - The 'CoherentAllocation' contains at least one object.
+ // - By the invariants of `CoherentAllocation` the pointer is valid.
+ (unsafe { (*gsp_mem).cpuq.tx.write_ptr() } % MSGQ_NUM_PAGES)
+ }
+
+ // Informs the GSP that it can process `elem_count` new pages from the command queue.
+ fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
+ let wptr = self.cpu_write_ptr().wrapping_add(elem_count) & MSGQ_NUM_PAGES;
+ let gsp_mem = self.0.start_ptr_mut();
+
+ // SAFETY:
+ // - The 'CoherentAllocation' contains at least one object.
+ // - By the invariants of `CoherentAllocation` the pointer is valid.
+ unsafe { (*gsp_mem).cpuq.tx.set_write_ptr(wptr) };
+
+ // Ensure all command data is visible before triggering the GSP read.
+ fence(Ordering::SeqCst);
+ }
+}
+
+/// GSP command queue.
+///
+///
+pub(crate) struct Cmdq {
+ /// Device this command queue belongs to.
+ dev: ARef<device::Device>,
+ /// Current command sequence number.
+ seq: u32,
+ /// Memory area shared with the GSP for communicating commands and messages.
+ gsp_mem: DmaGspMem,
+}
+
+impl Cmdq {
+ /// Creates a new command queue for `dev`.
+ pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<Cmdq> {
+ let gsp_mem = DmaGspMem::new(dev)?;
+
+ Ok(Cmdq {
+ dev: dev.into(),
+ seq: 0,
+ gsp_mem,
+ })
+ }
+
+ /// Computes the checksum for the message pointed to by `it`.
+ ///
+ /// A message is made of several parts, so `it` is an iterator over byte slices representing
+ /// these parts.
+ 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)
+ }
+
+ /// Notifies the GSP that we have updated the command queue pointers.
+ fn notify_gsp(bar: &Bar0) {
+ regs::NV_PGSP_QUEUE_HEAD::default()
+ .set_address(0)
+ .write(bar);
+ }
+
+ /// Sends `command` to the GSP.
+ ///
+ /// # Errors
+ ///
+ /// `EAGAIN` is returned if there was not enough space in the command queue to send the
+ /// command.
+ ///
+ /// `EIO` is returned if the variable payload requested by the command has not been entirely
+ /// written to by its [`CommandToGsp::init_variable_payload`] method.
+ ///
+ /// Error codes returned by the command initializers are propagated as-is.
+ #[expect(unused)]
+ pub(crate) fn send_gsp_command<M>(&mut self, bar: &Bar0, command: M) -> Result
+ where
+ M: CommandToGsp,
+ // This allows all error types, including `Infallible`, to be used with `init`. Without
+ // this we cannot use regular stack objects as `init` since their `Init` implementation
+ // does not return any error.
+ Error: From<M::InitError>,
+ {
+ let payload_size = command.variable_payload_len();
+ let (msg_header, cmd, payload_1, payload_2) = self
+ .gsp_mem
+ .allocate_command_regions::<M::Command>(payload_size)?;
+
+ let msg_element = GspMsgElement::init(
+ self.seq,
+ size_of::<M::Command>() + payload_size,
+ M::FUNCTION,
+ );
+
+ // Fill the header and command in-place.
+ //
+ // SAFETY: `msg_header` and `cmd` are valid references, and not touched if the initializer
+ // fails.
+ unsafe {
+ msg_element.__init(core::ptr::from_mut(msg_header))?;
+ command.init().__init(core::ptr::from_mut(cmd))?;
+ }
+
+ // Fill the variable-length payload.
+ if payload_size > 0 {
+ let mut sbuffer = SBufferIter::new_writer([&mut payload_1[..], &mut payload_2[..]]);
+ command.init_variable_payload(&mut sbuffer)?;
+
+ if !sbuffer.is_empty() {
+ return Err(EIO);
+ }
+ }
+
+ // Compute checksum now that the whole message is ready.
+ msg_header.set_checksum(Cmdq::calculate_checksum(SBufferIter::new_reader([
+ msg_header.as_bytes(),
+ cmd.as_bytes(),
+ payload_1,
+ payload_2,
+ ])));
+
+ dev_dbg!(
+ &self.dev,
+ "GSP RPC: send: seq# {}, function={:?}, length=0x{:x}\n",
+ self.seq,
+ msg_header.function(),
+ msg_header.length(),
+ );
+
+ // All set - update the write pointer and inform the GSP of the new command.
+ let elem_count = msg_header.element_count();
+ self.seq += 1;
+ self.gsp_mem.advance_cpu_write_ptr(elem_count);
+ Cmdq::notify_gsp(bar);
+
+ Ok(())
+ }
+
+ /// Receive a message from the GSP.
+ ///
+ /// `init` is a closure tasked with processing the message. It receives a reference to the
+ /// message in the message queue, and a [`SBufferIter`] pointing to its payload, if any.
+ ///
+ /// The expected message is specified using the `M` generic parameter. If the pending message
+ /// is different, `EAGAIN` is returned and the unexpected message is dropped.
+ ///
+ /// This design is by no means final, but it is simple and will let us go through GSP
+ /// initialization.
+ #[expect(unused)]
+ pub(crate) fn receive_msg_from_gsp<M: MessageFromGsp, R>(
+ &mut self,
+ timeout: Delta,
+ init: impl FnOnce(&M, SBufferIter<core::array::IntoIter<&[u8], 2>>) -> Result<R>,
+ ) -> Result<R> {
+ // Wait for a message to arrive from the GSP.
+ let (slice_1, slice_2) = read_poll_timeout(
+ || Ok(self.gsp_mem.driver_read_area()),
+ |driver_area| !driver_area.0.is_empty(),
+ Delta::from_millis(1),
+ timeout,
+ )
+ .map(|(slice_1, slice_2)| {
+ #[allow(clippy::incompatible_msrv)]
+ (slice_1.as_flattened(), slice_2.as_flattened())
+ })?;
+
+ // Extract the `GspMsgElement` and validate its length.
+ let (msg_header, slice_1) = GspMsgElement::from_bytes_prefix(slice_1).ok_or(EIO)?;
+ if msg_header.length() < size_of::<M>() {
+ return Err(EIO);
+ }
+
+ // Get message function.
+ let function = msg_header
+ .function()
+ .inspect_err(|bad_function| {
+ dev_err!(
+ self.dev,
+ "GSP RPC: receive: seq# {}, bad function=0x{:x}, length=0x{:x}\n",
+ msg_header.sequence(),
+ bad_function,
+ msg_header.length(),
+ )
+ })
+ .map_err(|_| EIO)?;
+
+ dev_dbg!(
+ self.dev,
+ "GSP RPC: receive: seq# {}, function={}, length=0x{:x}\n",
+ msg_header.sequence(),
+ function,
+ msg_header.length(),
+ );
+
+ // Cut the payload slices down to the actual length of the message.
+ let (cmd_payload_1, payload_2) = if slice_1.len() > msg_header.length() {
+ (slice_1.split_at(msg_header.length()).0, &slice_2[0..0])
+ } else {
+ (
+ slice_1,
+ slice_2.split_at(msg_header.length() - slice_1.len()).0,
+ )
+ };
+
+ // Validate checksum.
+ if Cmdq::calculate_checksum(SBufferIter::new_reader([
+ msg_header.as_bytes(),
+ cmd_payload_1,
+ payload_2,
+ ])) != 0
+ {
+ dev_err!(
+ self.dev,
+ "GSP RPC: receive: Call {} - bad checksum",
+ msg_header.sequence()
+ );
+ return Err(EIO);
+ }
+
+ // Extract the message. Store the result as we want to advance the read pointer even in
+ // case of failure.
+ let result = if function == M::FUNCTION {
+ let (cmd, payload_1) = M::from_bytes_prefix(cmd_payload_1).ok_or(EINVAL)?;
+ let sbuffer = SBufferIter::new_reader([payload_1, payload_2]);
+
+ init(cmd, sbuffer)
+ } else {
+ Err(ERANGE)
+ };
+
+ self.gsp_mem
+ .advance_cpu_read_ptr(u32::try_from(msg_header.length().div_ceil(GSP_PAGE_SIZE))?);
+
+ result
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index a6ee52475bdb..ceda61c99b92 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -5,10 +5,14 @@
// Alias to avoid repeating the version number with every use.
use r570_144 as bindings;
-use core::ops::Range;
+use core::{
+ fmt,
+ ops::Range, //
+};
use kernel::{
dma::CoherentAllocation,
+ prelude::*,
ptr::{
Alignable,
Alignment, //
@@ -27,6 +31,7 @@
fb::FbLayout,
firmware::gsp::GspFirmware,
gpu::Chipset,
+ gsp::GSP_PAGE_SIZE,
num::{
self,
FromSafeCast, //
@@ -181,6 +186,128 @@ pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self {
}
}
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[repr(u32)]
+pub(crate) enum MsgFunction {
+ // Common function codes
+ Nop = bindings::NV_VGPU_MSG_FUNCTION_NOP,
+ SetGuestSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO,
+ AllocRoot = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT,
+ AllocDevice = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE,
+ AllocMemory = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY,
+ AllocCtxDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA,
+ AllocChannelDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA,
+ MapMemory = bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY,
+ BindCtxDma = bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA,
+ AllocObject = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT,
+ Free = bindings::NV_VGPU_MSG_FUNCTION_FREE,
+ Log = bindings::NV_VGPU_MSG_FUNCTION_LOG,
+ GetGspStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO,
+ SetRegistry = bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
+ GspSetSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO,
+ GspInitPostObjGpu = bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU,
+ GspRmControl = bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL,
+ GetStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO,
+
+ // Event codes
+ GspInitDone = bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE,
+ GspRunCpuSequencer = bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER,
+ PostEvent = bindings::NV_VGPU_MSG_EVENT_POST_EVENT,
+ RcTriggered = bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED,
+ MmuFaultQueued = bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED,
+ OsErrorLog = bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG,
+ GspPostNoCat = bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD,
+ GspLockdownNotice = bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE,
+ UcodeLibOsPrint = bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT,
+}
+
+impl fmt::Display for MsgFunction {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ // Common function codes
+ MsgFunction::Nop => write!(f, "NOP"),
+ MsgFunction::SetGuestSystemInfo => write!(f, "SET_GUEST_SYSTEM_INFO"),
+ MsgFunction::AllocRoot => write!(f, "ALLOC_ROOT"),
+ MsgFunction::AllocDevice => write!(f, "ALLOC_DEVICE"),
+ MsgFunction::AllocMemory => write!(f, "ALLOC_MEMORY"),
+ MsgFunction::AllocCtxDma => write!(f, "ALLOC_CTX_DMA"),
+ MsgFunction::AllocChannelDma => write!(f, "ALLOC_CHANNEL_DMA"),
+ MsgFunction::MapMemory => write!(f, "MAP_MEMORY"),
+ MsgFunction::BindCtxDma => write!(f, "BIND_CTX_DMA"),
+ MsgFunction::AllocObject => write!(f, "ALLOC_OBJECT"),
+ MsgFunction::Free => write!(f, "FREE"),
+ MsgFunction::Log => write!(f, "LOG"),
+ MsgFunction::GetGspStaticInfo => write!(f, "GET_GSP_STATIC_INFO"),
+ MsgFunction::SetRegistry => write!(f, "SET_REGISTRY"),
+ MsgFunction::GspSetSystemInfo => write!(f, "GSP_SET_SYSTEM_INFO"),
+ MsgFunction::GspInitPostObjGpu => write!(f, "GSP_INIT_POST_OBJGPU"),
+ MsgFunction::GspRmControl => write!(f, "GSP_RM_CONTROL"),
+ MsgFunction::GetStaticInfo => write!(f, "GET_STATIC_INFO"),
+
+ // Event codes
+ MsgFunction::GspInitDone => write!(f, "INIT_DONE"),
+ MsgFunction::GspRunCpuSequencer => write!(f, "RUN_CPU_SEQUENCER"),
+ MsgFunction::PostEvent => write!(f, "POST_EVENT"),
+ MsgFunction::RcTriggered => write!(f, "RC_TRIGGERED"),
+ MsgFunction::MmuFaultQueued => write!(f, "MMU_FAULT_QUEUED"),
+ MsgFunction::OsErrorLog => write!(f, "OS_ERROR_LOG"),
+ MsgFunction::GspPostNoCat => write!(f, "NOCAT"),
+ MsgFunction::GspLockdownNotice => write!(f, "LOCKDOWN_NOTICE"),
+ MsgFunction::UcodeLibOsPrint => write!(f, "LIBOS_PRINT"),
+ }
+ }
+}
+
+impl TryFrom<u32> for MsgFunction {
+ type Error = kernel::error::Error;
+
+ fn try_from(value: u32) -> Result<MsgFunction> {
+ match value {
+ bindings::NV_VGPU_MSG_FUNCTION_NOP => Ok(MsgFunction::Nop),
+ bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO => {
+ Ok(MsgFunction::SetGuestSystemInfo)
+ }
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT => Ok(MsgFunction::AllocRoot),
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE => Ok(MsgFunction::AllocDevice),
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY => Ok(MsgFunction::AllocMemory),
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA => Ok(MsgFunction::AllocCtxDma),
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA => Ok(MsgFunction::AllocChannelDma),
+ bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY => Ok(MsgFunction::MapMemory),
+ bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA => Ok(MsgFunction::BindCtxDma),
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT => Ok(MsgFunction::AllocObject),
+ bindings::NV_VGPU_MSG_FUNCTION_FREE => Ok(MsgFunction::Free),
+ bindings::NV_VGPU_MSG_FUNCTION_LOG => Ok(MsgFunction::Log),
+ bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO => Ok(MsgFunction::GetGspStaticInfo),
+ bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY => Ok(MsgFunction::SetRegistry),
+ bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO => Ok(MsgFunction::GspSetSystemInfo),
+ bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU => {
+ Ok(MsgFunction::GspInitPostObjGpu)
+ }
+ bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL => Ok(MsgFunction::GspRmControl),
+ bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO => Ok(MsgFunction::GetStaticInfo),
+ bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE => Ok(MsgFunction::GspInitDone),
+ bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER => {
+ Ok(MsgFunction::GspRunCpuSequencer)
+ }
+ bindings::NV_VGPU_MSG_EVENT_POST_EVENT => Ok(MsgFunction::PostEvent),
+ bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED => Ok(MsgFunction::RcTriggered),
+ bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED => Ok(MsgFunction::MmuFaultQueued),
+ bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG => Ok(MsgFunction::OsErrorLog),
+ bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD => Ok(MsgFunction::GspPostNoCat),
+ bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE => Ok(MsgFunction::GspLockdownNotice),
+ bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT => Ok(MsgFunction::UcodeLibOsPrint),
+ _ => Err(EINVAL),
+ }
+ }
+}
+
+impl From<MsgFunction> for u32 {
+ fn from(value: MsgFunction) -> Self {
+ // CAST: `MsgFunction` is `repr(u32)` and can thus be cast losslessly.
+ value as u32
+ }
+}
+
/// Struct containing the arguments required to pass a memory buffer to the GSP
/// for use during initialisation.
///
@@ -235,3 +362,209 @@ fn id8(name: &str) -> u64 {
})
}
}
+
+/// TX header for setting up a message queue with the GSP.
+#[repr(transparent)]
+pub(crate) struct MsgqTxHeader(bindings::msgqTxHeader);
+
+impl MsgqTxHeader {
+ /// Create a new TX queue header.
+ ///
+ /// # Arguments
+ ///
+ /// * `msgq_size` - Total size of the message queue structure, in bytes.
+ /// * `rx_hdr_offset` - Offset, in bytes, of the start of the RX header in the message queue
+ /// structure.
+ /// * `msg_count` - Number of messages that can be sent, i.e. the number of memory pages
+ /// allocated for the message queue in the message queue structure.
+ pub(crate) fn new(msgq_size: u32, rx_hdr_offset: u32, msg_count: u32) -> Self {
+ Self(bindings::msgqTxHeader {
+ version: 0,
+ size: msgq_size,
+ msgSize: num::usize_into_u32::<GSP_PAGE_SIZE>(),
+ msgCount: msg_count,
+ writePtr: 0,
+ flags: 1,
+ rxHdrOff: rx_hdr_offset,
+ entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(),
+ })
+ }
+
+ /// Returns the value of the write pointer for this queue.
+ pub(crate) fn write_ptr(&self) -> u32 {
+ let ptr = core::ptr::from_ref(&self.0.writePtr);
+
+ // SAFETY: `ptr` is a valid pointer to a `u32`.
+ unsafe { ptr.read_volatile() }
+ }
+
+ /// Sets the value of the write pointer for this queue.
+ pub(crate) fn set_write_ptr(&mut self, val: u32) {
+ let ptr = core::ptr::from_mut(&mut self.0.writePtr);
+
+ // SAFETY: `ptr` is a valid pointer to a `u32`.
+ unsafe { ptr.write_volatile(val) }
+ }
+}
+
+// SAFETY: Padding is explicit and does not contain uninitialized data.
+unsafe impl AsBytes for MsgqTxHeader {}
+
+/// RX header for setting up a message queue with the GSP.
+#[repr(transparent)]
+pub(crate) struct MsgqRxHeader(bindings::msgqRxHeader);
+
+/// Header for the message RX queue.
+impl MsgqRxHeader {
+ /// Creates a new RX queue header.
+ pub(crate) fn new() -> Self {
+ Self(Default::default())
+ }
+
+ /// Returns the value of the read pointer for this queue.
+ pub(crate) fn read_ptr(&self) -> u32 {
+ let ptr = core::ptr::from_ref(&self.0.readPtr);
+
+ // SAFETY: `ptr` is a valid pointer to a `u32`.
+ unsafe { ptr.read_volatile() }
+ }
+
+ /// Sets the value of the read pointer for this queue.
+ pub(crate) fn set_read_ptr(&mut self, val: u32) {
+ let ptr = core::ptr::from_mut(&mut self.0.readPtr);
+
+ // SAFETY: `ptr` is a valid pointer to a `u32`.
+ unsafe { ptr.write_volatile(val) }
+ }
+}
+
+// SAFETY: Padding is explicit and does not contain uninitialized data.
+unsafe impl AsBytes for MsgqRxHeader {}
+
+bitfield! {
+ struct MsgHeaderVersion(u32) {
+ 31:24 major as u8;
+ 23:16 minor as u8;
+ }
+}
+
+impl MsgHeaderVersion {
+ const MAJOR_TOT: u8 = 3;
+ const MINOR_TOT: u8 = 0;
+
+ fn new() -> Self {
+ Self::default()
+ .set_major(Self::MAJOR_TOT)
+ .set_minor(Self::MINOR_TOT)
+ }
+}
+
+impl bindings::rpc_message_header_v {
+ fn init(cmd_size: usize, function: MsgFunction) -> impl Init<Self, Error> {
+ type RpcMessageHeader = bindings::rpc_message_header_v;
+
+ try_init!(RpcMessageHeader {
+ header_version: MsgHeaderVersion::new().into(),
+ signature: bindings::NV_VGPU_MSG_SIGNATURE_VALID,
+ function: function.into(),
+ length: size_of::<Self>()
+ .checked_add(cmd_size)
+ .ok_or(EOVERFLOW)
+ .and_then(|v| v.try_into().map_err(|_| EINVAL))?,
+ rpc_result: 0xffffffff,
+ rpc_result_private: 0xffffffff,
+ ..Zeroable::init_zeroed()
+ })
+ }
+}
+
+// SAFETY: We can't derive the Zeroable trait for this binding because the
+// procedural macro doesn't support the syntax used by bindgen to create the
+// __IncompleteArrayField types. So instead we implement it here, which is safe
+// because these are explicitly padded structures only containing types for
+// which any bit pattern, including all zeros, is valid.
+unsafe impl Zeroable for bindings::rpc_message_header_v {}
+
+/// GSP Message Element.
+///
+/// This is essentially a message header expected to be followed by the message data.
+#[repr(transparent)]
+pub(crate) struct GspMsgElement {
+ inner: bindings::GSP_MSG_QUEUE_ELEMENT,
+}
+
+impl GspMsgElement {
+ /// Creates a new message element.
+ ///
+ /// # Arguments
+ ///
+ /// * `sequence` - Sequence number of the message.
+ /// * `cmd_size` - Size of the command (not including the message element), in bytes.
+ /// * `function` - Function of the message.
+ #[allow(non_snake_case)]
+ pub(crate) fn init(
+ sequence: u32,
+ cmd_size: usize,
+ function: MsgFunction,
+ ) -> impl Init<Self, Error> {
+ type RpcMessageHeader = bindings::rpc_message_header_v;
+ type InnerGspMsgElement = bindings::GSP_MSG_QUEUE_ELEMENT;
+ let init_inner = try_init!(InnerGspMsgElement {
+ seqNum: sequence,
+ elemCount: size_of::<Self>()
+ .checked_add(cmd_size)
+ .ok_or(EOVERFLOW)?
+ .div_ceil(GSP_PAGE_SIZE)
+ .try_into()
+ .map_err(|_| EOVERFLOW)?,
+ rpc <- RpcMessageHeader::init(cmd_size, function),
+ ..Zeroable::init_zeroed()
+ });
+
+ try_init!(GspMsgElement {
+ inner <- init_inner,
+ })
+ }
+
+ /// Sets the checksum of this message.
+ ///
+ /// Since the header is also part of the checksum, this is usually called after the whole
+ /// message has been written to the shared memory area.
+ pub(crate) fn set_checksum(&mut self, checksum: u32) {
+ self.inner.checkSum = checksum;
+ }
+
+ /// Returns the total length of the message.
+ pub(crate) fn length(&self) -> usize {
+ // `rpc.length` includes the length of the GspRpcHeader but not the message header.
+ size_of::<Self>() - size_of::<bindings::rpc_message_header_v>()
+ + num::u32_as_usize(self.inner.rpc.length)
+ }
+
+ // Returns the sequence number of the message.
+ pub(crate) fn sequence(&self) -> u32 {
+ self.inner.rpc.sequence
+ }
+
+ // Returns the function of the message, if it is valid, or the invalid function number as an
+ // error.
+ pub(crate) fn function(&self) -> Result<MsgFunction, u32> {
+ self.inner
+ .rpc
+ .function
+ .try_into()
+ .map_err(|_| self.inner.rpc.function)
+ }
+
+ // Returns the number of elements (i.e. memory pages) used by this message.
+ pub(crate) fn element_count(&self) -> u32 {
+ self.inner.elemCount
+ }
+}
+
+// SAFETY: Padding is explicit and does not contain uninitialized data.
+unsafe impl AsBytes for GspMsgElement {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for GspMsgElement {}
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index f7b38978c5f8..17fb2392ec3c 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -1,5 +1,36 @@
// 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 NV_VGPU_MSG_SIGNATURE_VALID: u32 = 1129337430;
pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2: u32 = 0;
pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL: u32 = 23068672;
pub const GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X: u32 = 8388608;
@@ -11,6 +42,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;
@@ -19,6 +51,345 @@
pub type u16_ = __u16;
pub type u32_ = __u32;
pub type u64_ = __u64;
+pub const NV_VGPU_MSG_FUNCTION_NOP: _bindgen_ty_2 = 0;
+pub const NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO: _bindgen_ty_2 = 1;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_ROOT: _bindgen_ty_2 = 2;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE: _bindgen_ty_2 = 3;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY: _bindgen_ty_2 = 4;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA: _bindgen_ty_2 = 5;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA: _bindgen_ty_2 = 6;
+pub const NV_VGPU_MSG_FUNCTION_MAP_MEMORY: _bindgen_ty_2 = 7;
+pub const NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA: _bindgen_ty_2 = 8;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT: _bindgen_ty_2 = 9;
+pub const NV_VGPU_MSG_FUNCTION_FREE: _bindgen_ty_2 = 10;
+pub const NV_VGPU_MSG_FUNCTION_LOG: _bindgen_ty_2 = 11;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_VIDMEM: _bindgen_ty_2 = 12;
+pub const NV_VGPU_MSG_FUNCTION_UNMAP_MEMORY: _bindgen_ty_2 = 13;
+pub const NV_VGPU_MSG_FUNCTION_MAP_MEMORY_DMA: _bindgen_ty_2 = 14;
+pub const NV_VGPU_MSG_FUNCTION_UNMAP_MEMORY_DMA: _bindgen_ty_2 = 15;
+pub const NV_VGPU_MSG_FUNCTION_GET_EDID: _bindgen_ty_2 = 16;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DISP_CHANNEL: _bindgen_ty_2 = 17;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DISP_OBJECT: _bindgen_ty_2 = 18;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_SUBDEVICE: _bindgen_ty_2 = 19;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DYNAMIC_MEMORY: _bindgen_ty_2 = 20;
+pub const NV_VGPU_MSG_FUNCTION_DUP_OBJECT: _bindgen_ty_2 = 21;
+pub const NV_VGPU_MSG_FUNCTION_IDLE_CHANNELS: _bindgen_ty_2 = 22;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_EVENT: _bindgen_ty_2 = 23;
+pub const NV_VGPU_MSG_FUNCTION_SEND_EVENT: _bindgen_ty_2 = 24;
+pub const NV_VGPU_MSG_FUNCTION_REMAPPER_CONTROL: _bindgen_ty_2 = 25;
+pub const NV_VGPU_MSG_FUNCTION_DMA_CONTROL: _bindgen_ty_2 = 26;
+pub const NV_VGPU_MSG_FUNCTION_DMA_FILL_PTE_MEM: _bindgen_ty_2 = 27;
+pub const NV_VGPU_MSG_FUNCTION_MANAGE_HW_RESOURCE: _bindgen_ty_2 = 28;
+pub const NV_VGPU_MSG_FUNCTION_BIND_ARBITRARY_CTX_DMA: _bindgen_ty_2 = 29;
+pub const NV_VGPU_MSG_FUNCTION_CREATE_FB_SEGMENT: _bindgen_ty_2 = 30;
+pub const NV_VGPU_MSG_FUNCTION_DESTROY_FB_SEGMENT: _bindgen_ty_2 = 31;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_SHARE_DEVICE: _bindgen_ty_2 = 32;
+pub const NV_VGPU_MSG_FUNCTION_DEFERRED_API_CONTROL: _bindgen_ty_2 = 33;
+pub const NV_VGPU_MSG_FUNCTION_REMOVE_DEFERRED_API: _bindgen_ty_2 = 34;
+pub const NV_VGPU_MSG_FUNCTION_SIM_ESCAPE_READ: _bindgen_ty_2 = 35;
+pub const NV_VGPU_MSG_FUNCTION_SIM_ESCAPE_WRITE: _bindgen_ty_2 = 36;
+pub const NV_VGPU_MSG_FUNCTION_SIM_MANAGE_DISPLAY_CONTEXT_DMA: _bindgen_ty_2 = 37;
+pub const NV_VGPU_MSG_FUNCTION_FREE_VIDMEM_VIRT: _bindgen_ty_2 = 38;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_PSTATE_INFO: _bindgen_ty_2 = 39;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_PERFMON_SAMPLE: _bindgen_ty_2 = 40;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_VIRTUAL_PSTATE_INFO: _bindgen_ty_2 = 41;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_LEVEL_INFO: _bindgen_ty_2 = 42;
+pub const NV_VGPU_MSG_FUNCTION_MAP_SEMA_MEMORY: _bindgen_ty_2 = 43;
+pub const NV_VGPU_MSG_FUNCTION_UNMAP_SEMA_MEMORY: _bindgen_ty_2 = 44;
+pub const NV_VGPU_MSG_FUNCTION_SET_SURFACE_PROPERTIES: _bindgen_ty_2 = 45;
+pub const NV_VGPU_MSG_FUNCTION_CLEANUP_SURFACE: _bindgen_ty_2 = 46;
+pub const NV_VGPU_MSG_FUNCTION_UNLOADING_GUEST_DRIVER: _bindgen_ty_2 = 47;
+pub const NV_VGPU_MSG_FUNCTION_TDR_SET_TIMEOUT_STATE: _bindgen_ty_2 = 48;
+pub const NV_VGPU_MSG_FUNCTION_SWITCH_TO_VGA: _bindgen_ty_2 = 49;
+pub const NV_VGPU_MSG_FUNCTION_GPU_EXEC_REG_OPS: _bindgen_ty_2 = 50;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO: _bindgen_ty_2 = 51;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_VIRTMEM: _bindgen_ty_2 = 52;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_PDE_2: _bindgen_ty_2 = 53;
+pub const NV_VGPU_MSG_FUNCTION_SET_PAGE_DIRECTORY: _bindgen_ty_2 = 54;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_PSTATE_INFO: _bindgen_ty_2 = 55;
+pub const NV_VGPU_MSG_FUNCTION_TRANSLATE_GUEST_GPU_PTES: _bindgen_ty_2 = 56;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED_57: _bindgen_ty_2 = 57;
+pub const NV_VGPU_MSG_FUNCTION_RESET_CURRENT_GR_CONTEXT: _bindgen_ty_2 = 58;
+pub const NV_VGPU_MSG_FUNCTION_SET_SEMA_MEM_VALIDATION_STATE: _bindgen_ty_2 = 59;
+pub const NV_VGPU_MSG_FUNCTION_GET_ENGINE_UTILIZATION: _bindgen_ty_2 = 60;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_GPU_PDES: _bindgen_ty_2 = 61;
+pub const NV_VGPU_MSG_FUNCTION_GET_ENCODER_CAPACITY: _bindgen_ty_2 = 62;
+pub const NV_VGPU_MSG_FUNCTION_VGPU_PF_REG_READ32: _bindgen_ty_2 = 63;
+pub const NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO_EXT: _bindgen_ty_2 = 64;
+pub const NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO: _bindgen_ty_2 = 65;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_INIT: _bindgen_ty_2 = 66;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_CLOSE_QUEUE: _bindgen_ty_2 = 67;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_CLEANUP: _bindgen_ty_2 = 68;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_TEST: _bindgen_ty_2 = 69;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_BAR_PDE: _bindgen_ty_2 = 70;
+pub const NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD: _bindgen_ty_2 = 71;
+pub const NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO: _bindgen_ty_2 = 72;
+pub const NV_VGPU_MSG_FUNCTION_SET_REGISTRY: _bindgen_ty_2 = 73;
+pub const NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU: _bindgen_ty_2 = 74;
+pub const NV_VGPU_MSG_FUNCTION_SUBDEV_EVENT_SET_NOTIFICATION: _bindgen_ty_2 = 75;
+pub const NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL: _bindgen_ty_2 = 76;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO2: _bindgen_ty_2 = 77;
+pub const NV_VGPU_MSG_FUNCTION_DUMP_PROTOBUF_COMPONENT: _bindgen_ty_2 = 78;
+pub const NV_VGPU_MSG_FUNCTION_UNSET_PAGE_DIRECTORY: _bindgen_ty_2 = 79;
+pub const NV_VGPU_MSG_FUNCTION_GET_CONSOLIDATED_STATIC_INFO: _bindgen_ty_2 = 80;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_REGISTER_FAULT_BUFFER: _bindgen_ty_2 = 81;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_UNREGISTER_FAULT_BUFFER: _bindgen_ty_2 = 82;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_REGISTER_CLIENT_SHADOW_FAULT_BUFFER: _bindgen_ty_2 = 83;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_UNREGISTER_CLIENT_SHADOW_FAULT_BUFFER: _bindgen_ty_2 = 84;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_VGPU_FB_USAGE: _bindgen_ty_2 = 85;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_NVFBC_SW_SESSION_UPDATE_INFO: _bindgen_ty_2 = 86;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_NVENC_SW_SESSION_UPDATE_INFO: _bindgen_ty_2 = 87;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESET_CHANNEL: _bindgen_ty_2 = 88;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESET_ISOLATED_CHANNEL: _bindgen_ty_2 = 89;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_HANDLE_VF_PRI_FAULT: _bindgen_ty_2 = 90;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CLK_GET_EXTENDED_INFO: _bindgen_ty_2 = 91;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_BOOST: _bindgen_ty_2 = 92;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_VPSTATES_GET_CONTROL: _bindgen_ty_2 = 93;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_ZBC_CLEAR_TABLE: _bindgen_ty_2 = 94;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_COLOR_CLEAR: _bindgen_ty_2 = 95;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_DEPTH_CLEAR: _bindgen_ty_2 = 96;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_SCHEDULE: _bindgen_ty_2 = 97;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_TIMESLICE: _bindgen_ty_2 = 98;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PREEMPT: _bindgen_ty_2 = 99;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_DISABLE_CHANNELS: _bindgen_ty_2 = 100;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_TSG_INTERLEAVE_LEVEL: _bindgen_ty_2 = 101;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_CHANNEL_INTERLEAVE_LEVEL: _bindgen_ty_2 = 102;
+pub const NV_VGPU_MSG_FUNCTION_GSP_RM_ALLOC: _bindgen_ty_2 = 103;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS_V2: _bindgen_ty_2 = 104;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_AES_ENCRYPT: _bindgen_ty_2 = 105;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_SESSION_KEY: _bindgen_ty_2 = 106;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_SESSION_KEY_STATUS: _bindgen_ty_2 = 107;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_CLEAR_ALL_SM_ERROR_STATES: _bindgen_ty_2 = 108;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_READ_ALL_SM_ERROR_STATES: _bindgen_ty_2 = 109;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_EXCEPTION_MASK: _bindgen_ty_2 = 110;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_PROMOTE_CTX: _bindgen_ty_2 = 111;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_CTXSW_PREEMPTION_BIND: _bindgen_ty_2 = 112;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_SET_CTXSW_PREEMPTION_MODE: _bindgen_ty_2 = 113;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_CTXSW_ZCULL_BIND: _bindgen_ty_2 = 114;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_INITIALIZE_CTX: _bindgen_ty_2 = 115;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_VASPACE_COPY_SERVER_RESERVED_PDES: _bindgen_ty_2 = 116;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_CLEAR_FAULTED_BIT: _bindgen_ty_2 = 117;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_LATEST_ECC_ADDRESSES: _bindgen_ty_2 = 118;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_MC_SERVICE_INTERRUPTS: _bindgen_ty_2 = 119;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DMA_SET_DEFAULT_VASPACE: _bindgen_ty_2 = 120;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_CE_PCE_MASK: _bindgen_ty_2 = 121;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_ZBC_CLEAR_TABLE_ENTRY: _bindgen_ty_2 = 122;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_NVLINK_PEER_ID_MASK: _bindgen_ty_2 = 123;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_NVLINK_STATUS: _bindgen_ty_2 = 124;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS: _bindgen_ty_2 = 125;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS_MATRIX: _bindgen_ty_2 = 126;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED_0: _bindgen_ty_2 = 127;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_PM_AREA_SMPC: _bindgen_ty_2 = 128;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_HWPM_LEGACY: _bindgen_ty_2 = 129;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_B0CC_EXEC_REG_OPS: _bindgen_ty_2 = 130;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_BIND_PM_RESOURCES: _bindgen_ty_2 = 131;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SUSPEND_CONTEXT: _bindgen_ty_2 = 132;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_RESUME_CONTEXT: _bindgen_ty_2 = 133;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_EXEC_REG_OPS: _bindgen_ty_2 = 134;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_MMU_DEBUG: _bindgen_ty_2 = 135;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_READ_SINGLE_SM_ERROR_STATE: _bindgen_ty_2 = 136;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_CLEAR_SINGLE_SM_ERROR_STATE: _bindgen_ty_2 = 137;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_ERRBAR_DEBUG: _bindgen_ty_2 = 138;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_NEXT_STOP_TRIGGER_TYPE: _bindgen_ty_2 = 139;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_ALLOC_PMA_STREAM: _bindgen_ty_2 = 140;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PMA_STREAM_UPDATE_GET_PUT: _bindgen_ty_2 = 141;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FB_GET_INFO_V2: _bindgen_ty_2 = 142;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_SET_CHANNEL_PROPERTIES: _bindgen_ty_2 = 143;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_GET_CTX_BUFFER_INFO: _bindgen_ty_2 = 144;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_KGR_GET_CTX_BUFFER_PTES: _bindgen_ty_2 = 145;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_EVICT_CTX: _bindgen_ty_2 = 146;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FB_GET_FS_INFO: _bindgen_ty_2 = 147;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GRMGR_GET_GR_FS_INFO: _bindgen_ty_2 = 148;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_STOP_CHANNEL: _bindgen_ty_2 = 149;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_PC_SAMPLING_MODE: _bindgen_ty_2 = 150;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_RATED_TDP_GET_STATUS: _bindgen_ty_2 = 151;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_RATED_TDP_SET_CONTROL: _bindgen_ty_2 = 152;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FREE_PMA_STREAM: _bindgen_ty_2 = 153;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_TIMER_SET_GR_TICK_FREQ: _bindgen_ty_2 = 154;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_SETUP_VF_ZOMBIE_SUBCTX_PDB: _bindgen_ty_2 = 155;
+pub const NV_VGPU_MSG_FUNCTION_GET_CONSOLIDATED_GR_STATIC_INFO: _bindgen_ty_2 = 156;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_SINGLE_SM_SINGLE_STEP: _bindgen_ty_2 = 157;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_GET_TPC_PARTITION_MODE: _bindgen_ty_2 = 158;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_SET_TPC_PARTITION_MODE: _bindgen_ty_2 = 159;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_ALLOCATE: _bindgen_ty_2 = 160;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_DESTROY: _bindgen_ty_2 = 161;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_MAP: _bindgen_ty_2 = 162;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_UNMAP: _bindgen_ty_2 = 163;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_PUSH_STREAM: _bindgen_ty_2 = 164;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_SET_HANDLES: _bindgen_ty_2 = 165;
+pub const NV_VGPU_MSG_FUNCTION_UVM_METHOD_STREAM_GUEST_PAGES_OPERATION: _bindgen_ty_2 = 166;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_QUIESCE_PMA_CHANNEL: _bindgen_ty_2 = 167;
+pub const NV_VGPU_MSG_FUNCTION_DCE_RM_INIT: _bindgen_ty_2 = 168;
+pub const NV_VGPU_MSG_FUNCTION_REGISTER_VIRTUAL_EVENT_BUFFER: _bindgen_ty_2 = 169;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EVENT_BUFFER_UPDATE_GET: _bindgen_ty_2 = 170;
+pub const NV_VGPU_MSG_FUNCTION_GET_PLCABLE_ADDRESS_KIND: _bindgen_ty_2 = 171;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_LIMITS_SET_STATUS_V2: _bindgen_ty_2 = 172;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_SRIOV_PROMOTE_PMA_STREAM: _bindgen_ty_2 = 173;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_MMU_DEBUG_MODE: _bindgen_ty_2 = 174;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_PROMOTE_FAULT_METHOD_BUFFERS: _bindgen_ty_2 = 175;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FLCN_GET_CTX_BUFFER_SIZE: _bindgen_ty_2 = 176;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FLCN_GET_CTX_BUFFER_INFO: _bindgen_ty_2 = 177;
+pub const NV_VGPU_MSG_FUNCTION_DISABLE_CHANNELS: _bindgen_ty_2 = 178;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FABRIC_MEMORY_DESCRIBE: _bindgen_ty_2 = 179;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FABRIC_MEM_STATS: _bindgen_ty_2 = 180;
+pub const NV_VGPU_MSG_FUNCTION_SAVE_HIBERNATION_DATA: _bindgen_ty_2 = 181;
+pub const NV_VGPU_MSG_FUNCTION_RESTORE_HIBERNATION_DATA: _bindgen_ty_2 = 182;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_MEMSYS_SET_ZBC_REFERENCED: _bindgen_ty_2 = 183;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_CREATE: _bindgen_ty_2 = 184;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_DELETE: _bindgen_ty_2 = 185;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_GET_WORK_SUBMIT_TOKEN: _bindgen_ty_2 = 186;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX: _bindgen_ty_2 = 187;
+pub const NV_VGPU_MSG_FUNCTION_PMA_SCRUBBER_SHARED_BUFFER_GUEST_PAGES_OPERATION: _bindgen_ty_2 =
+ 188;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_MASTER_GET_VIRTUAL_FUNCTION_ERROR_CONT_INTR_MASK:
+ _bindgen_ty_2 = 189;
+pub const NV_VGPU_MSG_FUNCTION_SET_SYSMEM_DIRTY_PAGE_TRACKING_BUFFER: _bindgen_ty_2 = 190;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_P2P_CAPS: _bindgen_ty_2 = 191;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_BUS_SET_P2P_MAPPING: _bindgen_ty_2 = 192;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_BUS_UNSET_P2P_MAPPING: _bindgen_ty_2 = 193;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FLA_SETUP_INSTANCE_MEM_BLOCK: _bindgen_ty_2 = 194;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_MIGRATABLE_OPS: _bindgen_ty_2 = 195;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_TOTAL_HS_CREDITS: _bindgen_ty_2 = 196;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_HS_CREDITS: _bindgen_ty_2 = 197;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_HS_CREDITS: _bindgen_ty_2 = 198;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PM_AREA_PC_SAMPLER: _bindgen_ty_2 = 199;
+pub const NV_VGPU_MSG_FUNCTION_INVALIDATE_TLB: _bindgen_ty_2 = 200;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_QUERY_ECC_STATUS: _bindgen_ty_2 = 201;
+pub const NV_VGPU_MSG_FUNCTION_ECC_NOTIFIER_WRITE_ACK: _bindgen_ty_2 = 202;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_GET_MODE_MMU_DEBUG: _bindgen_ty_2 = 203;
+pub const NV_VGPU_MSG_FUNCTION_RM_API_CONTROL: _bindgen_ty_2 = 204;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_GPU_START_FABRIC_PROBE: _bindgen_ty_2 = 205;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_NVLINK_GET_INBAND_RECEIVED_DATA: _bindgen_ty_2 = 206;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_DATA: _bindgen_ty_2 = 207;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED_208: _bindgen_ty_2 = 208;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_GET_INFO_V2: _bindgen_ty_2 = 209;
+pub const NV_VGPU_MSG_FUNCTION_GET_BRAND_CAPS: _bindgen_ty_2 = 210;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_NVLINK_INBAND_SEND_DATA: _bindgen_ty_2 = 211;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_GPM_GUEST_BUFFER_INFO: _bindgen_ty_2 = 212;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_CONTROL_GSP_TRACE: _bindgen_ty_2 = 213;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_STENCIL_CLEAR: _bindgen_ty_2 = 214;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_VGPU_HEAP_STATS: _bindgen_ty_2 = 215;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_LIBOS_HEAP_STATS: _bindgen_ty_2 = 216;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_MMU_GCC_DEBUG: _bindgen_ty_2 = 217;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_GET_MODE_MMU_GCC_DEBUG: _bindgen_ty_2 = 218;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_HES: _bindgen_ty_2 = 219;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RELEASE_HES: _bindgen_ty_2 = 220;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_CCU_PROF: _bindgen_ty_2 = 221;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RELEASE_CCU_PROF: _bindgen_ty_2 = 222;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED: _bindgen_ty_2 = 223;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_GET_CHIPLET_HS_CREDIT_POOL: _bindgen_ty_2 = 224;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_GET_HS_CREDITS_MAPPING: _bindgen_ty_2 = 225;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_EXPORT: _bindgen_ty_2 = 226;
+pub const NV_VGPU_MSG_FUNCTION_NUM_FUNCTIONS: _bindgen_ty_2 = 227;
+pub type _bindgen_ty_2 = ffi::c_uint;
+pub const NV_VGPU_MSG_EVENT_FIRST_EVENT: _bindgen_ty_3 = 4096;
+pub const NV_VGPU_MSG_EVENT_GSP_INIT_DONE: _bindgen_ty_3 = 4097;
+pub const NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER: _bindgen_ty_3 = 4098;
+pub const NV_VGPU_MSG_EVENT_POST_EVENT: _bindgen_ty_3 = 4099;
+pub const NV_VGPU_MSG_EVENT_RC_TRIGGERED: _bindgen_ty_3 = 4100;
+pub const NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED: _bindgen_ty_3 = 4101;
+pub const NV_VGPU_MSG_EVENT_OS_ERROR_LOG: _bindgen_ty_3 = 4102;
+pub const NV_VGPU_MSG_EVENT_RG_LINE_INTR: _bindgen_ty_3 = 4103;
+pub const NV_VGPU_MSG_EVENT_GPUACCT_PERFMON_UTIL_SAMPLES: _bindgen_ty_3 = 4104;
+pub const NV_VGPU_MSG_EVENT_SIM_READ: _bindgen_ty_3 = 4105;
+pub const NV_VGPU_MSG_EVENT_SIM_WRITE: _bindgen_ty_3 = 4106;
+pub const NV_VGPU_MSG_EVENT_SEMAPHORE_SCHEDULE_CALLBACK: _bindgen_ty_3 = 4107;
+pub const NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT: _bindgen_ty_3 = 4108;
+pub const NV_VGPU_MSG_EVENT_VGPU_GSP_PLUGIN_TRIGGERED: _bindgen_ty_3 = 4109;
+pub const NV_VGPU_MSG_EVENT_PERF_GPU_BOOST_SYNC_LIMITS_CALLBACK: _bindgen_ty_3 = 4110;
+pub const NV_VGPU_MSG_EVENT_PERF_BRIDGELESS_INFO_UPDATE: _bindgen_ty_3 = 4111;
+pub const NV_VGPU_MSG_EVENT_VGPU_CONFIG: _bindgen_ty_3 = 4112;
+pub const NV_VGPU_MSG_EVENT_DISPLAY_MODESET: _bindgen_ty_3 = 4113;
+pub const NV_VGPU_MSG_EVENT_EXTDEV_INTR_SERVICE: _bindgen_ty_3 = 4114;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_256: _bindgen_ty_3 = 4115;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_512: _bindgen_ty_3 = 4116;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_1024: _bindgen_ty_3 = 4117;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_2048: _bindgen_ty_3 = 4118;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_4096: _bindgen_ty_3 = 4119;
+pub const NV_VGPU_MSG_EVENT_TIMED_SEMAPHORE_RELEASE: _bindgen_ty_3 = 4120;
+pub const NV_VGPU_MSG_EVENT_NVLINK_IS_GPU_DEGRADED: _bindgen_ty_3 = 4121;
+pub const NV_VGPU_MSG_EVENT_PFM_REQ_HNDLR_STATE_SYNC_CALLBACK: _bindgen_ty_3 = 4122;
+pub const NV_VGPU_MSG_EVENT_NVLINK_FAULT_UP: _bindgen_ty_3 = 4123;
+pub const NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE: _bindgen_ty_3 = 4124;
+pub const NV_VGPU_MSG_EVENT_MIG_CI_CONFIG_UPDATE: _bindgen_ty_3 = 4125;
+pub const NV_VGPU_MSG_EVENT_UPDATE_GSP_TRACE: _bindgen_ty_3 = 4126;
+pub const NV_VGPU_MSG_EVENT_NVLINK_FATAL_ERROR_RECOVERY: _bindgen_ty_3 = 4127;
+pub const NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD: _bindgen_ty_3 = 4128;
+pub const NV_VGPU_MSG_EVENT_FECS_ERROR: _bindgen_ty_3 = 4129;
+pub const NV_VGPU_MSG_EVENT_RECOVERY_ACTION: _bindgen_ty_3 = 4130;
+pub const NV_VGPU_MSG_EVENT_NUM_EVENTS: _bindgen_ty_3 = 4131;
+pub type _bindgen_ty_3 = ffi::c_uint;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+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, Zeroable)]
+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, Zeroable)]
+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, Zeroable)]
+pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_1 {
+ pub pa: u64_,
+ pub size: u64_,
+}
+#[repr(C)]
+#[derive(Copy, Clone, Zeroable)]
+pub union rpc_message_rpc_union_field_v03_00 {
+ pub spare: u32_,
+ pub cpuRmGfid: u32_,
+}
+impl Default for rpc_message_rpc_union_field_v03_00 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+pub type rpc_message_rpc_union_field_v = rpc_message_rpc_union_field_v03_00;
+#[repr(C)]
+pub struct rpc_message_header_v03_00 {
+ pub header_version: u32_,
+ pub signature: u32_,
+ pub length: u32_,
+ pub function: u32_,
+ pub rpc_result: u32_,
+ pub rpc_result_private: u32_,
+ pub sequence: u32_,
+ pub u: rpc_message_rpc_union_field_v,
+ pub rpc_message_data: __IncompleteArrayField<u8_>,
+}
+impl Default for rpc_message_header_v03_00 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+pub type rpc_message_header_v = rpc_message_header_v03_00;
#[repr(C)]
#[derive(Copy, Clone, Zeroable)]
pub struct GspFwWprMeta {
@@ -145,3 +516,41 @@ pub struct LibosMemoryRegionInitArgument {
pub loc: u8_,
pub __bindgen_padding_0: [u8; 6usize],
}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct msgqTxHeader {
+ pub version: u32_,
+ pub size: u32_,
+ pub msgSize: u32_,
+ pub msgCount: u32_,
+ pub writePtr: u32_,
+ pub flags: u32_,
+ pub rxHdrOff: u32_,
+ pub entryOff: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct msgqRxHeader {
+ pub readPtr: u32_,
+}
+#[repr(C)]
+#[repr(align(8))]
+#[derive(Zeroable)]
+pub struct GSP_MSG_QUEUE_ELEMENT {
+ pub authTagBuffer: [u8_; 16usize],
+ pub aadBuffer: [u8_; 16usize],
+ pub checkSum: u32_,
+ pub seqNum: u32_,
+ pub elemCount: u32_,
+ pub __bindgen_padding_0: [u8; 4usize],
+ pub rpc: rpc_message_header_v,
+}
+impl Default for GSP_MSG_QUEUE_ELEMENT {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 934003cab8a8..41fdda8a0748 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -86,6 +86,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 {
diff --git a/drivers/gpu/nova-core/sbuffer.rs b/drivers/gpu/nova-core/sbuffer.rs
index bfd64fc21bbb..57153c1c3515 100644
--- a/drivers/gpu/nova-core/sbuffer.rs
+++ b/drivers/gpu/nova-core/sbuffer.rs
@@ -49,7 +49,6 @@ impl<'a, I> SBufferIter<I>
/// let sum: u8 = sbuffer.sum();
/// assert_eq!(sum, 45);
/// ```
- #[expect(unused)]
pub(crate) fn new_reader(slices: impl IntoIterator<IntoIter = I>) -> Self
where
I: Iterator<Item = &'a [u8]>,
@@ -71,7 +70,6 @@ pub(crate) fn new_reader(slices: impl IntoIterator<IntoIter = I>) -> Self
/// assert_eq!(buf2, [5, 6, 7, 8, 9]);
///
/// ```
- #[expect(unused)]
pub(crate) fn new_writer(slices: impl IntoIterator<IntoIter = I>) -> Self
where
I: Iterator<Item = &'a mut [u8]>,
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 10/16] gpu: nova-core: gsp: Create rmargs
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (8 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 09/16] gpu: nova-core: gsp: Add GSP command queue bindings and handling Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 11/16] gpu: nova-core: gsp: Add SetSystemInfo command Alexandre Courbot
` (6 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
From: Alistair Popple <apopple@nvidia.com>
Initialise the GSP resource manager arguments (rmargs) which provides
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.
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/gpu/nova-core/gsp.rs | 12 +++++++++++
drivers/gpu/nova-core/gsp/cmdq.rs | 26 ++++++++++++++++++++++-
drivers/gpu/nova-core/gsp/fw.rs | 44 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 80 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 46b3e4819473..bc897e57f396 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -15,6 +15,7 @@
pub(crate) mod cmdq;
mod fw;
+use fw::GspArgumentsCached;
use fw::LibosMemoryRegionInitArgument;
pub(crate) use fw::{GspFwWprMeta, LibosParams};
@@ -101,6 +102,8 @@ pub(crate) struct Gsp {
logrm: LogBuffer,
/// Command queue.
pub(crate) cmdq: Cmdq,
+ /// RM arguments.
+ rmargs: CoherentAllocation<GspArgumentsCached>,
}
impl Gsp {
@@ -127,11 +130,20 @@ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<impl PinInit<Self
let cmdq = Cmdq::new(dev)?;
+ let rmargs = CoherentAllocation::<GspArgumentsCached>::alloc_coherent(
+ dev,
+ 1,
+ GFP_KERNEL | __GFP_ZERO,
+ )?;
+ dma_write!(rmargs[0] = fw::GspArgumentsCached::new(&cmdq))?;
+ dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", &rmargs))?;
+
Ok(try_pin_init!(Self {
libos,
loginit,
logintr,
logrm,
+ rmargs,
cmdq,
}))
}
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 8461738b1110..4a6c0e14ed53 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -11,7 +11,10 @@
use kernel::{
device,
- dma::CoherentAllocation,
+ dma::{
+ CoherentAllocation,
+ DmaAddress, //
+ },
dma_write,
io::poll::read_poll_timeout,
prelude::*,
@@ -33,6 +36,7 @@
MsgqTxHeader, //
},
PteArray,
+ GSP_PAGE_SHIFT,
GSP_PAGE_SIZE, //
},
num,
@@ -378,6 +382,22 @@ pub(crate) struct Cmdq {
}
impl Cmdq {
+ /// Offset of the data after the PTEs.
+ const POST_PTE_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq);
+
+ /// Offset of command queue ring buffer.
+ pub(crate) const CMDQ_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq)
+ + core::mem::offset_of!(Msgq, msgq)
+ - Self::POST_PTE_OFFSET;
+
+ /// Offset of message queue ring buffer.
+ pub(crate) const STATQ_OFFSET: usize = core::mem::offset_of!(GspMem, gspq)
+ + core::mem::offset_of!(Msgq, msgq)
+ - Self::POST_PTE_OFFSET;
+
+ /// Number of page table entries for the GSP shared region.
+ pub(crate) const NUM_PTES: usize = size_of::<GspMem>() >> GSP_PAGE_SHIFT;
+
/// Creates a new command queue for `dev`.
pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<Cmdq> {
let gsp_mem = DmaGspMem::new(dev)?;
@@ -581,4 +601,8 @@ pub(crate) fn receive_msg_from_gsp<M: MessageFromGsp, R>(
result
}
+
+ pub(crate) fn dma_handle(&self) -> DmaAddress {
+ self.gsp_mem.0.dma_handle()
+ }
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index ceda61c99b92..b083a6a5754c 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -31,7 +31,10 @@
fb::FbLayout,
firmware::gsp::GspFirmware,
gpu::Chipset,
- gsp::GSP_PAGE_SIZE,
+ gsp::{
+ cmdq::Cmdq, //
+ GSP_PAGE_SIZE,
+ },
num::{
self,
FromSafeCast, //
@@ -568,3 +571,42 @@ unsafe impl AsBytes for GspMsgElement {}
// SAFETY: This struct only contains integer types for which all bit patterns
// are valid.
unsafe impl FromBytes for GspMsgElement {}
+
+/// Arguments for GSP startup.
+#[repr(transparent)]
+pub(crate) struct GspArgumentsCached(bindings::GSP_ARGUMENTS_CACHED);
+
+impl GspArgumentsCached {
+ /// Creates the arguments for starting the GSP up using `cmdq` as its command queue.
+ pub(crate) fn new(cmdq: &Cmdq) -> Self {
+ Self(bindings::GSP_ARGUMENTS_CACHED {
+ messageQueueInitArguments: MessageQueueInitArguments::new(cmdq).0,
+ bDmemStack: 1,
+ ..Default::default()
+ })
+ }
+}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for GspArgumentsCached {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for GspArgumentsCached {}
+
+/// Init arguments for the message queue.
+#[repr(transparent)]
+struct MessageQueueInitArguments(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS);
+
+impl MessageQueueInitArguments {
+ /// Creates a new init arguments structure for `cmdq`.
+ fn new(cmdq: &Cmdq) -> Self {
+ Self(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS {
+ sharedMemPhysAddr: cmdq.dma_handle(),
+ pageTableEntryCount: num::usize_into_u32::<{ Cmdq::NUM_PTES }>(),
+ cmdQueueOffset: num::usize_as_u64(Cmdq::CMDQ_OFFSET),
+ statQueueOffset: num::usize_as_u64(Cmdq::STATQ_OFFSET),
+ ..Default::default()
+ })
+ }
+}
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 11/16] gpu: nova-core: gsp: Add SetSystemInfo command
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (9 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 10/16] gpu: nova-core: gsp: Create rmargs Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 12/16] gpu: nova-core: gsp: Add SetRegistry command Alexandre Courbot
` (5 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
From: Alistair Popple <apopple@nvidia.com>
Add support for sending the SetSystemInfo command, which provides
required hardware information to the GSP and is critical to its
initialization.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gsp.rs | 1 +
drivers/gpu/nova-core/gsp/boot.rs | 10 +-
drivers/gpu/nova-core/gsp/cmdq.rs | 1 -
drivers/gpu/nova-core/gsp/commands.rs | 31 +++++
drivers/gpu/nova-core/gsp/fw.rs | 1 +
drivers/gpu/nova-core/gsp/fw/commands.rs | 56 +++++++++
drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 132 ++++++++++++++++++++++
7 files changed, 229 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index bc897e57f396..325d8ecad7dd 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -13,6 +13,7 @@
use crate::gsp::cmdq::Cmdq;
pub(crate) mod cmdq;
+pub(crate) mod commands;
mod fw;
use fw::GspArgumentsCached;
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 5ea53250bf37..b1c1fe77cbf2 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -29,7 +29,10 @@
FIRMWARE_VERSION, //
},
gpu::Chipset,
- gsp::GspFwWprMeta,
+ gsp::{
+ commands,
+ GspFwWprMeta, //
+ },
regs,
vbios::Vbios,
};
@@ -119,7 +122,7 @@ fn run_fwsec_frts(
///
/// Upon return, the GSP is up and running, and its runtime object given as return value.
pub(crate) fn boot(
- self: Pin<&mut Self>,
+ mut self: Pin<&mut Self>,
pdev: &pci::Device<device::Bound>,
bar: &Bar0,
chipset: Chipset,
@@ -153,6 +156,9 @@ pub(crate) fn boot(
CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
+ self.cmdq
+ .send_gsp_command(bar, commands::SetSystemInfo::new(pdev))?;
+
Ok(())
}
}
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 4a6c0e14ed53..4749fe88e621 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -440,7 +440,6 @@ fn notify_gsp(bar: &Bar0) {
/// written to by its [`CommandToGsp::init_variable_payload`] method.
///
/// Error codes returned by the command initializers are propagated as-is.
- #[expect(unused)]
pub(crate) fn send_gsp_command<M>(&mut self, bar: &Bar0, command: M) -> Result
where
M: CommandToGsp,
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
new file mode 100644
index 000000000000..091dbe59f655
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::device;
+use kernel::pci;
+use kernel::prelude::*;
+
+use super::fw::commands::GspSetSystemInfo;
+use super::fw::MsgFunction;
+use crate::gsp::cmdq::CommandToGsp;
+
+/// The `GspSetSystemInfo` command.
+pub(crate) struct SetSystemInfo<'a> {
+ pdev: &'a pci::Device<device::Bound>,
+}
+
+impl<'a> SetSystemInfo<'a> {
+ /// Creates a new `GspSetSystemInfo` command using the parameters of `pdev`.
+ pub(crate) fn new(pdev: &'a pci::Device<device::Bound>) -> Self {
+ Self { pdev }
+ }
+}
+
+impl<'a> CommandToGsp for SetSystemInfo<'a> {
+ const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo;
+ type Command = GspSetSystemInfo;
+ type InitError = Error;
+
+ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
+ GspSetSystemInfo::init(self.pdev)
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index b083a6a5754c..cacdfb2d4810 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
+pub(crate) mod commands;
mod r570_144;
// Alias to avoid repeating the version number with every use.
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
new file mode 100644
index 000000000000..0d3c46f793dd
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::prelude::*;
+use kernel::transmute::{AsBytes, FromBytes};
+use kernel::{device, pci};
+
+use crate::gsp::GSP_PAGE_SIZE;
+
+use super::bindings;
+
+/// Payload of the `GspSetSystemInfo` command.
+#[repr(transparent)]
+pub(crate) struct GspSetSystemInfo {
+ inner: bindings::GspSystemInfo,
+}
+static_assert!(size_of::<GspSetSystemInfo>() < GSP_PAGE_SIZE);
+
+impl GspSetSystemInfo {
+ /// Returns an in-place initializer for the `GspSetSystemInfo` command.
+ #[allow(non_snake_case)]
+ pub(crate) fn init<'a>(dev: &'a pci::Device<device::Bound>) -> impl Init<Self, Error> + 'a {
+ type InnerGspSystemInfo = bindings::GspSystemInfo;
+ let init_inner = try_init!(InnerGspSystemInfo {
+ gpuPhysAddr: dev.resource_start(0)?,
+ gpuPhysFbAddr: dev.resource_start(1)?,
+ gpuPhysInstAddr: dev.resource_start(3)?,
+ nvDomainBusDeviceFunc: u64::from(dev.dev_id()),
+
+ // Using TASK_SIZE in r535_gsp_rpc_set_system_info() seems wrong because
+ // TASK_SIZE is per-task. That's probably a design issue in GSP-RM though.
+ maxUserVa: (1 << 47) - 4096,
+ pciConfigMirrorBase: 0x088000,
+ pciConfigMirrorSize: 0x001000,
+
+ PCIDeviceID: (u32::from(dev.device_id()) << 16) | u32::from(dev.vendor_id().as_raw()),
+ PCISubDeviceID: (u32::from(dev.subsystem_device_id()) << 16)
+ | u32::from(dev.subsystem_vendor_id()),
+ PCIRevisionID: u32::from(dev.revision_id()),
+ bIsPrimary: 0,
+ bPreserveVideoMemoryAllocations: 0,
+ ..Zeroable::init_zeroed()
+ });
+
+ try_init!(GspSetSystemInfo {
+ inner <- init_inner,
+ })
+ }
+}
+
+// SAFETY: These structs don't meet the no-padding requirements of AsBytes but
+// that is not a problem because they are not used outside the kernel.
+unsafe impl AsBytes for GspSetSystemInfo {}
+
+// SAFETY: These structs don't meet the no-padding requirements of FromBytes but
+// that is not a problem because they are not used outside the kernel.
+unsafe impl FromBytes for GspSetSystemInfo {}
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index 17fb2392ec3c..1251b0c313ce 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -321,6 +321,138 @@ fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
pub type _bindgen_ty_3 = ffi::c_uint;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct DOD_METHOD_DATA {
+ pub status: u32_,
+ pub acpiIdListLen: u32_,
+ pub acpiIdList: [u32_; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+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, Zeroable)]
+pub struct MUX_METHOD_DATA_ELEMENT {
+ pub acpiId: u32_,
+ pub mode: u32_,
+ pub status: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+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, Zeroable)]
+pub struct CAPS_METHOD_DATA {
+ pub status: u32_,
+ pub optimusCaps: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+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, Zeroable)]
+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, Zeroable)]
+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, Zeroable)]
+pub struct GSP_PCIE_CONFIG_REG {
+ pub linkCap: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+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, Zeroable)]
pub struct MESSAGE_QUEUE_INIT_ARGUMENTS {
pub sharedMemPhysAddr: u64_,
pub pageTableEntryCount: u32_,
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 12/16] gpu: nova-core: gsp: Add SetRegistry command
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (10 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 11/16] gpu: nova-core: gsp: Add SetSystemInfo command Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 13/16] gpu: nova-core: falcon: Add support to check if RISC-V is active Alexandre Courbot
` (4 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
Add support for sending the SetRegistry command, which is critical to
GSP initialization.
The RM registry is serialized into a packed format and sent via the
command queue. For now only three parameters which are required to boot
GSP are hardcoded. In the future a kernel module parameter will be added
to enable other parameters to be added.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
[acourbot@nvidia.com: split into its own patch.]
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 2 +
drivers/gpu/nova-core/gsp/commands.rs | 91 +++++++++++++++++++++++
drivers/gpu/nova-core/gsp/fw/commands.rs | 50 +++++++++++++
drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 16 ++++
drivers/gpu/nova-core/sbuffer.rs | 1 -
5 files changed, 159 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index b1c1fe77cbf2..dcb5b50e176f 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -158,6 +158,8 @@ pub(crate) fn boot(
self.cmdq
.send_gsp_command(bar, commands::SetSystemInfo::new(pdev))?;
+ self.cmdq
+ .send_gsp_command(bar, commands::SetRegistry::new())?;
Ok(())
}
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 091dbe59f655..cc32d4379283 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -1,12 +1,17 @@
// SPDX-License-Identifier: GPL-2.0
+use core::convert::Infallible;
+
use kernel::device;
use kernel::pci;
use kernel::prelude::*;
+use kernel::transmute::AsBytes;
use super::fw::commands::GspSetSystemInfo;
+use super::fw::commands::*;
use super::fw::MsgFunction;
use crate::gsp::cmdq::CommandToGsp;
+use crate::sbuffer::SBufferIter;
/// The `GspSetSystemInfo` command.
pub(crate) struct SetSystemInfo<'a> {
@@ -29,3 +34,89 @@ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
GspSetSystemInfo::init(self.pdev)
}
}
+
+struct RegistryEntry {
+ key: &'static str,
+ value: u32,
+}
+
+/// The `SetRegistry` command.
+pub(crate) struct SetRegistry {
+ entries: [RegistryEntry; Self::NUM_ENTRIES],
+}
+
+impl SetRegistry {
+ // For now we hard-code the registry entries. Future work will allow others to
+ // be added as module parameters.
+ const NUM_ENTRIES: usize = 3;
+
+ /// Creates a new `SetRegistry` command, using a set of hardcoded entries.
+ pub(crate) fn new() -> Self {
+ Self {
+ entries: [
+ // RMSecBusResetEnable - enables PCI secondary bus reset
+ RegistryEntry {
+ key: "RMSecBusResetEnable",
+ value: 1,
+ },
+ // RMForcePcieConfigSave - forces GSP-RM to preserve PCI configuration registers on
+ // any PCI reset.
+ RegistryEntry {
+ key: "RMForcePcieConfigSave",
+ value: 1,
+ },
+ // RMDevidCheckIgnore - allows GSP-RM to boot even if the PCI dev ID is not found
+ // in the internal product name database.
+ RegistryEntry {
+ key: "RMDevidCheckIgnore",
+ value: 1,
+ },
+ ],
+ }
+ }
+}
+
+impl CommandToGsp for SetRegistry {
+ const FUNCTION: MsgFunction = MsgFunction::SetRegistry;
+ type Command = PackedRegistryTable;
+ type InitError = Infallible;
+
+ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
+ PackedRegistryTable::init(Self::NUM_ENTRIES as u32, self.variable_payload_len() as u32)
+ }
+
+ fn variable_payload_len(&self) -> usize {
+ let mut key_size = 0;
+ for i in 0..Self::NUM_ENTRIES {
+ key_size += self.entries[i].key.len() + 1; // +1 for NULL terminator
+ }
+ Self::NUM_ENTRIES * size_of::<PackedRegistryEntry>() + key_size
+ }
+
+ fn init_variable_payload(
+ &self,
+ dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
+ ) -> Result {
+ let string_data_start_offset =
+ size_of::<PackedRegistryTable>() + Self::NUM_ENTRIES * size_of::<PackedRegistryEntry>();
+
+ // Array for string data.
+ let mut string_data = KVec::new();
+
+ for entry in self.entries.iter().take(Self::NUM_ENTRIES) {
+ dst.write_all(
+ PackedRegistryEntry::new(
+ (string_data_start_offset + string_data.len()) as u32,
+ entry.value,
+ )
+ .as_bytes(),
+ )?;
+
+ let key_bytes = entry.key.as_bytes();
+ string_data.extend_from_slice(key_bytes, GFP_KERNEL)?;
+ string_data.push(0, GFP_KERNEL)?;
+ }
+
+ dst.write_all(string_data.as_slice())
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index 0d3c46f793dd..e5aab4032175 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -54,3 +54,53 @@ unsafe impl AsBytes for GspSetSystemInfo {}
// SAFETY: These structs don't meet the no-padding requirements of FromBytes but
// that is not a problem because they are not used outside the kernel.
unsafe impl FromBytes for GspSetSystemInfo {}
+
+#[repr(transparent)]
+pub(crate) struct PackedRegistryEntry(bindings::PACKED_REGISTRY_ENTRY);
+
+impl PackedRegistryEntry {
+ pub(crate) fn new(offset: u32, value: u32) -> Self {
+ Self({
+ bindings::PACKED_REGISTRY_ENTRY {
+ nameOffset: offset,
+
+ // We only support DWORD types for now. Support for other types
+ // will come later if required.
+ type_: bindings::REGISTRY_TABLE_ENTRY_TYPE_DWORD as u8,
+ __bindgen_padding_0: Default::default(),
+ data: value,
+ length: 0,
+ }
+ })
+ }
+}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for PackedRegistryEntry {}
+
+/// Payload of the `SetRegistry` command.
+#[repr(transparent)]
+pub(crate) struct PackedRegistryTable {
+ inner: bindings::PACKED_REGISTRY_TABLE,
+}
+
+impl PackedRegistryTable {
+ #[allow(non_snake_case)]
+ pub(crate) fn init(num_entries: u32, size: u32) -> impl Init<Self> {
+ type InnerPackedRegistryTable = bindings::PACKED_REGISTRY_TABLE;
+ let init_inner = init!(InnerPackedRegistryTable {
+ numEntries: num_entries,
+ size,
+ entries: Default::default()
+ });
+
+ init!(PackedRegistryTable { inner <- init_inner })
+ }
+}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for PackedRegistryTable {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for PackedRegistryTable {}
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index 1251b0c313ce..32933874ff97 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -649,6 +649,22 @@ pub struct LibosMemoryRegionInitArgument {
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>,
+}
+#[repr(C)]
#[derive(Debug, Default, Copy, Clone, Zeroable)]
pub struct msgqTxHeader {
pub version: u32_,
diff --git a/drivers/gpu/nova-core/sbuffer.rs b/drivers/gpu/nova-core/sbuffer.rs
index 57153c1c3515..deb9c1469a95 100644
--- a/drivers/gpu/nova-core/sbuffer.rs
+++ b/drivers/gpu/nova-core/sbuffer.rs
@@ -198,7 +198,6 @@ fn get_slice_mut(&mut self, len: usize) -> Option<&'a mut [u8]> {
/// Ideally we would implement [`Write`], but it is not available in `core`.
/// So mimic `std::io::Write::write_all`.
- #[expect(unused)]
pub(crate) fn write_all(&mut self, mut src: &[u8]) -> Result {
while !src.is_empty() {
match self.get_slice_mut(src.len()) {
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 13/16] gpu: nova-core: falcon: Add support to check if RISC-V is active
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (11 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 12/16] gpu: nova-core: gsp: Add SetRegistry command Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 14/16] gpu: nova-core: falcon: Add support to write firmware version Alexandre Courbot
` (3 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot, Lyude Paul
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.
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 9 +++++++++
drivers/gpu/nova-core/regs.rs | 7 ++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index fe5b3256d972..4c1f36073533 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -612,4 +612,13 @@ 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.
+ #[expect(unused)]
+ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool {
+ let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID);
+ cpuctl.active_stat()
+ }
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 41fdda8a0748..5d5ba766cd61 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -339,7 +339,12 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
// PRISCV
-register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalconBase[0x00001668] {
+register!(NV_PRISCV_RISCV_CPUCTL @ PFalcon2Base[0x00000388] {
+ 0:0 halted as bool;
+ 7:7 active_stat as bool;
+});
+
+register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalcon2Base[0x00000668] {
0:0 valid as bool;
4:4 core_select as bool => PeregrineCoreSelect;
8:8 br_fetch as bool;
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 14/16] gpu: nova-core: falcon: Add support to write firmware version
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (12 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 13/16] gpu: nova-core: falcon: Add support to check if RISC-V is active Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 15/16] gpu: nova-core: gsp: Boot GSP Alexandre Courbot
` (2 subsequent siblings)
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot, Lyude Paul
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.
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 8 ++++++++
drivers/gpu/nova-core/regs.rs | 6 ++++++
2 files changed, 14 insertions(+)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 4c1f36073533..31904e1d804b 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -621,4 +621,12 @@ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool {
let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID);
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) {
+ regs::NV_PFALCON_FALCON_OS::default()
+ .set_value(app_version)
+ .write(bar, &E::ID);
+ }
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 5d5ba766cd61..274e53a1a44d 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -230,6 +230,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.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 15/16] gpu: nova-core: gsp: Boot GSP
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (13 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 14/16] gpu: nova-core: falcon: Add support to write firmware version Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 16/16] bitfields RANGE doc - not great Alexandre Courbot
2025-11-08 2:51 ` [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot, Lyude Paul
From: Alistair Popple <apopple@nvidia.com>
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.
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Alistair Popple <apopple@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 2 -
drivers/gpu/nova-core/firmware/riscv.rs | 3 +-
drivers/gpu/nova-core/gsp/boot.rs | 65 ++++++++++++++++++++++++++++++++-
3 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 31904e1d804b..05b124acbfc1 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -616,14 +616,12 @@ pub(crate) fn signature_reg_fuse_version(
/// Check if the RISC-V core is active.
///
/// Returns `true` if the RISC-V core is active, `false` otherwise.
- #[expect(unused)]
pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool {
let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID);
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) {
regs::NV_PFALCON_FALCON_OS::default()
.set_value(app_version)
diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs
index 7d82fb9876e8..28dfef63657a 100644
--- a/drivers/gpu/nova-core/firmware/riscv.rs
+++ b/drivers/gpu/nova-core/firmware/riscv.rs
@@ -57,7 +57,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(crate) code_offset: u32,
@@ -66,7 +65,7 @@ pub(crate) struct RiscvFirmware {
/// Offset at which the manifest starts in the firmware image.
pub(crate) manifest_offset: u32,
/// Application version.
- app_version: u32,
+ pub(crate) app_version: u32,
/// Device-mapped firmware image.
pub(crate) ucode: DmaObject,
}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index dcb5b50e176f..638ed6a14b80 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -4,8 +4,10 @@
device,
dma::CoherentAllocation,
dma_write,
+ io::poll::read_poll_timeout,
pci,
- prelude::*, //
+ prelude::*,
+ time::Delta, //
};
use crate::{
@@ -143,7 +145,7 @@ pub(crate) fn boot(
Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?;
- let _booter_loader = BooterFirmware::new(
+ let booter_loader = BooterFirmware::new(
dev,
BooterKind::Loader,
chipset,
@@ -161,6 +163,65 @@ pub(crate) fn boot(
self.cmdq
.send_gsp_command(bar, commands::SetRegistry::new())?;
+ gsp_falcon.reset(bar)?;
+ let libos_handle = self.libos.dma_handle();
+ 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, &booter_loader)?;
+ let wpr_handle = wpr_meta.dma_handle();
+ 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
+ );
+
+ if mbox0 != 0 {
+ dev_err!(
+ pdev.as_ref(),
+ "Booter-load failed with error {:#x}\n",
+ mbox0
+ );
+ return Err(ENODEV);
+ }
+
+ gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
+
+ // Poll for RISC-V to become active before running sequencer
+ read_poll_timeout(
+ || Ok(gsp_falcon.is_riscv_active(bar)),
+ |val: &bool| *val,
+ Delta::from_millis(10),
+ Delta::from_secs(5),
+ )?;
+
+ dev_dbg!(
+ pdev.as_ref(),
+ "RISC-V active? {}\n",
+ gsp_falcon.is_riscv_active(bar),
+ );
+
Ok(())
}
}
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 16/16] bitfields RANGE doc - not great
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (14 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 15/16] gpu: nova-core: gsp: Boot GSP Alexandre Courbot
@ 2025-11-07 23:43 ` Alexandre Courbot
2025-11-08 0:10 ` Alexandre Courbot
2025-11-08 2:51 ` [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
16 siblings, 1 reply; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-07 23:43 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot, Alexandre Courbot
From: Alexandre Courbot <gnurou@gmail.com>
---
drivers/gpu/nova-core/bitfield.rs | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitfield.rs
index 16e143658c51..90c714ec08ac 100644
--- a/drivers/gpu/nova-core/bitfield.rs
+++ b/drivers/gpu/nova-core/bitfield.rs
@@ -249,7 +249,17 @@ impl $name {
{ $process:expr } $prim_type:tt $to_type:ty => $res_type:ty $(, $comment:literal)?;
) => {
::kernel::macros::paste!(
+ // #[doc=::core::concat!(
+ // "Inclusive range of the bits covered by the `",
+ // ::core::stringify!($name),
+ // "` field."
+ // )]
+ #[doc="Inclusive range of "]
+ #[doc=::core::stringify!($name)]
+ #[doc="foo"]
const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
+
+ /// Mask of the bits making up this field.
const [<$field:upper _MASK>]: $storage = {
// Generate mask for shifting
match ::core::mem::size_of::<$storage>() {
@@ -260,11 +270,13 @@ impl $name {
_ => ::kernel::build_error!("Unsupported storage type size")
}
};
+
+ /// Shift to apply to a value to align it with the start of this field.
const [<$field:upper _SHIFT>]: u32 = $lo;
);
$(
- #[doc="Returns the value of this field:"]
+ /// Returns the value of this field:
#[doc=$comment]
)?
#[inline(always)]
@@ -280,7 +292,7 @@ impl $name {
::kernel::macros::paste!(
$(
- #[doc="Sets the value of this field:"]
+ /// Sets the value of this field:
#[doc=$comment]
)?
#[inline(always)]
--
2.51.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v8 16/16] bitfields RANGE doc - not great
2025-11-07 23:43 ` [PATCH v8 16/16] bitfields RANGE doc - not great Alexandre Courbot
@ 2025-11-08 0:10 ` Alexandre Courbot
0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-08 0:10 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter, Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot, Nouveau
On Sat Nov 8, 2025 at 8:43 AM JST, Alexandre Courbot wrote:
> From: Alexandre Courbot <gnurou@gmail.com>
>
> ---
> drivers/gpu/nova-core/bitfield.rs | 16 ++++++++++++++--
> 1 file changed, 14 insertions(+), 2 deletions(-)
Please ignore this patch that somehow slipped into the wrong branch.
^_^;
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
` (15 preceding siblings ...)
2025-11-07 23:43 ` [PATCH v8 16/16] bitfields RANGE doc - not great Alexandre Courbot
@ 2025-11-08 2:51 ` Alexandre Courbot
16 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2025-11-08 2:51 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter, Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, dri-devel, linux-kernel, rust-for-linux,
Lyude Paul, Alexandre Courbot, Nouveau
On Sat Nov 8, 2025 at 8:43 AM JST, Alexandre Courbot wrote:
> Hopefully a close-to-mergeable revision. Lots of documentation added,
> making the process of understanding the code hopefully easier, and I
> have finally given more attention to the top patches.
>
> This resulted in another refactor/simplification of the GSP command
> sending, moving it closer to Alistair's original idea of separating the
> commands from the objects representing them. This also means there is a
> single trait for commands, and a single method to send them. A couple
> Reviewed-by tags have been removed as that part of the code changed
> quite a bit.
>
> The message receive method in its current form won't be adequate as-is
> for the long-term, but it is fit to boot GSP.
>
> The hope is to merge this or a v9 before -rc6, so it can make it into
> 6.19.
... or maybe not, after looking more closely at Joel's follow-up series
it becomes clear that the message receive code should be improved. I'll
try to capture that in a v9, but maybe it will be too short a time to
get proper review before -rc6. It's not a big deal if this slips to the
next cycle though, as there are no outside dependencies on this series.
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2025-11-08 2:51 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-07 23:43 [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 01/16] gpu: nova-core: compute layout of more framebuffer regions required for GSP Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 02/16] gpu: nova-core: Set correct DMA mask Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 03/16] gpu: nova-core: num: add functions to safely convert a const value to a smaller type Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 04/16] gpu: nova-core: Create initial Gsp Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 05/16] gpu: nova-core: gsp: Create wpr metadata Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 06/16] gpu: nova-core: Add a slice-buffer (sbuffer) datastructure Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 07/16] gpu: nova-core: Add zeroable trait to bindings Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 08/16] rust: enable slice_flatten feature and provide it through an extension trait Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 09/16] gpu: nova-core: gsp: Add GSP command queue bindings and handling Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 10/16] gpu: nova-core: gsp: Create rmargs Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 11/16] gpu: nova-core: gsp: Add SetSystemInfo command Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 12/16] gpu: nova-core: gsp: Add SetRegistry command Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 13/16] gpu: nova-core: falcon: Add support to check if RISC-V is active Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 14/16] gpu: nova-core: falcon: Add support to write firmware version Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 15/16] gpu: nova-core: gsp: Boot GSP Alexandre Courbot
2025-11-07 23:43 ` [PATCH v8 16/16] bitfields RANGE doc - not great Alexandre Courbot
2025-11-08 0:10 ` Alexandre Courbot
2025-11-08 2:51 ` [PATCH v8 00/16] gpu: nova-core: Boot GSP to RISC-V active 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).