* [PATCH v3 0/8] gpu: nova-core: boot GSP with vGPU enabled
@ 2026-07-01 6:26 Zhi Wang
2026-07-01 6:26 ` [PATCH v3 1/8] PCI/IOV: Return unsigned int from pci_sriov_get_totalvfs() Zhi Wang
` (7 more replies)
0 siblings, 8 replies; 19+ messages in thread
From: Zhi Wang @ 2026-07-01 6:26 UTC (permalink / raw)
To: dakr, acourbot
Cc: airlied, simona, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, jhubbard, ecourtney,
joelagnelf, apopple, cjia, smitra, kjaju, alkumar, ankita,
aniketa, kwankhede, targupta, nova-gpu, linux-kernel, zhiwang,
Zhi Wang
Booting up GSP with vGPU enabled is part of the first milestone (M1)
together with the Rust fwctl abstraction [1] and the nova-core fwctl
driver [2] for upstream vGPU support. It allows us to validate the basic
GSP boot flow with vGPU enabled and upload vGPU types even before the
remaining nova-core dependencies are ready. The nova-vgpu WIP patches
for all milestones can be found at [3].
This version is based on drm-rust-next plus Alexandre's GSP boot process
consolidation series [4].
v3:
- Split the FSP response header rename from the FSP PRC vGPU mode query
into a separate patch.
- Change pci_sriov_get_totalvfs() to return unsigned int on the C side
while keeping the Rust helper as u16.
- Move the vGPU capability gate into a dedicated vgpu::hal module.
- Represent detected vGPU state as VgpuState instead of separate
enabled/total_vfs accessors.
- Keep total_vfs values below 2 on the disabled path, with an explicit
comment for the current single-VF limitation.
- Use the generated 570.144 GSP_FW_HEAP_SIZE_VGPU_DEFAULT binding for
vGPU WPR2 heap sizing, and keep unsupported 0/1-VF states out of the
vGPU heap path through VgpuState.
v2:
- Rebase on top of Alexandre's GSP boot process consolidation series.
- Drop the FSP response, FSP documentation, and GspBootContext patches
that are already in drm-rust-next or superseded by the prerequisite
boot consolidation series.
- Change pci_sriov_get_totalvfs() to return u16 and update existing C
callers accordingly.
- Make the Rust sriov_get_totalvfs() helper return u16 directly.
- Rework the FSP PRC vGPU mode query to use typed subcommand, object ID,
flags, request, and response structures.
- Move vGPU state detection before GSP boot into a read-only VgpuManager,
avoiding Mutex/Cell based mutation during boot.
- Add a HAL method for the vGPU capability gate.
- Split the SetRegistry changes into a dynamic-entry refactor and the
RMSetSriovMode functional change.
- Rework WPR2 heap sizing to consume VgpuManager, keep the vGPU heap-size
helper in gsp/fw.rs, and drop the 1VM heap-size special case.
[1] https://lore.kernel.org/rust-for-linux/20260217204909.211793-1-zhiw@nvidia.com/
[2] https://lore.kernel.org/rust-for-linux/20260305190936.398590-1-zhiw@nvidia.com/
[3] https://github.com/zhiwang-nvidia/nova-core/tree/zhi/nova-vgpu-wip
[4] https://lore.kernel.org/all/20260629-nova-bootcontext-v4-0-5539d8469590@nvidia.com/
Zhi Wang (8):
PCI/IOV: Return unsigned int from pci_sriov_get_totalvfs()
rust: pci: add sriov_get_totalvfs() helper
gpu: nova-core: fsp: rename FSP response header type
gpu: nova-core: read vGPU mode from FSP via PRC protocol
gpu: nova-core: add vGPU preludes
gpu: nova-core: build SetRegistry entries dynamically
gpu: nova-core: set RMSetSriovMode for vGPU
gpu: nova-core: reserve vGPU WPR2 heap
drivers/gpu/nova-core/fb.rs | 25 ++-
drivers/gpu/nova-core/fsp.rs | 189 +++++++++++++++++-
drivers/gpu/nova-core/gpu.rs | 7 +
drivers/gpu/nova-core/gsp.rs | 2 +
drivers/gpu/nova-core/gsp/boot.rs | 17 +-
drivers/gpu/nova-core/gsp/commands.rs | 80 +++++---
drivers/gpu/nova-core/gsp/fw.rs | 5 +
.../gpu/nova-core/gsp/fw/r570_144/bindings.rs | 1 +
drivers/gpu/nova-core/mctp.rs | 3 +
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/vgpu.rs | 79 ++++++++
drivers/gpu/nova-core/vgpu/hal.rs | 45 +++++
drivers/pci/iov.c | 2 +-
include/linux/pci.h | 4 +-
rust/kernel/pci.rs | 11 +
15 files changed, 425 insertions(+), 46 deletions(-)
create mode 100644 drivers/gpu/nova-core/vgpu.rs
create mode 100644 drivers/gpu/nova-core/vgpu/hal.rs
base-commit: 05508fc305cb0311ee21f9629a41f821f5e0216f
--
2.51.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v3 1/8] PCI/IOV: Return unsigned int from pci_sriov_get_totalvfs()
2026-07-01 6:26 [PATCH v3 0/8] gpu: nova-core: boot GSP with vGPU enabled Zhi Wang
@ 2026-07-01 6:26 ` Zhi Wang
2026-07-08 13:54 ` Alexandre Courbot
2026-07-01 6:26 ` [PATCH v3 2/8] rust: pci: add sriov_get_totalvfs() helper Zhi Wang
` (6 subsequent siblings)
7 siblings, 1 reply; 19+ messages in thread
From: Zhi Wang @ 2026-07-01 6:26 UTC (permalink / raw)
To: dakr, acourbot
Cc: airlied, simona, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, jhubbard, ecourtney,
joelagnelf, apopple, cjia, smitra, kjaju, alkumar, ankita,
aniketa, kwankhede, targupta, nova-gpu, linux-kernel, zhiwang,
Zhi Wang, Bjorn Helgaas, David Laight, linux-pci
pci_sriov_get_totalvfs() reports a VF count, not an errno-style
status. It returns 0 when SR-IOV is unavailable or the device is not a
PF, and otherwise returns the PF's driver_max_VFs value.
driver_max_VFs is stored as a u16 in struct pci_sriov. It is derived
from the SR-IOV TotalVFs field or from a driver-provided limit, so the
implementation cannot return a negative value.
Change the declaration, CONFIG_PCI_IOV stub, and implementation to
return unsigned int.
Suggested-by: Alexandre Courbot <acourbot@nvidia.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: David Laight <david.laight.linux@gmail.com>
Cc: Gary Guo <gary@garyguo.net>
Cc: linux-pci@vger.kernel.org
Link: https://lore.kernel.org/all/DJHPRE4TGGT8.BUTMYOF5YE05@nvidia.com/
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
drivers/pci/iov.c | 2 +-
include/linux/pci.h | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index b0d24839c084..9d408fb8ac25 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -1283,7 +1283,7 @@ EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
* SRIOV capability value of TotalVFs or the value of driver_max_VFs
* if the driver reduced it. Otherwise 0.
*/
-int pci_sriov_get_totalvfs(struct pci_dev *dev)
+unsigned int pci_sriov_get_totalvfs(struct pci_dev *dev)
{
if (!dev->is_physfn)
return 0;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index ebb5b9d76360..2b9c61de5f67 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -2569,7 +2569,7 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id);
int pci_num_vf(struct pci_dev *dev);
int pci_vfs_assigned(struct pci_dev *dev);
int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs);
-int pci_sriov_get_totalvfs(struct pci_dev *dev);
+unsigned int pci_sriov_get_totalvfs(struct pci_dev *dev);
int pci_sriov_configure_simple(struct pci_dev *dev, int nr_virtfn);
resource_size_t pci_iov_resource_size(const struct pci_dev *dev, int resno);
int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size);
@@ -2622,7 +2622,7 @@ static inline int pci_vfs_assigned(struct pci_dev *dev)
{ return 0; }
static inline int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
{ return 0; }
-static inline int pci_sriov_get_totalvfs(struct pci_dev *dev)
+static inline unsigned int pci_sriov_get_totalvfs(struct pci_dev *dev)
{ return 0; }
#define pci_sriov_configure_simple NULL
static inline resource_size_t pci_iov_resource_size(const struct pci_dev *dev,
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v3 2/8] rust: pci: add sriov_get_totalvfs() helper
2026-07-01 6:26 [PATCH v3 0/8] gpu: nova-core: boot GSP with vGPU enabled Zhi Wang
2026-07-01 6:26 ` [PATCH v3 1/8] PCI/IOV: Return unsigned int from pci_sriov_get_totalvfs() Zhi Wang
@ 2026-07-01 6:26 ` Zhi Wang
2026-07-08 14:03 ` Alexandre Courbot
2026-07-01 6:26 ` [PATCH v3 3/8] gpu: nova-core: fsp: rename FSP response header type Zhi Wang
` (5 subsequent siblings)
7 siblings, 1 reply; 19+ messages in thread
From: Zhi Wang @ 2026-07-01 6:26 UTC (permalink / raw)
To: dakr, acourbot
Cc: airlied, simona, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, jhubbard, ecourtney,
joelagnelf, apopple, cjia, smitra, kjaju, alkumar, ankita,
aniketa, kwankhede, targupta, nova-gpu, linux-kernel, zhiwang,
Zhi Wang, Bjorn Helgaas, David Laight, linux-pci
Expose pci_sriov_get_totalvfs() to Rust PCI drivers so they can query
how many SR-IOV VFs a device supports.
Cc: Alexandre Courbot <acourbot@nvidia.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: David Laight <david.laight.linux@gmail.com>
Cc: Gary Guo <gary@garyguo.net>
Cc: linux-pci@vger.kernel.org
Link: https://lore.kernel.org/all/DJHPRE4TGGT8.BUTMYOF5YE05@nvidia.com/
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
rust/kernel/pci.rs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 5071cae6543f..21c51981c02e 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -450,6 +450,17 @@ pub fn pci_class(&self) -> Class {
// SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
Class::from_raw(unsafe { (*self.as_raw()).class })
}
+
+ /// Returns the total number of VFs, or 0 if SR-IOV is not available.
+ #[inline]
+ pub fn sriov_get_totalvfs(&self) -> u16 {
+ // SAFETY: `self.as_raw()` is a valid pointer to a `struct pci_dev`.
+ let total_vfs = unsafe { bindings::pci_sriov_get_totalvfs(self.as_raw()) };
+
+ // CAST: The C helper returns `unsigned int`, but the value originates
+ // from TotalVFs/driver_max_VFs, so this cast cannot truncate.
+ total_vfs as u16
+ }
}
impl<'a> Device<device::Core<'a>> {
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v3 3/8] gpu: nova-core: fsp: rename FSP response header type
2026-07-01 6:26 [PATCH v3 0/8] gpu: nova-core: boot GSP with vGPU enabled Zhi Wang
2026-07-01 6:26 ` [PATCH v3 1/8] PCI/IOV: Return unsigned int from pci_sriov_get_totalvfs() Zhi Wang
2026-07-01 6:26 ` [PATCH v3 2/8] rust: pci: add sriov_get_totalvfs() helper Zhi Wang
@ 2026-07-01 6:26 ` Zhi Wang
2026-07-08 13:37 ` Alexandre Courbot
2026-07-01 6:26 ` [PATCH v3 4/8] gpu: nova-core: read vGPU mode from FSP via PRC protocol Zhi Wang
` (4 subsequent siblings)
7 siblings, 1 reply; 19+ messages in thread
From: Zhi Wang @ 2026-07-01 6:26 UTC (permalink / raw)
To: dakr, acourbot
Cc: airlied, simona, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, jhubbard, ecourtney,
joelagnelf, apopple, cjia, smitra, kjaju, alkumar, ankita,
aniketa, kwankhede, targupta, nova-gpu, linux-kernel, zhiwang,
Zhi Wang
FSP message handling currently uses FspResponse for the common response
prefix containing the MCTP header, NVDM header and command response
payload.
That name is too broad once other FSP response formats reuse the same
prefix and append protocol-specific payloads. Rename it to
FspResponseHeader so subsequent response structures can embed the common
header without overloading the meaning of FspResponse.
Suggested-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://lore.kernel.org/all/DJMBI9CN2Z67.2T02SR8TAWEC5@nvidia.com/
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
drivers/gpu/nova-core/fsp.rs | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index 574e1627e63c..a555c5290baa 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -84,16 +84,16 @@ fn new(nvdm_type: NvdmType) -> Self {
}
}
-/// Complete FSP response structure with MCTP and NVDM headers.
+/// Common FSP response header with MCTP, NVDM and command response payloads.
#[repr(C, packed)]
#[derive(Clone, Copy)]
-struct FspResponse {
+struct FspResponseHeader {
header: FspMessageHeader,
response: NvdmPayloadCommandResponse,
}
-// SAFETY: FspResponse is a packed C struct with only integral fields.
-unsafe impl FromBytes for FspResponse {}
+// SAFETY: FspResponseHeader is a packed C struct with only integral fields.
+unsafe impl FromBytes for FspResponseHeader {}
/// Trait implemented by types representing a message to send to FSP.
///
@@ -272,10 +272,11 @@ fn send_sync_fsp<M>(&mut self, dev: &device::Device, msg: &M) -> Result<KVec<u8>
dev_err!(dev, "FSP response error: {:?}\n", e);
})?;
- let (response, _) = FspResponse::from_bytes_prefix(&response_buf[..]).ok_or_else(|| {
- dev_err!(dev, "FSP response too small: {}\n", response_buf.len());
- EIO
- })?;
+ let (response, _) =
+ FspResponseHeader::from_bytes_prefix(&response_buf[..]).ok_or_else(|| {
+ dev_err!(dev, "FSP response too small: {}\n", response_buf.len());
+ EIO
+ })?;
let mctp_header = response.header.mctp_header;
let nvdm_header = response.header.nvdm_header;
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v3 4/8] gpu: nova-core: read vGPU mode from FSP via PRC protocol
2026-07-01 6:26 [PATCH v3 0/8] gpu: nova-core: boot GSP with vGPU enabled Zhi Wang
` (2 preceding siblings ...)
2026-07-01 6:26 ` [PATCH v3 3/8] gpu: nova-core: fsp: rename FSP response header type Zhi Wang
@ 2026-07-01 6:26 ` Zhi Wang
2026-07-01 6:26 ` [PATCH v3 5/8] gpu: nova-core: add vGPU preludes Zhi Wang
` (3 subsequent siblings)
7 siblings, 0 replies; 19+ messages in thread
From: Zhi Wang @ 2026-07-01 6:26 UTC (permalink / raw)
To: dakr, acourbot
Cc: airlied, simona, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, jhubbard, ecourtney,
joelagnelf, apopple, cjia, smitra, kjaju, alkumar, ankita,
aniketa, kwankhede, targupta, nova-gpu, linux-kernel, zhiwang,
Zhi Wang
vGPU boot needs to know whether firmware reports vGPU mode as active.
FSP's Management Partition exposes PRC (Product Reconfiguration Control)
as an API for reading device configuration knobs without firmware
updates. The vGPU mode knob is one such configuration value.
Add typed PRC request and response payloads for the vGPU mode object,
add the PRC NVDM type, and parse the returned knob value into VgpuMode.
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
drivers/gpu/nova-core/fsp.rs | 173 ++++++++++++++++++++++++++++++++++
drivers/gpu/nova-core/mctp.rs | 3 +
2 files changed, 176 insertions(+)
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index a555c5290baa..52eeee82f75e 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -51,6 +51,56 @@
mod hal;
+/// PRC message sub-command.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+enum PrcMessageSubcmd {
+ /// Read a PRC knob value.
+ Read = 0x0c,
+}
+
+impl From<PrcMessageSubcmd> for u8 {
+ fn from(value: PrcMessageSubcmd) -> Self {
+ value as u8
+ }
+}
+
+/// PRC object identifier.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+enum PrcObjectId {
+ /// vGPU mode configuration knob.
+ VgpuMode = 0x29,
+}
+
+impl From<PrcObjectId> for u8 {
+ fn from(value: PrcObjectId) -> Self {
+ value as u8
+ }
+}
+
+kernel::impl_flags!(
+ /// PRC request flags.
+ #[derive(Clone, Copy, Default, PartialEq, Eq)]
+ struct PrcFlags(u8);
+
+ /// Individual PRC request flag.
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ enum PrcFlag {
+ /// Request the active knob value for the current boot.
+ Active = 1 << 1,
+ }
+);
+
+/// vGPU operating mode as reported by FSP via the PRC protocol.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum VgpuMode {
+ /// vGPU support is disabled on this GPU.
+ Disabled,
+ /// vGPU support is enabled on this GPU.
+ Enabled,
+}
+
/// FSP command response payload (`NVDM_PAYLOAD_COMMAND_RESPONSE`).
#[repr(C, packed)]
#[derive(Clone, Copy)]
@@ -60,6 +110,62 @@ struct NvdmPayloadCommandResponse {
error_code: u32,
}
+/// PRC message payload.
+///
+/// Sent to FSP to query or modify a device configuration knob.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct NvdmPayloadPrc {
+ sub_message_id: u8,
+ flags: u8,
+ object_id: u8,
+ reserved: u8,
+}
+
+impl NvdmPayloadPrc {
+ /// Constructs a PRC payload from typed protocol fields.
+ fn new(subcmd: PrcMessageSubcmd, object_id: PrcObjectId, flags: PrcFlags) -> Self {
+ Self {
+ sub_message_id: subcmd.into(),
+ flags: flags.into(),
+ object_id: object_id.into(),
+ reserved: 0,
+ }
+ }
+}
+
+// SAFETY: NvdmPayloadPrc is a packed C struct with only integral fields.
+unsafe impl AsBytes for NvdmPayloadPrc {}
+
+/// PRC response payload containing the knob state value.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct NvdmPayloadPrcResponse {
+ value_low: u8,
+ value_high: u8,
+ reserved1: u8,
+ reserved2: u8,
+}
+
+impl NvdmPayloadPrcResponse {
+ /// Returns the PRC knob value as a little-endian 16-bit integer.
+ fn value(self) -> u16 {
+ u16::from(self.value_low) | (u16::from(self.value_high) << 8)
+ }
+}
+
+impl TryFrom<NvdmPayloadPrcResponse> for VgpuMode {
+ type Error = kernel::error::Error;
+
+ fn try_from(value: NvdmPayloadPrcResponse) -> Result<Self> {
+ match value.value() {
+ 0 => Ok(VgpuMode::Disabled),
+ 1 => Ok(VgpuMode::Enabled),
+ _ => Err(EINVAL),
+ }
+ }
+}
+
/// Common MCTP and NVDM headers shared by all FSP messages.
#[repr(C, packed)]
#[derive(Clone, Copy)]
@@ -95,6 +201,17 @@ struct FspResponseHeader {
// SAFETY: FspResponseHeader is a packed C struct with only integral fields.
unsafe impl FromBytes for FspResponseHeader {}
+/// Complete FSP PRC response including the knob state payload.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct FspPrcResponse {
+ header: FspResponseHeader,
+ prc_data: NvdmPayloadPrcResponse,
+}
+
+// SAFETY: FspPrcResponse is a packed C struct with only integral fields.
+unsafe impl FromBytes for FspPrcResponse {}
+
/// Trait implemented by types representing a message to send to FSP.
///
/// This provides [`Fsp::send_sync_fsp`] with the information it needs to send
@@ -182,10 +299,35 @@ fn new<'a>(
// bytes are initialized.
unsafe impl AsBytes for FspCotMessage {}
+/// Complete FSP PRC message.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct FspPrcMessage {
+ header: FspMessageHeader,
+ prc: NvdmPayloadPrc,
+}
+
+impl FspPrcMessage {
+ /// Constructs a PRC message.
+ fn new(subcmd: PrcMessageSubcmd, object_id: PrcObjectId, flags: PrcFlags) -> Self {
+ Self {
+ header: FspMessageHeader::new(NvdmType::Prc),
+ prc: NvdmPayloadPrc::new(subcmd, object_id, flags),
+ }
+ }
+}
+
+// SAFETY: FspPrcMessage is a packed C struct with only integral fields.
+unsafe impl AsBytes for FspPrcMessage {}
+
impl MessageToFsp for FspCotMessage {
const NVDM_TYPE: NvdmType = NvdmType::Cot;
}
+impl MessageToFsp for FspPrcMessage {
+ const NVDM_TYPE: NvdmType = NvdmType::Prc;
+}
+
/// Bundled arguments for FMC boot via FSP Chain of Trust.
pub(crate) struct FmcBootArgs {
chipset: Chipset,
@@ -324,6 +466,37 @@ fn send_sync_fsp<M>(&mut self, dev: &device::Device, msg: &M) -> Result<KVec<u8>
Ok(response_buf)
}
+ /// Reads the active vGPU mode from FSP using the PRC protocol.
+ ///
+ /// Queries FSP's Management Partition for the active vGPU mode knob value.
+ #[expect(dead_code)]
+ pub(crate) fn read_vgpu_mode(
+ &mut self,
+ dev: &device::Device<device::Bound>,
+ ) -> Result<VgpuMode> {
+ let msg = KBox::new(
+ FspPrcMessage::new(
+ PrcMessageSubcmd::Read,
+ PrcObjectId::VgpuMode,
+ PrcFlags::from(PrcFlag::Active),
+ ),
+ GFP_KERNEL,
+ )?;
+
+ let response_buf = self.send_sync_fsp(dev, &*msg)?;
+ let (prc_response, _) =
+ FspPrcResponse::from_bytes_prefix(&response_buf[..]).ok_or_else(|| {
+ dev_err!(dev, "PRC response too small: {}\n", response_buf.len());
+ EIO
+ })?;
+
+ let prc_data = prc_response.prc_data;
+
+ VgpuMode::try_from(prc_data).inspect_err(|_| {
+ dev_err!(dev, "Unexpected vGPU mode value: {:#x}\n", prc_data.value());
+ })
+ }
+
/// Boots GSP FMC via FSP Chain of Trust.
///
/// Builds the CoT message from the pre-configured [`FmcBootArgs`], sends it
diff --git a/drivers/gpu/nova-core/mctp.rs b/drivers/gpu/nova-core/mctp.rs
index 482786e07bc7..b203c632bf20 100644
--- a/drivers/gpu/nova-core/mctp.rs
+++ b/drivers/gpu/nova-core/mctp.rs
@@ -13,6 +13,8 @@
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(u8)]
pub(crate) enum NvdmType {
+ /// PRC (Product Reconfiguration Control) message.
+ Prc = 0x13,
#[default]
/// Chain of Trust boot message.
Cot = 0x14,
@@ -25,6 +27,7 @@ impl TryFrom<u8> for NvdmType {
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
+ x if x == u8::from(Self::Prc) => Ok(Self::Prc),
x if x == u8::from(Self::Cot) => Ok(Self::Cot),
x if x == u8::from(Self::FspResponse) => Ok(Self::FspResponse),
_ => Err(value),
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v3 5/8] gpu: nova-core: add vGPU preludes
2026-07-01 6:26 [PATCH v3 0/8] gpu: nova-core: boot GSP with vGPU enabled Zhi Wang
` (3 preceding siblings ...)
2026-07-01 6:26 ` [PATCH v3 4/8] gpu: nova-core: read vGPU mode from FSP via PRC protocol Zhi Wang
@ 2026-07-01 6:26 ` Zhi Wang
2026-07-09 5:25 ` Alexandre Courbot
2026-07-01 6:26 ` [PATCH v3 6/8] gpu: nova-core: build SetRegistry entries dynamically Zhi Wang
` (2 subsequent siblings)
7 siblings, 1 reply; 19+ messages in thread
From: Zhi Wang @ 2026-07-01 6:26 UTC (permalink / raw)
To: dakr, acourbot
Cc: airlied, simona, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, jhubbard, ecourtney,
joelagnelf, apopple, cjia, smitra, kjaju, alkumar, ankita,
aniketa, kwankhede, targupta, nova-gpu, linux-kernel, zhiwang,
Zhi Wang
GSP boot needs a stable view of vGPU state before it starts building the
boot-time data structures that depend on SR-IOV and firmware policy. That
state must be derived once from the PCI VF count and the FSP PRC vGPU mode
knob before booting GSP.
Add VgpuManager to detect and retain the vGPU state during GPU
construction. Keep the vGPU capability gate local to the vGPU module with
a small vGPU HAL.
Cc: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
drivers/gpu/nova-core/fsp.rs | 1 -
drivers/gpu/nova-core/gpu.rs | 7 +++
drivers/gpu/nova-core/gsp.rs | 2 +
drivers/gpu/nova-core/gsp/boot.rs | 13 +++++
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/vgpu.rs | 79 ++++++++++++++++++++++++++++++
drivers/gpu/nova-core/vgpu/hal.rs | 45 +++++++++++++++++
7 files changed, 147 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/nova-core/vgpu.rs
create mode 100644 drivers/gpu/nova-core/vgpu/hal.rs
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index 52eeee82f75e..3ce90d447685 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -469,7 +469,6 @@ fn send_sync_fsp<M>(&mut self, dev: &device::Device, msg: &M) -> Result<KVec<u8>
/// Reads the active vGPU mode from FSP using the PRC protocol.
///
/// Queries FSP's Management Partition for the active vGPU mode knob value.
- #[expect(dead_code)]
pub(crate) fn read_vgpu_mode(
&mut self,
dev: &device::Device<device::Bound>,
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index c0c0c82e3e39..6d6fc7d723e2 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -31,6 +31,7 @@
GspBootMethod, //
},
regs,
+ vgpu::VgpuManager, //
};
mod hal;
@@ -268,6 +269,8 @@ struct GspResources<'gpu> {
// TODO: use different resource types for each boot method, and make the relevant Gsp methods
// generic against them.
fsp: Option<Fsp<'gpu>>,
+ /// vGPU state detected before GSP boot.
+ vgpu: VgpuManager,
/// GSP runtime data.
#[pin]
gsp: Gsp,
@@ -312,6 +315,7 @@ fn drop(self: Pin<&mut Self>) {
gsp_falcon: &*this.gsp_falcon,
sec2_falcon: &*this.sec2_falcon,
fsp: this.fsp.as_mut(),
+ vgpu: &*this.vgpu,
},
bundle,
)
@@ -368,6 +372,8 @@ pub(crate) fn new(
GspBootMethod::Fsp => Some(Fsp::wait_secure_boot(dev, bar, spec.chipset)?),
},
+ vgpu: VgpuManager::new(pdev, spec.chipset, fsp.as_mut())?,
+
gsp <- Gsp::new(pdev),
// This member must be initialized last, so the `UnloadBundle` can never be dropped
@@ -380,6 +386,7 @@ pub(crate) fn new(
gsp_falcon,
sec2_falcon,
fsp: fsp.as_mut(),
+ vgpu,
})?,
}),
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index e89366b425fb..0012bcc6dc9a 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -51,6 +51,7 @@
},
},
num,
+ vgpu::VgpuManager, //
};
pub(crate) const GSP_PAGE_SHIFT: usize = 12;
@@ -69,6 +70,7 @@ pub(crate) struct GspBootContext<'ctx, 'gpu> {
pub(crate) gsp_falcon: &'ctx Falcon<'gpu, GspFalcon>,
pub(crate) sec2_falcon: &'ctx Falcon<'gpu, Sec2Falcon>,
pub(crate) fsp: Option<&'ctx mut Fsp<'gpu>>,
+ pub(crate) vgpu: &'ctx VgpuManager,
}
impl<'ctx, 'gpu> GspBootContext<'ctx, 'gpu> {
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index c347558aa8e5..5abab54639a4 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -26,6 +26,7 @@
commands,
GspFwWprMeta, //
},
+ vgpu::VgpuState, //
};
impl super::Gsp {
@@ -48,6 +49,18 @@ pub(crate) fn boot(
let dev = pdev.as_ref();
let hal = super::hal::gsp_hal(chipset);
+ let (vgpu_enabled, total_vfs) = match ctx.vgpu.state() {
+ VgpuState::Disabled => (false, 0),
+ VgpuState::Enabled { total_vfs } => (true, total_vfs),
+ };
+
+ dev_dbg!(
+ dev,
+ "vGPU enabled: {}, total VFs: {}\n",
+ vgpu_enabled,
+ total_vfs
+ );
+
let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?;
let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 735b8e17c6b6..2df2f773ec8e 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -26,6 +26,7 @@
mod regs;
mod sbuffer;
mod vbios;
+mod vgpu;
pub(crate) const MODULE_NAME: &core::ffi::CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
diff --git a/drivers/gpu/nova-core/vgpu.rs b/drivers/gpu/nova-core/vgpu.rs
new file mode 100644
index 000000000000..d2f311a8b2d5
--- /dev/null
+++ b/drivers/gpu/nova-core/vgpu.rs
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+ device,
+ pci,
+ prelude::*, //
+};
+
+use crate::{
+ fsp::{
+ Fsp,
+ VgpuMode, //
+ },
+ gpu::Chipset, //
+};
+
+mod hal;
+
+/// vGPU state detected during GPU construction.
+#[derive(Clone, Copy)]
+pub(crate) enum VgpuState {
+ /// vGPU mode is not enabled for this boot.
+ Disabled,
+ /// vGPU mode is enabled for this boot.
+ Enabled {
+ /// Total number of SR-IOV VFs supported by this device.
+ total_vfs: u16,
+ },
+}
+
+/// vGPU state manager.
+pub(crate) struct VgpuManager {
+ state: VgpuState,
+}
+
+impl VgpuManager {
+ /// Creates a vGPU manager by querying SR-IOV and the FSP PRC vGPU knob.
+ pub(crate) fn new(
+ pdev: &pci::Device<device::Bound>,
+ chipset: Chipset,
+ fsp: Option<&mut Fsp<'_>>,
+ ) -> Result<Self> {
+ let state = Self::detect_state(pdev, chipset, fsp)?;
+
+ Ok(Self { state })
+ }
+
+ fn detect_state(
+ pdev: &pci::Device<device::Bound>,
+ chipset: Chipset,
+ fsp: Option<&mut Fsp<'_>>,
+ ) -> Result<VgpuState> {
+ if !hal::supports_vgpu(chipset) {
+ return Ok(VgpuState::Disabled);
+ }
+
+ let total_vfs = pdev.sriov_get_totalvfs();
+ if total_vfs < 2 {
+ // The current vGPU path does not support single-VF SR-IOV devices yet.
+ // Treat 0 or 1 total VFs as vGPU-disabled for now; single-VF support can
+ // relax this gate once the manager handles that topology.
+ return Ok(VgpuState::Disabled);
+ }
+
+ let Some(fsp) = fsp else {
+ return Ok(VgpuState::Disabled);
+ };
+
+ match fsp.read_vgpu_mode(pdev.as_ref())? {
+ VgpuMode::Enabled => Ok(VgpuState::Enabled { total_vfs }),
+ VgpuMode::Disabled => Ok(VgpuState::Disabled),
+ }
+ }
+
+ /// Returns the detected vGPU state for this boot.
+ pub(crate) fn state(&self) -> VgpuState {
+ self.state
+ }
+}
diff --git a/drivers/gpu/nova-core/vgpu/hal.rs b/drivers/gpu/nova-core/vgpu/hal.rs
new file mode 100644
index 000000000000..e6fb6cec2599
--- /dev/null
+++ b/drivers/gpu/nova-core/vgpu/hal.rs
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use crate::gpu::{
+ Architecture,
+ Chipset, //
+};
+
+trait VgpuHal {
+ /// Returns whether this chipset can support vGPU.
+ fn supports_vgpu(&self) -> bool;
+}
+
+struct Tu102;
+
+impl VgpuHal for Tu102 {
+ fn supports_vgpu(&self) -> bool {
+ false
+ }
+}
+
+struct Gb202;
+
+impl VgpuHal for Gb202 {
+ fn supports_vgpu(&self) -> bool {
+ true
+ }
+}
+
+const TU102: Tu102 = Tu102;
+const GB202: Gb202 = Gb202;
+
+fn vgpu_hal(chipset: Chipset) -> &'static dyn VgpuHal {
+ match chipset.arch() {
+ Architecture::BlackwellGB20x => &GB202,
+ Architecture::Turing
+ | Architecture::Ampere
+ | Architecture::Hopper
+ | Architecture::Ada
+ | Architecture::BlackwellGB10x => &TU102,
+ }
+}
+
+pub(super) fn supports_vgpu(chipset: Chipset) -> bool {
+ vgpu_hal(chipset).supports_vgpu()
+}
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v3 6/8] gpu: nova-core: build SetRegistry entries dynamically
2026-07-01 6:26 [PATCH v3 0/8] gpu: nova-core: boot GSP with vGPU enabled Zhi Wang
` (4 preceding siblings ...)
2026-07-01 6:26 ` [PATCH v3 5/8] gpu: nova-core: add vGPU preludes Zhi Wang
@ 2026-07-01 6:26 ` Zhi Wang
2026-07-08 13:37 ` Alexandre Courbot
2026-07-01 6:26 ` [PATCH v3 7/8] gpu: nova-core: set RMSetSriovMode for vGPU Zhi Wang
2026-07-01 6:26 ` [PATCH v3 8/8] gpu: nova-core: reserve vGPU WPR2 heap Zhi Wang
7 siblings, 1 reply; 19+ messages in thread
From: Zhi Wang @ 2026-07-01 6:26 UTC (permalink / raw)
To: dakr, acourbot
Cc: airlied, simona, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, jhubbard, ecourtney,
joelagnelf, apopple, cjia, smitra, kjaju, alkumar, ankita,
aniketa, kwankhede, targupta, nova-gpu, linux-kernel, zhiwang,
Zhi Wang
The GSP SetRegistry command currently stores its registry entries in a
fixed-size array. That makes every additional runtime-dependent registry
object require reshaping the command data structure at the same time as the
feature that needs the new entry.
Keep the existing registry contents unchanged, but store them in a KVec so
SetRegistry can be constructed dynamically. The constructor now returns a
Result to propagate allocation failures while the command payload layout is
still computed from the final entry list.
Cc: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 2 +-
drivers/gpu/nova-core/gsp/commands.rs | 74 ++++++++++++++++-----------
2 files changed, 44 insertions(+), 32 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 5abab54639a4..139cedacffd2 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -102,7 +102,7 @@ pub(crate) fn boot(
self.cmdq
.send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
self.cmdq
- .send_command_no_wait(bar, commands::SetRegistry::new())?;
+ .send_command_no_wait(bar, commands::SetRegistry::new()?)?;
hal.post_boot(&self, ctx, &gsp_fw)?;
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 86a3747cd31c..dfb9f04e284d 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -67,37 +67,46 @@ struct RegistryEntry {
/// The `SetRegistry` command.
pub(crate) struct SetRegistry {
- entries: [RegistryEntry; Self::NUM_ENTRIES],
+ entries: KVec<RegistryEntry>,
}
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,
- },
- ],
- }
+ pub(crate) fn new() -> Result<Self> {
+ let mut entries = KVec::new();
+
+ // RMSecBusResetEnable - enables PCI secondary bus reset
+ entries.push(
+ RegistryEntry {
+ key: "RMSecBusResetEnable",
+ value: 1,
+ },
+ GFP_KERNEL,
+ )?;
+
+ // RMForcePcieConfigSave - forces GSP-RM to preserve PCI configuration registers on
+ // any PCI reset.
+ entries.push(
+ RegistryEntry {
+ key: "RMForcePcieConfigSave",
+ value: 1,
+ },
+ GFP_KERNEL,
+ )?;
+
+ // RMDevidCheckIgnore - allows GSP-RM to boot even if the PCI dev ID is not found
+ // in the internal product name database.
+ entries.push(
+ RegistryEntry {
+ key: "RMDevidCheckIgnore",
+ value: 1,
+ },
+ GFP_KERNEL,
+ )?;
+
+ Ok(Self { entries })
}
}
@@ -108,15 +117,18 @@ impl CommandToGsp for SetRegistry {
type InitError = Infallible;
fn init(&self) -> impl Init<Self::Command, Self::InitError> {
- Self::Command::init(Self::NUM_ENTRIES as u32, self.variable_payload_len() as u32)
+ Self::Command::init(
+ self.entries.len() 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
+ for entry in self.entries.iter() {
+ key_size += entry.key.len() + 1; // +1 for NULL terminator
}
- Self::NUM_ENTRIES * size_of::<fw::commands::PackedRegistryEntry>() + key_size
+ self.entries.len() * size_of::<fw::commands::PackedRegistryEntry>() + key_size
}
fn init_variable_payload(
@@ -124,12 +136,12 @@ fn init_variable_payload(
dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
) -> Result {
let string_data_start_offset = size_of::<Self::Command>()
- + Self::NUM_ENTRIES * size_of::<fw::commands::PackedRegistryEntry>();
+ + self.entries.len() * size_of::<fw::commands::PackedRegistryEntry>();
// Array for string data.
let mut string_data = KVec::new();
- for entry in self.entries.iter().take(Self::NUM_ENTRIES) {
+ for entry in self.entries.iter() {
dst.write_all(
fw::commands::PackedRegistryEntry::new(
(string_data_start_offset + string_data.len()) as u32,
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v3 7/8] gpu: nova-core: set RMSetSriovMode for vGPU
2026-07-01 6:26 [PATCH v3 0/8] gpu: nova-core: boot GSP with vGPU enabled Zhi Wang
` (5 preceding siblings ...)
2026-07-01 6:26 ` [PATCH v3 6/8] gpu: nova-core: build SetRegistry entries dynamically Zhi Wang
@ 2026-07-01 6:26 ` Zhi Wang
2026-07-01 6:26 ` [PATCH v3 8/8] gpu: nova-core: reserve vGPU WPR2 heap Zhi Wang
7 siblings, 0 replies; 19+ messages in thread
From: Zhi Wang @ 2026-07-01 6:26 UTC (permalink / raw)
To: dakr, acourbot
Cc: airlied, simona, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, jhubbard, ecourtney,
joelagnelf, apopple, cjia, smitra, kjaju, alkumar, ankita,
aniketa, kwankhede, targupta, nova-gpu, linux-kernel, zhiwang,
Zhi Wang
The GSP registry setup needs to advertise SR-IOV mode when nova-core boots
GSP for an enabled vGPU configuration. Without the registry entry, GSP-RM
is not told to initialize in the mode required by NVIDIA vGPU.
Append RMSetSriovMode to the SetRegistry command when the vGPU state
detected before GSP boot is enabled. Keep the existing registry entries
unchanged for non-vGPU boots.
Cc: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 2 +-
drivers/gpu/nova-core/gsp/commands.rs | 16 ++++++++++++++--
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 139cedacffd2..c0513ab7f4c1 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -102,7 +102,7 @@ pub(crate) fn boot(
self.cmdq
.send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
self.cmdq
- .send_command_no_wait(bar, commands::SetRegistry::new()?)?;
+ .send_command_no_wait(bar, commands::SetRegistry::new(ctx.vgpu.state())?)?;
hal.post_boot(&self, ctx, &gsp_fw)?;
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index dfb9f04e284d..07712957a5a9 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -34,6 +34,7 @@
},
},
sbuffer::SBufferIter,
+ vgpu::VgpuState, //
};
/// The `GspSetSystemInfo` command.
@@ -73,8 +74,8 @@ pub(crate) struct SetRegistry {
impl SetRegistry {
// For now we hard-code the registry entries. Future work will allow others to
// be added as module parameters.
- /// Creates a new `SetRegistry` command, using a set of hardcoded entries.
- pub(crate) fn new() -> Result<Self> {
+ /// Creates a new `SetRegistry` command.
+ pub(crate) fn new(vgpu_state: VgpuState) -> Result<Self> {
let mut entries = KVec::new();
// RMSecBusResetEnable - enables PCI secondary bus reset
@@ -106,6 +107,17 @@ pub(crate) fn new() -> Result<Self> {
GFP_KERNEL,
)?;
+ if matches!(vgpu_state, VgpuState::Enabled { .. }) {
+ // RMSetSriovMode - required when vGPU is enabled.
+ entries.push(
+ RegistryEntry {
+ key: "RMSetSriovMode",
+ value: 1,
+ },
+ GFP_KERNEL,
+ )?;
+ }
+
Ok(Self { entries })
}
}
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v3 8/8] gpu: nova-core: reserve vGPU WPR2 heap
2026-07-01 6:26 [PATCH v3 0/8] gpu: nova-core: boot GSP with vGPU enabled Zhi Wang
` (6 preceding siblings ...)
2026-07-01 6:26 ` [PATCH v3 7/8] gpu: nova-core: set RMSetSriovMode for vGPU Zhi Wang
@ 2026-07-01 6:26 ` Zhi Wang
7 siblings, 0 replies; 19+ messages in thread
From: Zhi Wang @ 2026-07-01 6:26 UTC (permalink / raw)
To: dakr, acourbot
Cc: airlied, simona, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, jhubbard, ecourtney,
joelagnelf, apopple, cjia, smitra, kjaju, alkumar, ankita,
aniketa, kwankhede, targupta, nova-gpu, linux-kernel, zhiwang,
Zhi Wang
GSP-RM needs a larger WPR2 heap when booting in vGPU mode. The heap size
is firmware-dependent, so it should come from the generated firmware
bindings instead of being open-coded in nova-core.
Pass the detected vGPU state into the framebuffer layout calculation. Keep
baremetal boots on the existing heap sizing path, and use the 570.144
vGPU default heap binding only when vGPU is enabled. The same state match
also sets the VF partition count, so disabled and invalid 0/1-VF states do
not enter the vGPU heap path.
Cc: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
drivers/gpu/nova-core/fb.rs | 25 +++++++++++++++----
drivers/gpu/nova-core/gsp/boot.rs | 2 +-
drivers/gpu/nova-core/gsp/fw.rs | 5 ++++
.../gpu/nova-core/gsp/fw/r570_144/bindings.rs | 1 +
4 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index 273cff752fae..e93ec46a6602 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -23,7 +23,8 @@
firmware::gsp::GspFirmware,
gpu::Chipset,
gsp,
- num::FromSafeCast, //
+ num::FromSafeCast,
+ vgpu::VgpuState, //
};
mod hal;
@@ -171,7 +172,12 @@ pub(crate) struct FbLayout {
impl FbLayout {
/// 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> {
+ pub(crate) fn new(
+ chipset: Chipset,
+ bar: Bar0<'_>,
+ gsp_fw: &GspFirmware,
+ vgpu_state: VgpuState,
+ ) -> Result<Self> {
let hal = hal::fb_hal(chipset);
let fb = {
@@ -234,10 +240,19 @@ pub(crate) fn new(chipset: Chipset, bar: Bar0<'_>, gsp_fw: &GspFirmware) -> Resu
FbRange(elf_addr..elf_addr + elf_size)
};
+ let (vf_partition_count, wpr2_heap_size) = match vgpu_state {
+ VgpuState::Disabled => (
+ 0,
+ gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end)?,
+ ),
+ VgpuState::Enabled { total_vfs } => (
+ u8::try_from(total_vfs).map_err(|_| EINVAL)?,
+ gsp::LibosParams::vgpu_wpr_heap_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);
FbRange(wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN))
@@ -265,7 +280,7 @@ pub(crate) fn new(chipset: Chipset, bar: Bar0<'_>, gsp_fw: &GspFirmware) -> Resu
wpr2_heap,
wpr2,
heap,
- vf_partition_count: 0,
+ vf_partition_count,
pmu_reserved_size: hal.pmu_reserved_size(),
})
}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index c0513ab7f4c1..bbc4505763a9 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -63,7 +63,7 @@ pub(crate) fn boot(
let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?;
- let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
+ let fb_layout = FbLayout::new(chipset, bar, &gsp_fw, ctx.vgpu.state())?;
dev_dbg!(dev, "{:#x?}\n", fb_layout);
let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 4db0cfa4dc4d..e8b33eaa97e7 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -177,6 +177,11 @@ pub(crate) fn from_chipset(chipset: Chipset) -> &'static LibosParams {
}
}
+ /// Returns the WPR heap size to reserve when vGPU is enabled.
+ pub(crate) fn vgpu_wpr_heap_size() -> u64 {
+ u64::from(bindings::GSP_FW_HEAP_SIZE_VGPU_DEFAULT)
+ }
+
/// 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) -> Result<u64> {
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 ea350f9b2cc4..afe3e007f088 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -40,6 +40,7 @@ fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
pub const GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100: u32 = 14680064;
pub const GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB: u32 = 98304;
pub const GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE: u32 = 100663296;
+pub const GSP_FW_HEAP_SIZE_VGPU_DEFAULT: u32 = 609222656;
pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB: u32 = 64;
pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB: u32 = 256;
pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB: u32 = 88;
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v3 3/8] gpu: nova-core: fsp: rename FSP response header type
2026-07-01 6:26 ` [PATCH v3 3/8] gpu: nova-core: fsp: rename FSP response header type Zhi Wang
@ 2026-07-08 13:37 ` Alexandre Courbot
0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2026-07-08 13:37 UTC (permalink / raw)
To: Zhi Wang
Cc: dakr, airlied, simona, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, jhubbard,
ecourtney, joelagnelf, apopple, cjia, smitra, kjaju, alkumar,
ankita, aniketa, kwankhede, targupta, nova-gpu, linux-kernel,
zhiwang
On Wed Jul 1, 2026 at 3:26 PM JST, Zhi Wang wrote:
> FSP message handling currently uses FspResponse for the common response
> prefix containing the MCTP header, NVDM header and command response
> payload.
>
> That name is too broad once other FSP response formats reuse the same
> prefix and append protocol-specific payloads. Rename it to
> FspResponseHeader so subsequent response structures can embed the common
> header without overloading the meaning of FspResponse.
>
> Suggested-by: Alexandre Courbot <acourbot@nvidia.com>
> Link: https://lore.kernel.org/all/DJMBI9CN2Z67.2T02SR8TAWEC5@nvidia.com/
> Signed-off-by: Zhi Wang <zhiw@nvidia.com>
Pushed to drm-rust-next, thanks!
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v3 6/8] gpu: nova-core: build SetRegistry entries dynamically
2026-07-01 6:26 ` [PATCH v3 6/8] gpu: nova-core: build SetRegistry entries dynamically Zhi Wang
@ 2026-07-08 13:37 ` Alexandre Courbot
0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2026-07-08 13:37 UTC (permalink / raw)
To: Zhi Wang
Cc: dakr, airlied, simona, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, jhubbard,
ecourtney, joelagnelf, apopple, cjia, smitra, kjaju, alkumar,
ankita, aniketa, kwankhede, targupta, nova-gpu, linux-kernel,
zhiwang
On Wed Jul 1, 2026 at 3:26 PM JST, Zhi Wang wrote:
> The GSP SetRegistry command currently stores its registry entries in a
> fixed-size array. That makes every additional runtime-dependent registry
> object require reshaping the command data structure at the same time as the
> feature that needs the new entry.
>
> Keep the existing registry contents unchanged, but store them in a KVec so
> SetRegistry can be constructed dynamically. The constructor now returns a
> Result to propagate allocation failures while the command payload layout is
> still computed from the final entry list.
>
> Cc: Alexandre Courbot <acourbot@nvidia.com>
> Signed-off-by: Zhi Wang <zhiw@nvidia.com>
Pushed to drm-rust-next, thanks! With one minor change documented below.
> ---
> drivers/gpu/nova-core/gsp/boot.rs | 2 +-
> drivers/gpu/nova-core/gsp/commands.rs | 74 ++++++++++++++++-----------
> 2 files changed, 44 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
> index 5abab54639a4..139cedacffd2 100644
> --- a/drivers/gpu/nova-core/gsp/boot.rs
> +++ b/drivers/gpu/nova-core/gsp/boot.rs
> @@ -102,7 +102,7 @@ pub(crate) fn boot(
> self.cmdq
> .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
> self.cmdq
> - .send_command_no_wait(bar, commands::SetRegistry::new())?;
> + .send_command_no_wait(bar, commands::SetRegistry::new()?)?;
>
> hal.post_boot(&self, ctx, &gsp_fw)?;
>
> diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
> index 86a3747cd31c..dfb9f04e284d 100644
> --- a/drivers/gpu/nova-core/gsp/commands.rs
> +++ b/drivers/gpu/nova-core/gsp/commands.rs
> @@ -67,37 +67,46 @@ struct RegistryEntry {
>
> /// The `SetRegistry` command.
> pub(crate) struct SetRegistry {
> - entries: [RegistryEntry; Self::NUM_ENTRIES],
> + entries: KVec<RegistryEntry>,
> }
>
> impl SetRegistry {
> // For now we hard-code the registry entries. Future work will allow others to
> // be added as module parameters.
That comment is now orphan, so I removed it.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v3 1/8] PCI/IOV: Return unsigned int from pci_sriov_get_totalvfs()
2026-07-01 6:26 ` [PATCH v3 1/8] PCI/IOV: Return unsigned int from pci_sriov_get_totalvfs() Zhi Wang
@ 2026-07-08 13:54 ` Alexandre Courbot
0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Courbot @ 2026-07-08 13:54 UTC (permalink / raw)
To: Zhi Wang
Cc: dakr, airlied, simona, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, jhubbard,
ecourtney, joelagnelf, apopple, cjia, smitra, kjaju, alkumar,
ankita, aniketa, kwankhede, targupta, nova-gpu, linux-kernel,
zhiwang, Bjorn Helgaas, David Laight, linux-pci
On Wed Jul 1, 2026 at 3:26 PM JST, Zhi Wang wrote:
> pci_sriov_get_totalvfs() reports a VF count, not an errno-style
> status. It returns 0 when SR-IOV is unavailable or the device is not a
> PF, and otherwise returns the PF's driver_max_VFs value.
>
> driver_max_VFs is stored as a u16 in struct pci_sriov. It is derived
> from the SR-IOV TotalVFs field or from a driver-provided limit, so the
> implementation cannot return a negative value.
>
> Change the declaration, CONFIG_PCI_IOV stub, and implementation to
> return unsigned int.
>
> Suggested-by: Alexandre Courbot <acourbot@nvidia.com>
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: David Laight <david.laight.linux@gmail.com>
> Cc: Gary Guo <gary@garyguo.net>
> Cc: linux-pci@vger.kernel.org
> Link: https://lore.kernel.org/all/DJHPRE4TGGT8.BUTMYOF5YE05@nvidia.com/
> Signed-off-by: Zhi Wang <zhiw@nvidia.com>
FWIW,
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v3 2/8] rust: pci: add sriov_get_totalvfs() helper
2026-07-01 6:26 ` [PATCH v3 2/8] rust: pci: add sriov_get_totalvfs() helper Zhi Wang
@ 2026-07-08 14:03 ` Alexandre Courbot
2026-07-08 14:13 ` Alexandre Courbot
2026-07-08 14:41 ` Alexandre Courbot
0 siblings, 2 replies; 19+ messages in thread
From: Alexandre Courbot @ 2026-07-08 14:03 UTC (permalink / raw)
To: Zhi Wang
Cc: dakr, airlied, simona, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, jhubbard,
ecourtney, joelagnelf, apopple, cjia, smitra, kjaju, alkumar,
ankita, aniketa, kwankhede, targupta, nova-gpu, linux-kernel,
zhiwang, Bjorn Helgaas, David Laight, linux-pci
On Wed Jul 1, 2026 at 3:26 PM JST, Zhi Wang wrote:
> Expose pci_sriov_get_totalvfs() to Rust PCI drivers so they can query
> how many SR-IOV VFs a device supports.
>
> Cc: Alexandre Courbot <acourbot@nvidia.com>
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: David Laight <david.laight.linux@gmail.com>
> Cc: Gary Guo <gary@garyguo.net>
> Cc: linux-pci@vger.kernel.org
> Link: https://lore.kernel.org/all/DJHPRE4TGGT8.BUTMYOF5YE05@nvidia.com/
> Signed-off-by: Zhi Wang <zhiw@nvidia.com>
> ---
> rust/kernel/pci.rs | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
> index 5071cae6543f..21c51981c02e 100644
> --- a/rust/kernel/pci.rs
> +++ b/rust/kernel/pci.rs
> @@ -450,6 +450,17 @@ pub fn pci_class(&self) -> Class {
> // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
> Class::from_raw(unsafe { (*self.as_raw()).class })
> }
> +
> + /// Returns the total number of VFs, or 0 if SR-IOV is not available.
> + #[inline]
> + pub fn sriov_get_totalvfs(&self) -> u16 {
> + // SAFETY: `self.as_raw()` is a valid pointer to a `struct pci_dev`.
> + let total_vfs = unsafe { bindings::pci_sriov_get_totalvfs(self.as_raw()) };
> +
> + // CAST: The C helper returns `unsigned int`, but the value originates
> + // from TotalVFs/driver_max_VFs, so this cast cannot truncate.
nit: "from TotalVFs/driver_max_VFs (which are defined as `u16`), ..."
With that,
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v3 2/8] rust: pci: add sriov_get_totalvfs() helper
2026-07-08 14:03 ` Alexandre Courbot
@ 2026-07-08 14:13 ` Alexandre Courbot
2026-07-09 14:24 ` Zhi Wang
2026-07-08 14:41 ` Alexandre Courbot
1 sibling, 1 reply; 19+ messages in thread
From: Alexandre Courbot @ 2026-07-08 14:13 UTC (permalink / raw)
To: Zhi Wang
Cc: dakr, airlied, simona, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, jhubbard,
ecourtney, joelagnelf, apopple, cjia, smitra, kjaju, alkumar,
ankita, aniketa, kwankhede, targupta, nova-gpu, linux-kernel,
zhiwang, Bjorn Helgaas, David Laight, linux-pci
On Wed Jul 8, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> On Wed Jul 1, 2026 at 3:26 PM JST, Zhi Wang wrote:
>> Expose pci_sriov_get_totalvfs() to Rust PCI drivers so they can query
>> how many SR-IOV VFs a device supports.
>>
>> Cc: Alexandre Courbot <acourbot@nvidia.com>
>> Cc: Bjorn Helgaas <bhelgaas@google.com>
>> Cc: David Laight <david.laight.linux@gmail.com>
>> Cc: Gary Guo <gary@garyguo.net>
>> Cc: linux-pci@vger.kernel.org
>> Link: https://lore.kernel.org/all/DJHPRE4TGGT8.BUTMYOF5YE05@nvidia.com/
>> Signed-off-by: Zhi Wang <zhiw@nvidia.com>
>> ---
>> rust/kernel/pci.rs | 11 +++++++++++
>> 1 file changed, 11 insertions(+)
>>
>> diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
>> index 5071cae6543f..21c51981c02e 100644
>> --- a/rust/kernel/pci.rs
>> +++ b/rust/kernel/pci.rs
>> @@ -450,6 +450,17 @@ pub fn pci_class(&self) -> Class {
>> // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
>> Class::from_raw(unsafe { (*self.as_raw()).class })
>> }
>> +
>> + /// Returns the total number of VFs, or 0 if SR-IOV is not available.
>> + #[inline]
>> + pub fn sriov_get_totalvfs(&self) -> u16 {
>> + // SAFETY: `self.as_raw()` is a valid pointer to a `struct pci_dev`.
>> + let total_vfs = unsafe { bindings::pci_sriov_get_totalvfs(self.as_raw()) };
>> +
>> + // CAST: The C helper returns `unsigned int`, but the value originates
>> + // from TotalVFs/driver_max_VFs, so this cast cannot truncate.
>
> nit: "from TotalVFs/driver_max_VFs (which are defined as `u16`), ..."
>
> With that,
>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
... with an additional item for thought: in a previous revision [1], I
had raised the question of making the return type
`Option<NonZero<u16>>`, as I think it better encodes "0 if SR-IOV is not
available" - callers are then forced to consider the dichotomy of the
result, and not interpret a particular value as having a special meaning.
OTOH, for all practical purposes 0 VFs also seems to be strictly
equivalent to "SR-IOV is not available", so please take this as a
nit to consider, not a blocker.
[1] https://lore.kernel.org/rust-for-linux/DETDILPA1GFY.27WND0TEC5352@nvidia.com/
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v3 2/8] rust: pci: add sriov_get_totalvfs() helper
2026-07-08 14:03 ` Alexandre Courbot
2026-07-08 14:13 ` Alexandre Courbot
@ 2026-07-08 14:41 ` Alexandre Courbot
2026-07-09 14:25 ` Zhi Wang
1 sibling, 1 reply; 19+ messages in thread
From: Alexandre Courbot @ 2026-07-08 14:41 UTC (permalink / raw)
To: Zhi Wang
Cc: dakr, airlied, simona, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, jhubbard,
ecourtney, joelagnelf, apopple, cjia, smitra, kjaju, alkumar,
ankita, aniketa, kwankhede, targupta, nova-gpu, linux-kernel,
zhiwang, Bjorn Helgaas, David Laight, linux-pci
On Wed Jul 8, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> On Wed Jul 1, 2026 at 3:26 PM JST, Zhi Wang wrote:
>> Expose pci_sriov_get_totalvfs() to Rust PCI drivers so they can query
>> how many SR-IOV VFs a device supports.
>>
>> Cc: Alexandre Courbot <acourbot@nvidia.com>
>> Cc: Bjorn Helgaas <bhelgaas@google.com>
>> Cc: David Laight <david.laight.linux@gmail.com>
>> Cc: Gary Guo <gary@garyguo.net>
>> Cc: linux-pci@vger.kernel.org
>> Link: https://lore.kernel.org/all/DJHPRE4TGGT8.BUTMYOF5YE05@nvidia.com/
>> Signed-off-by: Zhi Wang <zhiw@nvidia.com>
>> ---
>> rust/kernel/pci.rs | 11 +++++++++++
>> 1 file changed, 11 insertions(+)
>>
>> diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
>> index 5071cae6543f..21c51981c02e 100644
>> --- a/rust/kernel/pci.rs
>> +++ b/rust/kernel/pci.rs
>> @@ -450,6 +450,17 @@ pub fn pci_class(&self) -> Class {
>> // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
>> Class::from_raw(unsafe { (*self.as_raw()).class })
>> }
>> +
>> + /// Returns the total number of VFs, or 0 if SR-IOV is not available.
>> + #[inline]
>> + pub fn sriov_get_totalvfs(&self) -> u16 {
>> + // SAFETY: `self.as_raw()` is a valid pointer to a `struct pci_dev`.
>> + let total_vfs = unsafe { bindings::pci_sriov_get_totalvfs(self.as_raw()) };
>> +
>> + // CAST: The C helper returns `unsigned int`, but the value originates
>> + // from TotalVFs/driver_max_VFs, so this cast cannot truncate.
>
> nit: "from TotalVFs/driver_max_VFs (which are defined as `u16`), ..."
>
> With that,
>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Sorry, need to take my tag back. :( Build is failing if `CONFIG_PCI_IOV`
is not set:
error[E0425]: cannot find function `pci_sriov_get_totalvfs` in crate `bindings`
--> ../rust/kernel/pci.rs:458:44
|
458 | let total_vfs = unsafe { bindings::pci_sriov_get_totalvfs(self.as_raw()) };
| ^^^^^^^^^^^^^^^^^^^^^^ not found in `bindings`
Looks like you need to add a helper in `rust/helpers/pci.c` as the
inline C function used when `CONFIG_PCI_IOV` is not set can not be
invoked by Rust.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v3 5/8] gpu: nova-core: add vGPU preludes
2026-07-01 6:26 ` [PATCH v3 5/8] gpu: nova-core: add vGPU preludes Zhi Wang
@ 2026-07-09 5:25 ` Alexandre Courbot
2026-07-09 14:39 ` Zhi Wang
0 siblings, 1 reply; 19+ messages in thread
From: Alexandre Courbot @ 2026-07-09 5:25 UTC (permalink / raw)
To: Zhi Wang
Cc: dakr, airlied, simona, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, jhubbard,
ecourtney, joelagnelf, apopple, cjia, smitra, kjaju, alkumar,
ankita, aniketa, kwankhede, targupta, nova-gpu, linux-kernel,
zhiwang
On Wed Jul 1, 2026 at 3:26 PM JST, Zhi Wang wrote:
<...>
> diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
> index c347558aa8e5..5abab54639a4 100644
> --- a/drivers/gpu/nova-core/gsp/boot.rs
> +++ b/drivers/gpu/nova-core/gsp/boot.rs
> @@ -26,6 +26,7 @@
> commands,
> GspFwWprMeta, //
> },
> + vgpu::VgpuState, //
> };
>
> impl super::Gsp {
> @@ -48,6 +49,18 @@ pub(crate) fn boot(
> let dev = pdev.as_ref();
> let hal = super::hal::gsp_hal(chipset);
>
> + let (vgpu_enabled, total_vfs) = match ctx.vgpu.state() {
> + VgpuState::Disabled => (false, 0),
> + VgpuState::Enabled { total_vfs } => (true, total_vfs),
> + };
> +
> + dev_dbg!(
> + dev,
> + "vGPU enabled: {}, total VFs: {}\n",
> + vgpu_enabled,
> + total_vfs
> + );
This whole hunk is dedicated to collecting debugging information that we
most likely won't ever display. Can you instead derive `Debug` on
`VgpuState` and use something like:
dev_dbg!(dev, "vGPU state: {:?}\n", ctx.vgpu.state());
That statement would also be better located in `VgpuManager::new` imho,
right before returning the constructed value.
> +
> let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?;
>
> let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
> diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
> index 735b8e17c6b6..2df2f773ec8e 100644
> --- a/drivers/gpu/nova-core/nova_core.rs
> +++ b/drivers/gpu/nova-core/nova_core.rs
> @@ -26,6 +26,7 @@
> mod regs;
> mod sbuffer;
> mod vbios;
> +mod vgpu;
>
> pub(crate) const MODULE_NAME: &core::ffi::CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
>
> diff --git a/drivers/gpu/nova-core/vgpu.rs b/drivers/gpu/nova-core/vgpu.rs
> new file mode 100644
> index 000000000000..d2f311a8b2d5
> --- /dev/null
> +++ b/drivers/gpu/nova-core/vgpu.rs
> @@ -0,0 +1,79 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use kernel::{
> + device,
> + pci,
> + prelude::*, //
> +};
> +
> +use crate::{
> + fsp::{
> + Fsp,
> + VgpuMode, //
> + },
> + gpu::Chipset, //
> +};
> +
> +mod hal;
> +
> +/// vGPU state detected during GPU construction.
> +#[derive(Clone, Copy)]
> +pub(crate) enum VgpuState {
> + /// vGPU mode is not enabled for this boot.
> + Disabled,
> + /// vGPU mode is enabled for this boot.
> + Enabled {
> + /// Total number of SR-IOV VFs supported by this device.
> + total_vfs: u16,
> + },
> +}
> +
> +/// vGPU state manager.
> +pub(crate) struct VgpuManager {
> + state: VgpuState,
> +}
Just to confirm: right now `VgpuManager` is a wrapper for `VgpuState`,
but it is going to grow further in the future, right?
> +
> +impl VgpuManager {
> + /// Creates a vGPU manager by querying SR-IOV and the FSP PRC vGPU knob.
> + pub(crate) fn new(
> + pdev: &pci::Device<device::Bound>,
> + chipset: Chipset,
> + fsp: Option<&mut Fsp<'_>>,
> + ) -> Result<Self> {
> + let state = Self::detect_state(pdev, chipset, fsp)?;
Do we want to return an error (and make probe fail) if the vGPU state
cannot be detected for some reason? Or should we prefer printing a
warning and returning a `Disabled` state? I don't have a particular
preference but thought it is worth pondering.
> +
> + Ok(Self { state })
> + }
> +
> + fn detect_state(
Let's grant a short documentation to this method.
> + pdev: &pci::Device<device::Bound>,
> + chipset: Chipset,
> + fsp: Option<&mut Fsp<'_>>,
> + ) -> Result<VgpuState> {
> + if !hal::supports_vgpu(chipset) {
> + return Ok(VgpuState::Disabled);
> + }
> +
> + let total_vfs = pdev.sriov_get_totalvfs();
> + if total_vfs < 2 {
> + // The current vGPU path does not support single-VF SR-IOV devices yet.
> + // Treat 0 or 1 total VFs as vGPU-disabled for now; single-VF support can
> + // relax this gate once the manager handles that topology.
> + return Ok(VgpuState::Disabled);
> + }
> +
> + let Some(fsp) = fsp else {
> + return Ok(VgpuState::Disabled);
> + };
> +
> + match fsp.read_vgpu_mode(pdev.as_ref())? {
> + VgpuMode::Enabled => Ok(VgpuState::Enabled { total_vfs }),
> + VgpuMode::Disabled => Ok(VgpuState::Disabled),
> + }
> + }
> +
> + /// Returns the detected vGPU state for this boot.
> + pub(crate) fn state(&self) -> VgpuState {
> + self.state
> + }
> +}
> diff --git a/drivers/gpu/nova-core/vgpu/hal.rs b/drivers/gpu/nova-core/vgpu/hal.rs
> new file mode 100644
> index 000000000000..e6fb6cec2599
> --- /dev/null
> +++ b/drivers/gpu/nova-core/vgpu/hal.rs
> @@ -0,0 +1,45 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use crate::gpu::{
> + Architecture,
> + Chipset, //
> +};
> +
> +trait VgpuHal {
> + /// Returns whether this chipset can support vGPU.
> + fn supports_vgpu(&self) -> bool;
> +}
> +
> +struct Tu102;
> +
> +impl VgpuHal for Tu102 {
> + fn supports_vgpu(&self) -> bool {
> + false
> + }
> +}
> +
> +struct Gb202;
> +
> +impl VgpuHal for Gb202 {
> + fn supports_vgpu(&self) -> bool {
> + true
> + }
> +}
`Tu102` and `Gb202` should be in their own sub-modules, even if they are
small.
> +
> +const TU102: Tu102 = Tu102;
> +const GB202: Gb202 = Gb202;
> +
> +fn vgpu_hal(chipset: Chipset) -> &'static dyn VgpuHal {
> + match chipset.arch() {
> + Architecture::BlackwellGB20x => &GB202,
> + Architecture::Turing
> + | Architecture::Ampere
> + | Architecture::Hopper
> + | Architecture::Ada
> + | Architecture::BlackwellGB10x => &TU102,
> + }
> +}
> +
> +pub(super) fn supports_vgpu(chipset: Chipset) -> bool {
> + vgpu_hal(chipset).supports_vgpu()
> +}
We typically don't create functions for each hal method - let's make
`vgpu_hal` and `VgpuHal` `pub(super)` and use them from their caller,
like the other HALs are doing - `fsp/hal.rs` is a good model to follow.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v3 2/8] rust: pci: add sriov_get_totalvfs() helper
2026-07-08 14:13 ` Alexandre Courbot
@ 2026-07-09 14:24 ` Zhi Wang
0 siblings, 0 replies; 19+ messages in thread
From: Zhi Wang @ 2026-07-09 14:24 UTC (permalink / raw)
To: Alexandre Courbot
Cc: dakr, airlied, simona, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, jhubbard,
ecourtney, joelagnelf, apopple, cjia, smitra, kjaju, alkumar,
ankita, aniketa, kwankhede, targupta, nova-gpu, linux-kernel,
zhiwang, Bjorn Helgaas, David Laight, linux-pci
On Wed, 08 Jul 2026 23:13:08 +0900
"Alexandre Courbot" <acourbot@nvidia.com> wrote:
> On Wed Jul 8, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> > On Wed Jul 1, 2026 at 3:26 PM JST, Zhi Wang wrote:
> >> Expose pci_sriov_get_totalvfs() to Rust PCI drivers so they can
> >> query how many SR-IOV VFs a device supports.
> >>
> >> Cc: Alexandre Courbot <acourbot@nvidia.com>
> >> Cc: Bjorn Helgaas <bhelgaas@google.com>
> >> Cc: David Laight <david.laight.linux@gmail.com>
> >> Cc: Gary Guo <gary@garyguo.net>
> >> Cc: linux-pci@vger.kernel.org
> >> Link:
> >> https://lore.kernel.org/all/DJHPRE4TGGT8.BUTMYOF5YE05@nvidia.com/
> >> Signed-off-by: Zhi Wang <zhiw@nvidia.com> ---
> >> rust/kernel/pci.rs | 11 +++++++++++
> >> 1 file changed, 11 insertions(+)
> >>
> >> diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
> >> index 5071cae6543f..21c51981c02e 100644
> >> --- a/rust/kernel/pci.rs
> >> +++ b/rust/kernel/pci.rs
> >> @@ -450,6 +450,17 @@ pub fn pci_class(&self) -> Class {
> >> // SAFETY: `self.as_raw` is a valid pointer to a `struct
> >> pci_dev`. Class::from_raw(unsafe { (*self.as_raw()).class })
> >> }
> >> +
> >> + /// Returns the total number of VFs, or 0 if SR-IOV is not
> >> available.
> >> + #[inline]
> >> + pub fn sriov_get_totalvfs(&self) -> u16 {
> >> + // SAFETY: `self.as_raw()` is a valid pointer to a
> >> `struct pci_dev`.
> >> + let total_vfs = unsafe {
> >> bindings::pci_sriov_get_totalvfs(self.as_raw()) }; +
> >> + // CAST: The C helper returns `unsigned int`, but the
> >> value originates
> >> + // from TotalVFs/driver_max_VFs, so this cast cannot
> >> truncate.
> >
> > nit: "from TotalVFs/driver_max_VFs (which are defined as `u16`),
> > ..."
> >
> > With that,
> >
> > Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
>
> ... with an additional item for thought: in a previous revision [1], I
> had raised the question of making the return type
> `Option<NonZero<u16>>`, as I think it better encodes "0 if SR-IOV is
> not available" - callers are then forced to consider the dichotomy of
> the result, and not interpret a particular value as having a special
> meaning.
>
> OTOH, for all practical purposes 0 VFs also seems to be strictly
> equivalent to "SR-IOV is not available", so please take this as a
> nit to consider, not a blocker.
>
> [1]
> https://lore.kernel.org/rust-for-linux/DETDILPA1GFY.27WND0TEC5352@nvidia.com/
Nice advice. I will address it in v4. :)
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v3 2/8] rust: pci: add sriov_get_totalvfs() helper
2026-07-08 14:41 ` Alexandre Courbot
@ 2026-07-09 14:25 ` Zhi Wang
0 siblings, 0 replies; 19+ messages in thread
From: Zhi Wang @ 2026-07-09 14:25 UTC (permalink / raw)
To: Alexandre Courbot
Cc: dakr, airlied, simona, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, jhubbard,
ecourtney, joelagnelf, apopple, cjia, smitra, kjaju, alkumar,
ankita, aniketa, kwankhede, targupta, nova-gpu, linux-kernel,
zhiwang, Bjorn Helgaas, David Laight, linux-pci
On Wed, 08 Jul 2026 23:41:20 +0900
"Alexandre Courbot" <acourbot@nvidia.com> wrote:
> On Wed Jul 8, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> > On Wed Jul 1, 2026 at 3:26 PM JST, Zhi Wang wrote:
> >> Expose pci_sriov_get_totalvfs() to Rust PCI drivers so they can
> >> query how many SR-IOV VFs a device supports.
> >>
> >> Cc: Alexandre Courbot <acourbot@nvidia.com>
> >> Cc: Bjorn Helgaas <bhelgaas@google.com>
> >> Cc: David Laight <david.laight.linux@gmail.com>
> >> Cc: Gary Guo <gary@garyguo.net>
> >> Cc: linux-pci@vger.kernel.org
> >> Link:
> >> https://lore.kernel.org/all/DJHPRE4TGGT8.BUTMYOF5YE05@nvidia.com/
> >> Signed-off-by: Zhi Wang <zhiw@nvidia.com> ---
> >> rust/kernel/pci.rs | 11 +++++++++++
> >> 1 file changed, 11 insertions(+)
> >>
> >> diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
> >> index 5071cae6543f..21c51981c02e 100644
> >> --- a/rust/kernel/pci.rs
> >> +++ b/rust/kernel/pci.rs
> >> @@ -450,6 +450,17 @@ pub fn pci_class(&self) -> Class {
> >> // SAFETY: `self.as_raw` is a valid pointer to a `struct
> >> pci_dev`. Class::from_raw(unsafe { (*self.as_raw()).class })
> >> }
> >> +
> >> + /// Returns the total number of VFs, or 0 if SR-IOV is not
> >> available.
> >> + #[inline]
> >> + pub fn sriov_get_totalvfs(&self) -> u16 {
> >> + // SAFETY: `self.as_raw()` is a valid pointer to a
> >> `struct pci_dev`.
> >> + let total_vfs = unsafe {
> >> bindings::pci_sriov_get_totalvfs(self.as_raw()) }; +
> >> + // CAST: The C helper returns `unsigned int`, but the
> >> value originates
> >> + // from TotalVFs/driver_max_VFs, so this cast cannot
> >> truncate.
> >
> > nit: "from TotalVFs/driver_max_VFs (which are defined as `u16`),
> > ..."
> >
> > With that,
> >
> > Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
>
> Sorry, need to take my tag back. :( Build is failing if
> `CONFIG_PCI_IOV` is not set:
>
> error[E0425]: cannot find function `pci_sriov_get_totalvfs` in
> crate `bindings` --> ../rust/kernel/pci.rs:458:44
> |
> 458 | let total_vfs = unsafe {
> bindings::pci_sriov_get_totalvfs(self.as_raw()) }; |
> ^^^^^^^^^^^^^^^^^^^^^^ not found in
> `bindings`
>
> Looks like you need to add a helper in `rust/helpers/pci.c` as the
> inline C function used when `CONFIG_PCI_IOV` is not set can not be
> invoked by Rust.
Add it in v4.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v3 5/8] gpu: nova-core: add vGPU preludes
2026-07-09 5:25 ` Alexandre Courbot
@ 2026-07-09 14:39 ` Zhi Wang
0 siblings, 0 replies; 19+ messages in thread
From: Zhi Wang @ 2026-07-09 14:39 UTC (permalink / raw)
To: Alexandre Courbot
Cc: dakr, airlied, simona, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, jhubbard,
ecourtney, joelagnelf, apopple, cjia, smitra, kjaju, alkumar,
ankita, aniketa, kwankhede, targupta, nova-gpu, linux-kernel,
zhiwang
On Thu, 09 Jul 2026 14:25:25 +0900
"Alexandre Courbot" <acourbot@nvidia.com> wrote:
> On Wed Jul 1, 2026 at 3:26 PM JST, Zhi Wang wrote:
> <...>
snip
> > +/// vGPU state manager.
> > +pub(crate) struct VgpuManager {
> > + state: VgpuState,
> > +}
>
> Just to confirm: right now `VgpuManager` is a wrapper for `VgpuState`,
> but it is going to grow further in the future, right?
>
Yes. I have some example code here:
https://github.com/zhiwang-nvidia/nova-core/blob/zhi/nova-vgpu-wip/drivers/gpu/nova-core/vgpu/mod.rs#L62
It should extend more when more features are coming besides the current
functions.
> > +
> > +impl VgpuManager {
> > + /// Creates a vGPU manager by querying SR-IOV and the FSP PRC
> > vGPU knob.
> > + pub(crate) fn new(
> > + pdev: &pci::Device<device::Bound>,
> > + chipset: Chipset,
> > + fsp: Option<&mut Fsp<'_>>,
> > + ) -> Result<Self> {
> > + let state = Self::detect_state(pdev, chipset, fsp)?;
>
> Do we want to return an error (and make probe fail) if the vGPU state
> cannot be detected for some reason? Or should we prefer printing a
> warning and returning a `Disabled` state? I don't have a particular
> preference but thought it is worth pondering.
>
Good question. After thinking for a while, I think it should be
'Disabled'. E.g. if the user has updated the vBIOS and the PRC is not
there, we don't wanna disturb the booting flow. Let me update this in
v7.
> > +
> > + Ok(Self { state })
> > + }
> > +
> > + fn detect_state(
>
> Let's grant a short documentation to this method.
>
Sure.
> > + pdev: &pci::Device<device::Bound>,
> > + chipset: Chipset,
> > + fsp: Option<&mut Fsp<'_>>,
> > + ) -> Result<VgpuState> {
> > + if !hal::supports_vgpu(chipset) {
snip
> We typically don't create functions for each hal method - let's make
> `vgpu_hal` and `VgpuHal` `pub(super)` and use them from their caller,
> like the other HALs are doing - `fsp/hal.rs` is a good model to
> follow.
Sure.
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-07-09 14:40 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 6:26 [PATCH v3 0/8] gpu: nova-core: boot GSP with vGPU enabled Zhi Wang
2026-07-01 6:26 ` [PATCH v3 1/8] PCI/IOV: Return unsigned int from pci_sriov_get_totalvfs() Zhi Wang
2026-07-08 13:54 ` Alexandre Courbot
2026-07-01 6:26 ` [PATCH v3 2/8] rust: pci: add sriov_get_totalvfs() helper Zhi Wang
2026-07-08 14:03 ` Alexandre Courbot
2026-07-08 14:13 ` Alexandre Courbot
2026-07-09 14:24 ` Zhi Wang
2026-07-08 14:41 ` Alexandre Courbot
2026-07-09 14:25 ` Zhi Wang
2026-07-01 6:26 ` [PATCH v3 3/8] gpu: nova-core: fsp: rename FSP response header type Zhi Wang
2026-07-08 13:37 ` Alexandre Courbot
2026-07-01 6:26 ` [PATCH v3 4/8] gpu: nova-core: read vGPU mode from FSP via PRC protocol Zhi Wang
2026-07-01 6:26 ` [PATCH v3 5/8] gpu: nova-core: add vGPU preludes Zhi Wang
2026-07-09 5:25 ` Alexandre Courbot
2026-07-09 14:39 ` Zhi Wang
2026-07-01 6:26 ` [PATCH v3 6/8] gpu: nova-core: build SetRegistry entries dynamically Zhi Wang
2026-07-08 13:37 ` Alexandre Courbot
2026-07-01 6:26 ` [PATCH v3 7/8] gpu: nova-core: set RMSetSriovMode for vGPU Zhi Wang
2026-07-01 6:26 ` [PATCH v3 8/8] gpu: nova-core: reserve vGPU WPR2 heap Zhi Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox