NVIDIA GPU driver infrastructure
 help / color / mirror / Atom feed
* [PATCH v2 0/1] gpu: nova-core: add fwctl driver for firmware control interface
@ 2026-11-06  6:09 Alok Kumar
  2026-11-06  6:09 ` [PATCH v2 1/1] " Alok Kumar
  0 siblings, 1 reply; 2+ messages in thread
From: Alok Kumar @ 2026-11-06  6:09 UTC (permalink / raw)
  To: dakr, acourbot, kvm, rust-for-linux, nova-gpu, linux-pci
  Cc: alex, jgg, yishaih, skolothumtho, kevin.tian, airlied, simona,
	ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin,
	a.hindborg, aliceryhl, tmgross, markus.probst, jhubbard, apopple,
	joelagnelf, ecourtney, cjia, smitra, kjaju, alkumar, ankita,
	aniketa, kwankhede, targupta, linux-kernel, zhiwang, zhiw, boqun,
	daniel.almeida, tamird, work, jgg, bhelgaas, kwilczynski,
	dri-devel, peter, xinmengs

Add a fwctl endpoint to nova-core for the GSP GMC API commands used to
manage vGPU types on a physical GPU.

The request path is:

  userspace
      |
      | FWCTL_RPC (FWCTL_RPC_CONFIGURATION)
      v
  nova-core command allowlist
      |
      | GMC command ID and payload
      v
  GSP command queue -> GSP-RM

Userspace supplies the native GMC command ID and command-specific
payload. nova-core constructs the transport headers and permits only the
seven vGPU management commands for adding, querying, assigning, and
deassigning vGPU types. All other commands and scopes are rejected.

The request starts with an eight-byte header containing command_id and
a zero reserved field, followed by the command-specific payload. The
response echoes this header and appends the firmware response payload.

This series is based on:

    [PATCH v4 00/32] Introduce NVIDIA vGPU manager [1]

A userspace implementation is available at:

  https://github.com/alokk-nvidia/vgpu-tools/tree/m1-nvkv/fwctl

Changes since the previous posting [2]:

  - Pass native GMC command IDs and payloads instead of translating a
    driver-specific command.
  - Keep the MCTP/NVDM transport framing inside nova-core.
  - Allowlist the seven vGPU management commands in configuration scope.
  - Borrow the GPU-owned command queue through lifetime-bound fwctl
    registration data instead of looking up PCI driver data in callbacks.
    No separate BAR0 reference is stored in the registration data.
  - Register fwctl after GPU and auxiliary-device initialization, and
    unregister it and drain callbacks before dropping GPU resources.

The complete integration tree, including the prerequisites and the
companion VFIO driver, is available at:

    https://github.com/alokk-nvidia/nova-core/tree/nova-vgpu-wip-20261105

[1] https://lore.kernel.org/nova-gpu/cover.1793906667.git.alkumar@nvidia.com/
[2] https://lore.kernel.org/rust-for-linux/20260305190936.398590-1-zhiw@nvidia.com/

Zhi Wang (1):
  gpu: nova-core: add fwctl driver for firmware control interface

 drivers/gpu/nova-core/Kconfig      |   2 +
 drivers/gpu/nova-core/driver.rs    |  27 +++++++
 drivers/gpu/nova-core/fwctl.rs     | 119 +++++++++++++++++++++++++++++
 drivers/gpu/nova-core/nova_core.rs |   2 +
 drivers/gpu/nova-core/vgpu.rs      |  10 +++
 drivers/gpu/nova-core/vgpu/fw.rs   |  18 ++++-
 include/uapi/fwctl/fwctl.h         |   1 +
 include/uapi/fwctl/nova-core.h     |  34 +++++++++
 rust/kernel/fwctl.rs               |   2 +
 rust/uapi/uapi_helper.h            |   2 +
 10 files changed, 215 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/nova-core/fwctl.rs
 create mode 100644 include/uapi/fwctl/nova-core.h


base-commit: 132a3ca37584de0473193e8186c61e46ff8042d3
-- 
2.53.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [PATCH v2 1/1] gpu: nova-core: add fwctl driver for firmware control interface
  2026-11-06  6:09 [PATCH v2 0/1] gpu: nova-core: add fwctl driver for firmware control interface Alok Kumar
@ 2026-11-06  6:09 ` Alok Kumar
  0 siblings, 0 replies; 2+ messages in thread
