Add support for querying the vGPU mode configuration from FSP using
the PRC (Product Reconfiguration Control) protocol. PRC is an API
system exposed through FSP's Management Partition that allows querying
device configuration "knobs" without firmware updates.

Add a VgpuMode enum that validates the raw PRC response value,
returning an error for unexpected values.

Signed-off-by: Zhi Wang <[email protected]>
---
 drivers/gpu/nova-core/fsp.rs  | 175 +++++++++++++++++++++++++++++++---
 drivers/gpu/nova-core/mctp.rs |   2 +
 2 files changed, 165 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index 69e48b655879..e342bbf733b3 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -23,12 +23,18 @@
     },
 };
 
-use crate::regs;
-
-use crate::mctp::{
-    MctpHeader,
-    NvdmHeader,
-    NvdmType, //
+use crate::{
+    driver::Bar0,
+    falcon::{
+        fsp::Fsp as FspEngine,
+        Falcon, //
+    },
+    mctp::{
+        MctpHeader,
+        NvdmHeader,
+        NvdmType, //
+    },
+    regs,
 };
 
 /// FSP Chain of Trust protocol version.
@@ -55,6 +61,23 @@ pub(crate) const fn raw(self) -> u16 {
 /// FSP secure boot completion timeout in milliseconds.
 const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 4000;
 
+/// PRC (Product Reconfiguration Control) protocol constants.
+///
+/// PRC is an API system exposed through FSP's Management Partition that allows
+/// querying and modifying device configuration "knobs" without firmware 
updates.
+/// Each knob is identified by a unique object ID and controls a specific 
device
+/// behavior (e.g., vGPU mode, ECC, confidential computing).
+mod prc {
+    /// Sub-command to read a PRC knob value.
+    pub(super) const SUBCMD_READ: u8 = 0x0c;
+
+    /// PRC object ID for vGPU mode configuration (knob ID 41).
+    pub(super) const OBJECT_VGPU_MODE: u8 = 0x29;
+
+    /// Request the active knob value (currently effective this boot).
+    pub(super) const FLAG_ACTIVE: u8 = 1 << 1;
+}
+
 /// GSP FMC initialization parameters.
 #[repr(C)]
 #[derive(Debug, Clone, Copy, Default)]
@@ -171,7 +194,61 @@ struct NvdmPayloadCommandResponse {
     error_code: u32,
 }
 
-/// NVDM (NVIDIA Device Management) COT (Chain of Trust) payload structure.
+// SAFETY: NvdmPayloadCommandResponse is a packed C struct with only integral 
fields.
+unsafe impl FromBytes for NvdmPayloadCommandResponse {}
+
+/// 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 = 0,
+    /// vGPU support is enabled on this GPU.
+    Enabled = 1,
+}
+
+impl TryFrom<u16> for VgpuMode {
+    type Error = kernel::error::Error;
+
+    fn try_from(value: u16) -> Result<Self> {
+        match value {
+            0 => Ok(VgpuMode::Disabled),
+            1 => Ok(VgpuMode::Enabled),
+            _ => Err(EINVAL),
+        }
+    }
+}
+
+/// PRC message payload.
+///
+/// Sent to FSP to query or modify a device configuration knob.
+/// The response includes the common FSP response header followed by
+/// a [`NvdmPayloadPrcResponse`] with the knob's current state value.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct NvdmPayloadPrc {
+    sub_message_id: u8,
+    flags: u8,
+    object_id: u8,
+    reserved: u8,
+}
+
+// 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,
+}
+
+// SAFETY: NvdmPayloadPrcResponse is a packed C struct with only integral 
fields.
+unsafe impl FromBytes for NvdmPayloadPrcResponse {}
+
+/// NVDM (NVIDIA Vendor Defined Message) COT (Chain of Trust) payload 
structure.
 /// This is the main message payload sent to FSP for Chain of Trust.
 #[repr(C, packed)]
 #[derive(Clone, Copy)]
@@ -224,6 +301,17 @@ struct FspCotMessage {
 // SAFETY: FspCotMessage is a packed C struct with only integral fields.
 unsafe impl AsBytes for FspCotMessage {}
 
+/// Complete FSP PRC message.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct FspPrcMessage {
+    header: FspMessageHeader,
+    prc: NvdmPayloadPrc,
+}
+
+// SAFETY: FspPrcMessage is a packed C struct with only integral fields.
+unsafe impl AsBytes for FspPrcMessage {}
+
 /// Complete FSP response structure with MCTP and NVDM headers.
 #[repr(C, packed)]
 #[derive(Clone, Copy)]
