[PATCH v4 3/6] gpu: nova-core: read vGPU mode from FSP via PRC protocol
From: Zhi Wang
Date: Thu Jul 09 2026 - 11:19:37 EST
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@xxxxxxxxxx>
---
drivers/gpu/nova-core/fsp.rs | 173 ++++++++++++++++++++++++++++++++++
drivers/gpu/nova-core/mctp.rs | 2 +
2 files changed, 175 insertions(+)
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index afbd75879d16..3a6b02f0dc11 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -52,6 +52,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)]
@@ -61,6 +111,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)]
@@ -96,6 +202,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
@@ -183,10 +300,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,
@@ -342,6 +484,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 acc2abbd4b0c..90c642c91a72 100644
--- a/drivers/gpu/nova-core/mctp.rs
+++ b/drivers/gpu/nova-core/mctp.rs
@@ -22,6 +22,8 @@
/// NVDM message type identifiers carried over MCTP.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum NvdmType with TryFrom<Bounded<u32, 8>> {
+ /// PRC (Product Reconfiguration Control) message.
+ Prc = 0x13,
/// Chain of Trust boot message.
Cot = 0x14,
/// FSP command response.
--
2.51.0