From: Alok Kumar @ 2026-11-06  6:09 UTC (permalink / raw)
  To: dakr, acourbot, kvm, rust-for-linux, nova-gpu, linux-pci
  Cc: alex, jgg, yishaih, skolothumtho, kevin.tian, airlied, simona,
	ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin,
	a.hindborg, aliceryhl, tmgross, markus.probst, jhubbard, apopple,
	joelagnelf, ecourtney, cjia, smitra, kjaju, alkumar, ankita,
	aniketa, kwankhede, targupta, linux-kernel, zhiwang, zhiw, boqun,
	daniel.almeida, tamird, work, jgg, bhelgaas, kwilczynski,
	dri-devel, peter, xinmengs

From: Zhi Wang <zhiw@nvidia.com>

Add a fwctl driver that provides an interface for userspace to issue
GMC API commands to the GSP. Userspace supplies the command ID and
payload while the transport headers remain internal to the kernel.

Allow the seven vGPU management commands in the configuration scope.
Reject all other commands and scopes.

zhiw: Adapt to the GPU-owned command queue and its GMC transaction API.
Use the existing vGPU firmware module for command constants and register
fwctl after GPU and auxiliary-device initialization.

Signed-off-by: Zhi Wang <zhiw@nvidia.com>
Signed-off-by: Alok Kumar <alkumar@nvidia.com>
---
 drivers/gpu/nova-core/Kconfig      |   2 +
 drivers/gpu/nova-core/driver.rs    |  27 +++++++
 drivers/gpu/nova-core/fwctl.rs     | 119 +++++++++++++++++++++++++++++
 drivers/gpu/nova-core/nova_core.rs |   2 +
 drivers/gpu/nova-core/vgpu.rs      |  10 +++
 drivers/gpu/nova-core/vgpu/fw.rs   |  18 ++++-
 include/uapi/fwctl/fwctl.h         |   1 +
 include/uapi/fwctl/nova-core.h     |  34 +++++++++
 rust/kernel/fwctl.rs               |   2 +
 rust/uapi/uapi_helper.h            |   2 +
 10 files changed, 215 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/nova-core/fwctl.rs
 create mode 100644 include/uapi/fwctl/nova-core.h

diff --git a/drivers/gpu/nova-core/Kconfig b/drivers/gpu/nova-core/Kconfig
index 2e11e46c99c7..3431cac7f3ba 100644
--- a/drivers/gpu/nova-core/Kconfig
+++ b/drivers/gpu/nova-core/Kconfig
@@ -4,8 +4,10 @@ config NOVA_CORE
 	depends on PCI
 	depends on RUST
 	depends on !CPU_BIG_ENDIAN
+	depends on FWCTL=y
 	select AUXILIARY_BUS
 	select GPU_BUDDY
+	select RUST_FWCTL_ABSTRACTIONS
 	select RUST_FW_LOADER_ABSTRACTIONS
 	default n
 	help
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 560a70c4f5a7..7ad93eca485b 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -6,6 +6,7 @@
         Bound,
         Core, //
     },
+    fwctl,
     io::resource,
     pci,
     pci::{
@@ -24,6 +25,10 @@
 
 use crate::{
     api::NovaCoreApi,
+    fwctl::{
+        NovaCoreFwCtl,
+        NovaCoreFwCtlData, //
+    },
     gpu::Gpu, //
 };
 
@@ -34,6 +39,8 @@
 pub(crate) struct NovaCore<'bound> {
     #[allow(clippy::type_complexity)]
     _reg: auxiliary::Registration<'bound, ForLt!(NovaCoreApi<'_>)>,
+    /// Unregisters fwctl and drains its callbacks before GPU resources are dropped.
+    _fwctl: fwctl::Registration<'bound, NovaCoreFwCtl>,
     #[pin]
     pub(crate) gpu: Gpu<'bound>,
     bar: pci::Bar<'bound, BAR0_SIZE>,
@@ -168,6 +175,26 @@ fn probe<'bound>(
                     )?
                 }
             },