@@ -235,6 +323,18 @@ struct FspResponse {
 // SAFETY: FspResponse is a packed C struct with only integral fields.
 unsafe impl FromBytes for FspResponse {}
 
+/// Complete FSP PRC response including the knob state payload.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct FspPrcResponse {
+    header: FspMessageHeader,
+    response: NvdmPayloadCommandResponse,
+    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
@@ -305,17 +405,68 @@ pub(crate) fn boot_params_dma_handle(&self) -> u64 {
     }
 }
 
+impl MessageToFsp for FspPrcMessage {
+    const NVDM_TYPE: u32 = NvdmType::Prc as u32;
+}
+
 /// FSP interface for Hopper/Blackwell GPUs.
 pub(crate) struct Fsp;
 
 impl Fsp {
+    /// Read vGPU mode from FSP using the PRC protocol.
+    ///
+    /// Queries FSP's Management Partition for the active vGPU mode knob value.
+    /// Returns [`VgpuMode::Enabled`] if vGPU support is active on this GPU,
+    /// [`VgpuMode::Disabled`] otherwise.
+    pub(crate) fn read_vgpu_mode(
+        dev: &device::Device<device::Bound>,
+        bar: &Bar0,
+        fsp_falcon: &Falcon<FspEngine>,
+    ) -> Result<VgpuMode> {
+        let msg = KBox::new(
+            FspPrcMessage {
+                header: FspMessageHeader::new(NvdmType::Prc),
+                prc: NvdmPayloadPrc {
+                    sub_message_id: prc::SUBCMD_READ,
+                    flags: prc::FLAG_ACTIVE,
+                    object_id: prc::OBJECT_VGPU_MODE,
+                    reserved: 0,
+                },
+            },
+            GFP_KERNEL,
+        )?;
+
+        let response_buf = Self::send_sync_fsp(dev, bar, fsp_falcon, &*msg)?;
+
+        let prc_resp_size = core::mem::size_of::<FspPrcResponse>();
+        if response_buf.len() < prc_resp_size {
+            dev_err!(
+                dev,
+                "PRC response too small: {} bytes (expected {})\n",
+                response_buf.len(),
+                prc_resp_size
+            );
+            return Err(EIO);
+        }
+
+        let prc_response =
+            
FspPrcResponse::from_bytes(&response_buf[..prc_resp_size]).ok_or(EIO)?;
+
+        let raw_value = u16::from(prc_response.prc_data.value_low)
+            | (u16::from(prc_response.prc_data.value_high) << 8);
+
+        VgpuMode::try_from(raw_value).inspect_err(|_| {
+            dev_err!(dev, "unexpected vGPU mode value: {:#x}\n", raw_value);
+        })
+    }
+
     /// Wait for FSP secure boot completion.
     ///
     /// Polls the thermal scratch register until FSP signals boot completion
     /// or timeout occurs.
     pub(crate) fn wait_secure_boot(
         dev: &device::Device<device::Bound>,
-        bar: &crate::driver::Bar0,
+        bar: &Bar0,
         arch: crate::gpu::Architecture,
     ) -> Result {
         debug_assert!(
@@ -403,8 +554,8 @@ pub(crate) fn extract_fmc_signatures(
     /// to FSP, and waits for the response.
     pub(crate) fn boot_fmc(
         dev: &device::Device<device::Bound>,
-        bar: &crate::driver::Bar0,
-        fsp_falcon: &crate::falcon::Falcon<crate::falcon::fsp::Fsp>,
+        bar: &Bar0,
+        fsp_falcon: &Falcon<FspEngine>,
         args: &FmcBootArgs<'_>,
     ) -> Result {
         dev_dbg!(dev, "Starting FSP boot sequence for {}\n", args.chipset);
@@ -457,8 +608,8 @@ pub(crate) fn boot_fmc(
     /// Returns the raw response buffer for protocol-specific parsing.
     fn send_sync_fsp<M>(
         dev: &device::Device<device::Bound>,
-        bar: &crate::driver::Bar0,
-        fsp_falcon: &crate::falcon::Falcon<crate::falcon::fsp::Fsp>,
+        bar: &Bar0,
+        fsp_falcon: &Falcon<FspEngine>,
         msg: &M,
     ) -> Result<KVec<u8>>
     where
diff --git a/drivers/gpu/nova-core/mctp.rs b/drivers/gpu/nova-core/mctp.rs
index c4e36a46fd69..5c53e156d482 100644
--- a/drivers/gpu/nova-core/mctp.rs
+++ b/drivers/gpu/nova-core/mctp.rs
@@ -10,6 +10,8 @@
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 #[repr(u32)]
 pub(crate) enum NvdmType {
+    /// PRC (Product Reconfiguration Control) message.
+    Prc = 0x13,
     /// Chain of Trust boot message.
     Cot = 0x14,
     /// FSP command response.
-- 
2.51.0

Reply via email to