[RFC v2 07/10] gpu: nova-core: populate GSP_VF_INFO when vGPU is enabled

From: Zhi Wang

Date: Fri Mar 13 2026 - 12:59:04 EST


GSP firmware needs to know the VF BAR offsets to correctly calculate the
VF events.

The VF BAR information is stored in GSP_VF_INFO, which needs to be
initialized and uploaded together with the GSP_SYSTEM_INFO.

Populate GSP_VF_INFO when nova-core uploads the GSP_SYSTEM_INFO if NVIDIA
vGPU is enabled.

Cc: Joel Fernandes <joelagnelf@xxxxxxxxxx>
Cc: John Hubbard <jhubbard@xxxxxxxxxx>
Cc: Alexandre Courbot <acourbot@xxxxxxxxxx>
Signed-off-by: Zhi Wang <zhiw@xxxxxxxxxx>
---
drivers/gpu/nova-core/gsp/boot.rs | 15 ++++++++--
drivers/gpu/nova-core/gsp/commands.rs | 16 ++++++++--
drivers/gpu/nova-core/gsp/fw.rs | 38 ++++++++++++++++++++++++
drivers/gpu/nova-core/gsp/fw/commands.rs | 12 +++++++-
4 files changed, 74 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 4238df5c8104..921d5e892f8a 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -41,7 +41,10 @@
gpu::Chipset,
gsp::{
commands,
- fw::LibosMemoryRegionInitArgument,
+ fw::{
+ GspVfInfo,
+ LibosMemoryRegionInitArgument, //
+ },
sequencer::{
GspSequencer,
GspSequencerParams, //
@@ -349,11 +352,17 @@ pub(crate) fn boot(
let wpr_meta = Coherent::<GspFwWprMeta>::zeroed(dev, GFP_KERNEL)?;
io_write!(wpr_meta, , GspFwWprMeta::new(&gsp_fw, &fb_layout));

+ let vf_info = if ctx.vgpu_requested {
+ Some(GspVfInfo::new(ctx.pdev)?)
+ } else {
+ None
+ };
+
// Architecture-specific boot path
if arch.uses_sec2_boot() {
// SEC2 path: send commands before GSP reset/boot (original order).
self.cmdq
- .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
+ .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset, vf_info))?;
self.cmdq
.send_command_no_wait(bar, commands::SetRegistry::new())?;

@@ -395,7 +404,7 @@ pub(crate) fn boot(
// For FSP path, send commands after GSP becomes active.
if !arch.uses_sec2_boot() {
self.cmdq
- .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
+ .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset, vf_info))?;
self.cmdq
.send_command_no_wait(bar, commands::SetRegistry::new())?;
}
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index d31ee782ff8b..0445d05990e7 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -29,6 +29,7 @@
},
fw::{
commands::*,
+ GspVfInfo,
MsgFunction, //
},
},
@@ -39,12 +40,21 @@
pub(crate) struct SetSystemInfo<'a> {
pdev: &'a pci::Device<device::Bound>,
chipset: Chipset,
+ vf_info: Option<GspVfInfo>,
}

impl<'a> SetSystemInfo<'a> {
/// Creates a new `GspSetSystemInfo` command using the parameters of `pdev`.
- pub(crate) fn new(pdev: &'a pci::Device<device::Bound>, chipset: Chipset) -> Self {
- Self { pdev, chipset }
+ pub(crate) fn new(
+ pdev: &'a pci::Device<device::Bound>,
+ chipset: Chipset,
+ vf_info: Option<GspVfInfo>,
+ ) -> Self {
+ Self {
+ pdev,
+ chipset,
+ vf_info,
+ }
}
}

@@ -55,7 +65,7 @@ impl<'a> CommandToGsp for SetSystemInfo<'a> {
type InitError = Error;

fn init(&self) -> impl Init<Self::Command, Self::InitError> {
- GspSetSystemInfo::init(self.pdev, self.chipset)
+ GspSetSystemInfo::init(self.pdev, self.chipset, self.vf_info)
}
}

diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index e5f8db74a677..6d56b9b920fb 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -10,7 +10,9 @@
use core::ops::Range;

use kernel::{
+ device,
dma::Coherent,
+ pci,
prelude::*,
ptr::{
Alignable,
@@ -1309,3 +1311,39 @@ fn new(cmdq: &Cmdq) -> Self {
})
}
}
+
+/// VF information — `gspVFInfo` in `GspSetSystemInfo`.
+///
+/// Populated from the PCI SR-IOV extended capability when vGPU support
+/// is enabled.
+#[derive(Clone, Copy, Zeroable)]
+#[repr(transparent)]
+pub(crate) struct GspVfInfo(pub(crate) bindings::GSP_VF_INFO);
+
+impl GspVfInfo {
+ /// Reads SR-IOV capability data from the PCI extended configuration
+ /// space and builds the VF information required by GSP firmware.
+ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<GspVfInfo> {
+ let total_vfs = pdev.sriov_get_totalvfs()?;
+
+ let cfg = pdev.config_space_extended()?;
+ let sriov = pci::ExtSriovCapability::find(&cfg)?;
+
+ Ok(GspVfInfo(bindings::GSP_VF_INFO {
+ totalVFs: u32::from(total_vfs),
+ firstVFOffset: u32::from(kernel::io_read!(sriov, .vf_offset)),
+ FirstVFBar0Address: u64::from(kernel::io_read!(sriov, .vf_bar[0]?)),
+ b64bitBar1: u8::from(sriov.vf_bar_is_64bit(1)?),
+ FirstVFBar1Address: sriov.read_vf_bar64_addr(1)?,
+ b64bitBar2: u8::from(sriov.vf_bar_is_64bit(3)?),
+ FirstVFBar2Address: sriov.read_vf_bar64_addr(3)?,
+ ..Zeroable::zeroed()
+ }))
+ }
+}
+
+// SAFETY: Padding is explicit and does not contain uninitialized data.
+unsafe impl AsBytes for GspVfInfo {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for GspVfInfo {}
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index c9822fcbc499..3edd451e531b 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -15,7 +15,10 @@
Architecture,
Chipset, //
},
- gsp::GSP_PAGE_SIZE, //
+ gsp::{
+ fw::GspVfInfo,
+ GSP_PAGE_SIZE, //
+ },
};

use super::bindings;
@@ -33,6 +36,7 @@ impl GspSetSystemInfo {
pub(crate) fn init<'a>(
dev: &'a pci::Device<device::Bound>,
chipset: Chipset,
+ vf_info: Option<GspVfInfo>,
) -> impl Init<Self, Error> + 'a {
type InnerGspSystemInfo = bindings::GspSystemInfo;
let init_inner = try_init!(InnerGspSystemInfo {
@@ -57,6 +61,12 @@ pub(crate) fn init<'a>(
bIsPrimary: 0,
bPreserveVideoMemoryAllocations: 0,
..Zeroable::init_zeroed()
+ })
+ .chain(move |si| {
+ if let Some(vf) = vf_info {
+ si.gspVFInfo = vf.0;
+ }
+ Ok(())
});

try_init!(GspSetSystemInfo {
--
2.51.0