+
+            _fwctl: {
+                // SAFETY: `gpu` is initialized at its final pinned address. `_fwctl` is
+                // declared before `gpu`, so unregistering fwctl drains callbacks before the
+                // GPU is dropped. Initializer cleanup also unregisters it first, since this
+                // registration is initialized last. The private registration is never leaked.
+                let gpu = unsafe { &*core::ptr::from_ref(gpu.as_ref().get_ref()) };
+                let fwctl_dev = fwctl::Device::<NovaCoreFwCtl>::new(pdev.as_ref())?;
+
+                // SAFETY: The device is newly allocated, unregistered, and parented to `pdev`.
+                // The registration borrows the pinned GPU as described above, and its Drop
+                // runs before the parent device unbinds.
+                unsafe {
+                    fwctl::Registration::new(
+                        pdev.as_ref(),
+                        &fwctl_dev,
+                        NovaCoreFwCtlData::new(gpu.cmdq()),
+                    )?
+                }
+            },
         })
     }
 }
diff --git a/drivers/gpu/nova-core/fwctl.rs b/drivers/gpu/nova-core/fwctl.rs
new file mode 100644
index 000000000000..f1f750e86e4f
--- /dev/null
+++ b/drivers/gpu/nova-core/fwctl.rs
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! fwctl driver for nova-core GMC API pass-through.
+
+use kernel::{
+    fwctl,
+    fwctl::{
+        DeviceType,
+        FwRpcResponse,
+        RpcScope, //
+    },
+    prelude::*, //
+};
+
+use crate::{
+    gsp::cmdq::Cmdq,
+    vgpu::{
+        GMCAPI_CMD_ADD_VGPU_TYPE,
+        GMCAPI_CMD_ASSIGN_VGPU_TYPE,
+        GMCAPI_CMD_DEASSIGN_VGPU_TYPE,
+        GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE,
+        GMCAPI_CMD_QUERY_CREATABLE_VGPU_TYPES,
+        GMCAPI_CMD_QUERY_SUPPORTED_VGPU_TYPES,
+        GMCAPI_CMD_QUERY_VGPU_PROPERTIES, //
+    },
+};
+
+/// Resources used by fwctl callbacks while the GPU is bound.
+#[pin_data]
+pub(crate) struct NovaCoreFwCtlData<'a> {
+    cmdq: &'a Cmdq<'a>,
+}
+
+impl<'a> NovaCoreFwCtlData<'a> {
+    pub(crate) fn new(cmdq: &'a Cmdq<'a>) -> impl PinInit<Self, Error> {
+        try_pin_init!(Self { cmdq })
+    }
+}
+
+/// Per-file fwctl context for nova-core.
+#[pin_data]
+pub(crate) struct NovaCoreFwCtl {}
+
+impl fwctl::Operations for NovaCoreFwCtl {
+    type RegistrationData<'a> = NovaCoreFwCtlData<'a>;
+
+    const DEVICE_TYPE: DeviceType = DeviceType::NovaCore;
+
+    fn open<'a>(
+        _device: &fwctl::Device<Self>,
+        _reg_data: &Self::RegistrationData<'a>,
+    ) -> impl PinInit<Self, Error> {
+        try_pin_init!(Self {})
+    }
+
+    fn fw_rpc<'a>(
+        _this: Pin<&Self>,
+        _device: &fwctl::Device<Self>,
+        reg_data: &Self::RegistrationData<'a>,
+        scope: RpcScope,
+        rpc_buf: &mut [u8],
+        _max_output_len: usize,
+    ) -> Result<FwRpcResponse, Error> {
+        if rpc_buf.len() < RPC_HEADER_SIZE {
+            return Err(EINVAL);
+        }
+
+        let (header, payload) = rpc_buf.split_at(RPC_HEADER_SIZE);
+        let command_id = u32::from_le_bytes(header[..4].try_into().map_err(|_| EINVAL)?);
+        let reserved = u32::from_le_bytes(header[4..].try_into().map_err(|_| EINVAL)?);
+
+        if reserved != 0 {
+            return Err(EINVAL);
+        }
+        if !is_command_permitted(scope, command_id) {
+            return Err(EPERM);
+        }
+
+        let response =
+            reg_data
+                .cmdq
+                .send_gmc_and_receive(command_id, payload, GSP_GMC_MAX_RESPONSE_SIZE)?;
+        if response.status != 0 {
+            return Err(EIO);
+        }
+
+        let response_len = RPC_HEADER_SIZE
+            .checked_add(response.payload.len())
+            .ok_or(EOVERFLOW)?;
+        let mut out = KVVec::with_capacity(response_len, GFP_KERNEL)?;
+        out.extend_from_slice(&command_id.to_le_bytes(), GFP_KERNEL)?;
+        out.extend_from_slice(&0u32.to_le_bytes(), GFP_KERNEL)?;
+        out.extend_from_slice(&response.payload, GFP_KERNEL)?;
+
+        Ok(FwRpcResponse::NewBuffer(out))
+    }
+}
+
+/// Size of `fwctl_rpc_nova_core` (`command_id` plus `reserved`).
+const RPC_HEADER_SIZE: usize = 8;
+
+const GSP_GMC_MAX_RESPONSE_SIZE: u32 = 16_384;
+
+/// Returns whether `command_id` may be issued under `scope`.
+fn is_command_permitted(scope: RpcScope, command_id: u32) -> bool {
+    match scope {
+        RpcScope::Configuration => matches!(
+            command_id,
+            GMCAPI_CMD_ADD_VGPU_TYPE
+                | GMCAPI_CMD_QUERY_SUPPORTED_VGPU_TYPES
+                | GMCAPI_CMD_QUERY_CREATABLE_VGPU_TYPES
+                | GMCAPI_CMD_ASSIGN_VGPU_TYPE
+                | GMCAPI_CMD_DEASSIGN_VGPU_TYPE
+                | GMCAPI_CMD_QUERY_VGPU_PROPERTIES
+                | GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE
+        ),
+        _ => false,
+    }
+}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index f7753d63c432..5f09b69f3b54 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -16,6 +16,7 @@
 mod fb;
 mod firmware;
 mod fsp;
+mod fwctl;
 mod gpu;
 mod gsp;
 mod irq;
@@ -83,6 +84,7 @@ fn init(module: &'static kernel::ThisModule) -> impl PinInit<Self, Error> {
     description: "Nova Core GPU driver",
     license: "GPL v2",
     firmware: [],
+    imports_ns: ["FWCTL"],
 }
 
 kernel::module_firmware!(firmware::ModInfoBuilder);
diff --git a/drivers/gpu/nova-core/vgpu.rs b/drivers/gpu/nova-core/vgpu.rs
index 47a27236a086..f198fa218297 100644
--- a/drivers/gpu/nova-core/vgpu.rs
+++ b/drivers/gpu/nova-core/vgpu.rs
@@ -46,6 +46,16 @@
 pub(crate) mod vgpu_api;
 mod vram;
 
+pub(crate) use fw::{
+    GMCAPI_CMD_ADD_VGPU_TYPE,
+    GMCAPI_CMD_ASSIGN_VGPU_TYPE,
+    GMCAPI_CMD_DEASSIGN_VGPU_TYPE,
+    GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE,
+    GMCAPI_CMD_QUERY_CREATABLE_VGPU_TYPES,
+    GMCAPI_CMD_QUERY_SUPPORTED_VGPU_TYPES,
+    GMCAPI_CMD_QUERY_VGPU_PROPERTIES, //
+};
+
 /// vGPU state detected during GPU construction.
 #[derive(Debug, Clone, Copy)]
 pub(crate) enum VgpuState {
diff --git a/drivers/gpu/nova-core/vgpu/fw.rs b/drivers/gpu/nova-core/vgpu/fw.rs
index 795ce4855586..8f0fb26da86b 100644
--- a/drivers/gpu/nova-core/vgpu/fw.rs
+++ b/drivers/gpu/nova-core/vgpu/fw.rs
@@ -31,10 +31,24 @@
 
 pub(super) use commands::RpcMessage;
 
-pub(super) const GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE: u32 =
+pub(crate) const GMCAPI_CMD_ADD_VGPU_TYPE: u32 = bindings::GMCAPI_COMMANDS_GMCAPI_CMD_ADD_VGPU_TYPE;
+
+pub(crate) const GMCAPI_CMD_QUERY_SUPPORTED_VGPU_TYPES: u32 =
+    bindings::GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_SUPPORTED_VGPU_TYPES;
+
+pub(crate) const GMCAPI_CMD_QUERY_CREATABLE_VGPU_TYPES: u32 =
+    bindings::GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_CREATABLE_VGPU_TYPES;
+
+pub(crate) const GMCAPI_CMD_ASSIGN_VGPU_TYPE: u32 =
+    bindings::GMCAPI_COMMANDS_GMCAPI_CMD_ASSIGN_VGPU_TYPE;
+
+pub(crate) const GMCAPI_CMD_DEASSIGN_VGPU_TYPE: u32 =
+    bindings::GMCAPI_COMMANDS_GMCAPI_CMD_DEASSIGN_VGPU_TYPE;
+
+pub(crate) const GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE: u32 =
     bindings::GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE;
 
-pub(super) const GMCAPI_CMD_QUERY_VGPU_PROPERTIES: u32 =
+pub(crate) const GMCAPI_CMD_QUERY_VGPU_PROPERTIES: u32 =
     bindings::GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_VGPU_PROPERTIES;
 
 pub(super) const GMCAPI_CMD_BOOTLOAD_GSP_VGPU_PLUGIN_TASK: u32 =
diff --git a/include/uapi/fwctl/fwctl.h b/include/uapi/fwctl/fwctl.h
index 2d6d4049c205..0e336cbbeb6b 100644
--- a/include/uapi/fwctl/fwctl.h
+++ b/include/uapi/fwctl/fwctl.h
@@ -46,6 +46,7 @@ enum fwctl_device_type {
 	FWCTL_DEVICE_TYPE_CXL = 2,
 	FWCTL_DEVICE_TYPE_BNXT = 3,
 	FWCTL_DEVICE_TYPE_PDS = 4,
+	FWCTL_DEVICE_TYPE_NOVA_CORE = 8,
 };
 
 /**
diff --git a/include/uapi/fwctl/nova-core.h b/include/uapi/fwctl/nova-core.h
new file mode 100644
index 000000000000..c11619660058
--- /dev/null
+++ b/include/uapi/fwctl/nova-core.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES
+ *
+ * NVIDIA nova-core fwctl interface. All fwctl traffic uses the GMC API
+ * transport. User-space supplies a command_id and optional payload; the
+ * kernel constructs the GMC transport headers, validates the command_id
+ * against the permitted set for the requested scope, and returns only the
+ * response payload.
+ *
+ * The device_type for this file is FWCTL_DEVICE_TYPE_NOVA_CORE.
+ */
+#ifndef _UAPI_FWCTL_NOVA_CORE_H
+#define _UAPI_FWCTL_NOVA_CORE_H
+
+#include <linux/types.h>
+
+/**
+ * struct fwctl_rpc_nova_core - ioctl(FWCTL_RPC) in/out buffer format
+ * @command_id: GMC API command identifier (from GMCAPI_COMMANDS).
+ * @reserved: Must be zero.
+ *
+ * The request buffer passed to FWCTL_RPC begins with this header,
+ * followed by the command-specific payload bytes (if any).
+ *
+ * The response buffer returned by FWCTL_RPC begins with this same
+ * header (echoing the command_id), followed by the response payload.
+ */
+struct fwctl_rpc_nova_core {
+	__u32 command_id;
+	__u32 reserved;
+};
+
+#endif
diff --git a/rust/kernel/fwctl.rs b/rust/kernel/fwctl.rs
index b7778e4d5ee0..92e1cdf6da2d 100644
--- a/rust/kernel/fwctl.rs
+++ b/rust/kernel/fwctl.rs
@@ -44,6 +44,8 @@ pub enum DeviceType {
     Pds = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_PDS,
     /// Broadcom NetXtreme (bnxt) device.
     Bnxt = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_BNXT,
+    /// NVIDIA nova-core GPU device.
+    NovaCore = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_NOVA_CORE,
 }
 
 /// Scope of access for an RPC request.
diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h
index 44fb6203c0ad..428a756a9b5c 100644
--- a/rust/uapi/uapi_helper.h
+++ b/rust/uapi/uapi_helper.h
@@ -11,6 +11,8 @@
 #include <uapi/drm/drm.h>
 #include <uapi/drm/nova_drm.h>
 #include <uapi/drm/panthor_drm.h>
+#include <uapi/fwctl/fwctl.h>
+#include <uapi/fwctl/nova-core.h>
 #include <uapi/linux/android/binder.h>
 #include <uapi/linux/android/binder_netlink.h>
 #include <uapi/linux/ioctl.h>
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-06  6:10 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-06  6:09 [PATCH v2 0/1] gpu: nova-core: add fwctl driver for firmware control interface Alok Kumar
2026-11-06  6:09 ` [PATCH v2 1/1] " Alok Kumar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox