summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Kconfig44
-rw-r--r--kernel/Makefile33
-rw-r--r--kernel/Makefile.gsp_rpc24
-rw-r--r--kernel/bar_emulation.c267
-rw-r--r--kernel/bar_init.c146
-rw-r--r--kernel/gsp_comm.c636
-rw-r--r--kernel/gsp_rpc_client.c872
-rw-r--r--kernel/gsp_rpc_client.h313
-rw-r--r--kernel/gsp_rpc_integration.c535
-rw-r--r--kernel/include/bar_emulation.h57
-rw-r--r--kernel/include/gsp_comm.h155
-rw-r--r--kernel/include/gsp_rpc_client.h313
-rw-r--r--kernel/include/gsp_rpc_integration.h97
-rw-r--r--kernel/include/ioctl.h105
-rw-r--r--kernel/include/irq.h45
-rw-r--r--kernel/include/memory.h45
-rw-r--r--kernel/include/migration.h75
-rw-r--r--kernel/include/nv-hypervisor.h93
-rw-r--r--kernel/include/nv-vgpu-vfio-interface.h83
-rw-r--r--kernel/include/nvidia-vgpu-vfio.h194
-rw-r--r--kernel/include/nvstatus.h123
-rw-r--r--kernel/include/nvtypes.h830
-rw-r--r--kernel/ioctl.c263
-rw-r--r--kernel/irq.c246
-rw-r--r--kernel/main.c834
-rw-r--r--kernel/memory.c285
-rw-r--r--kernel/memory.c.iommu335
-rw-r--r--kernel/migration.c324
-rw-r--r--kernel/tests/Makefile40
-rw-r--r--kernel/tests/Module.symvers0
-rw-r--r--kernel/tests/gsp_rpc_test.c425
-rw-r--r--kernel/tests/gsp_vgpu_comprehensive_test.c432
-rw-r--r--kernel/tests/gsp_vgpu_kunit_test.c327
-rw-r--r--kernel/tests/gsp_vgpu_mock_test.c624
34 files changed, 9220 insertions, 0 deletions
diff --git a/kernel/Kconfig b/kernel/Kconfig
new file mode 100644
index 0000000..5cee953
--- /dev/null
+++ b/kernel/Kconfig
@@ -0,0 +1,44 @@
+config NVIDIA_VGPU_VFIO
+ tristate "NVIDIA vGPU VFIO Mediated Device Driver"
+ depends on VFIO_MDEV && PCI && IOMMU_API
+ select VFIO_PCI_CORE
+ help
+ This driver provides VFIO mediated device support for NVIDIA GPUs,
+ enabling vGPU functionality through the Linux VFIO/mdev framework.
+ It replaces the proprietary nvidia-vgpu-vfio.ko kernel module.
+
+ The driver handles:
+ - VFIO device creation and management
+ - BAR trapping and emulation
+ - GSP (GPU System Processor) communication
+ - Memory mapping and pinning
+ - IRQFD/IOEVENTFD handling
+ - Live migration support
+
+ To compile this driver as a module, choose M here: the
+ module will be called nvidia-vgpu-vfio.
+
+if NVIDIA_VGPU_VFIO
+
+config NVIDIA_VGPU_VFIO_DEBUG
+ bool "Enable debug logging for NVIDIA vGPU VFIO driver"
+ default n
+ help
+ Enable verbose debug logging in the NVIDIA vGPU VFIO driver.
+ This will increase kernel log verbosity significantly.
+
+config NVIDIA_VGPU_VFIO_MIGRATION
+ bool "Enable live migration support"
+ default y
+ help
+ Enable live migration support for vGPU mediated devices.
+ This adds the migration ioctl interface and state serialization.
+
+config NVIDIA_VGPU_VFIO_GSP_RPC
+ bool "Enable GSP RPC communication"
+ default y
+ help
+ Enable communication with the GPU System Processor (GSP)
+ via RPC for scheduling and resource management.
+
+endif \ No newline at end of file
diff --git a/kernel/Makefile b/kernel/Makefile
new file mode 100644
index 0000000..18651cf
--- /dev/null
+++ b/kernel/Makefile
@@ -0,0 +1,33 @@
+obj-m += open-iov-vfio.o
+
+open-iov-vfio-y := \
+ main.o \
+ bar_init.o \
+ bar_emulation.o \
+ gsp_comm.o \
+ gsp_rpc_client.o \
+ gsp_rpc_integration.o \
+ memory.o \
+ irq.o \
+ migration.o \
+ ioctl.o
+
+ccflags-y := -I$(src)/include -Wall -Wextra -Wno-error -Wno-unused-parameter -Wno-unused-function
+
+# mdev dependencies
+KBUILD_MODPOST_WARN := 1
+
+# Use symbols from host nvidia.ko
+KBUILD_EXTRA_SYMBOLS := $OPEN_IOV_ROOT/nvidia-open-gpu-kernel-modules/kernel-open/Module.symvers
+
+all:
+ $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+clean:
+ $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
+
+install:
+ $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules_install
+ depmod -a
+
+.PHONY: all clean install \ No newline at end of file
diff --git a/kernel/Makefile.gsp_rpc b/kernel/Makefile.gsp_rpc
new file mode 100644
index 0000000..8bbee6b
--- /dev/null
+++ b/kernel/Makefile.gsp_rpc
@@ -0,0 +1,24 @@
+# GSP RPC Client Kernel Module Makefile
+# Build: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) -f Makefile.gsp_rpc
+
+obj-m += gsp_rpc_client.o
+
+gsp_rpc_client-y := gsp_rpc_client.o
+
+ccflags-y += -I$(src)/include
+ccflags-y += -Wall -Wextra -Werror
+ccflags-y += -std=gnu11
+
+# For debugging
+# ccflags-y += -DDEBUG
+
+all:
+ $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) -f $(PWD)/Makefile.gsp_rpc modules
+
+clean:
+ $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) -f $(PWD)/Makefile.gsp_rpc clean
+
+install:
+ $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) -f $(PWD)/Makefile.gsp_rpc modules_install
+
+.PHONY: all clean install
diff --git a/kernel/bar_emulation.c b/kernel/bar_emulation.c
new file mode 100644
index 0000000..17904fe
--- /dev/null
+++ b/kernel/bar_emulation.c
@@ -0,0 +1,267 @@
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/vmalloc.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "bar_emulation.h"
+#include "gsp_comm.h"
+
+#define NV_VGPU_BAR0_TRAP_REGIONS 64
+
+static const struct {
+ u32 offset;
+ u32 size;
+ const char *name;
+} nv_vgpu_bar0_traps[] = {
+ { 0x000000, 0x1000, "PMC" },
+ { 0x001000, 0x1000, "PRAMIN" },
+ { 0x002000, 0x1000, "PCR" },
+ { 0x003000, 0x1000, "PRAMDAC" },
+ { 0x004000, 0x1000, "PVIO" },
+ { 0x005000, 0x1000, "PFIFO" },
+ { 0x006000, 0x1000, "PGRAPH" },
+ { 0x007000, 0x1000, "PTIMER" },
+ { 0x008000, 0x1000, "PFUSE" },
+ { 0x009000, 0x1000, "PBUS" },
+ { 0x00A000, 0x1000, "PCOP" },
+ { 0x00B000, 0x1000, "PVP" },
+ { 0x00C000, 0x1000, "PCE" },
+ { 0x00D000, 0x1000, "PDISP" },
+ { 0x00E000, 0x1000, "PNVDEC" },
+ { 0x00F000, 0x1000, "PNVENC" },
+ { 0x010000, 0x1000, "PSEC2" },
+ { 0x011000, 0x1000, "PMU" },
+ { 0x012000, 0x1000, "PGFX" },
+ { 0x013000, 0x1000, "PXVE" },
+ { 0x020000, 0x10000, "FIFO" },
+ { 0x040000, 0x10000, "GR" },
+ { 0x050000, 0x10000, "CE" },
+ { 0x060000, 0x10000, "NVDEC" },
+ { 0x070000, 0x10000, "NVENC" },
+ { 0x080000, 0x10000, "NVJPG" },
+ { 0x090000, 0x10000, "OFA" },
+ { 0x100000, 0x10000, "FB" },
+ { 0x110000, 0x10000, "LTC" },
+ { 0x120000, 0x10000, "GPC" },
+ { 0x130000, 0x10000, "TPC" },
+ { 0x140000, 0x10000, "ROP" },
+ { 0x150000, 0x10000, "HUB" },
+ { 0x160000, 0x10000, "NVLINK" },
+ { 0x170000, 0x10000, "GSP" },
+ { 0x180000, 0x10000, "RAP" },
+ { 0x190000, 0x10000, "SEC" },
+ { 0x1A0000, 0x10000, "PMU_RISCV" },
+ { 0x1B0000, 0x10000, "FALCON" },
+ { 0x1C0000, 0x10000, "XVE" },
+ { 0x1D0000, 0x10000, "DMCE" },
+ { 0x1E0000, 0x10000, "P2P" },
+ { 0x1F0000, 0x10000, "MISC" },
+};
+
+int nv_vgpu_bar_emul_init(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx;
+ int i, ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vgpu = vgpu;
+ ctx->bar0_shadow_size = NV_VGPU_BAR0_SIZE;
+ ctx->bar0_shadow = vzalloc(ctx->bar0_shadow_size);
+ if (!ctx->bar0_shadow) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ spin_lock_init(&ctx->shadow_lock);
+ ctx->trap_count = 0;
+
+ for (i = 0; i < ARRAY_SIZE(nv_vgpu_bar0_traps); i++) {
+ if (ctx->trap_count >= 256)
+ break;
+ ctx->trap_regs[ctx->trap_count].offset = nv_vgpu_bar0_traps[i].offset;
+ ctx->trap_regs[ctx->trap_count].trapped = true;
+ ctx->trap_count++;
+ }
+
+ vgpu->bar_emul_ctx = ctx;
+ return 0;
+
+err_free:
+ kfree(ctx);
+ return ret;
+}
+
+void nv_vgpu_bar_emul_cleanup(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+
+ if (!ctx)
+ return;
+
+ if (ctx->bar0_shadow)
+ vfree(ctx->bar0_shadow);
+
+ kfree(ctx);
+ vgpu->bar_emul_ctx = NULL;
+}
+
+int nv_vgpu_bar_trap_register(struct nv_vgpu_device *vgpu, int bar_idx,
+ u32 offset, u32 size)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+ unsigned long flags;
+ int i;
+
+ if (!ctx || bar_idx != 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctx->shadow_lock, flags);
+
+ for (i = 0; i < ctx->trap_count; i++) {
+ if (ctx->trap_regs[i].offset == offset) {
+ ctx->trap_regs[i].trapped = true;
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return 0;
+ }
+ }
+
+ if (ctx->trap_count >= 256) {
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return -ENOSPC;
+ }
+
+ ctx->trap_regs[ctx->trap_count].offset = offset;
+ ctx->trap_regs[ctx->trap_count].trapped = true;
+ ctx->trap_count++;
+
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return 0;
+}
+
+int nv_vgpu_bar_untrap_register(struct nv_vgpu_device *vgpu, int bar_idx,
+ u32 offset)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+ unsigned long flags;
+ int i;
+
+ if (!ctx || bar_idx != 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctx->shadow_lock, flags);
+
+ for (i = 0; i < ctx->trap_count; i++) {
+ if (ctx->trap_regs[i].offset == offset) {
+ ctx->trap_regs[i].trapped = false;
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return -ENOENT;
+}
+
+int nv_vgpu_bar_emul_read(struct nv_vgpu_device *vgpu, int bar_idx,
+ u64 offset, void *val, size_t size)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+ unsigned long flags;
+ u32 *val32 = val;
+ u64 *val64 = val;
+
+ if (!ctx || bar_idx != 0 || offset >= ctx->bar0_shadow_size)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctx->shadow_lock, flags);
+
+ switch (size) {
+ case 4:
+ *val32 = *(u32 *)((u8 *)ctx->bar0_shadow + offset);
+ break;
+ case 8:
+ *val64 = *(u64 *)((u8 *)ctx->bar0_shadow + offset);
+ break;
+ default:
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return 0;
+}
+
+int nv_vgpu_bar_emul_write(struct nv_vgpu_device *vgpu, int bar_idx,
+ u64 offset, void *val, size_t size)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+ unsigned long flags;
+ u32 *val32 = val;
+ u64 *val64 = val;
+ bool trapped = false;
+ int i;
+
+ if (!ctx || bar_idx != 0 || offset >= ctx->bar0_shadow_size)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctx->shadow_lock, flags);
+
+ for (i = 0; i < ctx->trap_count; i++) {
+ if (ctx->trap_regs[i].offset == offset && ctx->trap_regs[i].trapped) {
+ trapped = true;
+ ctx->trap_regs[i].value = (size == 4) ? *val32 : (u32)*val64;
+ ctx->trap_regs[i].is_write = true;
+ break;
+ }
+ }
+
+ switch (size) {
+ case 4:
+ *(u32 *)((u8 *)ctx->bar0_shadow + offset) = *val32;
+ break;
+ case 8:
+ *(u64 *)((u8 *)ctx->bar0_shadow + offset) = *val64;
+ break;
+ default:
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+
+ if (trapped) {
+ nv_vgpu_dbg(vgpu, "BAR0 trap: offset=0x%llx, size=%zu, write=0x%x",
+ offset, size, (size == 4) ? *val32 : (u32)*val64);
+ nv_vgpu_gsp_rpc_bar_trap(vgpu, offset, 1, size, *(u64 *)val, 0, NULL);
+ }
+
+ return 0;
+}
+
+int nv_vgpu_bar_emul_handle_fault(struct nv_vgpu_device *vgpu,
+ unsigned long addr, bool write)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+ struct nv_vgpu_bar *bar = &vgpu->bars[0];
+ u64 offset = addr - (unsigned long)bar->vaddr;
+ u32 val = 0;
+ int ret;
+
+ if (!ctx || !bar->mapped || !bar->trapped)
+ return -EINVAL;
+
+ if (write) {
+ ret = nv_vgpu_bar_emul_write(vgpu, 0, offset, &val, 4);
+ } else {
+ ret = nv_vgpu_bar_emul_read(vgpu, 0, offset, &val, 4);
+ }
+
+ return ret;
+} \ No newline at end of file
diff --git a/kernel/bar_init.c b/kernel/bar_init.c
new file mode 100644
index 0000000..bf993ab
--- /dev/null
+++ b/kernel/bar_init.c
@@ -0,0 +1,146 @@
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/iommu.h>
+#include <linux/vmalloc.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "bar_emulation.h"
+#include "irq.h"
+
+int nv_vgpu_bar_init(struct nv_vgpu_device *vgpu)
+{
+ struct pci_dev *pdev = vgpu->pdev;
+ struct nv_vgpu_bar *bar;
+ u64 bar_addr;
+ u64 bar_size;
+ int i, ret;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: BAR init validation");
+ for (i = 0; i < 4; i++) {
+ bar_addr = pci_resource_start(pdev, i);
+ bar_size = pci_resource_len(pdev, i);
+ if (bar_addr && bar_size) {
+ nv_vgpu_info(vgpu, " BAR%d: gpa=0x%llx, size=0x%llx (%s)",
+ i, bar_addr, bar_size,
+ i == 0 ? "config" : (i == 1 ? "FB" : (i == 2 ? "I/O" : "other")));
+ }
+ }
+ nv_vgpu_info(vgpu, "DRY-RUN: BAR validation passed");
+ return 0;
+ }
+
+ for (i = 0; i < NV_VGPU_MAX_REGIONS; i++) {
+ bar = &vgpu->bars[i];
+ mutex_init(&bar->lock);
+ bar->mapped = false;
+ bar->trapped = false;
+ }
+
+ bar_addr = pci_resource_start(pdev, 0);
+ bar_size = pci_resource_len(pdev, 0);
+ if (bar_addr && bar_size) {
+ vgpu->bars[0].gpa = bar_addr;
+ vgpu->bars[0].size = bar_size;
+ vgpu->bars[0].vaddr = ioremap_wc(bar_addr, bar_size);
+ if (!vgpu->bars[0].vaddr) {
+ nv_vgpu_err(vgpu, "Failed to map BAR0");
+ return -ENOMEM;
+ }
+ vgpu->bar0_config_base = bar_addr;
+ nv_vgpu_info(vgpu, "BAR0: gpa=0x%llx, size=0x%llx, vaddr=%p", bar_addr, bar_size, vgpu->bars[0].vaddr);
+ }
+
+ bar_addr = pci_resource_start(pdev, 1);
+ bar_size = pci_resource_len(pdev, 1);
+ if (bar_addr && bar_size) {
+ vgpu->bars[1].gpa = bar_addr;
+ vgpu->bars[1].size = bar_size;
+ /* BAR1 (FB) is already mapped by nvidia.ko - don't remap */
+ vgpu->bars[1].vaddr = NULL;
+ nv_vgpu_info(vgpu, "BAR1: gpa=0x%llx, size=0x%llx (managed by nvidia.ko)", bar_addr, bar_size);
+ }
+
+ bar_addr = pci_resource_start(pdev, 2);
+ bar_size = pci_resource_len(pdev, 2);
+ if (bar_addr && bar_size) {
+ vgpu->bars[2].gpa = bar_addr;
+ vgpu->bars[2].size = bar_size;
+ vgpu->bars[2].vaddr = ioremap_wc(bar_addr, bar_size);
+ if (!vgpu->bars[2].vaddr) {
+ nv_vgpu_err(vgpu, "Failed to map BAR2");
+ return -ENOMEM;
+ }
+ nv_vgpu_info(vgpu, "BAR2: gpa=0x%llx, size=0x%llx, vaddr=%p", bar_addr, bar_size, vgpu->bars[2].vaddr);
+ }
+
+ bar_addr = pci_resource_start(pdev, 3);
+ bar_size = pci_resource_len(pdev, 3);
+ if (bar_addr && bar_size) {
+ vgpu->bars[3].gpa = bar_addr;
+ vgpu->bars[3].size = bar_size;
+ vgpu->bars[3].vaddr = ioremap_wc(bar_addr, bar_size);
+ if (!vgpu->bars[3].vaddr) {
+ nv_vgpu_err(vgpu, "Failed to map BAR3");
+ return -ENOMEM;
+ }
+ nv_vgpu_info(vgpu, "BAR3: gpa=0x%llx, size=0x%llx, vaddr=%p", bar_addr, bar_size, vgpu->bars[3].vaddr);
+ }
+
+ ret = nv_vgpu_bar_emul_init(vgpu);
+ if (ret) {
+ nv_vgpu_err(vgpu, "BAR emulation init failed: %d", ret);
+ return ret;
+ }
+
+ /* MSI/MSI-X is managed by nvidia.ko - don't try to set up our own */
+ nv_vgpu_info(vgpu, "MSI/MSI-X managed by nvidia.ko");
+
+ vgpu->fb_base = pci_resource_start(pdev, 1);
+ vgpu->fb_size = pci_resource_len(pdev, 1);
+
+ return 0;
+}
+
+void nv_vgpu_bar_cleanup(struct nv_vgpu_device *vgpu)
+{
+ int i;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: BAR cleanup (no hardware resources to release)");
+ return;
+ }
+
+ nv_vgpu_irq_teardown_msix(vgpu);
+ nv_vgpu_irq_teardown_msi(vgpu);
+ nv_vgpu_bar_emul_cleanup(vgpu);
+
+ for (i = 0; i < NV_VGPU_MAX_REGIONS; i++) {
+ if (vgpu->bars[i].vaddr) {
+ iounmap(vgpu->bars[i].vaddr);
+ vgpu->bars[i].vaddr = NULL;
+ }
+ vgpu->bars[i].mapped = false;
+ vgpu->bars[i].trapped = false;
+ vgpu->bars[i].iova = 0;
+ }
+}
+
+int nv_vgpu_bar_trap(struct nv_vgpu_device *vgpu, int bar_idx)
+{
+ if (bar_idx < 0 || bar_idx >= NV_VGPU_MAX_REGIONS)
+ return -EINVAL;
+
+ if (!vgpu->bars[bar_idx].vaddr)
+ return -ENODEV;
+
+ return nv_vgpu_bar_trap_register(vgpu, bar_idx, 0, vgpu->bars[bar_idx].size);
+}
+
+void nv_vgpu_bar_untrap(struct nv_vgpu_device *vgpu, int bar_idx)
+{
+ if (bar_idx < 0 || bar_idx >= NV_VGPU_MAX_REGIONS)
+ return;
+
+ nv_vgpu_bar_untrap_register(vgpu, bar_idx, 0);
+}
diff --git a/kernel/gsp_comm.c b/kernel/gsp_comm.c
new file mode 100644
index 0000000..e5f38aa
--- /dev/null
+++ b/kernel/gsp_comm.c
@@ -0,0 +1,636 @@
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "gsp_comm.h"
+#include "gsp_rpc_integration.h"
+
+#define NV_VGPU_GSP_BOOTLOADER_FILE "nvidia/tu102/gsp/bootloader-535.113.01.bin"
+#define NV_VGPU_GSP_BOOTER_LOAD_FILE "nvidia/tu102/gsp/booter_load-535.113.01.bin"
+#define NV_VGPU_GSP_GSP_FILE "nvidia/tu102/gsp/gsp-535.113.01.bin"
+
+static void nv_vgpu_gsp_rpc_work(struct work_struct *work)
+{
+ struct nv_vgpu_gsp_comm_ctx *ctx = container_of(work, struct nv_vgpu_gsp_comm_ctx, rpc_work);
+ struct nv_vgpu_gsp_rpc_entry *entry, *tmp;
+ LIST_HEAD(completed);
+
+ mutex_lock(&ctx->queue_lock);
+ list_splice_tail_init(&ctx->complete_queue, &completed);
+ mutex_unlock(&ctx->queue_lock);
+
+ list_for_each_entry_safe(entry, tmp, &completed, list) {
+ list_del(&entry->list);
+ complete(&entry->done);
+ }
+}
+
+static void nv_vgpu_gsp_poll_work(struct work_struct *work)
+{
+ struct nv_vgpu_gsp_comm_ctx *ctx = container_of(work, struct nv_vgpu_gsp_comm_ctx, poll_work.work);
+ struct nv_vgpu_device *vgpu = ctx->vgpu;
+ u32 status;
+
+ if (!ctx || !ctx->irq_enabled || !ctx->gsp_doorbell)
+ goto reschedule;
+
+ status = readl(ctx->gsp_doorbell + 0x100);
+ if (status & 0x1) {
+ writel(0x1, ctx->gsp_doorbell + 0x100);
+ schedule_work(&ctx->rpc_work);
+ }
+
+reschedule:
+ if (ctx->poll_active)
+ schedule_delayed_work(&ctx->poll_work, msecs_to_jiffies(1));
+}
+
+static irqreturn_t nv_vgpu_gsp_irq_handler(int irq, void *data)
+{
+ struct nv_vgpu_device *vgpu = data;
+ struct nv_vgpu_gsp_comm_ctx *ctx = vgpu->gsp_comm_ctx;
+ u32 status;
+
+ if (!ctx || !ctx->irq_enabled)
+ return IRQ_NONE;
+
+ status = readl(ctx->gsp_doorbell + 0x100);
+ if (status & 0x1) {
+ writel(0x1, ctx->gsp_doorbell + 0x100);
+ schedule_work(&ctx->rpc_work);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int nv_vgpu_gsp_load_firmware_file(struct nv_vgpu_device *vgpu, const char *filename, void **data, size_t *size)
+{
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, filename, &vgpu->pdev->dev);
+ if (ret) {
+ nv_vgpu_err(vgpu, "Failed to load firmware %s: %d", filename, ret);
+ return ret;
+ }
+
+ /* Use vmalloc for large firmware files (GSP is ~23MB) */
+ *data = vzalloc(fw->size);
+ if (!*data) {
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ memcpy(*data, fw->data, fw->size);
+ *size = fw->size;
+ release_firmware(fw);
+
+ nv_vgpu_info(vgpu, "Loaded firmware %s (%zu bytes)", filename, *size);
+ return 0;
+}
+
+int nv_vgpu_gsp_load_firmware(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_gsp_comm_ctx *ctx = vgpu->gsp_comm_ctx;
+ int ret;
+
+ if (!ctx)
+ return -ENODEV;
+
+ if (ctx->firmware_loaded)
+ return 0;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: validating GSP firmware files");
+ /* Just verify firmware files exist without loading */
+ ret = request_firmware(NULL, NV_VGPU_GSP_BOOTLOADER_FILE, &vgpu->pdev->dev);
+ if (ret) {
+ nv_vgpu_err(vgpu, "DRY-RUN: Firmware %s not found: %d", NV_VGPU_GSP_BOOTLOADER_FILE, ret);
+ return ret;
+ }
+ release_firmware(NULL);
+
+ ret = request_firmware(NULL, NV_VGPU_GSP_BOOTER_LOAD_FILE, &vgpu->pdev->dev);
+ if (ret) {
+ nv_vgpu_err(vgpu, "DRY-RUN: Firmware %s not found: %d", NV_VGPU_GSP_BOOTER_LOAD_FILE, ret);
+ return ret;
+ }
+ release_firmware(NULL);
+
+ ret = request_firmware(NULL, NV_VGPU_GSP_GSP_FILE, &vgpu->pdev->dev);
+ if (ret) {
+ nv_vgpu_err(vgpu, "DRY-RUN: Firmware %s not found: %d", NV_VGPU_GSP_GSP_FILE, ret);
+ return ret;
+ }
+ release_firmware(NULL);
+
+ nv_vgpu_info(vgpu, "DRY-RUN: All GSP firmware files present");
+ return 0;
+ }
+
+ ret = nv_vgpu_gsp_load_firmware_file(vgpu, NV_VGPU_GSP_BOOTLOADER_FILE,
+ &ctx->firmware.bootloader, &ctx->firmware.bootloader_size);
+ if (ret)
+ return ret;
+
+ ret = nv_vgpu_gsp_load_firmware_file(vgpu, NV_VGPU_GSP_BOOTER_LOAD_FILE,
+ &ctx->firmware.booter_load, &ctx->firmware.booter_load_size);
+ if (ret)
+ goto err_bootloader;
+
+ ret = nv_vgpu_gsp_load_firmware_file(vgpu, NV_VGPU_GSP_GSP_FILE,
+ &ctx->firmware.gsp, &ctx->firmware.gsp_size);
+ if (ret)
+ goto err_booter_load;
+
+ ctx->firmware_loaded = true;
+ nv_vgpu_info(vgpu, "GSP firmware loaded successfully");
+ return 0;
+
+err_booter_load:
+ vfree(ctx->firmware.booter_load);
+ ctx->firmware.booter_load = NULL;
+ ctx->firmware.booter_load_size = 0;
+err_bootloader:
+ vfree(ctx->firmware.bootloader);
+ ctx->firmware.bootloader = NULL;
+ ctx->firmware.bootloader_size = 0;
+ return ret;
+}
+
+void nv_vgpu_gsp_release_firmware(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_gsp_comm_ctx *ctx = vgpu->gsp_comm_ctx;
+
+ if (!ctx || !ctx->firmware_loaded)
+ return;
+
+ vfree(ctx->firmware.bootloader);
+ ctx->firmware.bootloader = NULL;
+ ctx->firmware.bootloader_size = 0;
+
+ vfree(ctx->firmware.booter_load);
+ ctx->firmware.booter_load = NULL;
+ ctx->firmware.booter_load_size = 0;
+
+ vfree(ctx->firmware.gsp);
+ ctx->firmware.gsp = NULL;
+ ctx->firmware.gsp_size = 0;
+
+ ctx->firmware_loaded = false;
+ nv_vgpu_info(vgpu, "GSP firmware released");
+}
+
+static int nv_vgpu_gsp_write_firmware(struct nv_vgpu_device *vgpu, void *data, size_t size, u64 offset)
+{
+ struct nv_vgpu_bar *bar = &vgpu->bars[0];
+ void __iomem *dst;
+
+ if (!bar->vaddr)
+ return -ENODEV;
+
+ dst = bar->vaddr + offset;
+ memcpy_toio(dst, data, size);
+ return 0;
+}
+
+int nv_vgpu_gsp_boot(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_gsp_comm_ctx *ctx = vgpu->gsp_comm_ctx;
+ int ret;
+
+ if (!ctx || !ctx->firmware_loaded)
+ return -ENODEV;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: GSP boot sequence validated (would write firmware to BAR0)");
+ nv_vgpu_info(vgpu, "DRY-RUN: Would write bootloader (%zu bytes) to offset 0x0",
+ ctx->firmware.bootloader_size);
+ nv_vgpu_info(vgpu, "DRY-RUN: Would write booter_load (%zu bytes) to offset 0x10000",
+ ctx->firmware.booter_load_size);
+ nv_vgpu_info(vgpu, "DRY-RUN: Would write GSP binary (%zu bytes) to offset 0x200000",
+ ctx->firmware.gsp_size);
+ nv_vgpu_info(vgpu, "DRY-RUN: Would kick GSP doorbell at offset 0x%llx",
+ NV_VGPU_GSP_DOORBELL_OFFSET);
+ return 0;
+ }
+
+ nv_vgpu_info(vgpu, "Starting GSP boot sequence");
+
+ ret = nv_vgpu_gsp_write_firmware(vgpu, ctx->firmware.bootloader,
+ ctx->firmware.bootloader_size, 0x0);
+ if (ret) {
+ nv_vgpu_err(vgpu, "Failed to write bootloader: %d", ret);
+ return ret;
+ }
+
+ ret = nv_vgpu_gsp_write_firmware(vgpu, ctx->firmware.booter_load,
+ ctx->firmware.booter_load_size, 0x10000);
+ if (ret) {
+ nv_vgpu_err(vgpu, "Failed to write booter_load: %d", ret);
+ return ret;
+ }
+
+ ret = nv_vgpu_gsp_write_firmware(vgpu, ctx->firmware.gsp,
+ ctx->firmware.gsp_size, 0x200000);
+ if (ret) {
+ nv_vgpu_err(vgpu, "Failed to write GSP binary: %d", ret);
+ return ret;
+ }
+
+ writel(0x1, ctx->gsp_doorbell);
+
+ nv_vgpu_info(vgpu, "GSP boot sequence initiated");
+ return 0;
+}
+
+int nv_vgpu_gsp_init(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_gsp_comm_ctx *ctx;
+ struct nv_vgpu_bar *bar = &vgpu->bars[0];
+ int ret;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: GSP init validation");
+ if (!bar->vaddr) {
+ nv_vgpu_err(vgpu, "DRY-RUN: BAR0 not mapped, cannot validate GSP offsets");
+ return -ENODEV;
+ }
+ nv_vgpu_info(vgpu, "DRY-RUN: BAR0 mapped at %p, GSP doorbell offset 0x%llx, shared mem offset 0x%llx",
+ bar->vaddr, NV_VGPU_GSP_DOORBELL_OFFSET, NV_VGPU_GSP_SHARED_MEM_OFFSET);
+ /* Validate firmware files exist */
+ ret = request_firmware(NULL, NV_VGPU_GSP_BOOTLOADER_FILE, &vgpu->pdev->dev);
+ if (ret) {
+ nv_vgpu_err(vgpu, "DRY-RUN: Firmware %s not found: %d", NV_VGPU_GSP_BOOTLOADER_FILE, ret);
+ return ret;
+ }
+ release_firmware(NULL);
+ ret = request_firmware(NULL, NV_VGPU_GSP_BOOTER_LOAD_FILE, &vgpu->pdev->dev);
+ if (ret) {
+ nv_vgpu_err(vgpu, "DRY-RUN: Firmware %s not found: %d", NV_VGPU_GSP_BOOTER_LOAD_FILE, ret);
+ return ret;
+ }
+ release_firmware(NULL);
+ ret = request_firmware(NULL, NV_VGPU_GSP_GSP_FILE, &vgpu->pdev->dev);
+ if (ret) {
+ nv_vgpu_err(vgpu, "DRY-RUN: Firmware %s not found: %d", NV_VGPU_GSP_GSP_FILE, ret);
+ return ret;
+ }
+ release_firmware(NULL);
+ nv_vgpu_info(vgpu, "DRY-RUN: GSP init validation passed");
+ return 0;
+ }
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vgpu = vgpu;
+ mutex_init(&ctx->queue_lock);
+ INIT_LIST_HEAD(&ctx->pending_queue);
+ INIT_LIST_HEAD(&ctx->complete_queue);
+ init_waitqueue_head(&ctx->wait_queue);
+ atomic_set(&ctx->sequence_counter, 0);
+ spin_lock_init(&ctx->irq_lock);
+
+ if (bar->vaddr) {
+ ctx->gsp_doorbell = bar->vaddr + NV_VGPU_GSP_DOORBELL_OFFSET;
+ ctx->gsp_shared_mem = bar->vaddr + NV_VGPU_GSP_SHARED_MEM_OFFSET;
+ ctx->shared_mem_size = NV_VGPU_GSP_SHARED_MEM_SIZE;
+ }
+
+ ctx->rpc_wq = alloc_workqueue("nv-vgpu-gsp-rpc", WQ_UNBOUND | WQ_HIGHPRI, 1);
+ if (!ctx->rpc_wq) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ INIT_WORK(&ctx->rpc_work, nv_vgpu_gsp_rpc_work);
+ INIT_DELAYED_WORK(&ctx->poll_work, nv_vgpu_gsp_poll_work);
+
+ /* Use polling instead of IRQ since nvidia.ko owns the MSI interrupt */
+ ctx->irq_enabled = true;
+ ctx->poll_active = true;
+ schedule_delayed_work(&ctx->poll_work, msecs_to_jiffies(1));
+
+ vgpu->gsp_comm_ctx = ctx;
+
+ /* Initialize new firmware-decompiled RPC client */
+ ret = nv_vgpu_gsp_rpc_integration_init(vgpu, ctx);
+ if (ret) {
+ nv_vgpu_warn(vgpu, "Failed to initialize GSP RPC integration: %d (non-fatal)", ret);
+ /* Continue anyway - legacy RPC still works */
+ }
+
+ ret = nv_vgpu_gsp_load_firmware(vgpu);
+ if (ret) {
+ nv_vgpu_warn(vgpu, "Failed to load GSP firmware: %d", ret);
+ }
+
+ ret = nv_vgpu_gsp_boot(vgpu);
+ if (ret) {
+ nv_vgpu_warn(vgpu, "Failed to boot GSP: %d", ret);
+ }
+
+ return 0;
+
+err_free:
+ kfree(ctx);
+ return ret;
+}
+
+void nv_vgpu_gsp_cleanup(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_gsp_comm_ctx *ctx = vgpu->gsp_comm_ctx;
+ struct nv_vgpu_gsp_rpc_entry *entry, *tmp;
+
+ if (!ctx)
+ return;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: GSP cleanup (no hardware resources to release)");
+ return;
+ }
+
+ /* Cleanup new firmware-decompiled RPC client */
+ nv_vgpu_gsp_rpc_integration_cleanup(vgpu);
+
+ /* Stop polling */
+ ctx->poll_active = false;
+ cancel_delayed_work_sync(&ctx->poll_work);
+
+ if (ctx->rpc_wq) {
+ destroy_workqueue(ctx->rpc_wq);
+ ctx->rpc_wq = NULL;
+ }
+
+ mutex_lock(&ctx->queue_lock);
+ list_for_each_entry_safe(entry, tmp, &ctx->pending_queue, list) {
+ list_del(&entry->list);
+ entry->result = -ENODEV;
+ complete(&entry->done);
+ }
+ list_for_each_entry_safe(entry, tmp, &ctx->complete_queue, list) {
+ list_del(&entry->list);
+ entry->result = -ENODEV;
+ complete(&entry->done);
+ }
+ mutex_unlock(&ctx->queue_lock);
+
+ nv_vgpu_gsp_release_firmware(vgpu);
+
+ kfree(ctx);
+ vgpu->gsp_comm_ctx = NULL;
+}
+
+int nv_vgpu_gsp_send_rpc(struct nv_vgpu_device *vgpu, void *msg, size_t size)
+{
+ struct nv_vgpu_gsp_comm_ctx *ctx = vgpu->gsp_comm_ctx;
+ struct nv_vgpu_gsp_rpc_entry *entry;
+ struct nv_vgpu_gsp_rpc_msg *rpc_msg;
+ int ret;
+
+ if (!ctx || !ctx->gsp_doorbell || !ctx->gsp_shared_mem)
+ return -ENODEV;
+
+ if (size > NV_VGPU_GSP_RPC_MAX_SIZE - sizeof(struct nv_vgpu_gsp_rpc_header))
+ return -EINVAL;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ init_completion(&entry->done);
+ entry->msg = kmalloc(size + sizeof(struct nv_vgpu_gsp_rpc_header), GFP_KERNEL);
+ if (!entry->msg) {
+ kfree(entry);
+ return -ENOMEM;
+ }
+
+ rpc_msg = entry->msg;
+ rpc_msg->hdr.type = NV_GSP_RPC_TYPE_ALLOC;
+ rpc_msg->hdr.sequence = nv_vgpu_gsp_next_sequence(ctx);
+ rpc_msg->hdr.size = size;
+ rpc_msg->hdr.flags = 0;
+ rpc_msg->hdr.timestamp = ktime_get_ns();
+ memcpy(rpc_msg->data, msg, size);
+
+ mutex_lock(&ctx->queue_lock);
+ list_add_tail(&entry->list, &ctx->pending_queue);
+ mutex_unlock(&ctx->queue_lock);
+
+ memcpy(ctx->gsp_shared_mem, rpc_msg, size + sizeof(struct nv_vgpu_gsp_rpc_header));
+ writel(rpc_msg->hdr.sequence, ctx->gsp_doorbell);
+
+ ret = wait_for_completion_timeout(&entry->done,
+ msecs_to_jiffies(NV_VGPU_GSP_RPC_TIMEOUT_MS));
+ if (ret == 0) {
+ ret = -ETIMEDOUT;
+ } else {
+ ret = entry->result;
+ }
+
+ mutex_lock(&ctx->queue_lock);
+ list_del(&entry->list);
+ mutex_unlock(&ctx->queue_lock);
+
+ kfree(entry->msg);
+ kfree(entry);
+
+ return ret;
+}
+
+int nv_vgpu_gsp_recv_rpc(struct nv_vgpu_device *vgpu, void *msg, size_t size, int timeout_ms)
+{
+ struct nv_vgpu_gsp_comm_ctx *ctx = vgpu->gsp_comm_ctx;
+ struct nv_vgpu_gsp_rpc_msg *rpc_msg;
+ int ret;
+
+ if (!ctx || !ctx->gsp_shared_mem)
+ return -ENODEV;
+
+ ret = wait_event_timeout(ctx->wait_queue,
+ !list_empty(&ctx->complete_queue),
+ msecs_to_jiffies(timeout_ms));
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ mutex_lock(&ctx->queue_lock);
+ if (list_empty(&ctx->complete_queue)) {
+ mutex_unlock(&ctx->queue_lock);
+ return -ENOENT;
+ }
+
+ rpc_msg = ctx->gsp_shared_mem;
+ if (rpc_msg->hdr.size > size) {
+ mutex_unlock(&ctx->queue_lock);
+ return -EOVERFLOW;
+ }
+
+ memcpy(msg, rpc_msg->data, rpc_msg->hdr.size);
+ ret = rpc_msg->hdr.size;
+ mutex_unlock(&ctx->queue_lock);
+
+ return ret;
+}
+
+int nv_vgpu_gsp_alloc(struct nv_vgpu_device *vgpu, u64 size, u32 align, u64 *handle)
+{
+ struct {
+ u64 size;
+ u32 align;
+ u64 handle;
+ } __packed msg = { .size = size, .align = align };
+ int ret;
+
+ ret = nv_vgpu_gsp_send_rpc(vgpu, &msg, sizeof(msg));
+ if (ret >= 0)
+ *handle = msg.handle;
+
+ return ret;
+}
+
+int nv_vgpu_gsp_free(struct nv_vgpu_device *vgpu, u64 handle)
+{
+ struct {
+ u64 handle;
+ } __packed msg = { .handle = handle };
+
+ return nv_vgpu_gsp_send_rpc(vgpu, &msg, sizeof(msg));
+}
+
+int nv_vgpu_gsp_map(struct nv_vgpu_device *vgpu, u64 handle, u64 iova, u64 size, u32 flags)
+{
+ struct {
+ u64 handle;
+ u64 iova;
+ u64 size;
+ u32 flags;
+ } __packed msg = { .handle = handle, .iova = iova, .size = size, .flags = flags };
+
+ return nv_vgpu_gsp_send_rpc(vgpu, &msg, sizeof(msg));
+}
+
+int nv_vgpu_gsp_unmap(struct nv_vgpu_device *vgpu, u64 handle, u64 iova, u64 size)
+{
+ struct {
+ u64 handle;
+ u64 iova;
+ u64 size;
+ } __packed msg = { .handle = handle, .iova = iova, .size = size };
+
+ return nv_vgpu_gsp_send_rpc(vgpu, &msg, sizeof(msg));
+}
+
+int nv_vgpu_gsp_schedule(struct nv_vgpu_device *vgpu, u32 runlist_id, u32 channel_id)
+{
+ struct {
+ u32 runlist_id;
+ u32 channel_id;
+ } __packed msg = { .runlist_id = runlist_id, .channel_id = channel_id };
+
+ return nv_vgpu_gsp_send_rpc(vgpu, &msg, sizeof(msg));
+}
+
+int nv_vgpu_gsp_notify_bar_trap(struct nv_vgpu_device *vgpu, u64 offset, u32 size, void *val)
+{
+ struct {
+ u64 offset;
+ u32 size;
+ u64 val;
+ } __packed msg = { .offset = offset, .size = size, .val = *(u64 *)val };
+
+ return nv_vgpu_gsp_send_rpc(vgpu, &msg, sizeof(msg));
+}
+
+/* ============================================================
+ * New Firmware-Decompiled RPC Operations (enhanced APIs)
+ * ============================================================ */
+
+int nv_vgpu_gsp_rpc_alloc(struct nv_vgpu_device *vgpu,
+ u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid,
+ u64 *gpu_va, u64 *handle, u64 *actual_size)
+{
+ return nv_vgpu_gsp_rpc_alloc_fw(vgpu, size, alignment, mem_type, flags, addr_hint, pid,
+ gpu_va, handle, actual_size);
+}
+
+int nv_vgpu_gsp_rpc_free(struct nv_vgpu_device *vgpu, u64 handle)
+{
+ return nv_vgpu_gsp_rpc_free_fw(vgpu, handle);
+}
+
+int nv_vgpu_gsp_rpc_map(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset,
+ u64 *mapped_va)
+{
+ return nv_vgpu_gsp_rpc_map_fw(vgpu, handle, gpu_va, size, prot, flags, offset, mapped_va);
+}
+
+int nv_vgpu_gsp_rpc_unmap(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 gpu_va, u64 size)
+{
+ return nv_vgpu_gsp_rpc_unmap_fw(vgpu, handle, gpu_va, size);
+}
+
+int nv_vgpu_gsp_rpc_pin(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages)
+{
+ return nv_vgpu_gsp_rpc_pin_fw(vgpu, handle, offset, size, flags, dma_addr, num_pages);
+}
+
+int nv_vgpu_gsp_rpc_unpin(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 offset, u64 size)
+{
+ return nv_vgpu_gsp_rpc_unpin_fw(vgpu, handle, offset, size);
+}
+
+int nv_vgpu_gsp_rpc_schedule(struct nv_vgpu_device *vgpu,
+ u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags)
+{
+ return nv_vgpu_gsp_rpc_schedule_fw(vgpu, work_handle, callback, arg, priority, flags);
+}
+
+int nv_vgpu_gsp_rpc_yield(struct nv_vgpu_device *vgpu)
+{
+ return nv_vgpu_gsp_rpc_yield_fw(vgpu);
+}
+
+int nv_vgpu_gsp_rpc_bar_trap(struct nv_vgpu_device *vgpu,
+ u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value)
+{
+ return nv_vgpu_gsp_rpc_bar_trap_fw(vgpu, bar_addr, access_type, size, value, bar_num, ret_value);
+}
+
+int nv_vgpu_gsp_rpc_migrate_save(struct nv_vgpu_device *vgpu,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 *checkpoint)
+{
+ return nv_vgpu_gsp_rpc_migrate_save_fw(vgpu, src_gpu_id, dst_gpu_id,
+ memory_bitmap, page_table_ptr, checkpoint);
+}
+
+int nv_vgpu_gsp_rpc_migrate_restore(struct nv_vgpu_device *vgpu,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 checkpoint)
+{
+ return nv_vgpu_gsp_rpc_migrate_restore_fw(vgpu, src_gpu_id, dst_gpu_id,
+ memory_bitmap, page_table_ptr, checkpoint);
+} \ No newline at end of file
diff --git a/kernel/gsp_rpc_client.c b/kernel/gsp_rpc_client.c
new file mode 100644
index 0000000..a1579f7
--- /dev/null
+++ b/kernel/gsp_rpc_client.c
@@ -0,0 +1,872 @@
+/*
+ * GSP RPC Client for vGPU-IO
+ * Based on GSP 570.144W firmware decompilation
+ * Firmware base: 0x04000000
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+/* ============================================================
+ * FIRMWARE ADDRESSES (from decompilation)
+ * ============================================================ */
+
+#define GSP_FW_BASE 0x04000000UL
+
+/* Primary RPC Dispatcher */
+#define GSP_RPC_DISPATCH_PRIMARY (GSP_FW_BASE + 0x17A1AC0) /* 0x057a1ac0 */
+#define GSP_RPC_JUMP_TABLE (GSP_FW_BASE + 0x17A3AD8) /* 0x057a3ad8 - 32 entries */
+
+/* Secondary RPC Dispatcher */
+#define GSP_RPC_DISPATCH_SECONDARY (GSP_FW_BASE + 0x0ED39DC) /* 0x04ed39dc */
+
+/* Specialized Handlers */
+#define GSP_ALLOC_MAIN (GSP_FW_BASE + 0x17E94EC) /* 0x057e94ec */
+#define GSP_SCHEDULE_MAIN (GSP_FW_BASE + 0x11EFBF4) /* 0x051efbf4 */
+#define GSP_BAR_TRAP_HANDLER_JT (GSP_FW_BASE + 0x17675B7) /* 0x057675b7 */
+#define GSP_BAR_TRAP_HANDLER_PRIM (GSP_FW_BASE + 0x0CB8189) /* 0x04cb8189 */
+#define GSP_PIN_HANDLER_SEC (GSP_FW_BASE + 0x0ED34B8) /* 0x04ed34b8 */
+
+/* Jump Table Handlers (from 0x059bfeb0) */
+#define GSP_MAP_HANDLER_A (GSP_FW_BASE + 0x1766AB7) /* 0x05766ab7 */
+#define GSP_MAP_HANDLER_B (GSP_FW_BASE + 0x1778A37) /* 0x05778a37 */
+#define GSP_UNMAP_HANDLER_A (GSP_FW_BASE + 0x17669B7) /* 0x057669b7 */
+#define GSP_UNMAP_HANDLER_B (GSP_FW_BASE + 0x1779937) /* 0x05779937 */
+#define GSP_PIN_HANDLER_A (GSP_FW_BASE + 0x17664B7) /* 0x057664b7 */
+#define GSP_PIN_HANDLER_B (GSP_FW_BASE + 0x177A3B7) /* 0x0577a3b7 */
+#define GSP_UNPIN_HANDLER_A (GSP_FW_BASE + 0x17682B7) /* 0x057682b7 */
+#define GSP_UNPIN_HANDLER_B (GSP_FW_BASE + 0x1779FB7) /* 0x05779fb7 */
+#define GSP_SCHEDULE_HANDLER_A (GSP_FW_BASE + 0x1767F37) /* 0x05767f37 */
+#define GSP_SCHEDULE_HANDLER_B (GSP_FW_BASE + 0x175AEB7) /* 0x0575aeb7 */
+#define GSP_YIELD_HANDLER_A (GSP_FW_BASE + 0x175AE37) /* 0x0575ae37 */
+#define GSP_YIELD_HANDLER_B (GSP_FW_BASE + 0x1767337) /* 0x05767337 */
+#define GSP_NOTIFY_HANDLER_A (GSP_FW_BASE + 0x177C8B7) /* 0x0577c8b7 */
+#define GSP_NOTIFY_HANDLER_B (GSP_FW_BASE + 0x1767837) /* 0x05767837 */
+
+/* Entry point */
+#define GSP_ENTRY_POINT (GSP_FW_BASE + 0x0009000) /* 0x04009000 */
+
+/* ============================================================
+ * RPC PROTOCOL DEFINITIONS
+ * ============================================================ */
+
+#define GSP_RPC_ALLOC 0x01
+#define GSP_RPC_FREE 0x02
+#define GSP_RPC_MAP 0x03
+#define GSP_RPC_UNMAP 0x04
+#define GSP_RPC_PIN 0x05
+#define GSP_RPC_UNPIN 0x06
+#define GSP_RPC_SCHEDULE 0x07
+#define GSP_RPC_YIELD 0x08
+#define GSP_RPC_NOTIFY 0x09
+#define GSP_RPC_BAR_TRAP 0x0A
+#define GSP_RPC_MIGRATE 0x0B
+#define GSP_RPC_DEBUG 0xFF
+
+/* ALLOC sub-commands (primary dispatcher jump table indices) */
+#define GSP_ALLOC_SUBCMD_DEFAULT 0
+#define GSP_ALLOC_SUBCMD_TYPE1 1
+#define GSP_ALLOC_SUBCMD_TYPE2 3
+#define GSP_ALLOC_SUBCMD_TYPE3 4
+#define GSP_ALLOC_SUBCMD_TYPE4 5
+#define GSP_ALLOC_SUBCMD_TYPE5 8
+#define GSP_ALLOC_SUBCMD_TYPE6 10
+#define GSP_ALLOC_SUBCMD_TYPE7 12
+#define GSP_ALLOC_SUBCMD_TYPE8 17
+#define GSP_ALLOC_SUBCMD_TYPE9 19
+#define GSP_ALLOC_SUBCMD_TYPE10 20
+#define GSP_ALLOC_SUBCMD_TYPE11 21
+#define GSP_ALLOC_SUBCMD_TYPE12 22
+#define GSP_ALLOC_SUBCMD_TYPE13 23
+#define GSP_ALLOC_SUBCMD_TYPE14 24
+#define GSP_ALLOC_SUBCMD_TYPE15 25
+#define GSP_ALLOC_SUBCMD_TYPE16 26
+#define GSP_ALLOC_SUBCMD_TYPE17 27
+#define GSP_ALLOC_SUBCMD_TYPE18 28
+#define GSP_ALLOC_SUBCMD_TYPE19 29
+#define GSP_ALLOC_SUBCMD_TYPE20 30
+#define GSP_ALLOC_SUBCMD_TYPE21 31
+
+/* MIGRATE sub-commands */
+#define GSP_MIGRATE_SUBCMD_SAVE 0x04 /* From primary dispatcher branch */
+#define GSP_MIGRATE_SUBCMD_RESTORE 0x05
+
+/* PIN sub-commands */
+#define GSP_PIN_SUBCMD_DEFAULT 0x05 /* From secondary dispatcher */
+
+/* SCHEDULE sub-commands */
+#define GSP_SCHED_SUBCMD_DEFAULT 0x07
+
+/* BAR TRAP sub-commands */
+#define GSP_BAR_TRAP_SUBCMD_DEFAULT 0x0A
+
+/* ============================================================
+ * RPC MESSAGE STRUCTURES
+ * ============================================================ */
+
+struct gsp_rpc_msg {
+ u32 type; /* RPC type (0x01-0x0B, 0xFF) */
+ u32 subcmd; /* Sub-command for jump table dispatch */
+ u64 payload[6]; /* Variable payload */
+ u32 seq_id; /* Sequence ID for async ops */
+ u32 flags; /* Flags */
+ u32 reserved[2]; /* Padding to 64 bytes */
+} __packed;
+
+struct gsp_rpc_response {
+ u32 status; /* 0 = success, negative = error */
+ u32 type; /* Echo of request type */
+ u64 result[6]; /* Return values */
+ u32 seq_id; /* Echo of request seq_id */
+} __packed;
+
+/* ALLOC payload */
+struct gsp_alloc_req {
+ u64 size; /* Allocation size in bytes */
+ u64 alignment; /* Alignment requirement */
+ u32 mem_type; /* Memory type (0=VRAM, 1=SYSMEM, 2=COHERENT) */
+ u32 flags; /* Allocation flags */
+ u64 addr_hint; /* Hint address (optional) */
+ u32 pid; /* Process ID */
+ u32 reserved;
+};
+
+struct gsp_alloc_rsp {
+ u64 gpu_va; /* GPU virtual address */
+ u64 handle; /* Allocation handle */
+ u64 size; /* Actual allocated size */
+};
+
+/* MAP payload */
+struct gsp_map_req {
+ u64 handle; /* Allocation handle from ALLOC */
+ u64 gpu_va; /* GPU virtual address to map at */
+ u64 size; /* Size to map */
+ u32 prot; /* Protection (R/W/X) */
+ u32 flags; /* Map flags */
+ u64 offset; /* Offset into allocation */
+ u32 reserved[2];
+};
+
+struct gsp_map_rsp {
+ u64 gpu_va; /* Mapped GPU VA */
+};
+
+/* PIN/UNPIN payload */
+struct gsp_pin_req {
+ u64 handle; /* Allocation handle */
+ u64 offset; /* Offset into allocation */
+ u64 size; /* Size to pin */
+ u32 flags; /* Pin flags (0=read, 1=write, 2=read+write) */
+ u32 reserved[3];
+};
+
+struct gsp_pin_rsp {
+ u64 dma_addr; /* DMA address for device */
+ u32 num_pages; /* Number of pages pinned */
+ u32 reserved;
+};
+
+/* SCHEDULE payload */
+struct gsp_sched_req {
+ u64 work_handle; /* Work descriptor handle */
+ u64 callback; /* Callback function pointer */
+ u64 arg; /* Callback argument */
+ u32 priority; /* Priority */
+ u32 flags; /* Schedule flags */
+ u32 reserved[2];
+};
+
+/* BAR TRAP payload */
+struct gsp_bar_trap_req {
+ u64 bar_addr; /* BAR register address */
+ u32 access_type; /* 0=read, 1=write */
+ u32 size; /* Access size (1/2/4/8) */
+ u64 value; /* Value (for write) */
+ u32 bar_num; /* BAR number (0-5) */
+ u32 reserved;
+};
+
+struct gsp_bar_trap_rsp {
+ u64 value; /* Value (for read) */
+ u32 handled; /* 1 if handled */
+ u32 reserved;
+};
+
+/* MIGRATE payload */
+struct gsp_migrate_req {
+ u64 src_gpu_id; /* Source GPU ID */
+ u64 dst_gpu_id; /* Destination GPU ID */
+ u64 memory_bitmap; /* Memory bitmap pointer */
+ u64 page_table_ptr; /* Page table pointer */
+ u32 state; /* Migration state */
+ u32 flags; /* Migration flags */
+ u64 context; /* Migration context */
+ u32 reserved;
+};
+
+struct gsp_migrate_rsp {
+ u32 status; /* Migration status */
+ u32 progress; /* Progress percentage */
+ u64 checkpoint; /* Checkpoint handle */
+};
+
+/* ============================================================
+ * GSP RPC CLIENT CONTEXT
+ * ============================================================ */
+
+struct gsp_client {
+ struct device *dev;
+ void __iomem *gsp_fw_base; /* Mapped firmware base */
+ void __iomem *rpc_doorbell; /* Doorbell register */
+ spinlock_t lock; /* Submission lock */
+ struct mutex rpc_mutex; /* RPC serialization */
+ atomic_t seq_counter; /* Sequence ID counter */
+ struct list_head pending_rpcs; /* Pending RPC list */
+ wait_queue_head_t resp_wq; /* Response wait queue */
+ struct workqueue_struct *wq; /* Workqueue for async */
+ bool initialized;
+};
+
+struct gsp_pending_rpc {
+ struct list_head list;
+ struct gsp_rpc_msg msg;
+ struct gsp_rpc_response *rsp;
+ struct completion done;
+ u32 timeout_ms;
+};
+
+/* ============================================================
+ * LOW-LEVEL RPC SUBMISSION
+ * ============================================================ */
+
+static inline u32 gsp_next_seq(struct gsp_client *client)
+{
+ return atomic_inc_return(&client->seq_counter);
+}
+
+static int gsp_rpc_submit_raw(struct gsp_client *client,
+ u32 type, u32 subcmd,
+ u64 *payload, u32 seq_id,
+ struct gsp_rpc_response *rsp,
+ u32 timeout_ms)
+{
+ struct gsp_pending_rpc *prpc;
+ u64 *dispatch_ptr;
+ int ret = 0;
+
+ prpc = kzalloc(sizeof(*prpc), GFP_KERNEL);
+ if (!prpc)
+ return -ENOMEM;
+
+ prpc->msg.type = type;
+ prpc->msg.subcmd = subcmd;
+ if (payload)
+ memcpy(prpc->msg.payload, payload, sizeof(prpc->msg.payload));
+ prpc->msg.seq_id = seq_id;
+ prpc->rsp = rsp;
+ prpc->timeout_ms = timeout_ms;
+ init_completion(&prpc->done);
+
+ spin_lock(&client->lock);
+ list_add_tail(&prpc->list, &client->pending_rpcs);
+
+ /* Write RPC message to firmware mailbox */
+ dispatch_ptr = (u64 *)(client->gsp_fw_base + 0x1000); /* Mailbox offset */
+ for (int i = 0; i < 8; i++)
+ writeq(((u64 *)&prpc->msg)[i], &dispatch_ptr[i]);
+
+ /* Ring doorbell */
+ writel(type, client->rpc_doorbell);
+ spin_unlock(&client->lock);
+
+ /* Wait for completion */
+ if (!wait_for_completion_timeout(&prpc->done,
+ msecs_to_jiffies(timeout_ms))) {
+ ret = -ETIMEDOUT;
+ goto cleanup;
+ }
+
+ if (rsp)
+ memcpy(rsp, prpc->rsp, sizeof(*rsp));
+
+cleanup:
+ spin_lock(&client->lock);
+ list_del(&prpc->list);
+ spin_unlock(&client->lock);
+ kfree(prpc);
+ return ret;
+}
+
+/* ============================================================
+ * HIGH-LEVEL RPC API
+ * ============================================================ */
+
+/* Memory Allocation */
+int gsp_rpc_alloc(struct gsp_client *client,
+ u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid,
+ u64 *gpu_va, u64 *handle, u64 *actual_size)
+{
+ struct gsp_alloc_req req = {0};
+ struct gsp_alloc_rsp rsp = {0};
+ struct gsp_rpc_response response = {0};
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ req.size = size;
+ req.alignment = alignment;
+ req.mem_type = mem_type;
+ req.flags = flags;
+ req.addr_hint = addr_hint;
+ req.pid = pid;
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_ALLOC, GSP_ALLOC_SUBCMD_DEFAULT,
+ (u64 *)&req, seq, &response, 5000);
+ if (ret)
+ return ret;
+
+ if (response.status) {
+ pr_err("GSP ALLOC failed: status=%d\n", response.status);
+ return -EIO;
+ }
+
+ memcpy(&rsp, response.result, sizeof(rsp));
+ if (gpu_va) *gpu_va = rsp.gpu_va;
+ if (handle) *handle = rsp.handle;
+ if (actual_size) *actual_size = rsp.size;
+
+ return 0;
+}
+
+int gsp_rpc_free(struct gsp_client *client, u64 handle)
+{
+ struct gsp_rpc_response response = {0};
+ u64 payload[6] = { handle, 0, 0, 0, 0, 0 };
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_FREE, 0,
+ payload, seq, &response, 2000);
+ if (ret)
+ return ret;
+
+ return response.status ? -EIO : 0;
+}
+
+/* Memory Mapping */
+int gsp_rpc_map(struct gsp_client *client,
+ u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset,
+ u64 *mapped_va)
+{
+ struct gsp_map_req req = {0};
+ struct gsp_map_rsp rsp = {0};
+ struct gsp_rpc_response response = {0};
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ req.handle = handle;
+ req.gpu_va = gpu_va;
+ req.size = size;
+ req.prot = prot;
+ req.flags = flags;
+ req.offset = offset;
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_MAP, 0,
+ (u64 *)&req, seq, &response, 2000);
+ if (ret)
+ return ret;
+
+ if (response.status)
+ return -EIO;
+
+ memcpy(&rsp, response.result, sizeof(rsp));
+ if (mapped_va) *mapped_va = rsp.gpu_va;
+
+ return 0;
+}
+
+int gsp_rpc_unmap(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size)
+{
+ struct gsp_rpc_response response = {0};
+ u64 payload[6] = { handle, gpu_va, size, 0, 0, 0 };
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_UNMAP, 0,
+ payload, seq, &response, 2000);
+ if (ret)
+ return ret;
+
+ return response.status ? -EIO : 0;
+}
+
+/* Page Pinning */
+int gsp_rpc_pin(struct gsp_client *client,
+ u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages)
+{
+ struct gsp_pin_req req = {0};
+ struct gsp_pin_rsp rsp = {0};
+ struct gsp_rpc_response response = {0};
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ req.handle = handle;
+ req.offset = offset;
+ req.size = size;
+ req.flags = flags;
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_PIN, GSP_PIN_SUBCMD_DEFAULT,
+ (u64 *)&req, seq, &response, 5000);
+ if (ret)
+ return ret;
+
+ if (response.status)
+ return -EIO;
+
+ memcpy(&rsp, response.result, sizeof(rsp));
+ if (dma_addr) *dma_addr = rsp.dma_addr;
+ if (num_pages) *num_pages = rsp.num_pages;
+
+ return 0;
+}
+
+int gsp_rpc_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size)
+{
+ struct gsp_rpc_response response = {0};
+ u64 payload[6] = { handle, offset, size, 0, 0, 0 };
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_UNPIN, 0,
+ payload, seq, &response, 2000);
+ if (ret)
+ return ret;
+
+ return response.status ? -EIO : 0;
+}
+
+/* BAR Trap Emulation */
+int gsp_rpc_bar_trap(struct gsp_client *client,
+ u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value)
+{
+ struct gsp_bar_trap_req req = {0};
+ struct gsp_bar_trap_rsp rsp = {0};
+ struct gsp_rpc_response response = {0};
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ req.bar_addr = bar_addr;
+ req.access_type = access_type;
+ req.size = size;
+ req.value = value;
+ req.bar_num = bar_num;
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_BAR_TRAP, GSP_BAR_TRAP_SUBCMD_DEFAULT,
+ (u64 *)&req, seq, &response, 2000);
+ if (ret)
+ return ret;
+
+ if (response.status)
+ return -EIO;
+
+ memcpy(&rsp, response.result, sizeof(rsp));
+ if (ret_value) *ret_value = rsp.value;
+
+ return rsp.handled ? 0 : -ENODEV;
+}
+
+/* Migration */
+int gsp_rpc_migrate_save(struct gsp_client *client,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 *checkpoint)
+{
+ struct gsp_migrate_req req = {0};
+ struct gsp_migrate_rsp rsp = {0};
+ struct gsp_rpc_response response = {0};
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ req.src_gpu_id = src_gpu_id;
+ req.dst_gpu_id = dst_gpu_id;
+ req.memory_bitmap = memory_bitmap;
+ req.page_table_ptr = page_table_ptr;
+ req.state = 0; /* SAVE */
+ req.flags = GSP_MIGRATE_SUBCMD_SAVE;
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_MIGRATE, GSP_MIGRATE_SUBCMD_SAVE,
+ (u64 *)&req, seq, &response, 30000);
+ if (ret)
+ return ret;
+
+ if (response.status)
+ return -EIO;
+
+ memcpy(&rsp, response.result, sizeof(rsp));
+ if (checkpoint) *checkpoint = rsp.checkpoint;
+
+ return 0;
+}
+
+int gsp_rpc_migrate_restore(struct gsp_client *client,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 checkpoint)
+{
+ struct gsp_migrate_req req = {0};
+ struct gsp_rpc_response response = {0};
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ req.src_gpu_id = src_gpu_id;
+ req.dst_gpu_id = dst_gpu_id;
+ req.memory_bitmap = memory_bitmap;
+ req.page_table_ptr = page_table_ptr;
+ req.state = 1; /* RESTORE */
+ req.flags = GSP_MIGRATE_SUBCMD_RESTORE;
+ req.context = checkpoint;
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_MIGRATE, GSP_MIGRATE_SUBCMD_RESTORE,
+ (u64 *)&req, seq, &response, 30000);
+ if (ret)
+ return ret;
+
+ return response.status ? -EIO : 0;
+}
+
+/* ============================================================
+ * EMULATION FALLBACKS
+ * ============================================================ */
+
+static bool gsp_emulation_enabled = false;
+
+void gsp_rpc_emulation_set(bool enable)
+{
+ gsp_emulation_enabled = enable;
+}
+
+bool gsp_rpc_emulation_is_enabled(void)
+{
+ return gsp_emulation_enabled;
+}
+
+/* Scheduling emulation - local work queue */
+struct gsp_sched_work {
+ struct list_head list;
+ struct work_struct work;
+ u64 work_handle;
+ u64 callback;
+ u64 arg;
+ u32 priority;
+ u32 flags;
+ struct completion done;
+};
+
+static LIST_HEAD(gsp_sched_queue);
+static DEFINE_SPINLOCK(gsp_sched_lock);
+static struct workqueue_struct *gsp_sched_wq = NULL;
+
+static void gsp_sched_worker(struct work_struct *work)
+{
+ struct gsp_sched_work *sched = container_of(work, struct gsp_sched_work, work);
+
+ /* In emulation mode, we just complete successfully */
+ complete(&sched->done);
+}
+
+int gsp_rpc_schedule(struct gsp_client *client,
+ u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags)
+{
+ struct gsp_sched_req req = {0};
+ struct gsp_rpc_response response = {0};
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ if (gsp_emulation_enabled) {
+ struct gsp_sched_work *sched = kzalloc(sizeof(*sched), GFP_KERNEL);
+ if (!sched)
+ return -ENOMEM;
+
+ INIT_WORK(&sched->work, gsp_sched_worker);
+ sched->work_handle = work_handle;
+ sched->callback = callback;
+ sched->arg = arg;
+ sched->priority = priority;
+ sched->flags = flags;
+ init_completion(&sched->done);
+
+ spin_lock(&gsp_sched_lock);
+ list_add_tail(&sched->list, &gsp_sched_queue);
+ spin_unlock(&gsp_sched_lock);
+
+ if (gsp_sched_wq)
+ queue_work(gsp_sched_wq, &sched->work);
+
+ wait_for_completion(&sched->done);
+
+ spin_lock(&gsp_sched_lock);
+ list_del(&sched->list);
+ spin_unlock(&gsp_sched_lock);
+
+ kfree(sched);
+ return 0;
+ }
+
+ req.work_handle = work_handle;
+ req.callback = callback;
+ req.arg = arg;
+ req.priority = priority;
+ req.flags = flags;
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_SCHEDULE, GSP_SCHED_SUBCMD_DEFAULT,
+ (u64 *)&req, seq, &response, 2000);
+ if (ret)
+ return ret;
+
+ return response.status ? -EIO : 0;
+}
+
+int gsp_rpc_yield(struct gsp_client *client)
+{
+ struct gsp_rpc_response response = {0};
+ u32 seq = gsp_next_seq(client);
+ int ret;
+
+ if (gsp_emulation_enabled) {
+ /* In emulation, yield is a no-op success */
+ return 0;
+ }
+
+ ret = gsp_rpc_submit_raw(client, GSP_RPC_YIELD, 0,
+ NULL, seq, &response, 1000);
+ if (ret)
+ return ret;
+
+ return response.status ? -EIO : 0;
+}
+
+/* Notification emulation - async eventfd support */
+struct gsp_notify_cb {
+ struct list_head list;
+ u32 type;
+ void (*callback)(struct gsp_client *, struct gsp_rpc_response *, void *);
+ void *ctx;
+};
+
+static LIST_HEAD(gsp_notify_callbacks);
+static DEFINE_SPINLOCK(gsp_notify_lock);
+
+int gsp_rpc_notify_register(struct gsp_client *client,
+ u32 type,
+ void (*callback)(struct gsp_client *,
+ struct gsp_rpc_response *,
+ void *),
+ void *ctx)
+{
+ struct gsp_notify_cb *nc = kzalloc(sizeof(*nc), GFP_KERNEL);
+ if (!nc)
+ return -ENOMEM;
+
+ nc->type = type;
+ nc->callback = callback;
+ nc->ctx = ctx;
+
+ spin_lock(&gsp_notify_lock);
+ list_add_tail(&nc->list, &gsp_notify_callbacks);
+ spin_unlock(&gsp_notify_lock);
+
+ return 0;
+}
+
+void gsp_rpc_notify_unregister(struct gsp_client *client, u32 type)
+{
+ struct gsp_notify_cb *nc, *tmp;
+
+ spin_lock(&gsp_notify_lock);
+ list_for_each_entry_safe(nc, tmp, &gsp_notify_callbacks, list) {
+ if (nc->type == type) {
+ list_del(&nc->list);
+ kfree(nc);
+ }
+ }
+ spin_unlock(&gsp_notify_lock);
+}
+
+/* Handle async notifications from GSP */
+void gsp_handle_notification(struct gsp_client *client, struct gsp_rpc_msg *msg)
+{
+ struct gsp_notify_cb *nc;
+
+ spin_lock(&gsp_notify_lock);
+ list_for_each_entry(nc, &gsp_notify_callbacks, list) {
+ if (nc->type == msg->type || nc->type == 0) {
+ /* Copy message to response format for callback */
+ struct gsp_rpc_response rsp = {0};
+ rsp.status = 0;
+ rsp.type = msg->type;
+ rsp.seq_id = msg->seq_id;
+ memcpy(rsp.result, msg->payload, sizeof(msg->payload));
+ nc->callback(client, &rsp, nc->ctx);
+ }
+ }
+ spin_unlock(&gsp_notify_lock);
+}
+
+/* ============================================================
+ * CLIENT INITIALIZATION
+ * ============================================================ */
+
+struct gsp_client *gsp_client_init(struct device *dev, phys_addr_t fw_base, phys_addr_t doorbell)
+{
+ struct gsp_client *client;
+ resource_size_t fw_size = 0x2000000; /* 32MB */
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return ERR_PTR(-ENOMEM);
+
+ client->dev = dev;
+ spin_lock_init(&client->lock);
+ mutex_init(&client->rpc_mutex);
+ INIT_LIST_HEAD(&client->pending_rpcs);
+ init_waitqueue_head(&client->resp_wq);
+ atomic_set(&client->seq_counter, 0);
+
+ client->gsp_fw_base = ioremap(fw_base, fw_size);
+ if (!client->gsp_fw_base) {
+ kfree(client);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ client->rpc_doorbell = ioremap(doorbell, 0x1000);
+ if (!client->rpc_doorbell) {
+ iounmap(client->gsp_fw_base);
+ kfree(client);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ client->wq = alloc_workqueue("gsp_rpc", WQ_UNBOUND, 0);
+ if (!client->wq) {
+ iounmap(client->rpc_doorbell);
+ iounmap(client->gsp_fw_base);
+ kfree(client);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Init emulation workqueue for scheduling */
+ gsp_sched_wq = alloc_workqueue("gsp_sched", WQ_UNBOUND, 0);
+ if (!gsp_sched_wq) {
+ destroy_workqueue(client->wq);
+ iounmap(client->rpc_doorbell);
+ iounmap(client->gsp_fw_base);
+ kfree(client);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ client->initialized = true;
+ pr_info("GSP RPC client initialized: fw_base=%pa, doorbell=%pa\n", &fw_base, &doorbell);
+
+ return client;
+}
+
+void gsp_client_fini(struct gsp_client *client)
+{
+ if (!client)
+ return;
+
+ if (client->wq)
+ destroy_workqueue(client->wq);
+
+ if (gsp_sched_wq) {
+ destroy_workqueue(gsp_sched_wq);
+ gsp_sched_wq = NULL;
+ }
+
+ if (client->rpc_doorbell)
+ iounmap(client->rpc_doorbell);
+
+ if (client->gsp_fw_base)
+ iounmap(client->gsp_fw_base);
+
+ kfree(client);
+}
+
+/* ============================================================
+ * WPR MANAGEMENT (from firmware strings)
+ * ============================================================ */
+
+/* WPR mutex operations (from firmware: VPR_WPR_WRITE_MUTEX*) */
+int gsp_wpr_write_mutex_acquire(struct gsp_client *client, u32 wpr_id)
+{
+ /* Calls firmware function at VPR_WPR_WRITE_MUTEX addresses */
+ /* Implementation would use RPC_DEBUG or dedicated WPR RPC */
+ (void)client;
+ (void)wpr_id;
+ return 0;
+}
+
+int gsp_wpr_write_mutex_release(struct gsp_client *client, u32 wpr_id)
+{
+ (void)client;
+ (void)wpr_id;
+ return 0;
+}
+
+int gsp_wpr_scratch_mutex_acquire(struct gsp_client *client, u32 wpr_id)
+{
+ /* From firmware: VPR_WPR_SCRATCH_MUTEX */
+ (void)client;
+ (void)wpr_id;
+ return 0;
+}
+
+int gsp_wpr_scratch_mutex_release(struct gsp_client *client, u32 wpr_id)
+{
+ (void)client;
+ (void)wpr_id;
+ return 0;
+}
+
+/* ============================================================
+ * MODULE EXPORTS
+ * ============================================================ */
+
+EXPORT_SYMBOL(gsp_client_init);
+EXPORT_SYMBOL(gsp_client_fini);
+EXPORT_SYMBOL(gsp_rpc_alloc);
+EXPORT_SYMBOL(gsp_rpc_free);
+EXPORT_SYMBOL(gsp_rpc_map);
+EXPORT_SYMBOL(gsp_rpc_unmap);
+EXPORT_SYMBOL(gsp_rpc_pin);
+EXPORT_SYMBOL(gsp_rpc_unpin);
+EXPORT_SYMBOL(gsp_rpc_schedule);
+EXPORT_SYMBOL(gsp_rpc_yield);
+EXPORT_SYMBOL(gsp_rpc_bar_trap);
+EXPORT_SYMBOL(gsp_rpc_migrate_save);
+EXPORT_SYMBOL(gsp_rpc_migrate_restore);
+EXPORT_SYMBOL(gsp_wpr_write_mutex_acquire);
+EXPORT_SYMBOL(gsp_wpr_write_mutex_release);
+EXPORT_SYMBOL(gsp_wpr_scratch_mutex_acquire);
+EXPORT_SYMBOL(gsp_wpr_scratch_mutex_release);
+EXPORT_SYMBOL(gsp_rpc_emulation_set);
+EXPORT_SYMBOL(gsp_rpc_emulation_is_enabled);
+EXPORT_SYMBOL(gsp_rpc_notify_register);
+EXPORT_SYMBOL(gsp_rpc_notify_unregister);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("vGPU-IO Team");
+MODULE_DESCRIPTION("GSP 570.144W RPC Client for vGPU-IO");
diff --git a/kernel/gsp_rpc_client.h b/kernel/gsp_rpc_client.h
new file mode 100644
index 0000000..8702b0f
--- /dev/null
+++ b/kernel/gsp_rpc_client.h
@@ -0,0 +1,313 @@
+/*
+ * GSP 570.144W RPC Client Header
+ * Auto-generated from firmware decompilation
+ */
+
+#ifndef _GSP_RPC_CLIENT_H_
+#define _GSP_RPC_CLIENT_H_
+
+#include <linux/types.h>
+#include <linux/device.h>
+
+/* ============================================================
+ * FIRMWARE BASE ADDRESS
+ * ============================================================ */
+#define GSP_FW_BASE 0x04000000UL
+
+/* ============================================================
+ * RPC DISPATCHER ADDRESSES
+ * ============================================================ */
+
+/* Primary Dispatcher - Jump table switch for 6 RPC types */
+#define GSP_RPC_DISPATCH_PRIMARY (GSP_FW_BASE + 0x17A1AC0) /* 0x057a1ac0 */
+#define GSP_RPC_JUMP_TABLE (GSP_FW_BASE + 0x17A3AD8) /* 0x057a3ad8 - 32 sub-commands */
+
+/* Secondary Dispatcher - Full implementation for all 11 RPC types */
+#define GSP_RPC_DISPATCH_SECONDARY (GSP_FW_BASE + 0x0ED39DC) /* 0x04ed39dc */
+
+/* Tertiary Dispatcher - Stack=160 bytes */
+#define GSP_RPC_DISPATCH_TERTIARY (GSP_FW_BASE + 0x0CC58F8) /* 0x04cc58f8 */
+
+/* Quaternary Dispatcher - Stack=96 bytes */
+#define GSP_RPC_DISPATCH_QUATERNARY (GSP_FW_BASE + 0x1723EF4) /* 0x05723ef4 */
+
+/* ============================================================
+ * SPECIALIZED HANDLER ADDRESSES
+ * ============================================================ */
+
+#define GSP_ALLOC_MAIN (GSP_FW_BASE + 0x17E94EC) /* 0x057e94ec */
+#define GSP_SCHEDULE_MAIN (GSP_FW_BASE + 0x11EFBF4) /* 0x051efbf4 */
+#define GSP_BAR_TRAP_HANDLER_JT (GSP_FW_BASE + 0x17675B7) /* 0x057675b7 */
+#define GSP_BAR_TRAP_HANDLER_PRIM (GSP_FW_BASE + 0x0CB8189) /* 0x04cb8189 */
+#define GSP_PIN_HANDLER_SEC (GSP_FW_BASE + 0x0ED34B8) /* 0x04ed34b8 */
+
+/* Jump Table Handlers (from decoded table at 0x059bfeb0) */
+#define GSP_MAP_HANDLER_A (GSP_FW_BASE + 0x1766AB7) /* 0x05766ab7 */
+#define GSP_MAP_HANDLER_B (GSP_FW_BASE + 0x1778A37) /* 0x05778a37 */
+#define GSP_UNMAP_HANDLER_A (GSP_FW_BASE + 0x17669B7) /* 0x057669b7 */
+#define GSP_UNMAP_HANDLER_B (GSP_FW_BASE + 0x1779937) /* 0x05779937 */
+#define GSP_PIN_HANDLER_A (GSP_FW_BASE + 0x17664B7) /* 0x057664b7 */
+#define GSP_PIN_HANDLER_B (GSP_FW_BASE + 0x177A3B7) /* 0x0577a3b7 */
+#define GSP_UNPIN_HANDLER_A (GSP_FW_BASE + 0x17682B7) /* 0x057682b7 */
+#define GSP_UNPIN_HANDLER_B (GSP_FW_BASE + 0x1779FB7) /* 0x05779fb7 */
+#define GSP_SCHEDULE_HANDLER_A (GSP_FW_BASE + 0x1767F37) /* 0x05767f37 */
+#define GSP_SCHEDULE_HANDLER_B (GSP_FW_BASE + 0x175AEB7) /* 0x0575aeb7 */
+#define GSP_YIELD_HANDLER_A (GSP_FW_BASE + 0x175AE37) /* 0x0575ae37 */
+#define GSP_YIELD_HANDLER_B (GSP_FW_BASE + 0x1767337) /* 0x05767337 */
+#define GSP_NOTIFY_HANDLER_A (GSP_FW_BASE + 0x177C8B7) /* 0x0577c8b7 */
+#define GSP_NOTIFY_HANDLER_B (GSP_FW_BASE + 0x1767837) /* 0x05767837 */
+
+/* Entry Point */
+#define GSP_ENTRY_POINT (GSP_FW_BASE + 0x0009000) /* 0x04009000 */
+
+/* ============================================================
+ * RPC TYPE DEFINITIONS
+ * ============================================================ */
+enum gsp_rpc_type {
+ GSP_RPC_ALLOC = 0x01,
+ GSP_RPC_FREE = 0x02,
+ GSP_RPC_MAP = 0x03,
+ GSP_RPC_UNMAP = 0x04,
+ GSP_RPC_PIN = 0x05,
+ GSP_RPC_UNPIN = 0x06,
+ GSP_RPC_SCHEDULE = 0x07,
+ GSP_RPC_YIELD = 0x08,
+ GSP_RPC_NOTIFY = 0x09,
+ GSP_RPC_BAR_TRAP = 0x0A,
+ GSP_RPC_MIGRATE = 0x0B,
+ GSP_RPC_DEBUG = 0xFF,
+};
+
+/* ============================================================
+ * ALLOC SUB-COMMANDS (Primary Dispatcher Jump Table Indices)
+ * ============================================================ */
+enum gsp_alloc_subcmd {
+ GSP_ALLOC_SUBCMD_DEFAULT = 0,
+ GSP_ALLOC_SUBCMD_TYPE1 = 1,
+ GSP_ALLOC_SUBCMD_TYPE2 = 3,
+ GSP_ALLOC_SUBCMD_TYPE3 = 4,
+ GSP_ALLOC_SUBCMD_TYPE4 = 5,
+ GSP_ALLOC_SUBCMD_TYPE5 = 8,
+ GSP_ALLOC_SUBCMD_TYPE6 = 10,
+ GSP_ALLOC_SUBCMD_TYPE7 = 12,
+ GSP_ALLOC_SUBCMD_TYPE8 = 17,
+ GSP_ALLOC_SUBCMD_TYPE9 = 19,
+ GSP_ALLOC_SUBCMD_TYPE10 = 20,
+ GSP_ALLOC_SUBCMD_TYPE11 = 21,
+ GSP_ALLOC_SUBCMD_TYPE12 = 22,
+ GSP_ALLOC_SUBCMD_TYPE13 = 23,
+ GSP_ALLOC_SUBCMD_TYPE14 = 24,
+ GSP_ALLOC_SUBCMD_TYPE15 = 25,
+ GSP_ALLOC_SUBCMD_TYPE16 = 26,
+ GSP_ALLOC_SUBCMD_TYPE17 = 27,
+ GSP_ALLOC_SUBCMD_TYPE18 = 28,
+ GSP_ALLOC_SUBCMD_TYPE19 = 29,
+ GSP_ALLOC_SUBCMD_TYPE20 = 30,
+ GSP_ALLOC_SUBCMD_TYPE21 = 31,
+};
+
+/* ============================================================
+ * MIGRATE SUB-COMMANDS
+ * ============================================================ */
+enum gsp_migrate_subcmd {
+ GSP_MIGRATE_SUBCMD_SAVE = 0x04,
+ GSP_MIGRATE_SUBCMD_RESTORE = 0x05,
+};
+
+/* ============================================================
+ * PIN SUB-COMMANDS
+ * ============================================================ */
+enum gsp_pin_subcmd {
+ GSP_PIN_SUBCMD_DEFAULT = 0x05,
+};
+
+/* ============================================================
+ * SCHEDULE SUB-COMMANDS
+ * ============================================================ */
+enum gsp_sched_subcmd {
+ GSP_SCHED_SUBCMD_DEFAULT = 0x07,
+};
+
+/* ============================================================
+ * BAR TRAP SUB-COMMANDS
+ * ============================================================ */
+enum gsp_bar_trap_subcmd {
+ GSP_BAR_TRAP_SUBCMD_DEFAULT = 0x0A,
+};
+
+/* ============================================================
+ * RPC MESSAGE STRUCTURES
+ * ============================================================ */
+
+struct gsp_rpc_msg {
+ __le32 type;
+ __le32 subcmd;
+ __le64 payload[6];
+ __le32 seq_id;
+ __le32 flags;
+ __le32 reserved[2];
+} __packed;
+
+struct gsp_rpc_response {
+ __le32 status;
+ __le32 type;
+ __le64 result[6];
+ __le32 seq_id;
+} __packed;
+
+/* ALLOC request/response */
+struct gsp_alloc_req {
+ __le64 size;
+ __le64 alignment;
+ __le32 mem_type;
+ __le32 flags;
+ __le64 addr_hint;
+ __le32 pid;
+ __le32 reserved;
+} __packed;
+
+struct gsp_alloc_rsp {
+ __le64 gpu_va;
+ __le64 handle;
+ __le64 size;
+} __packed;
+
+/* MAP request/response */
+struct gsp_map_req {
+ __le64 handle;
+ __le64 gpu_va;
+ __le64 size;
+ __le32 prot;
+ __le32 flags;
+ __le64 offset;
+ __le32 reserved[2];
+} __packed;
+
+struct gsp_map_rsp {
+ __le64 gpu_va;
+} __packed;
+
+/* PIN/UNPIN request/response */
+struct gsp_pin_req {
+ __le64 handle;
+ __le64 offset;
+ __le64 size;
+ __le32 flags;
+ __le32 reserved[3];
+} __packed;
+
+struct gsp_pin_rsp {
+ __le64 dma_addr;
+ __le32 num_pages;
+ __le32 reserved;
+} __packed;
+
+/* SCHEDULE request */
+struct gsp_sched_req {
+ __le64 work_handle;
+ __le64 callback;
+ __le64 arg;
+ __le32 priority;
+ __le32 flags;
+ __le32 reserved[2];
+} __packed;
+
+/* BAR TRAP request/response */
+struct gsp_bar_trap_req {
+ __le64 bar_addr;
+ __le32 access_type;
+ __le32 size;
+ __le64 value;
+ __le32 bar_num;
+ __le32 reserved;
+} __packed;
+
+struct gsp_bar_trap_rsp {
+ __le64 value;
+ __le32 handled;
+ __le32 reserved;
+} __packed;
+
+/* MIGRATE request/response */
+struct gsp_migrate_req {
+ __le64 src_gpu_id;
+ __le64 dst_gpu_id;
+ __le64 memory_bitmap;
+ __le64 page_table_ptr;
+ __le32 state;
+ __le32 flags;
+ __le64 context;
+ __le32 reserved;
+} __packed;
+
+struct gsp_migrate_rsp {
+ __le32 status;
+ __le32 progress;
+ __le64 checkpoint;
+} __packed;
+
+/* ============================================================
+ * CLIENT OPAQUE STRUCT
+ * ============================================================ */
+struct gsp_client;
+
+/* ============================================================
+ * PUBLIC API
+ * ============================================================ */
+
+/* Client lifecycle */
+struct gsp_client *gsp_client_init(struct device *dev, phys_addr_t fw_base, phys_addr_t doorbell);
+void gsp_client_fini(struct gsp_client *client);
+
+/* Memory management */
+int gsp_rpc_alloc(struct gsp_client *client,
+ u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid,
+ u64 *gpu_va, u64 *handle, u64 *actual_size);
+
+int gsp_rpc_free(struct gsp_client *client, u64 handle);
+
+int gsp_rpc_map(struct gsp_client *client,
+ u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset,
+ u64 *mapped_va);
+
+int gsp_rpc_unmap(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size);
+
+/* Page pinning */
+int gsp_rpc_pin(struct gsp_client *client,
+ u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages);
+
+int gsp_rpc_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size);
+
+/* Scheduling */
+int gsp_rpc_schedule(struct gsp_client *client,
+ u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags);
+
+int gsp_rpc_yield(struct gsp_client *client);
+
+/* BAR trap emulation */
+int gsp_rpc_bar_trap(struct gsp_client *client,
+ u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value);
+
+/* Migration */
+int gsp_rpc_migrate_save(struct gsp_client *client,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 *checkpoint);
+
+int gsp_rpc_migrate_restore(struct gsp_client *client,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 checkpoint);
+
+/* WPR Management */
+int gsp_wpr_write_mutex_acquire(struct gsp_client *client, u32 wpr_id);
+int gsp_wpr_write_mutex_release(struct gsp_client *client, u32 wpr_id);
+int gsp_wpr_scratch_mutex_acquire(struct gsp_client *client, u32 wpr_id);
+int gsp_wpr_scratch_mutex_release(struct gsp_client *client, u32 wpr_id);
+
+#endif /* _GSP_RPC_CLIENT_H_ */
diff --git a/kernel/gsp_rpc_integration.c b/kernel/gsp_rpc_integration.c
new file mode 100644
index 0000000..4d43392
--- /dev/null
+++ b/kernel/gsp_rpc_integration.c
@@ -0,0 +1,535 @@
+/*
+ * GSP RPC Integration Layer
+ * Bridges the firmware-decompiled RPC client with existing gsp_comm.c
+ */
+
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "gsp_comm.h"
+#include "gsp_rpc_client.h"
+#include "gsp_rpc_integration.h"
+#include "bar_emulation.h"
+#include "migration.h"
+
+/* Integration context - wraps the new RPC client */
+struct gsp_rpc_integration {
+ struct nv_vgpu_device *vgpu;
+ struct gsp_client *rpc_client;
+ struct nv_vgpu_gsp_comm_ctx *comm_ctx;
+ struct mutex lock;
+ bool initialized;
+};
+
+/* Global integration instance (per vGPU) */
+static struct gsp_rpc_integration *g_integration = NULL;
+
+/* ============================================================
+ * RPC Message Building Helpers
+ * ============================================================ */
+
+static void gsp_build_alloc_msg(struct gsp_alloc_req *req,
+ u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid)
+{
+ req->size = cpu_to_le64(size);
+ req->alignment = cpu_to_le64(alignment);
+ req->mem_type = cpu_to_le32(mem_type);
+ req->flags = cpu_to_le32(flags);
+ req->addr_hint = cpu_to_le64(addr_hint);
+ req->pid = cpu_to_le32(pid);
+ req->reserved = 0;
+}
+
+static void gsp_build_map_msg(struct gsp_map_req *req,
+ u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset)
+{
+ req->handle = cpu_to_le64(handle);
+ req->gpu_va = cpu_to_le64(gpu_va);
+ req->size = cpu_to_le64(size);
+ req->prot = cpu_to_le32(prot);
+ req->flags = cpu_to_le32(flags);
+ req->offset = cpu_to_le64(offset);
+ req->reserved[0] = 0;
+ req->reserved[1] = 0;
+}
+
+static void gsp_build_pin_msg(struct gsp_pin_req *req,
+ u64 handle, u64 offset, u64 size, u32 flags)
+{
+ req->handle = cpu_to_le64(handle);
+ req->offset = cpu_to_le64(offset);
+ req->size = cpu_to_le64(size);
+ req->flags = cpu_to_le32(flags);
+ req->reserved[0] = 0;
+ req->reserved[1] = 0;
+ req->reserved[2] = 0;
+}
+
+static void gsp_build_sched_msg(struct gsp_sched_req *req,
+ u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags)
+{
+ req->work_handle = cpu_to_le64(work_handle);
+ req->callback = cpu_to_le64(callback);
+ req->arg = cpu_to_le64(arg);
+ req->priority = cpu_to_le32(priority);
+ req->flags = cpu_to_le32(flags);
+ req->reserved[0] = 0;
+ req->reserved[1] = 0;
+}
+
+static void gsp_build_bar_trap_msg(struct gsp_bar_trap_req *req,
+ u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num)
+{
+ req->bar_addr = cpu_to_le64(bar_addr);
+ req->access_type = cpu_to_le32(access_type);
+ req->size = cpu_to_le32(size);
+ req->value = cpu_to_le64(value);
+ req->bar_num = cpu_to_le32(bar_num);
+ req->reserved = 0;
+}
+
+static void gsp_build_migrate_msg(struct gsp_migrate_req *req,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u32 state, u32 flags, u64 context)
+{
+ req->src_gpu_id = cpu_to_le64(src_gpu_id);
+ req->dst_gpu_id = cpu_to_le64(dst_gpu_id);
+ req->memory_bitmap = cpu_to_le64(memory_bitmap);
+ req->page_table_ptr = cpu_to_le64(page_table_ptr);
+ req->state = cpu_to_le32(state);
+ req->flags = cpu_to_le32(flags);
+ req->context = cpu_to_le64(context);
+ req->reserved = 0;
+}
+
+/* ============================================================
+ * Integration API
+ * ============================================================ */
+
+int nv_vgpu_gsp_rpc_integration_init(struct nv_vgpu_device *vgpu,
+ struct nv_vgpu_gsp_comm_ctx *comm_ctx)
+{
+ struct gsp_rpc_integration *integ;
+ phys_addr_t fw_base, doorbell;
+ int ret;
+
+ if (!vgpu || !comm_ctx)
+ return -EINVAL;
+
+ integ = kzalloc(sizeof(*integ), GFP_KERNEL);
+ if (!integ)
+ return -ENOMEM;
+
+ integ->vgpu = vgpu;
+ integ->comm_ctx = comm_ctx;
+ mutex_init(&integ->lock);
+
+ /* Calculate firmware base and doorbell from BAR0 */
+ if (vgpu->bars[0].vaddr) {
+ fw_base = vgpu->bars[0].gpa;
+ doorbell = vgpu->bars[0].gpa + NV_VGPU_GSP_DOORBELL_OFFSET;
+ } else {
+ /* Default for TU102/TU104 */
+ fw_base = GSP_FW_BASE;
+ doorbell = GSP_FW_BASE + NV_VGPU_GSP_DOORBELL_OFFSET;
+ }
+
+ /* Initialize the new RPC client */
+ integ->rpc_client = gsp_client_init(&vgpu->pdev->dev, fw_base, doorbell);
+ if (IS_ERR(integ->rpc_client)) {
+ ret = PTR_ERR(integ->rpc_client);
+ integ->rpc_client = NULL;
+ goto err_free;
+ }
+
+ integ->initialized = true;
+ g_integration = integ;
+
+ nv_vgpu_info(vgpu, "GSP RPC integration initialized (fw_base=0x%llx, doorbell=0x%llx)",
+ (unsigned long long)fw_base, (unsigned long long)doorbell);
+
+ return 0;
+
+err_free:
+ kfree(integ);
+ return ret;
+}
+
+void nv_vgpu_gsp_rpc_integration_cleanup(struct nv_vgpu_device *vgpu)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+
+ if (!integ)
+ return;
+
+ mutex_lock(&integ->lock);
+
+ if (integ->rpc_client) {
+ gsp_client_fini(integ->rpc_client);
+ integ->rpc_client = NULL;
+ }
+
+ integ->initialized = false;
+ mutex_unlock(&integ->lock);
+
+ kfree(integ);
+ g_integration = NULL;
+
+ if (vgpu)
+ nv_vgpu_info(vgpu, "GSP RPC integration cleaned up");
+}
+
+/* ============================================================
+ * Enhanced RPC Operations (using firmware-decompiled addresses)
+ * ============================================================ */
+
+int nv_vgpu_gsp_rpc_alloc_fw(struct nv_vgpu_device *vgpu,
+ u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid,
+ u64 *gpu_va, u64 *handle, u64 *actual_size)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+ struct gsp_alloc_req req;
+ int ret;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ gsp_build_alloc_msg(&req, size, alignment, mem_type, flags, addr_hint, pid);
+
+ ret = gsp_rpc_alloc(integ->rpc_client,
+ size, alignment, mem_type, flags, addr_hint, pid,
+ gpu_va, handle, actual_size);
+
+ if (ret == 0) {
+ nv_vgpu_dbg(vgpu, "ALLOC: size=%llu align=%llu -> gpu_va=0x%llx handle=0x%llx",
+ size, alignment, *gpu_va, *handle);
+ }
+
+ return ret;
+}
+
+int nv_vgpu_gsp_rpc_free_fw(struct nv_vgpu_device *vgpu, u64 handle)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ return gsp_rpc_free(integ->rpc_client, handle);
+}
+
+int nv_vgpu_gsp_rpc_map_fw(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset,
+ u64 *mapped_va)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+ struct gsp_map_req req;
+ int ret;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ gsp_build_map_msg(&req, handle, gpu_va, size, prot, flags, offset);
+
+ ret = gsp_rpc_map(integ->rpc_client, handle, gpu_va, size,
+ prot, flags, offset, mapped_va);
+
+ if (ret == 0) {
+ nv_vgpu_dbg(vgpu, "MAP: handle=0x%llx gpu_va=0x%llx size=%llu -> mapped_va=0x%llx",
+ handle, gpu_va, size, *mapped_va);
+ }
+
+ return ret;
+}
+
+int nv_vgpu_gsp_rpc_unmap_fw(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 gpu_va, u64 size)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ return gsp_rpc_unmap(integ->rpc_client, handle, gpu_va, size);
+}
+
+int nv_vgpu_gsp_rpc_pin_fw(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+ struct gsp_pin_req req;
+ int ret;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ gsp_build_pin_msg(&req, handle, offset, size, flags);
+
+ ret = gsp_rpc_pin(integ->rpc_client, handle, offset, size,
+ flags, dma_addr, num_pages);
+
+ if (ret == 0) {
+ nv_vgpu_dbg(vgpu, "PIN: handle=0x%llx offset=%llu size=%llu -> dma_addr=0x%llx pages=%u",
+ handle, offset, size, *dma_addr, *num_pages);
+ }
+
+ return ret;
+}
+
+int nv_vgpu_gsp_rpc_unpin_fw(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 offset, u64 size)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ return gsp_rpc_unpin(integ->rpc_client, handle, offset, size);
+}
+
+int nv_vgpu_gsp_rpc_schedule_fw(struct nv_vgpu_device *vgpu,
+ u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+ struct gsp_sched_req req;
+ int ret;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ gsp_build_sched_msg(&req, work_handle, callback, arg, priority, flags);
+
+ ret = gsp_rpc_schedule(integ->rpc_client, work_handle, callback, arg,
+ priority, flags);
+
+ if (ret == 0) {
+ nv_vgpu_dbg(vgpu, "SCHEDULE: work_handle=0x%llx priority=%u", work_handle, priority);
+ }
+
+ return ret;
+}
+
+int nv_vgpu_gsp_rpc_yield_fw(struct nv_vgpu_device *vgpu)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ return gsp_rpc_yield(integ->rpc_client);
+}
+
+int nv_vgpu_gsp_rpc_bar_trap_fw(struct nv_vgpu_device *vgpu,
+ u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+ struct gsp_bar_trap_req req;
+ int ret;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ gsp_build_bar_trap_msg(&req, bar_addr, access_type, size, value, bar_num);
+
+ ret = gsp_rpc_bar_trap(integ->rpc_client, bar_addr, access_type, size,
+ value, bar_num, ret_value);
+
+ if (ret == 0) {
+ nv_vgpu_dbg(vgpu, "BAR_TRAP: bar=%u addr=0x%llx type=%u -> value=0x%llx",
+ bar_num, bar_addr, access_type, *ret_value);
+ }
+
+ return ret;
+}
+
+int nv_vgpu_gsp_rpc_migrate_save_fw(struct nv_vgpu_device *vgpu,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 *checkpoint)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+ struct gsp_migrate_req req;
+ int ret;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ gsp_build_migrate_msg(&req, src_gpu_id, dst_gpu_id,
+ memory_bitmap, page_table_ptr,
+ GSP_MIGRATE_SUBCMD_SAVE, 0, 0);
+
+ ret = gsp_rpc_migrate_save(integ->rpc_client,
+ src_gpu_id, dst_gpu_id,
+ memory_bitmap, page_table_ptr,
+ checkpoint);
+
+ if (ret == 0) {
+ nv_vgpu_info(vgpu, "MIGRATE SAVE: src=%llu dst=%llu -> checkpoint=0x%llx",
+ src_gpu_id, dst_gpu_id, *checkpoint);
+ }
+
+ return ret;
+}
+
+int nv_vgpu_gsp_rpc_migrate_restore_fw(struct nv_vgpu_device *vgpu,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 checkpoint)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+ struct gsp_migrate_req req;
+ int ret;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ gsp_build_migrate_msg(&req, src_gpu_id, dst_gpu_id,
+ memory_bitmap, page_table_ptr,
+ GSP_MIGRATE_SUBCMD_RESTORE, 0, checkpoint);
+
+ ret = gsp_rpc_migrate_restore(integ->rpc_client,
+ src_gpu_id, dst_gpu_id,
+ memory_bitmap, page_table_ptr,
+ checkpoint);
+
+ if (ret == 0) {
+ nv_vgpu_info(vgpu, "MIGRATE RESTORE: src=%llu dst=%llu checkpoint=0x%llx",
+ src_gpu_id, dst_gpu_id, checkpoint);
+ }
+
+ return ret;
+}
+
+/* ============================================================
+ * WPR Management (from firmware strings)
+ * ============================================================ */
+
+int nv_vgpu_gsp_wpr_write_mutex_acquire(struct nv_vgpu_device *vgpu, u32 wpr_id)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+
+ (void)vgpu; /* integ is global, vgpu not directly used */
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ return gsp_wpr_write_mutex_acquire(integ->rpc_client, wpr_id);
+}
+
+int nv_vgpu_gsp_wpr_write_mutex_release(struct nv_vgpu_device *vgpu, u32 wpr_id)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+
+ (void)vgpu;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ return gsp_wpr_write_mutex_release(integ->rpc_client, wpr_id);
+}
+
+int nv_vgpu_gsp_wpr_scratch_mutex_acquire(struct nv_vgpu_device *vgpu, u32 wpr_id)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+
+ (void)vgpu;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ return gsp_wpr_scratch_mutex_acquire(integ->rpc_client, wpr_id);
+}
+
+int nv_vgpu_gsp_wpr_scratch_mutex_release(struct nv_vgpu_device *vgpu, u32 wpr_id)
+{
+ struct gsp_rpc_integration *integ = g_integration;
+
+ (void)vgpu;
+
+ if (!integ || !integ->initialized || !integ->rpc_client)
+ return -ENODEV;
+
+ return gsp_wpr_scratch_mutex_release(integ->rpc_client, wpr_id);
+}
+
+/* ============================================================
+ * Firmware Address Accessors (for debugging/validation)
+ * ============================================================ */
+
+u64 nv_vgpu_gsp_get_primary_dispatcher(void)
+{
+ return GSP_RPC_DISPATCH_PRIMARY;
+}
+
+u64 nv_vgpu_gsp_get_secondary_dispatcher(void)
+{
+ return GSP_RPC_DISPATCH_SECONDARY;
+}
+
+u64 nv_vgpu_gsp_get_alloc_main(void)
+{
+ return GSP_ALLOC_MAIN;
+}
+
+u64 nv_vgpu_gsp_get_schedule_main(void)
+{
+ return GSP_SCHEDULE_MAIN;
+}
+
+u64 nv_vgpu_gsp_get_bar_trap_handler_jt(void)
+{
+ return GSP_BAR_TRAP_HANDLER_JT;
+}
+
+u64 nv_vgpu_gsp_get_bar_trap_handler_primary(void)
+{
+ return GSP_BAR_TRAP_HANDLER_PRIM;
+}
+
+u64 nv_vgpu_gsp_get_entry_point(void)
+{
+ return GSP_ENTRY_POINT;
+}
+
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_integration_init);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_integration_cleanup);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_alloc_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_free_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_map_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_unmap_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_pin_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_unpin_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_schedule_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_yield_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_bar_trap_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_migrate_save_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_rpc_migrate_restore_fw);
+EXPORT_SYMBOL(nv_vgpu_gsp_wpr_write_mutex_acquire);
+EXPORT_SYMBOL(nv_vgpu_gsp_wpr_write_mutex_release);
+EXPORT_SYMBOL(nv_vgpu_gsp_wpr_scratch_mutex_acquire);
+EXPORT_SYMBOL(nv_vgpu_gsp_wpr_scratch_mutex_release);
+EXPORT_SYMBOL(nv_vgpu_gsp_get_primary_dispatcher);
+EXPORT_SYMBOL(nv_vgpu_gsp_get_secondary_dispatcher);
+EXPORT_SYMBOL(nv_vgpu_gsp_get_alloc_main);
+EXPORT_SYMBOL(nv_vgpu_gsp_get_schedule_main);
+EXPORT_SYMBOL(nv_vgpu_gsp_get_bar_trap_handler_jt);
+EXPORT_SYMBOL(nv_vgpu_gsp_get_bar_trap_handler_primary);
+EXPORT_SYMBOL(nv_vgpu_gsp_get_entry_point);
diff --git a/kernel/include/bar_emulation.h b/kernel/include/bar_emulation.h
new file mode 100644
index 0000000..759fa25
--- /dev/null
+++ b/kernel/include/bar_emulation.h
@@ -0,0 +1,57 @@
+#ifndef _NV_VGPU_BAR_EMULATION_H_
+#define _NV_VGPU_BAR_EMULATION_H_
+
+#include "nvidia-vgpu-vfio.h"
+
+struct nv_vgpu_bar_emul_ctx {
+ struct nv_vgpu_device *vgpu;
+ void *bar0_shadow;
+ size_t bar0_shadow_size;
+ spinlock_t shadow_lock;
+
+ struct {
+ u32 offset;
+ u32 value;
+ bool is_write;
+ bool trapped;
+ } trap_regs[256];
+ int trap_count;
+};
+
+int nv_vgpu_bar_emul_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_bar_emul_cleanup(struct nv_vgpu_device *vgpu);
+
+int nv_vgpu_bar_trap_register(struct nv_vgpu_device *vgpu, int bar_idx,
+ u32 offset, u32 size);
+int nv_vgpu_bar_untrap_register(struct nv_vgpu_device *vgpu, int bar_idx,
+ u32 offset);
+
+int nv_vgpu_bar_emul_read(struct nv_vgpu_device *vgpu, int bar_idx,
+ u64 offset, void *val, size_t size);
+int nv_vgpu_bar_emul_write(struct nv_vgpu_device *vgpu, int bar_idx,
+ u64 offset, void *val, size_t size);
+
+int nv_vgpu_bar_emul_handle_fault(struct nv_vgpu_device *vgpu,
+ unsigned long addr, bool write);
+
+static inline u32 nv_vgpu_read_reg(void __iomem *base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline void nv_vgpu_write_reg(void __iomem *base, u32 offset, u32 val)
+{
+ writel(val, base + offset);
+}
+
+static inline u64 nv_vgpu_read_reg64(void __iomem *base, u32 offset)
+{
+ return readq(base + offset);
+}
+
+static inline void nv_vgpu_write_reg64(void __iomem *base, u32 offset, u64 val)
+{
+ writeq(val, base + offset);
+}
+
+#endif \ No newline at end of file
diff --git a/kernel/include/gsp_comm.h b/kernel/include/gsp_comm.h
new file mode 100644
index 0000000..622f298
--- /dev/null
+++ b/kernel/include/gsp_comm.h
@@ -0,0 +1,155 @@
+#ifndef _NV_VGPU_GSP_COMM_H_
+#define _NV_VGPU_GSP_COMM_H_
+
+#include "nvidia-vgpu-vfio.h"
+
+#define NV_VGPU_GSP_RPC_MAX_SIZE 4096
+#define NV_VGPU_GSP_RPC_TIMEOUT_MS 5000
+#define NV_VGPU_GSP_QUEUE_DEPTH 256
+
+#define NV_VGPU_GSP_DOORBELL_OFFSET 0x100000
+#define NV_VGPU_GSP_SHARED_MEM_OFFSET 0x200000
+#define NV_VGPU_GSP_SHARED_MEM_SIZE (64 * 1024)
+
+#define NV_VGPU_GSP_FIRMWARE_PATH "/lib/firmware/nvidia/tu102/gsp/"
+
+enum nv_vgpu_gsp_rpc_type {
+ NV_GSP_RPC_TYPE_ALLOC = 0x01,
+ NV_GSP_RPC_TYPE_FREE = 0x02,
+ NV_GSP_RPC_TYPE_MAP = 0x03,
+ NV_GSP_RPC_TYPE_UNMAP = 0x04,
+ NV_GSP_RPC_TYPE_PIN = 0x05,
+ NV_GSP_RPC_TYPE_UNPIN = 0x06,
+ NV_GSP_RPC_TYPE_SCHEDULE = 0x07,
+ NV_GSP_RPC_TYPE_YIELD = 0x08,
+ NV_GSP_RPC_TYPE_NOTIFY = 0x09,
+ NV_GSP_RPC_TYPE_BAR_TRAP = 0x0A,
+ NV_GSP_RPC_TYPE_MIGRATE = 0x0B,
+ NV_GSP_RPC_TYPE_DEBUG = 0xFF,
+};
+
+struct nv_vgpu_gsp_rpc_header {
+ u32 type;
+ u32 sequence;
+ u32 size;
+ u32 flags;
+ u64 timestamp;
+};
+
+struct nv_vgpu_gsp_rpc_msg {
+ struct nv_vgpu_gsp_rpc_header hdr;
+ u8 data[];
+};
+
+struct nv_vgpu_gsp_firmware {
+ void *bootloader;
+ size_t bootloader_size;
+ void *booter_load;
+ size_t booter_load_size;
+ void *gsp;
+ size_t gsp_size;
+};
+
+struct nv_vgpu_gsp_comm_ctx {
+ struct nv_vgpu_device *vgpu;
+
+ void __iomem *gsp_doorbell;
+ void __iomem *gsp_shared_mem;
+ size_t shared_mem_size;
+
+ struct mutex queue_lock;
+ struct list_head pending_queue;
+ struct list_head complete_queue;
+ atomic_t sequence_counter;
+
+ wait_queue_head_t wait_queue;
+
+ struct workqueue_struct *rpc_wq;
+ struct work_struct rpc_work;
+
+ struct delayed_work poll_work;
+ bool poll_active;
+
+ spinlock_t irq_lock;
+ bool irq_enabled;
+
+ struct nv_vgpu_gsp_firmware firmware;
+ bool firmware_loaded;
+};
+
+struct nv_vgpu_gsp_rpc_entry {
+ struct list_head list;
+ struct nv_vgpu_gsp_rpc_msg *msg;
+ struct completion done;
+ int result;
+};
+
+int nv_vgpu_gsp_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_gsp_cleanup(struct nv_vgpu_device *vgpu);
+
+int nv_vgpu_gsp_send_rpc(struct nv_vgpu_device *vgpu, void *msg, size_t size);
+int nv_vgpu_gsp_recv_rpc(struct nv_vgpu_device *vgpu, void *msg, size_t size, int timeout_ms);
+
+int nv_vgpu_gsp_alloc(struct nv_vgpu_device *vgpu, u64 size, u32 align, u64 *handle);
+int nv_vgpu_gsp_free(struct nv_vgpu_device *vgpu, u64 handle);
+int nv_vgpu_gsp_map(struct nv_vgpu_device *vgpu, u64 handle, u64 iova, u64 size, u32 flags);
+int nv_vgpu_gsp_unmap(struct nv_vgpu_device *vgpu, u64 handle, u64 iova, u64 size);
+int nv_vgpu_gsp_schedule(struct nv_vgpu_device *vgpu, u32 runlist_id, u32 channel_id);
+int nv_vgpu_gsp_notify_bar_trap(struct nv_vgpu_device *vgpu, u64 offset, u32 size, void *val);
+
+int nv_vgpu_gsp_load_firmware(struct nv_vgpu_device *vgpu);
+void nv_vgpu_gsp_release_firmware(struct nv_vgpu_device *vgpu);
+int nv_vgpu_gsp_boot(struct nv_vgpu_device *vgpu);
+
+/* ============================================================
+ * New Firmware-Decompiled RPC Operations (enhanced APIs)
+ * ============================================================ */
+
+int nv_vgpu_gsp_rpc_alloc(struct nv_vgpu_device *vgpu,
+ u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid,
+ u64 *gpu_va, u64 *handle, u64 *actual_size);
+
+int nv_vgpu_gsp_rpc_free(struct nv_vgpu_device *vgpu, u64 handle);
+
+int nv_vgpu_gsp_rpc_map(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset,
+ u64 *mapped_va);
+
+int nv_vgpu_gsp_rpc_unmap(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 gpu_va, u64 size);
+
+int nv_vgpu_gsp_rpc_pin(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages);
+
+int nv_vgpu_gsp_rpc_unpin(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 offset, u64 size);
+
+int nv_vgpu_gsp_rpc_schedule(struct nv_vgpu_device *vgpu,
+ u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags);
+
+int nv_vgpu_gsp_rpc_yield(struct nv_vgpu_device *vgpu);
+
+int nv_vgpu_gsp_rpc_bar_trap(struct nv_vgpu_device *vgpu,
+ u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value);
+
+int nv_vgpu_gsp_rpc_migrate_save(struct nv_vgpu_device *vgpu,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 *checkpoint);
+
+int nv_vgpu_gsp_rpc_migrate_restore(struct nv_vgpu_device *vgpu,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 checkpoint);
+
+static inline u32 nv_vgpu_gsp_next_sequence(struct nv_vgpu_gsp_comm_ctx *ctx)
+{
+ return atomic_inc_return(&ctx->sequence_counter);
+}
+
+#endif \ No newline at end of file
diff --git a/kernel/include/gsp_rpc_client.h b/kernel/include/gsp_rpc_client.h
new file mode 100644
index 0000000..4b48953
--- /dev/null
+++ b/kernel/include/gsp_rpc_client.h
@@ -0,0 +1,313 @@
+/*
+ * GSP 570.144W RPC Client Header
+ * Auto-generated from firmware decompilation
+ */
+
+#ifndef _GSP_RPC_CLIENT_H_
+#define _GSP_RPC_CLIENT_H_
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include "nvidia-vgpu-vfio.h"
+
+/* ============================================================
+ * FIRMWARE BASE ADDRESS
+ * ============================================================ */
+#define GSP_FW_BASE 0x04000000UL
+
+/* ============================================================
+ * RPC DISPATCHER ADDRESSES
+ * ============================================================ */
+
+/* Primary Dispatcher - Jump table switch for 6 RPC types */
+#define GSP_RPC_DISPATCH_PRIMARY (GSP_FW_BASE + 0x17A1AC0) /* 0x057a1ac0 */
+#define GSP_RPC_JUMP_TABLE (GSP_FW_BASE + 0x17A3AD8) /* 0x057a3ad8 - 32 sub-commands */
+
+/* Secondary Dispatcher - Full implementation for all 11 RPC types */
+#define GSP_RPC_DISPATCH_SECONDARY (GSP_FW_BASE + 0x0ED39DC) /* 0x04ed39dc */
+
+/* Tertiary Dispatcher - Stack=160 bytes */
+#define GSP_RPC_DISPATCH_TERTIARY (GSP_FW_BASE + 0x0CC58F8) /* 0x04cc58f8 */
+
+/* Quaternary Dispatcher - Stack=96 bytes */
+#define GSP_RPC_DISPATCH_QUATERNARY (GSP_FW_BASE + 0x1723EF4) /* 0x05723ef4 */
+
+/* ============================================================
+ * SPECIALIZED HANDLER ADDRESSES
+ * ============================================================ */
+
+#define GSP_ALLOC_MAIN (GSP_FW_BASE + 0x17E94EC) /* 0x057e94ec */
+#define GSP_SCHEDULE_MAIN (GSP_FW_BASE + 0x11EFBF4) /* 0x051efbf4 */
+#define GSP_BAR_TRAP_HANDLER_JT (GSP_FW_BASE + 0x17675B7) /* 0x057675b7 */
+#define GSP_BAR_TRAP_HANDLER_PRIM (GSP_FW_BASE + 0x0CB8189) /* 0x04cb8189 */
+#define GSP_PIN_HANDLER_SEC (GSP_FW_BASE + 0x0ED34B8) /* 0x04ed34b8 */
+
+/* Jump Table Handlers (from decoded table at 0x059bfeb0) */
+#define GSP_MAP_HANDLER_A (GSP_FW_BASE + 0x1766AB7) /* 0x05766ab7 */
+#define GSP_MAP_HANDLER_B (GSP_FW_BASE + 0x1778A37) /* 0x05778a37 */
+#define GSP_UNMAP_HANDLER_A (GSP_FW_BASE + 0x17669B7) /* 0x057669b7 */
+#define GSP_UNMAP_HANDLER_B (GSP_FW_BASE + 0x1779937) /* 0x05779937 */
+#define GSP_PIN_HANDLER_A (GSP_FW_BASE + 0x17664B7) /* 0x057664b7 */
+#define GSP_PIN_HANDLER_B (GSP_FW_BASE + 0x177A3B7) /* 0x0577a3b7 */
+#define GSP_UNPIN_HANDLER_A (GSP_FW_BASE + 0x17682B7) /* 0x057682b7 */
+#define GSP_UNPIN_HANDLER_B (GSP_FW_BASE + 0x1779FB7) /* 0x05779fb7 */
+#define GSP_SCHEDULE_HANDLER_A (GSP_FW_BASE + 0x1767F37) /* 0x05767f37 */
+#define GSP_SCHEDULE_HANDLER_B (GSP_FW_BASE + 0x175AEB7) /* 0x0575aeb7 */
+#define GSP_YIELD_HANDLER_A (GSP_FW_BASE + 0x175AE37) /* 0x0575ae37 */
+#define GSP_YIELD_HANDLER_B (GSP_FW_BASE + 0x1767337) /* 0x05767337 */
+#define GSP_NOTIFY_HANDLER_A (GSP_FW_BASE + 0x177C8B7) /* 0x0577c8b7 */
+#define GSP_NOTIFY_HANDLER_B (GSP_FW_BASE + 0x1767837) /* 0x05767837 */
+
+/* Entry Point */
+#define GSP_ENTRY_POINT (GSP_FW_BASE + 0x0009000) /* 0x04009000 */
+
+/* ============================================================
+ * RPC TYPE DEFINITIONS (match existing nv_vgpu_gsp_rpc_type)
+ * ============================================================ */
+enum gsp_rpc_type {
+ GSP_RPC_ALLOC = 0x01,
+ GSP_RPC_FREE = 0x02,
+ GSP_RPC_MAP = 0x03,
+ GSP_RPC_UNMAP = 0x04,
+ GSP_RPC_PIN = 0x05,
+ GSP_RPC_UNPIN = 0x06,
+ GSP_RPC_SCHEDULE = 0x07,
+ GSP_RPC_YIELD = 0x08,
+ GSP_RPC_NOTIFY = 0x09,
+ GSP_RPC_BAR_TRAP = 0x0A,
+ GSP_RPC_MIGRATE = 0x0B,
+ GSP_RPC_DEBUG = 0xFF,
+};
+
+/* ============================================================
+ * ALLOC SUB-COMMANDS (Primary Dispatcher Jump Table Indices)
+ * ============================================================ */
+enum gsp_alloc_subcmd {
+ GSP_ALLOC_SUBCMD_DEFAULT = 0,
+ GSP_ALLOC_SUBCMD_TYPE1 = 1,
+ GSP_ALLOC_SUBCMD_TYPE2 = 3,
+ GSP_ALLOC_SUBCMD_TYPE3 = 4,
+ GSP_ALLOC_SUBCMD_TYPE4 = 5,
+ GSP_ALLOC_SUBCMD_TYPE5 = 8,
+ GSP_ALLOC_SUBCMD_TYPE6 = 10,
+ GSP_ALLOC_SUBCMD_TYPE7 = 12,
+ GSP_ALLOC_SUBCMD_TYPE8 = 17,
+ GSP_ALLOC_SUBCMD_TYPE9 = 19,
+ GSP_ALLOC_SUBCMD_TYPE10 = 20,
+ GSP_ALLOC_SUBCMD_TYPE11 = 21,
+ GSP_ALLOC_SUBCMD_TYPE12 = 22,
+ GSP_ALLOC_SUBCMD_TYPE13 = 23,
+ GSP_ALLOC_SUBCMD_TYPE14 = 24,
+ GSP_ALLOC_SUBCMD_TYPE15 = 25,
+ GSP_ALLOC_SUBCMD_TYPE16 = 26,
+ GSP_ALLOC_SUBCMD_TYPE17 = 27,
+ GSP_ALLOC_SUBCMD_TYPE18 = 28,
+ GSP_ALLOC_SUBCMD_TYPE19 = 29,
+ GSP_ALLOC_SUBCMD_TYPE20 = 30,
+ GSP_ALLOC_SUBCMD_TYPE21 = 31,
+};
+
+/* ============================================================
+ * MIGRATE SUB-COMMANDS
+ * ============================================================ */
+enum gsp_migrate_subcmd {
+ GSP_MIGRATE_SUBCMD_SAVE = 0x04,
+ GSP_MIGRATE_SUBCMD_RESTORE = 0x05,
+};
+
+/* ============================================================
+ * PIN SUB-COMMANDS
+ * ============================================================ */
+enum gsp_pin_subcmd {
+ GSP_PIN_SUBCMD_DEFAULT = 0x05,
+};
+
+/* ============================================================
+ * SCHEDULE SUB-COMMANDS
+ * ============================================================ */
+enum gsp_sched_subcmd {
+ GSP_SCHED_SUBCMD_DEFAULT = 0x07,
+};
+
+/* ============================================================
+ * BAR TRAP SUB-COMMANDS
+ * ============================================================ */
+enum gsp_bar_trap_subcmd {
+ GSP_BAR_TRAP_SUBCMD_DEFAULT = 0x0A,
+};
+
+/* ============================================================
+ * RPC MESSAGE STRUCTURES
+ * ============================================================ */
+
+struct gsp_rpc_msg {
+ __le32 type;
+ __le32 subcmd;
+ __le64 payload[6];
+ __le32 seq_id;
+ __le32 flags;
+ __le32 reserved;
+} __attribute__((packed, aligned(8)));
+
+struct gsp_rpc_response {
+ __le32 status;
+ __le32 type;
+ __le64 result[6];
+ __le32 seq_id;
+} __attribute__((packed, aligned(8)));
+
+/* ALLOC request/response */
+struct gsp_alloc_req {
+ __le64 size;
+ __le64 alignment;
+ __le32 mem_type;
+ __le32 flags;
+ __le64 addr_hint;
+ __le32 pid;
+ __le32 reserved;
+} __attribute__((packed, aligned(8)));
+
+struct gsp_alloc_rsp {
+ __le64 gpu_va;
+ __le64 handle;
+ __le64 size;
+} __attribute__((packed, aligned(8)));
+
+/* MAP request/response */
+struct gsp_map_req {
+ __le64 handle;
+ __le64 gpu_va;
+ __le64 size;
+ __le32 prot;
+ __le32 flags;
+ __le64 offset;
+ __le32 reserved;
+} __attribute__((packed, aligned(8)));
+
+struct gsp_map_rsp {
+ __le64 gpu_va;
+} __attribute__((packed, aligned(8)));
+
+/* PIN/UNPIN request/response */
+struct gsp_pin_req {
+ __le64 handle;
+ __le64 offset;
+ __le64 size;
+ __le32 flags;
+ __le32 reserved[2];
+} __attribute__((packed, aligned(8)));
+
+struct gsp_pin_rsp {
+ __le64 dma_addr;
+ __le32 num_pages;
+ __le32 reserved;
+} __attribute__((packed, aligned(8)));
+
+/* SCHEDULE request */
+struct gsp_sched_req {
+ __le64 work_handle;
+ __le64 callback;
+ __le64 arg;
+ __le32 priority;
+ __le32 flags;
+ __le32 reserved[2];
+} __attribute__((packed, aligned(8)));
+
+/* BAR TRAP request/response */
+struct gsp_bar_trap_req {
+ __le64 bar_addr;
+ __le32 access_type;
+ __le32 size;
+ __le64 value;
+ __le32 bar_num;
+} __attribute__((packed, aligned(8)));
+
+struct gsp_bar_trap_rsp {
+ __le64 value;
+ __le32 handled;
+ __le32 reserved;
+} __attribute__((packed, aligned(8)));
+
+/* MIGRATE request/response */
+struct gsp_migrate_req {
+ __le64 src_gpu_id;
+ __le64 dst_gpu_id;
+ __le64 memory_bitmap;
+ __le64 page_table_ptr;
+ __le32 state;
+ __le32 flags;
+ __le64 context;
+ __le32 reserved;
+} __attribute__((packed, aligned(8)));
+
+struct gsp_migrate_rsp {
+ __le32 status;
+ __le32 progress;
+ __le64 checkpoint;
+} __attribute__((packed, aligned(8)));
+
+/* ============================================================
+ * CLIENT OPAQUE STRUCT
+ * ============================================================ */
+struct gsp_client;
+
+/* ============================================================
+ * PUBLIC API
+ * ============================================================ */
+
+/* Client lifecycle */
+struct gsp_client *gsp_client_init(struct device *dev, phys_addr_t fw_base, phys_addr_t doorbell);
+void gsp_client_fini(struct gsp_client *client);
+
+/* Memory management */
+int gsp_rpc_alloc(struct gsp_client *client,
+ u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid,
+ u64 *gpu_va, u64 *handle, u64 *actual_size);
+
+int gsp_rpc_free(struct gsp_client *client, u64 handle);
+
+int gsp_rpc_map(struct gsp_client *client,
+ u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset,
+ u64 *mapped_va);
+
+int gsp_rpc_unmap(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size);
+
+/* Page pinning */
+int gsp_rpc_pin(struct gsp_client *client,
+ u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages);
+
+int gsp_rpc_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size);
+
+/* Scheduling */
+int gsp_rpc_schedule(struct gsp_client *client,
+ u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags);
+
+int gsp_rpc_yield(struct gsp_client *client);
+
+/* BAR trap emulation */
+int gsp_rpc_bar_trap(struct gsp_client *client,
+ u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value);
+
+/* Migration */
+int gsp_rpc_migrate_save(struct gsp_client *client,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 *checkpoint);
+
+int gsp_rpc_migrate_restore(struct gsp_client *client,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 checkpoint);
+
+/* WPR Management */
+int gsp_wpr_write_mutex_acquire(struct gsp_client *client, u32 wpr_id);
+int gsp_wpr_write_mutex_release(struct gsp_client *client, u32 wpr_id);
+int gsp_wpr_scratch_mutex_acquire(struct gsp_client *client, u32 wpr_id);
+int gsp_wpr_scratch_mutex_release(struct gsp_client *client, u32 wpr_id);
+
+#endif /* _GSP_RPC_CLIENT_H_ */
diff --git a/kernel/include/gsp_rpc_integration.h b/kernel/include/gsp_rpc_integration.h
new file mode 100644
index 0000000..eea3bf3
--- /dev/null
+++ b/kernel/include/gsp_rpc_integration.h
@@ -0,0 +1,97 @@
+/*
+ * GSP RPC Integration Layer Header
+ * Bridges the firmware-decompiled RPC client with existing gsp_comm.c
+ */
+
+#ifndef _NV_VGPU_GSP_RPC_INTEGRATION_H_
+#define _NV_VGPU_GSP_RPC_INTEGRATION_H_
+
+#include "nvidia-vgpu-vfio.h"
+#include "gsp_comm.h"
+
+/* Initialize the RPC integration layer */
+int nv_vgpu_gsp_rpc_integration_init(struct nv_vgpu_device *vgpu,
+ struct nv_vgpu_gsp_comm_ctx *comm_ctx);
+
+/* Cleanup the RPC integration layer */
+void nv_vgpu_gsp_rpc_integration_cleanup(struct nv_vgpu_device *vgpu);
+
+/* ============================================================
+ * Enhanced RPC Operations (using firmware-decompiled addresses)
+ * ============================================================ */
+
+/* Memory allocation with full firmware parameter support */
+int nv_vgpu_gsp_rpc_alloc_fw(struct nv_vgpu_device *vgpu,
+ u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid,
+ u64 *gpu_va, u64 *handle, u64 *actual_size);
+
+/* Memory free */
+int nv_vgpu_gsp_rpc_free_fw(struct nv_vgpu_device *vgpu, u64 handle);
+
+/* Memory mapping with full firmware parameter support */
+int nv_vgpu_gsp_rpc_map_fw(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset,
+ u64 *mapped_va);
+
+/* Memory unmapping */
+int nv_vgpu_gsp_rpc_unmap_fw(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 gpu_va, u64 size);
+
+/* Page pinning for DMA */
+int nv_vgpu_gsp_rpc_pin_fw(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages);
+
+/* Page unpinning */
+int nv_vgpu_gsp_rpc_unpin_fw(struct nv_vgpu_device *vgpu,
+ u64 handle, u64 offset, u64 size);
+
+/* Work scheduling */
+int nv_vgpu_gsp_rpc_schedule_fw(struct nv_vgpu_device *vgpu,
+ u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags);
+
+/* Yield current work */
+int nv_vgpu_gsp_rpc_yield_fw(struct nv_vgpu_device *vgpu);
+
+/* BAR trap emulation */
+int nv_vgpu_gsp_rpc_bar_trap_fw(struct nv_vgpu_device *vgpu,
+ u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value);
+
+/* Migration - save state */
+int nv_vgpu_gsp_rpc_migrate_save_fw(struct nv_vgpu_device *vgpu,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 *checkpoint);
+
+/* Migration - restore state */
+int nv_vgpu_gsp_rpc_migrate_restore_fw(struct nv_vgpu_device *vgpu,
+ u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr,
+ u64 checkpoint);
+
+/* ============================================================
+ * WPR Management (from firmware strings: VPR_WPR_WRITE_MUTEX*)
+ * ============================================================ */
+
+int nv_vgpu_gsp_wpr_write_mutex_acquire(struct nv_vgpu_device *vgpu, u32 wpr_id);
+int nv_vgpu_gsp_wpr_write_mutex_release(struct nv_vgpu_device *vgpu, u32 wpr_id);
+int nv_vgpu_gsp_wpr_scratch_mutex_acquire(struct nv_vgpu_device *vgpu, u32 wpr_id);
+int nv_vgpu_gsp_wpr_scratch_mutex_release(struct nv_vgpu_device *vgpu, u32 wpr_id);
+
+/* ============================================================
+ * Firmware Address Accessors (for debugging/validation)
+ * ============================================================ */
+
+u64 nv_vgpu_gsp_get_primary_dispatcher(void); /* 0x057a1ac0 */
+u64 nv_vgpu_gsp_get_secondary_dispatcher(void); /* 0x04ed39dc */
+u64 nv_vgpu_gsp_get_alloc_main(void); /* 0x057e94ec */
+u64 nv_vgpu_gsp_get_schedule_main(void); /* 0x051efbf4 */
+u64 nv_vgpu_gsp_get_bar_trap_handler_jt(void); /* 0x057675b7 */
+u64 nv_vgpu_gsp_get_bar_trap_handler_primary(void); /* 0x04cb8189 */
+u64 nv_vgpu_gsp_get_entry_point(void); /* 0x04009000 */
+
+#endif /* _NV_VGPU_GSP_RPC_INTEGRATION_H_ */
diff --git a/kernel/include/ioctl.h b/kernel/include/ioctl.h
new file mode 100644
index 0000000..4ff3df9
--- /dev/null
+++ b/kernel/include/ioctl.h
@@ -0,0 +1,105 @@
+#ifndef _NV_VGPU_IOCTL_H_
+#define _NV_VGPU_IOCTL_H_
+
+#include "nvidia-vgpu-vfio.h"
+
+#define NV_VGPU_IOCTL_MAGIC 'N'
+
+#define NV_VGPU_IOCTL_GET_INFO \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x01, struct nv_vgpu_ioctl_get_info)
+
+#define NV_VGPU_IOCTL_MAP_BAR \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x02, struct nv_vgpu_ioctl_map_bar)
+
+#define NV_VGPU_IOCTL_UNMAP_BAR \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x03, struct nv_vgpu_ioctl_unmap_bar)
+
+#define NV_VGPU_IOCTL_ENABLE_IRQ \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x04, struct nv_vgpu_ioctl_irq)
+
+#define NV_VGPU_IOCTL_DISABLE_IRQ \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x05, struct nv_vgpu_ioctl_irq)
+
+#define NV_VGPU_IOCTL_GSP_RPC \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x06, struct nv_vgpu_ioctl_gsp_rpc)
+
+#define NV_VGPU_IOCTL_MIGRATE_SAVE \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x07, struct nv_vgpu_ioctl_migrate)
+
+#define NV_VGPU_IOCTL_MIGRATE_RESTORE \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x08, struct nv_vgpu_ioctl_migrate)
+
+#define NV_VGPU_IOCTL_SET_GSP_FIRMWARE \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x09, struct nv_vgpu_ioctl_firmware)
+
+struct nv_vgpu_ioctl_get_info {
+ u32 vgpu_type;
+ u32 vgpu_id;
+ u32 gpu_instance_id;
+ u32 compute_instance_id;
+ u32 state;
+ u64 fb_base;
+ u64 fb_size;
+ u64 bar0_config_base;
+ u32 num_bars;
+ struct {
+ u32 index;
+ u64 gpa;
+ u64 size;
+ u64 iova;
+ bool mapped;
+ bool trapped;
+ } bars[NV_VGPU_MAX_REGIONS];
+};
+
+struct nv_vgpu_ioctl_map_bar {
+ u32 bar_index;
+ u64 gpa;
+ u64 size;
+ u64 iova;
+ u32 flags;
+};
+
+struct nv_vgpu_ioctl_unmap_bar {
+ u32 bar_index;
+ u64 iova;
+ u64 size;
+};
+
+struct nv_vgpu_ioctl_irq {
+ u32 irq_index;
+ int fd;
+};
+
+struct nv_vgpu_ioctl_gsp_rpc {
+ u32 type;
+ u32 flags;
+ u64 data_ptr;
+ u32 data_size;
+ u32 result;
+};
+
+struct nv_vgpu_ioctl_migrate {
+ u64 data_ptr;
+ u32 data_size;
+ u32 flags;
+ int result;
+};
+
+struct nv_vgpu_ioctl_firmware {
+ u32 chip_id;
+ u64 firmware_ptr;
+ u32 firmware_size;
+ u32 firmware_type;
+};
+
+int nv_vgpu_ioctl_get_info(struct nv_vgpu_device *vgpu, void __user *arg);
+int nv_vgpu_ioctl_map_bar(struct nv_vgpu_device *vgpu, void __user *arg);
+int nv_vgpu_ioctl_unmap_bar(struct nv_vgpu_device *vgpu, void __user *arg);
+int nv_vgpu_ioctl_enable_irq(struct nv_vgpu_device *vgpu, void __user *arg);
+int nv_vgpu_ioctl_disable_irq(struct nv_vgpu_device *vgpu, void __user *arg);
+int nv_vgpu_ioctl_gsp_rpc(struct nv_vgpu_device *vgpu, void __user *arg);
+int nv_vgpu_ioctl_migrate_save(struct nv_vgpu_device *vgpu, void __user *arg);
+int nv_vgpu_ioctl_migrate_restore(struct nv_vgpu_device *vgpu, void __user *arg);
+
+#endif \ No newline at end of file
diff --git a/kernel/include/irq.h b/kernel/include/irq.h
new file mode 100644
index 0000000..d06ac5b
--- /dev/null
+++ b/kernel/include/irq.h
@@ -0,0 +1,45 @@
+#ifndef _NV_VGPU_IRQ_H_
+#define _NV_VGPU_IRQ_H_
+
+#include "nvidia-vgpu-vfio.h"
+
+#define NV_VGPU_MAX_MSI_VECTORS 32
+
+struct nv_vgpu_msi_irq {
+ int virq;
+ u32 vector;
+ bool enabled;
+ struct eventfd_ctx *trigger;
+ struct irq_domain *domain;
+ struct msi_msg msg;
+};
+
+struct nv_vgpu_irq_ctx {
+ struct nv_vgpu_device *vgpu;
+ struct mutex lock;
+ struct nv_vgpu_msi_irq msis[NV_VGPU_MAX_MSI_VECTORS];
+ int msi_count;
+ bool msi_enabled;
+ bool msix_enabled;
+};
+
+int nv_vgpu_irq_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_irq_cleanup(struct nv_vgpu_device *vgpu);
+
+int nv_vgpu_irq_enable(struct nv_vgpu_device *vgpu, int irq_idx);
+int nv_vgpu_irq_disable(struct nv_vgpu_device *vgpu, int irq_idx);
+
+int nv_vgpu_irq_setup_msi(struct nv_vgpu_device *vgpu, int num_vectors);
+int nv_vgpu_irq_setup_msix(struct nv_vgpu_device *vgpu, int num_vectors);
+void nv_vgpu_irq_teardown_msi(struct nv_vgpu_device *vgpu);
+void nv_vgpu_irq_teardown_msix(struct nv_vgpu_device *vgpu);
+
+int nv_vgpu_irq_set_trigger(struct nv_vgpu_device *vgpu, int irq_idx, int fd);
+int nv_vgpu_irq_get_virq(struct nv_vgpu_device *vgpu, int irq_idx);
+
+static inline struct nv_vgpu_irq_ctx *vgpu_to_irq_ctx(struct nv_vgpu_device *vgpu)
+{
+ return (struct nv_vgpu_irq_ctx *)vgpu->irq_ctx;
+}
+
+#endif \ No newline at end of file
diff --git a/kernel/include/memory.h b/kernel/include/memory.h
new file mode 100644
index 0000000..14a35e9
--- /dev/null
+++ b/kernel/include/memory.h
@@ -0,0 +1,45 @@
+#ifndef _NV_VGPU_MEMORY_H_
+#define _NV_VGPU_MEMORY_H_
+
+#include "nvidia-vgpu-vfio.h"
+
+#define NV_VGPU_MAX_MAPPINGS 1024
+
+struct nv_vgpu_mapping {
+ u64 gpa;
+ u64 iova;
+ u64 size;
+ struct page **pages;
+ int page_count;
+ bool pinned;
+ bool mapped;
+ struct list_head list;
+};
+
+struct nv_vgpu_mm_ctx {
+ struct nv_vgpu_device *vgpu;
+ struct mutex lock;
+ struct list_head mappings;
+ u32 mapping_count;
+ struct iommu_domain *iommu_domain;
+ struct dma_iommu_mapping *dma_mapping;
+ bool use_simple;
+};
+
+int nv_vgpu_memory_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_memory_cleanup(struct nv_vgpu_device *vgpu);
+
+int nv_vgpu_memory_map(struct nv_vgpu_device *vgpu, u64 gpa, u64 size, u64 *iova);
+int nv_vgpu_memory_unmap(struct nv_vgpu_device *vgpu, u64 iova, u64 size);
+
+int nv_vgpu_memory_pin(struct nv_vgpu_device *vgpu, u64 gpa, u64 size);
+int nv_vgpu_memory_unpin(struct nv_vgpu_device *vgpu, u64 gpa, u64 size);
+
+int nv_vgpu_memory_mmap(struct nv_vgpu_device *vgpu, struct vm_area_struct *vma);
+
+static inline struct nv_vgpu_mm_ctx *vgpu_to_mm_ctx(struct nv_vgpu_device *vgpu)
+{
+ return vgpu->mm_ctx;
+}
+
+#endif \ No newline at end of file
diff --git a/kernel/include/migration.h b/kernel/include/migration.h
new file mode 100644
index 0000000..17cf8db
--- /dev/null
+++ b/kernel/include/migration.h
@@ -0,0 +1,75 @@
+#ifndef _NV_VGPU_MIGRATION_H_
+#define _NV_VGPU_MIGRATION_H_
+
+#include "nvidia-vgpu-vfio.h"
+
+#define NV_VGPU_MIGRATION_MAGIC 0x4E564750
+#define NV_VGPU_MIGRATION_VERSION 1
+
+struct nv_vgpu_migration_ctx {
+ struct nv_vgpu_device *vgpu;
+ struct mutex lock;
+ struct nv_vgpu_migration_state state;
+ struct task_struct *save_thread;
+ struct task_struct *restore_thread;
+ bool in_progress;
+};
+
+struct nv_vgpu_migration_header {
+ u32 magic;
+ u32 version;
+ u32 flags;
+ u32 num_sections;
+ u64 total_size;
+ u64 timestamp;
+};
+
+enum nv_vgpu_migration_section_type {
+ NV_VGPU_MIG_SECTION_DEVICE_STATE = 0x01,
+ NV_VGPU_MIG_SECTION_BAR_STATE = 0x02,
+ NV_VGPU_MIG_SECTION_MEMORY_STATE = 0x03,
+ NV_VGPU_MIG_SECTION_IRQ_STATE = 0x04,
+ NV_VGPU_MIG_SECTION_GSP_STATE = 0x05,
+ NV_VGPU_MIG_SECTION_ENGINE_STATE = 0x06,
+ NV_VGPU_MIG_SECTION_CHANNEL_STATE = 0x07,
+ NV_VGPU_MIG_SECTION_CONTEXT_STATE = 0x08,
+};
+
+struct nv_vgpu_migration_section {
+ u32 type;
+ u32 flags;
+ u64 offset;
+ u64 size;
+ u32 checksum;
+};
+
+struct nv_vgpu_migration_device_state {
+ u32 vgpu_type;
+ u32 vgpu_id;
+ u32 gpu_instance_id;
+ u32 compute_instance_id;
+ u32 state;
+ u64 fb_base;
+ u64 fb_size;
+ u64 bar0_config_base;
+};
+
+struct nv_vgpu_migration_bar_state {
+ u32 bar_index;
+ u64 gpa;
+ u64 size;
+ u64 iova;
+ u8 data[];
+};
+
+int nv_vgpu_migration_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_migration_cleanup(struct nv_vgpu_device *vgpu);
+
+int nv_vgpu_migration_save_state(struct nv_vgpu_device *vgpu, u8 **data, size_t *size);
+int nv_vgpu_migration_restore_state(struct nv_vgpu_device *vgpu, u8 *data, size_t size);
+
+int nv_vgpu_migration_prepare(struct nv_vgpu_device *vgpu);
+int nv_vgpu_migration_complete(struct nv_vgpu_device *vgpu);
+int nv_vgpu_migration_cancel(struct nv_vgpu_device *vgpu);
+
+#endif \ No newline at end of file
diff --git a/kernel/include/nv-hypervisor.h b/kernel/include/nv-hypervisor.h
new file mode 100644
index 0000000..a2b2649
--- /dev/null
+++ b/kernel/include/nv-hypervisor.h
@@ -0,0 +1,93 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 1999-2018 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _NV_HYPERVISOR_H_
+#define _NV_HYPERVISOR_H_
+
+#include <nv-kernel-interface-api.h>
+
+// Enums for supported hypervisor types.
+// New hypervisor type should be added before OS_HYPERVISOR_UNKNOWN
+typedef enum _HYPERVISOR_TYPE
+{
+ OS_HYPERVISOR_XEN = 0,
+ OS_HYPERVISOR_VMWARE,
+ OS_HYPERVISOR_HYPERV,
+ OS_HYPERVISOR_KVM,
+ OS_HYPERVISOR_UNKNOWN
+} HYPERVISOR_TYPE;
+
+#define CMD_VFIO_WAKE_REMOVE_GPU 1
+#define CMD_VGPU_VFIO_PRESENT 2
+#define CMD_VFIO_PCI_CORE_PRESENT 3
+
+#define MAX_VF_COUNT_PER_GPU 64
+
+typedef enum _VGPU_TYPE_INFO
+{
+ VGPU_TYPE_NAME = 0,
+ VGPU_TYPE_DESCRIPTION,
+ VGPU_TYPE_INSTANCES,
+} VGPU_TYPE_INFO;
+
+typedef struct
+{
+ void *nv;
+ NvU32 domain;
+ NvU32 bus;
+ NvU32 device;
+ NvU32 return_status;
+} vgpu_vfio_info;
+
+typedef struct
+{
+ NvU32 domain;
+ NvU8 bus;
+ NvU8 slot;
+ NvU8 function;
+ NvBool isNvidiaAttached;
+ NvBool isMdevAttached;
+} vgpu_vf_pci_info;
+
+typedef enum VGPU_CMD_PROCESS_VF_INFO_E
+{
+ NV_VGPU_SAVE_VF_INFO = 0,
+ NV_VGPU_REMOVE_VF_PCI_INFO = 1,
+ NV_VGPU_REMOVE_VF_MDEV_INFO = 2,
+ NV_VGPU_GET_VF_INFO = 3
+} VGPU_CMD_PROCESS_VF_INFO;
+
+typedef enum VGPU_DEVICE_STATE_E
+{
+ NV_VGPU_DEV_UNUSED = 0,
+ NV_VGPU_DEV_OPENED = 1,
+ NV_VGPU_DEV_IN_USE = 2
+} VGPU_DEVICE_STATE;
+
+/*
+ * Function prototypes
+ */
+
+HYPERVISOR_TYPE NV_API_CALL nv_get_hypervisor_type(void);
+
+#endif // _NV_HYPERVISOR_H_
diff --git a/kernel/include/nv-vgpu-vfio-interface.h b/kernel/include/nv-vgpu-vfio-interface.h
new file mode 100644
index 0000000..2553722
--- /dev/null
+++ b/kernel/include/nv-vgpu-vfio-interface.h
@@ -0,0 +1,83 @@
+/* SPDX-FileCopyrightText: Copyright (c) 2016-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef _NV_VGPU_VFIO_INTERFACE_H_
+#define _NV_VGPU_VFIO_INTERFACE_H_
+
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/mdev.h>
+#include <linux/vfio.h>
+
+/* Use standard kernel types */
+typedef int NV_STATUS;
+typedef unsigned char NvU8;
+typedef unsigned int NvU32;
+typedef unsigned short NvU16;
+typedef unsigned long long NvU64;
+typedef int NvS32;
+typedef unsigned char NvBool;
+
+#define NV_OK 0
+#define NV_ERR_NOT_SUPPORTED -1
+#define NV_ERR_GENERIC -2
+#define NV_ERR_INVALID_ARGUMENT -3
+#define NV_ERR_NO_MEMORY -4
+
+typedef enum {
+ VGPU_DEVICE_STATE_UNINITIALIZED = 0,
+ VGPU_DEVICE_STATE_INITIALIZING,
+ VGPU_DEVICE_STATE_READY,
+ VGPU_DEVICE_STATE_RUNNING,
+ VGPU_DEVICE_STATE_SUSPENDED,
+ VGPU_DEVICE_STATE_ERROR,
+ VGPU_DEVICE_STATE_MIGRATING,
+} VGPU_DEVICE_STATE;
+
+typedef NV_STATUS (*vgpu_vfio_probe_t)(struct pci_dev *, NvU32, NvU32 *);
+typedef void (*vgpu_vfio_remove_t)(struct pci_dev *);
+typedef NV_STATUS (*vgpu_vfio_inject_interrupt_t)(void *);
+
+typedef struct
+{
+ vgpu_vfio_probe_t probe;
+ vgpu_vfio_remove_t remove;
+ vgpu_vfio_inject_interrupt_t inject_interrupt;
+} vgpu_vfio_ops_t;
+
+typedef NV_STATUS (*vgpu_vfio_vgpu_create_t) (struct pci_dev *dev, const NvU8 *uuid,
+ NvU32 vgpu_type_id, NvU16 *vgpu_id,
+ NvU32 *gpu_pci_id, NvU32 gpu_pci_bdf);
+typedef NV_STATUS (*vgpu_vfio_vgpu_destroy_t) (const NvU8 *uuid, NvU16 vgpu_id);
+typedef NV_STATUS (*vgpu_vfio_start_t) (const NvU8 *uuid, void *wait_queue, NvS32 *return_status,
+ NvU8 *vm_name, NvU32 qemu_pid);
+typedef NV_STATUS (*vgpu_vfio_bar_info_t) (struct pci_dev *dev, const NvU8 *, NvU64 *,
+ NvU32 bar_index, void *);
+typedef NV_STATUS (*vgpu_vfio_get_description) (struct pci_dev *, NvU32, char *);
+typedef NV_STATUS (*vgpu_vfio_get_name) (struct pci_dev *, NvU32, char *);
+typedef NV_STATUS (*vgpu_vfio_get_instance) (struct pci_dev *, NvU32, char *);
+typedef NV_STATUS (*vgpu_vfio_sparse_mmap_t) (struct pci_dev *, const NvU8 *, NvU64 **, NvU64 **, NvU32 *);
+typedef void (*vgpu_vfio_update_request_t) (const NvU8 *, NvU64 *, NvU64 *, VGPU_DEVICE_STATE, const char *);
+
+typedef struct
+{
+ const char *version_string;
+ vgpu_vfio_vgpu_create_t vgpu_create;
+ vgpu_vfio_vgpu_destroy_t vgpu_delete;
+ vgpu_vfio_bar_info_t vgpu_bar_info;
+ vgpu_vfio_start_t vgpu_start;
+ vgpu_vfio_get_description get_description;
+ vgpu_vfio_get_instance get_instances;
+ vgpu_vfio_get_name get_name;
+ vgpu_vfio_sparse_mmap_t get_sparse_mmap;
+ vgpu_vfio_update_request_t update_request;
+} rm_vgpu_vfio_ops_t;
+
+extern NV_STATUS nvidia_vgpu_vfio_set_ops(vgpu_vfio_ops_t *vgpu_vfio_ops);
+extern NV_STATUS nvidia_vgpu_vfio_get_ops(rm_vgpu_vfio_ops_t *ops);
+
+extern NV_STATUS nvidia_vgpu_vfio_probe(struct pci_dev *);
+extern void nvidia_vgpu_vfio_remove(struct pci_dev *, NvBool);
+
+#endif /* _NV_VGPU_VFIO_INTERFACE_H_ */ \ No newline at end of file
diff --git a/kernel/include/nvidia-vgpu-vfio.h b/kernel/include/nvidia-vgpu-vfio.h
new file mode 100644
index 0000000..7f778ee
--- /dev/null
+++ b/kernel/include/nvidia-vgpu-vfio.h
@@ -0,0 +1,194 @@
+#ifndef _NVIDIA_VGPU_VFIO_H_
+#define _NVIDIA_VGPU_VFIO_H_
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/vfio.h>
+#include <linux/mdev.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/atomic.h>
+
+#define OPEN_IOV_VFIO_VERSION "0.1.0"
+#define OPEN_IOV_VFIO_NAME "open-iov-vfio"
+
+#define NVIDIA_VGPU_VFIO_VERSION OPEN_IOV_VFIO_VERSION
+#define NVIDIA_VGPU_VFIO_NAME OPEN_IOV_VFIO_NAME
+
+#define NVIDIA_VGPU_MAX_VF_COUNT 32
+#define NVIDIA_VGPU_MAX_REGIONS 8
+#define NVIDIA_VGPU_MAX_IRQS 16
+
+#define NV_VGPU_MAX_REGIONS NVIDIA_VGPU_MAX_REGIONS
+#define NV_VGPU_MAX_IRQS NVIDIA_VGPU_MAX_IRQS
+
+#define NV_VGPU_BAR0_SIZE (16 * 1024 * 1024)
+#define NV_VGPU_BAR1_SIZE (256 * 1024 * 1024)
+#define NV_VGPU_BAR2_SIZE (32 * 1024 * 1024)
+#define NV_VGPU_BAR3_SIZE (32 * 1024 * 1024)
+
+enum nv_vgpu_type {
+ NV_VGPU_TYPE_UNKNOWN = 0,
+ NV_VGPU_TYPE_AMPERE,
+ NV_VGPU_TYPE_HOPPER,
+ NV_VGPU_TYPE_TURING,
+ NV_VGPU_TYPE_PASCAL,
+ NV_VGPU_TYPE_MAXWELL,
+ NV_VGPU_TYPE_KEPLER,
+};
+
+enum nv_vgpu_state {
+ NV_VGPU_STATE_UNINITIALIZED = 0,
+ NV_VGPU_STATE_INITIALIZING,
+ NV_VGPU_STATE_READY,
+ NV_VGPU_STATE_RUNNING,
+ NV_VGPU_STATE_SUSPENDED,
+ NV_VGPU_STATE_ERROR,
+ NV_VGPU_STATE_MIGRATING,
+};
+
+struct nv_vgpu_bar {
+ u64 gpa;
+ u64 size;
+ u64 iova;
+ void *vaddr;
+ bool mapped;
+ bool trapped;
+ struct mutex lock;
+};
+
+struct nv_vgpu_irq {
+ int virq;
+ int type;
+ bool masked;
+ struct eventfd_ctx *trigger;
+ struct irq_domain *domain;
+};
+
+struct nv_vgpu_region {
+ u64 offset;
+ u64 size;
+ u64 iova;
+ int fd;
+ void *vaddr;
+ bool readonly;
+};
+
+struct nv_vgpu_migration_ctx;
+
+struct nv_vgpu_migration_state {
+ u32 version;
+ u32 flags;
+ u64 timestamp;
+ u8 *data;
+ size_t data_size;
+ struct completion done;
+ int result;
+ struct nv_vgpu_migration_ctx *ctx;
+};
+
+struct nv_vgpu_device {
+ struct mdev_device *mdev;
+ struct pci_dev *pdev;
+ struct device dev;
+ struct mutex lock;
+ enum nv_vgpu_type type;
+ enum nv_vgpu_state state;
+ u32 vgpu_id;
+ u32 gpu_instance_id;
+ u32 compute_instance_id;
+
+ struct nv_vgpu_bar bars[NV_VGPU_MAX_REGIONS];
+ struct nv_vgpu_irq irqs[NV_VGPU_MAX_IRQS];
+ struct nv_vgpu_region regions[NV_VGPU_MAX_REGIONS];
+
+ struct nv_vgpu_migration_state migration;
+
+ struct work_struct init_work;
+ struct work_struct cleanup_work;
+
+ void *gsp_comm_ctx;
+ void *bar_emul_ctx;
+ void *mm_ctx;
+ void *irq_ctx;
+
+ u64 fb_base;
+ u64 fb_size;
+ u64 bar0_config_base;
+
+ struct list_head list;
+
+ bool dry_run;
+
+ /* PCI Config Space for VFIO mdev */
+ u8 pci_config[256];
+ bool config_space_dirty;
+};
+
+struct nv_vgpu_driver_data {
+ struct mutex lock;
+ struct list_head devices;
+ u32 device_count;
+ struct class *class;
+ struct bus_type *bus;
+};
+
+extern struct nv_vgpu_driver_data nv_vgpu_driver;
+
+int nv_vgpu_device_create(struct mdev_device *mdev);
+void nv_vgpu_device_destroy(struct mdev_device *mdev);
+int nv_vgpu_device_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_device_cleanup(struct nv_vgpu_device *vgpu);
+
+int nv_vgpu_bar_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_bar_cleanup(struct nv_vgpu_device *vgpu);
+int nv_vgpu_bar_trap(struct nv_vgpu_device *vgpu, int bar_idx);
+void nv_vgpu_bar_untrap(struct nv_vgpu_device *vgpu, int bar_idx);
+
+int nv_vgpu_irq_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_irq_cleanup(struct nv_vgpu_device *vgpu);
+int nv_vgpu_irq_enable(struct nv_vgpu_device *vgpu, int irq_idx);
+int nv_vgpu_irq_disable(struct nv_vgpu_device *vgpu, int irq_idx);
+
+int nv_vgpu_memory_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_memory_cleanup(struct nv_vgpu_device *vgpu);
+int nv_vgpu_memory_map(struct nv_vgpu_device *vgpu, u64 gpa, u64 size, u64 *iova);
+int nv_vgpu_memory_unmap(struct nv_vgpu_device *vgpu, u64 iova, u64 size);
+
+int nv_vgpu_gsp_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_gsp_cleanup(struct nv_vgpu_device *vgpu);
+int nv_vgpu_gsp_send_rpc(struct nv_vgpu_device *vgpu, void *msg, size_t size);
+int nv_vgpu_gsp_recv_rpc(struct nv_vgpu_device *vgpu, void *msg, size_t size, int timeout_ms);
+int nv_vgpu_gsp_load_firmware(struct nv_vgpu_device *vgpu);
+void nv_vgpu_gsp_release_firmware(struct nv_vgpu_device *vgpu);
+int nv_vgpu_gsp_boot(struct nv_vgpu_device *vgpu);
+
+int nv_vgpu_migration_init(struct nv_vgpu_device *vgpu);
+void nv_vgpu_migration_cleanup(struct nv_vgpu_device *vgpu);
+int nv_vgpu_migration_save_state(struct nv_vgpu_device *vgpu, u8 **data, size_t *size);
+int nv_vgpu_migration_restore_state(struct nv_vgpu_device *vgpu, u8 *data, size_t size);
+int nv_vgpu_migration_prepare(struct nv_vgpu_device *vgpu);
+int nv_vgpu_migration_complete(struct nv_vgpu_device *vgpu);
+int nv_vgpu_migration_cancel(struct nv_vgpu_device *vgpu);
+u8 *nv_vgpu_migration_get_data(struct nv_vgpu_device *vgpu, size_t *size);
+
+static inline struct nv_vgpu_device *mdev_to_vgpu(struct mdev_device *mdev)
+{
+ return dev_get_drvdata(&mdev->dev);
+}
+
+#define nv_vgpu_dbg(vgpu, fmt, ...) \
+ dev_dbg(&(vgpu)->dev, "%s: " fmt, __func__, ##__VA_ARGS__)
+
+#define nv_vgpu_info(vgpu, fmt, ...) \
+ dev_info(&(vgpu)->dev, "%s: " fmt, __func__, ##__VA_ARGS__)
+
+#define nv_vgpu_warn(vgpu, fmt, ...) \
+ dev_warn(&(vgpu)->dev, "%s: " fmt, __func__, ##__VA_ARGS__)
+
+#define nv_vgpu_err(vgpu, fmt, ...) \
+ dev_err(&(vgpu)->dev, "%s: " fmt, __func__, ##__VA_ARGS__)
+
+#endif \ No newline at end of file
diff --git a/kernel/include/nvstatus.h b/kernel/include/nvstatus.h
new file mode 100644
index 0000000..4f5284d
--- /dev/null
+++ b/kernel/include/nvstatus.h
@@ -0,0 +1,123 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 2014-2019 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SDK_NVSTATUS_H
+#define SDK_NVSTATUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "nvtypes.h"
+
+typedef NvU32 NV_STATUS;
+
+#define NV_STATUS_CODE( name, code, string ) name = (code),
+
+enum
+{
+ #include "nvstatuscodes.h"
+};
+
+#undef NV_STATUS_CODE
+
+/*!
+ * @def NV_STATUS_LEVEL_OK
+ * @see NV_STATUS_LEVEL
+ * @brief Success: No error or special condition
+ */
+#define NV_STATUS_LEVEL_OK 0
+
+/*!
+ * @def NV_STATUS_LEVEL_WARN
+ * @see NV_STATUS_LEVEL
+ * @brief Success, but there is an special condition
+ *
+ * @details In general, NV_STATUS_LEVEL_WARN status codes are handled the
+ * same as NV_STATUS_LEVEL_OK, but are usefil to indicate that
+ * there is a condition that may be specially handled.
+ *
+ * Therefore, in most cases, client function should test for
+ * status <= NV_STATUS_LEVEL_WARN or status > NV_STATUS_LEVEL_WARN
+ * to determine success v. failure of a call.
+ */
+#define NV_STATUS_LEVEL_WARN 1
+
+/*!
+ * @def NV_STATUS_LEVEL_ERR
+ * @see NV_STATUS_LEVEL
+ * @brief Unrecoverable error condition
+ */
+#define NV_STATUS_LEVEL_ERR 3
+
+/*!
+ * @def NV_STATUS_LEVEL
+ * @see NV_STATUS_LEVEL_OK
+ * @see NV_STATUS_LEVEL_WARN
+ * @see NV_STATUS_LEVEL_ERR
+ * @brief Level of the status code
+ *
+ * @warning IMPORTANT: When comparing NV_STATUS_LEVEL(_S) against one of
+ * these constants, it is important to use '<=' or '>' (rather
+ * than '<' or '>=').
+ *
+ * For example. do:
+ * if (NV_STATUS_LEVEL(status) <= NV_STATUS_LEVEL_WARN)
+ * rather than:
+ * if (NV_STATUS_LEVEL(status) < NV_STATUS_LEVEL_ERR)
+ *
+ * By being consistent in this manner, it is easier to systematically
+ * add additional level constants. New levels are likely to lower
+ * (rather than raise) the severity of _ERR codes. For example,
+ * if we were to add NV_STATUS_LEVEL_RETRY to indicate hardware
+ * failures that may be recoverable (e.g. RM_ERR_TIMEOUT_RETRY
+ * or RM_ERR_BUSY_RETRY), it would be less severe than
+ * NV_STATUS_LEVEL_ERR the level to which these status codes now
+ * belong. Using '<=' and '>' ensures your code is not broken in
+ * cases like this.
+ */
+#define NV_STATUS_LEVEL(_S) \
+ ((_S) == NV_OK? NV_STATUS_LEVEL_OK: \
+ ((_S) != NV_ERR_GENERIC && (_S) & 0x00010000? NV_STATUS_LEVEL_WARN: \
+ NV_STATUS_LEVEL_ERR))
+
+/*!
+ * @def NV_STATUS_LEVEL
+ * @see NV_STATUS_LEVEL_OK
+ * @see NV_STATUS_LEVEL_WARN
+ * @see NV_STATUS_LEVEL_ERR
+ * @brief Character representing status code level
+ */
+#define NV_STATUS_LEVEL_CHAR(_S) \
+ ((_S) == NV_OK? '0': \
+ ((_S) != NV_ERR_GENERIC && (_S) & 0x00010000? 'W': \
+ 'E'))
+
+// Function definitions
+const char *nvstatusToString(NV_STATUS nvStatusIn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SDK_NVSTATUS_H */
diff --git a/kernel/include/nvtypes.h b/kernel/include/nvtypes.h
new file mode 100644
index 0000000..7810d34
--- /dev/null
+++ b/kernel/include/nvtypes.h
@@ -0,0 +1,830 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 1993-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef NVTYPES_INCLUDED
+#define NVTYPES_INCLUDED
+
+#include "cpuopsys.h"
+
+#ifndef NVTYPES_USE_STDINT
+#define NVTYPES_USE_STDINT 0
+#endif
+
+#if NVTYPES_USE_STDINT
+#ifdef __cplusplus
+#include <cstdint>
+#include <cinttypes>
+#else
+#include <stdint.h>
+#include <inttypes.h>
+#endif // __cplusplus
+#endif // NVTYPES_USE_STDINT
+
+#ifndef __cplusplus
+// Header includes to make sure wchar_t is defined for C-file compilation
+// (C++ is not affected as it is a fundamental type there)
+// _MSC_VER is a hack to avoid failures for old setup of UEFI builds which are
+// currently set to msvc100 but do not properly set the include paths
+#if defined(NV_WINDOWS) && (!defined(_MSC_VER) || (_MSC_VER > 1600))
+#include <stddef.h>
+#define NV_HAS_WCHAR_T_TYPEDEF 1
+#endif
+#endif // __cplusplus
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(MAKE_NV64TYPES_8BYTES_ALIGNED) && defined(__i386__)
+// ensure or force 8-bytes alignment of NV 64-bit types
+#define OPTIONAL_ALIGN8_ATTR __attribute__((aligned(8)))
+#else
+// nothing needed
+#define OPTIONAL_ALIGN8_ATTR
+#endif // MAKE_NV64TYPES_8BYTES_ALIGNED && i386
+
+ /***************************************************************************\
+|* Typedefs *|
+ \***************************************************************************/
+
+#ifdef NV_MISRA_COMPLIANCE_REQUIRED
+//Typedefs for MISRA COMPLIANCE
+typedef unsigned long long UInt64;
+typedef signed long long Int64;
+typedef unsigned int UInt32;
+typedef signed int Int32;
+typedef unsigned short UInt16;
+typedef signed short Int16;
+typedef unsigned char UInt8 ;
+typedef signed char Int8 ;
+
+typedef void Void;
+typedef float float32_t;
+typedef double float64_t;
+#endif
+
+
+// Floating point types
+#ifdef NV_MISRA_COMPLIANCE_REQUIRED
+typedef float32_t NvF32; /* IEEE Single Precision (S1E8M23) */
+typedef float64_t NvF64 OPTIONAL_ALIGN8_ATTR; /* IEEE Double Precision (S1E11M52) */
+#else
+typedef float NvF32; /* IEEE Single Precision (S1E8M23) */
+typedef double NvF64 OPTIONAL_ALIGN8_ATTR; /* IEEE Double Precision (S1E11M52) */
+#endif
+
+
+// 8-bit: 'char' is the only 8-bit in the C89 standard and after.
+#if NVTYPES_USE_STDINT
+typedef uint8_t NvV8; /* "void": enumerated or multiple fields */
+typedef uint8_t NvU8; /* 0 to 255 */
+typedef int8_t NvS8; /* -128 to 127 */
+#else
+#ifdef NV_MISRA_COMPLIANCE_REQUIRED
+typedef UInt8 NvV8; /* "void": enumerated or multiple fields */
+typedef UInt8 NvU8; /* 0 to 255 */
+typedef Int8 NvS8; /* -128 to 127 */
+#else
+typedef unsigned char NvV8; /* "void": enumerated or multiple fields */
+typedef unsigned char NvU8; /* 0 to 255 */
+typedef signed char NvS8; /* -128 to 127 */
+#endif
+#endif // NVTYPES_USE_STDINT
+
+
+#if NVTYPES_USE_STDINT
+typedef uint16_t NvV16; /* "void": enumerated or multiple fields */
+typedef uint16_t NvU16; /* 0 to 65535 */
+typedef int16_t NvS16; /* -32768 to 32767 */
+#else
+// 16-bit: If the compiler tells us what we can use, then use it.
+#ifdef __INT16_TYPE__
+typedef unsigned __INT16_TYPE__ NvV16; /* "void": enumerated or multiple fields */
+typedef unsigned __INT16_TYPE__ NvU16; /* 0 to 65535 */
+typedef signed __INT16_TYPE__ NvS16; /* -32768 to 32767 */
+
+// The minimal standard for C89 and after
+#else // __INT16_TYPE__
+#ifdef NV_MISRA_COMPLIANCE_REQUIRED
+typedef UInt16 NvV16; /* "void": enumerated or multiple fields */
+typedef UInt16 NvU16; /* 0 to 65535 */
+typedef Int16 NvS16; /* -32768 to 32767 */
+#else
+typedef unsigned short NvV16; /* "void": enumerated or multiple fields */
+typedef unsigned short NvU16; /* 0 to 65535 */
+typedef signed short NvS16; /* -32768 to 32767 */
+#endif
+#endif // __INT16_TYPE__
+#endif // NVTYPES_USE_STDINT
+
+// wchar type (fixed size types consistent across Linux/Windows boundaries)
+#if defined(NV_HAS_WCHAR_T_TYPEDEF)
+ typedef wchar_t NvWchar;
+#else
+ typedef NvV16 NvWchar;
+#endif
+
+// Macro to build an NvU32 from four bytes, listed from msb to lsb
+#define NvU32_BUILD(a, b, c, d) \
+ ((NvU32)( \
+ (((NvU32)(a) & 0xff) << 24) | \
+ (((NvU32)(b) & 0xff) << 16) | \
+ (((NvU32)(c) & 0xff) << 8) | \
+ (((NvU32)(d) & 0xff))))
+
+// Macro to build an NvU64 from two DWORDS, listed from msb to lsb
+#define NvU64_BUILD(a, b) \
+ ((NvU64)( \
+ (((NvU64)(a) & ~0U) << 32) | \
+ (((NvU64)(b) & ~0U))))
+
+#if NVTYPES_USE_STDINT
+typedef uint32_t NvV32; /* "void": enumerated or multiple fields */
+typedef uint32_t NvU32; /* 0 to 4294967295 */
+typedef int32_t NvS32; /* -2147483648 to 2147483647 */
+#else
+// 32-bit: If the compiler tells us what we can use, then use it.
+#ifdef __INT32_TYPE__
+typedef unsigned __INT32_TYPE__ NvV32; /* "void": enumerated or multiple fields */
+typedef unsigned __INT32_TYPE__ NvU32; /* 0 to 4294967295 */
+typedef signed __INT32_TYPE__ NvS32; /* -2147483648 to 2147483647 */
+
+// Older compilers
+#else // __INT32_TYPE__
+
+// For historical reasons, NvU32/NvV32 are defined to different base intrinsic
+// types than NvS32 on some platforms.
+// Mainly for 64-bit linux, where long is 64 bits and win9x, where int is 16 bit.
+#if (defined(NV_UNIX) || defined(vxworks) || defined(NV_WINDOWS_CE) || \
+ defined(__arm) || defined(__IAR_SYSTEMS_ICC__) || defined(NV_QNX) || \
+ defined(NV_INTEGRITY) || defined(NV_MODS) || \
+ defined(__GNUC__) || defined(__clang__) || defined(NV_MACINTOSH_64)) && \
+ (!defined(NV_MACINTOSH) || defined(NV_MACINTOSH_64))
+#ifdef NV_MISRA_COMPLIANCE_REQUIRED
+typedef UInt32 NvV32; /* "void": enumerated or multiple fields */
+typedef UInt32 NvU32; /* 0 to 4294967295 */
+#else
+typedef unsigned int NvV32; /* "void": enumerated or multiple fields */
+typedef unsigned int NvU32; /* 0 to 4294967295 */
+#endif
+
+// The minimal standard for C89 and after
+#else // (defined(NV_UNIX) || defined(vxworks) || ...
+typedef unsigned long NvV32; /* "void": enumerated or multiple fields */
+typedef unsigned long NvU32; /* 0 to 4294967295 */
+#endif // (defined(NV_UNIX) || defined(vxworks) || ...
+
+// Mac OS 32-bit still needs this
+#if defined(NV_MACINTOSH) && !defined(NV_MACINTOSH_64)
+typedef signed long NvS32; /* -2147483648 to 2147483647 */
+#else
+#ifdef NV_MISRA_COMPLIANCE_REQUIRED
+typedef Int32 NvS32; /* -2147483648 to 2147483647 */
+#else
+typedef signed int NvS32; /* -2147483648 to 2147483647 */
+#endif
+#endif // defined(NV_MACINTOSH) && !defined(NV_MACINTOSH_64)
+#endif // __INT32_TYPE__
+#endif // NVTYPES_USE_STDINT
+
+
+
+#if NVTYPES_USE_STDINT
+typedef uint64_t NvU64 OPTIONAL_ALIGN8_ATTR; /* 0 to 18446744073709551615 */
+typedef int64_t NvS64 OPTIONAL_ALIGN8_ATTR; /* -9223372036854775808 to 9223372036854775807 */
+
+#define NvU64_fmtX PRIX64
+#define NvU64_fmtx PRIx64
+#define NvU64_fmtu PRIu64
+#define NvU64_fmto PRIo64
+#define NvS64_fmtd PRId64
+#define NvS64_fmti PRIi64
+#else
+// 64-bit types for compilers that support them, plus some obsolete variants
+#if defined(__GNUC__) || defined(__clang__) || defined(__arm) || \
+ defined(__IAR_SYSTEMS_ICC__) || defined(__ghs__) || defined(_WIN64) || \
+ defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined (__xlC__)
+#ifdef NV_MISRA_COMPLIANCE_REQUIRED
+typedef UInt64 NvU64 OPTIONAL_ALIGN8_ATTR; /* 0 to 18446744073709551615 */
+typedef Int64 NvS64 OPTIONAL_ALIGN8_ATTR; /* -9223372036854775808 to 9223372036854775807 */
+#else
+typedef unsigned long long NvU64 OPTIONAL_ALIGN8_ATTR; /* 0 to 18446744073709551615 */
+typedef long long NvS64 OPTIONAL_ALIGN8_ATTR; /* -9223372036854775808 to 9223372036854775807 */
+#endif
+
+#define NvU64_fmtX "llX"
+#define NvU64_fmtx "llx"
+#define NvU64_fmtu "llu"
+#define NvU64_fmto "llo"
+#define NvS64_fmtd "lld"
+#define NvS64_fmti "lli"
+
+// Microsoft since 2003 -- https://msdn.microsoft.com/en-us/library/29dh1w7z.aspx
+#else
+typedef unsigned __int64 NvU64 OPTIONAL_ALIGN8_ATTR; /* 0 to 18446744073709551615 */
+typedef __int64 NvS64 OPTIONAL_ALIGN8_ATTR; /* -9223372036854775808 to 9223372036854775807 */
+
+#define NvU64_fmtX "I64X"
+#define NvU64_fmtx "I64x"
+#define NvU64_fmtu "I64u"
+#define NvU64_fmto "I64o"
+#define NvS64_fmtd "I64d"
+#define NvS64_fmti "I64i"
+
+#endif
+#endif // NVTYPES_USE_STDINT
+
+#ifdef NV_TYPESAFE_HANDLES
+/*
+ * Can't use opaque pointer as clients might be compiled with mismatched
+ * pointer sizes. TYPESAFE check will eventually be removed once all clients
+ * have transistioned safely to NvHandle.
+ * The plan is to then eventually scale up the handle to be 64-bits.
+ */
+typedef struct
+{
+ NvU32 val;
+} NvHandle;
+#else
+/*
+ * For compatibility with modules that haven't moved typesafe handles.
+ */
+typedef NvU32 NvHandle;
+#endif // NV_TYPESAFE_HANDLES
+
+/* Boolean type */
+typedef NvU8 NvBool;
+#define NV_TRUE ((NvBool)(0 == 0))
+#define NV_FALSE ((NvBool)(0 != 0))
+
+/* Tristate type: NV_TRISTATE_FALSE, NV_TRISTATE_TRUE, NV_TRISTATE_INDETERMINATE */
+typedef NvU8 NvTristate;
+#define NV_TRISTATE_FALSE ((NvTristate) 0)
+#define NV_TRISTATE_TRUE ((NvTristate) 1)
+#define NV_TRISTATE_INDETERMINATE ((NvTristate) 2)
+
+/* Macros to extract the low and high parts of a 64-bit unsigned integer */
+/* Also designed to work if someone happens to pass in a 32-bit integer */
+#ifdef NV_MISRA_COMPLIANCE_REQUIRED
+#define NvU64_HI32(n) ((NvU32)((((NvU64)(n)) >> 32) & 0xffffffffU))
+#define NvU64_LO32(n) ((NvU32)(( (NvU64)(n)) & 0xffffffffU))
+#else
+#define NvU64_HI32(n) ((NvU32)((((NvU64)(n)) >> 32) & 0xffffffff))
+#define NvU64_LO32(n) ((NvU32)(( (NvU64)(n)) & 0xffffffff))
+#endif
+#define NvU40_HI32(n) ((NvU32)((((NvU64)(n)) >> 8) & 0xffffffffU))
+#define NvU40_HI24of32(n) ((NvU32)( (NvU64)(n) & 0xffffff00U))
+
+/* Macros to get the MSB and LSB of a 32 bit unsigned number */
+#define NvU32_HI16(n) ((NvU16)((((NvU32)(n)) >> 16) & 0xffffU))
+#define NvU32_LO16(n) ((NvU16)(( (NvU32)(n)) & 0xffffU))
+
+ /***************************************************************************\
+|* *|
+|* 64 bit type definitions for use in interface structures. *|
+|* *|
+ \***************************************************************************/
+
+#if defined(NV_64_BITS)
+
+typedef void* NvP64; /* 64 bit void pointer */
+typedef NvU64 NvUPtr; /* pointer sized unsigned int */
+typedef NvS64 NvSPtr; /* pointer sized signed int */
+typedef NvU64 NvLength; /* length to agree with sizeof */
+
+#define NvP64_VALUE(n) (n)
+#define NvP64_fmt "%p"
+
+#define KERNEL_POINTER_FROM_NvP64(p,v) ((p)(v))
+#define NvP64_PLUS_OFFSET(p,o) (NvP64)((NvU64)(p) + (NvU64)(o))
+
+#define NvUPtr_fmtX NvU64_fmtX
+#define NvUPtr_fmtx NvU64_fmtx
+#define NvUPtr_fmtu NvU64_fmtu
+#define NvUPtr_fmto NvU64_fmto
+#define NvSPtr_fmtd NvS64_fmtd
+#define NvSPtr_fmti NvS64_fmti
+
+#else
+
+typedef NvU64 NvP64; /* 64 bit void pointer */
+typedef NvU32 NvUPtr; /* pointer sized unsigned int */
+typedef NvS32 NvSPtr; /* pointer sized signed int */
+typedef NvU32 NvLength; /* length to agree with sizeof */
+
+#define NvP64_VALUE(n) ((void *)(NvUPtr)(n))
+#define NvP64_fmt "0x%llx"
+
+#define KERNEL_POINTER_FROM_NvP64(p,v) ((p)(NvUPtr)(v))
+#define NvP64_PLUS_OFFSET(p,o) ((p) + (NvU64)(o))
+
+#define NvUPtr_fmtX "X"
+#define NvUPtr_fmtx "x"
+#define NvUPtr_fmtu "u"
+#define NvUPtr_fmto "o"
+#define NvSPtr_fmtd "d"
+#define NvSPtr_fmti "i"
+
+#endif
+
+#define NvP64_NULL (NvP64)0
+
+/*!
+ * Helper macro to pack an @ref NvU64_ALIGN32 structure from a @ref NvU64.
+ *
+ * @param[out] pDst Pointer to NvU64_ALIGN32 structure to pack
+ * @param[in] pSrc Pointer to NvU64 with which to pack
+ */
+#define NvU64_ALIGN32_PACK(pDst, pSrc) \
+do { \
+ (pDst)->lo = NvU64_LO32(*(pSrc)); \
+ (pDst)->hi = NvU64_HI32(*(pSrc)); \
+} while (NV_FALSE)
+
+/*!
+ * Helper macro to unpack a @ref NvU64_ALIGN32 structure into a @ref NvU64.
+ *
+ * @param[out] pDst Pointer to NvU64 in which to unpack
+ * @param[in] pSrc Pointer to NvU64_ALIGN32 structure from which to unpack
+ */
+#define NvU64_ALIGN32_UNPACK(pDst, pSrc) \
+do { \
+ (*(pDst)) = NvU64_ALIGN32_VAL(pSrc); \
+} while (NV_FALSE)
+
+/*!
+ * Helper macro to unpack a @ref NvU64_ALIGN32 structure as a @ref NvU64.
+ *
+ * @param[in] pSrc Pointer to NvU64_ALIGN32 structure to unpack
+ */
+#define NvU64_ALIGN32_VAL(pSrc) \
+ ((NvU64) ((NvU64)((pSrc)->lo) | (((NvU64)(pSrc)->hi) << 32U)))
+
+/*!
+ * Helper macro to check whether the 32 bit aligned 64 bit number is zero.
+ *
+ * @param[in] _pU64 Pointer to NvU64_ALIGN32 structure.
+ *
+ * @return
+ * NV_TRUE _pU64 is zero.
+ * NV_FALSE otherwise.
+ */
+#define NvU64_ALIGN32_IS_ZERO(_pU64) \
+ (((_pU64)->lo == 0U) && ((_pU64)->hi == 0U))
+
+/*!
+ * Helper macro to sub two 32 aligned 64 bit numbers on 64 bit processor.
+ *
+ * @param[in] pSrc1 Pointer to NvU64_ALIGN32 source 1 structure.
+ * @param[in] pSrc2 Pointer to NvU64_ALIGN32 source 2 structure.
+ * @param[in/out] pDst Pointer to NvU64_ALIGN32 dest. structure.
+ */
+#define NvU64_ALIGN32_ADD(pDst, pSrc1, pSrc2) \
+do { \
+ NvU64 __dst, __src1, __scr2; \
+ \
+ NvU64_ALIGN32_UNPACK(&__src1, (pSrc1)); \
+ NvU64_ALIGN32_UNPACK(&__scr2, (pSrc2)); \
+ __dst = __src1 + __scr2; \
+ NvU64_ALIGN32_PACK((pDst), &__dst); \
+} while (NV_FALSE)
+
+/*!
+ * Helper macro to sub two 32 aligned 64 bit numbers on 64 bit processor.
+ *
+ * @param[in] pSrc1 Pointer to NvU64_ALIGN32 source 1 structure.
+ * @param[in] pSrc2 Pointer to NvU64_ALIGN32 source 2 structure.
+ * @param[in/out] pDst Pointer to NvU64_ALIGN32 dest. structure.
+ */
+#define NvU64_ALIGN32_SUB(pDst, pSrc1, pSrc2) \
+do { \
+ NvU64 __dst, __src1, __scr2; \
+ \
+ NvU64_ALIGN32_UNPACK(&__src1, (pSrc1)); \
+ NvU64_ALIGN32_UNPACK(&__scr2, (pSrc2)); \
+ __dst = __src1 - __scr2; \
+ NvU64_ALIGN32_PACK((pDst), &__dst); \
+} while (NV_FALSE)
+
+/*!
+ * Structure for representing 32 bit aligned NvU64 (64-bit unsigned integer)
+ * structures. This structure must be used because the 32 bit processor and
+ * 64 bit processor compilers will pack/align NvU64 differently.
+ *
+ * One use case is RM being 64 bit proc whereas PMU being 32 bit proc, this
+ * alignment difference will result in corrupted transactions between the RM
+ * and PMU.
+ *
+ * See the @ref NvU64_ALIGN32_PACK and @ref NvU64_ALIGN32_UNPACK macros for
+ * packing and unpacking these structures.
+ *
+ * @note The intention of this structure is to provide a datatype which will
+ * packed/aligned consistently and efficiently across all platforms.
+ * We don't want to use "NV_DECLARE_ALIGNED(NvU64, 8)" because that
+ * leads to memory waste on our 32-bit uprocessors (e.g. FALCONs) where
+ * DMEM efficiency is vital.
+ */
+typedef struct
+{
+ /*!
+ * Low 32 bits.
+ */
+ NvU32 lo;
+ /*!
+ * High 32 bits.
+ */
+ NvU32 hi;
+} NvU64_ALIGN32;
+
+/* Useful macro to hide required double cast */
+#define NV_PTR_TO_NvP64(n) (NvP64)(NvUPtr)(n)
+#define NV_SIGN_EXT_PTR_TO_NvP64(p) ((NvP64)(NvS64)(NvSPtr)(p))
+#define KERNEL_POINTER_TO_NvP64(p) ((NvP64)(uintptr_t)(p))
+
+ /***************************************************************************\
+|* *|
+|* Limits for common types. *|
+|* *|
+ \***************************************************************************/
+
+/* Explanation of the current form of these limits:
+ *
+ * - Decimal is used, as hex values are by default positive.
+ * - Casts are not used, as usage in the preprocessor itself (#if) ends poorly.
+ * - The subtraction of 1 for some MIN values is used to get around the fact
+ * that the C syntax actually treats -x as NEGATE(x) instead of a distinct
+ * number. Since 214748648 isn't a valid positive 32-bit signed value, we
+ * take the largest valid positive signed number, negate it, and subtract 1.
+ */
+#define NV_S8_MIN (-128)
+#define NV_S8_MAX (+127)
+#define NV_U8_MIN (0U)
+#define NV_U8_MAX (+255U)
+#define NV_S16_MIN (-32768)
+#define NV_S16_MAX (+32767)
+#define NV_U16_MIN (0U)
+#define NV_U16_MAX (+65535U)
+#define NV_S32_MIN (-2147483647 - 1)
+#define NV_S32_MAX (+2147483647)
+#define NV_U32_MIN (0U)
+#define NV_U32_MAX (+4294967295U)
+#define NV_S64_MIN (-9223372036854775807LL - 1LL)
+#define NV_S64_MAX (+9223372036854775807LL)
+#define NV_U64_MIN (0ULL)
+#define NV_U64_MAX (+18446744073709551615ULL)
+
+/* Aligns fields in structs so they match up between 32 and 64 bit builds */
+#if defined(__GNUC__) || defined(__clang__) || defined(NV_QNX)
+#define NV_ALIGN_BYTES(size) __attribute__ ((aligned (size)))
+#elif defined(__arm)
+#define NV_ALIGN_BYTES(size) __align(ALIGN)
+#else
+// XXX This is dangerously nonportable! We really shouldn't provide a default
+// version of this that doesn't do anything.
+#define NV_ALIGN_BYTES(size)
+#endif
+
+// NV_DECLARE_ALIGNED() can be used on all platforms.
+// This macro form accounts for the fact that __declspec on Windows is required
+// before the variable type,
+// and NV_ALIGN_BYTES is required after the variable name.
+#if defined(__GNUC__) || defined(__clang__) || defined(NV_QNX)
+#define NV_DECLARE_ALIGNED(TYPE_VAR, ALIGN) TYPE_VAR __attribute__ ((aligned (ALIGN)))
+#elif defined(_MSC_VER)
+#define NV_DECLARE_ALIGNED(TYPE_VAR, ALIGN) __declspec(align(ALIGN)) TYPE_VAR
+#elif defined(__arm)
+#define NV_DECLARE_ALIGNED(TYPE_VAR, ALIGN) __align(ALIGN) TYPE_VAR
+#endif
+
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+#define NV_ALIGNOF(type) _Alignof(type)
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+#define NV_ALIGNOF(type) alignof(type)
+#elif defined(__GNUC__) || defined(__clang__)
+#define NV_ALIGNOF(type) __alignof__(type)
+#elif defined(_MSC_VER)
+#define NV_ALIGNOF(type) __alignof(type)
+#endif
+
+ /***************************************************************************\
+|* Function Declaration Types *|
+ \***************************************************************************/
+
+// stretching the meaning of "nvtypes", but this seems to least offensive
+// place to re-locate these from nvos.h which cannot be included by a number
+// of builds that need them
+
+#if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER)
+ #define NV_ATTRIBUTE_UNUSED __attribute__((__unused__))
+#else
+ #define NV_ATTRIBUTE_UNUSED
+#endif
+
+#if defined(_MSC_VER)
+
+ #if _MSC_VER >= 1310
+ #define NV_NOINLINE __declspec(noinline)
+ #else
+ #define NV_NOINLINE
+ #endif
+
+ #define NV_INLINE __inline
+
+ #if _MSC_VER >= 1200
+ #define NV_FORCEINLINE __forceinline
+ #else
+ #define NV_FORCEINLINE __inline
+ #endif
+
+ #define NV_APIENTRY __stdcall
+ #define NV_FASTCALL __fastcall
+ #define NV_CDECLCALL __cdecl
+ #define NV_STDCALL __stdcall
+
+ #define NV_FORCERESULTCHECK
+
+ #define NV_FORMAT_PRINTF(_f, _a)
+
+#else // ! defined(_MSC_VER)
+
+ #if defined(__GNUC__)
+ #if (__GNUC__ > 3) || \
+ ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) && (__GNUC_PATCHLEVEL__ >= 1))
+ #define NV_NOINLINE __attribute__((__noinline__))
+ #endif
+ #elif defined(__clang__)
+ #if __has_attribute(noinline)
+ #define NV_NOINLINE __attribute__((__noinline__))
+ #endif
+ #elif defined(__arm) && (__ARMCC_VERSION >= 300000)
+ #define NV_NOINLINE __attribute__((__noinline__))
+ #elif (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) ||\
+ (defined(__SUNPRO_CC) && (__SUNPRO_CC >= 0x590))
+ #define NV_NOINLINE __attribute__((__noinline__))
+ #elif defined (__INTEL_COMPILER)
+ #define NV_NOINLINE __attribute__((__noinline__))
+ #endif
+
+ #if !defined(NV_NOINLINE)
+ #define NV_NOINLINE
+ #endif
+
+ /* GreenHills compiler defines __GNUC__, but doesn't support
+ * __inline__ keyword. */
+ #if defined(__ghs__)
+ #define NV_INLINE inline
+ #elif defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER)
+ #define NV_INLINE __inline__
+ #elif defined (macintosh) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+ #define NV_INLINE inline
+ #elif defined(__arm)
+ #define NV_INLINE __inline
+ #else
+ #define NV_INLINE
+ #endif
+
+ /* Don't force inline on DEBUG builds -- it's annoying for debuggers. */
+ #if !defined(DEBUG)
+ /* GreenHills compiler defines __GNUC__, but doesn't support
+ * __attribute__ or __inline__ keyword. */
+ #if defined(__ghs__)
+ #define NV_FORCEINLINE inline
+ #elif defined(__GNUC__)
+ // GCC 3.1 and beyond support the always_inline function attribute.
+ #if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))
+ #define NV_FORCEINLINE __attribute__((__always_inline__)) __inline__
+ #else
+ #define NV_FORCEINLINE __inline__
+ #endif
+ #elif defined(__clang__)
+ #if __has_attribute(always_inline)
+ #define NV_FORCEINLINE __attribute__((__always_inline__)) __inline__
+ #else
+ #define NV_FORCEINLINE __inline__
+ #endif
+ #elif defined(__arm) && (__ARMCC_VERSION >= 220000)
+ // RVDS 2.2 also supports forceinline, but ADS 1.2 does not
+ #define NV_FORCEINLINE __forceinline
+ #else /* defined(__GNUC__) */
+ #define NV_FORCEINLINE NV_INLINE
+ #endif
+ #else
+ #define NV_FORCEINLINE NV_INLINE
+ #endif
+
+ #define NV_APIENTRY
+ #define NV_FASTCALL
+ #define NV_CDECLCALL
+ #define NV_STDCALL
+
+ /*
+ * The 'warn_unused_result' function attribute prompts GCC to issue a
+ * warning if the result of a function tagged with this attribute
+ * is ignored by a caller. In combination with '-Werror', it can be
+ * used to enforce result checking in RM code; at this point, this
+ * is only done on UNIX.
+ */
+ #if defined(__GNUC__) && defined(NV_UNIX)
+ #if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))
+ #define NV_FORCERESULTCHECK __attribute__((__warn_unused_result__))
+ #else
+ #define NV_FORCERESULTCHECK
+ #endif
+ #elif defined(__clang__)
+ #if __has_attribute(warn_unused_result)
+ #define NV_FORCERESULTCHECK __attribute__((__warn_unused_result__))
+ #else
+ #define NV_FORCERESULTCHECK
+ #endif
+ #else /* defined(__GNUC__) */
+ #define NV_FORCERESULTCHECK
+ #endif
+
+ /*
+ * Functions decorated with NV_FORMAT_PRINTF(f, a) have a format string at
+ * parameter number 'f' and variadic arguments start at parameter number 'a'.
+ * (Note that for C++ methods, there is an implicit 'this' parameter so
+ * explicit parameters are numbered from 2.)
+ */
+ #if defined(__GNUC__)
+ #define NV_FORMAT_PRINTF(_f, _a) __attribute__((format(printf, _f, _a)))
+ #else
+ #define NV_FORMAT_PRINTF(_f, _a)
+ #endif
+
+#endif // defined(_MSC_VER)
+
+ /***************************************************************************\
+|* *|
+|* Definitions of binary/bytes representations of floating point values *|
+|* *|
+|* Intended for use when passing values across kernel contexts in which *|
+|* floating point use is forbidden *|
+|* *|
+ \***************************************************************************/
+//
+// Determine whether we have to turn FP32 support off based on compiler flags.
+// For now, this is only done when we can detect that we're compiling for x86_64
+// with GCC/clang and SSE is disabled.
+//
+// This is also currently only used to control NvF32Bytes definitions.
+//
+// In the future, it could be possible to:
+// 1.) Define this more generally
+// and
+// 2.) Use it in more places
+//
+// This would allow us to fully prevent usage of NvF32 in codebases where it is
+// not supported.
+//
+#if !defined(NVTYPES_NV_F32_SUPPORTED) && NVCPU_IS_X86_64 && (defined(__GNUC__) || defined(__clang__)) && !defined(__SSE__)
+#define NVTYPES_NV_F32_SUPPORTED 0
+#else
+#define NVTYPES_NV_F32_SUPPORTED 1
+#endif
+
+/*!
+ * A struct containing the 32-bit representation of an (IEEE-754 binary32)
+ * floating point value, i.e., the raw underlying bytes of an @ref NvF32
+ *
+ * This struct must be used in places where a value may be passed across a
+ * kernel boundary, as use of floating point within some kernels is, in general,
+ * banned.
+ */
+typedef struct
+{
+ NvU32 value;
+} NvF32Bytes;
+
+#if !defined(NVDEC_1_0)
+/*!
+ * @brief Maps the bytes of a given @ref NvF32 to an @ref NvF32Bytes
+ *
+ * @param[in] pfValue
+ * Pointer to value to convert
+ *
+ * @return @ref NvF32Bytes byte-mapped from @p *pfValue
+ */
+static NV_FORCEINLINE NvF32Bytes
+nvF32BytesMapFromNvF32
+(
+ const NvF32 *pfValue
+)
+{
+ union
+ {
+ NvF32 f;
+ NvF32Bytes b;
+ } value;
+
+ value.f = *pfValue;
+
+ return value.b;
+}
+
+#if NVTYPES_NV_F32_SUPPORTED
+/*!
+ * @brief Maps the bytes of a given @ref NvF32Bytes to an @ref NvF32
+ *
+ * @param[in] pfValue
+ * Pointer to value to convert
+ *
+ * @return @ref NvF32 byte-mapped from @p *pfValue
+ */
+static NV_FORCEINLINE NvF32
+nvF32BytesMapToNvF32
+(
+ const NvF32Bytes *pfValue
+)
+{
+ union
+ {
+ NvF32Bytes b;
+ NvF32 f;
+ } value;
+
+ value.b = *pfValue;
+
+ return value.f;
+}
+#endif // NVTYPES_NV_F32_SUPPORTED
+
+/*!
+ * @brief Maps the bytes of a given @ref NvU32 to an @ref NvF32Bytes
+ *
+ * @details This function is intended for first "moving" raw byte values into
+ * the "floating point domain."
+ *
+ * @param[in] pfValue
+ * Pointer to value to convert
+ *
+ * @return @ref NvF32Bytes byte-mapped from @p *pfValue
+ */
+static NV_FORCEINLINE NvF32Bytes
+nvF32BytesMapFromNvU32
+(
+ const NvU32 *pfValue
+)
+{
+ union
+ {
+ NvU32 u;
+ NvF32Bytes b;
+ } value;
+
+ value.u = *pfValue;
+
+ return value.b;
+}
+
+/*!
+ * @brief Maps the bytes of a given @ref NvF32Bytes to an @ref NvU32
+ *
+ * @param[in] pfValue
+ * Pointer to value to convert
+ *
+ * @return @ref NvU32 byte-mapped from @p *pfValue
+ */
+static NV_FORCEINLINE NvU32
+nvF32BytesMapToNvU32
+(
+ const NvF32Bytes *pfValue
+)
+{
+ union
+ {
+ NvF32Bytes b;
+ NvU32 u;
+ } value;
+
+ value.b = *pfValue;
+
+ return value.u;
+}
+#endif // !defined(NVDEC_1_0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NVTYPES_INCLUDED */
diff --git a/kernel/ioctl.c b/kernel/ioctl.c
new file mode 100644
index 0000000..571b209
--- /dev/null
+++ b/kernel/ioctl.c
@@ -0,0 +1,263 @@
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "ioctl.h"
+#include "bar_emulation.h"
+#include "gsp_comm.h"
+#include "memory.h"
+#include "irq.h"
+#include "migration.h"
+
+int nv_vgpu_ioctl_get_info(struct nv_vgpu_device *vgpu, void __user *arg)
+{
+ struct nv_vgpu_ioctl_get_info info;
+ int i;
+
+ memset(&info, 0, sizeof(info));
+
+ mutex_lock(&vgpu->lock);
+ info.vgpu_type = vgpu->type;
+ info.vgpu_id = vgpu->vgpu_id;
+ info.gpu_instance_id = vgpu->gpu_instance_id;
+ info.compute_instance_id = vgpu->compute_instance_id;
+ info.state = vgpu->state;
+ info.fb_base = vgpu->fb_base;
+ info.fb_size = vgpu->fb_size;
+ info.bar0_config_base = vgpu->bar0_config_base;
+ info.num_bars = 0;
+
+ for (i = 0; i < NV_VGPU_MAX_REGIONS; i++) {
+ if (vgpu->bars[i].size > 0) {
+ info.bars[info.num_bars].index = i;
+ info.bars[info.num_bars].gpa = vgpu->bars[i].gpa;
+ info.bars[info.num_bars].size = vgpu->bars[i].size;
+ info.bars[info.num_bars].iova = vgpu->bars[i].iova;
+ info.bars[info.num_bars].mapped = vgpu->bars[i].mapped;
+ info.bars[info.num_bars].trapped = vgpu->bars[i].trapped;
+ info.num_bars++;
+ }
+ }
+ mutex_unlock(&vgpu->lock);
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+int nv_vgpu_ioctl_map_bar(struct nv_vgpu_device *vgpu, void __user *arg)
+{
+ struct nv_vgpu_ioctl_map_bar params;
+ int ret;
+
+ if (copy_from_user(&params, arg, sizeof(params)))
+ return -EFAULT;
+
+ if (params.bar_index >= NV_VGPU_MAX_REGIONS)
+ return -EINVAL;
+
+ mutex_lock(&vgpu->lock);
+
+ ret = nv_vgpu_memory_pin(vgpu, params.gpa, params.size);
+ if (ret) {
+ mutex_unlock(&vgpu->lock);
+ return ret;
+ }
+
+ ret = nv_vgpu_memory_map(vgpu, params.gpa, params.size, &params.iova);
+ if (ret) {
+ nv_vgpu_memory_unpin(vgpu, params.gpa, params.size);
+ mutex_unlock(&vgpu->lock);
+ return ret;
+ }
+
+ vgpu->bars[params.bar_index].gpa = params.gpa;
+ vgpu->bars[params.bar_index].size = params.size;
+ vgpu->bars[params.bar_index].iova = params.iova;
+ vgpu->bars[params.bar_index].mapped = true;
+
+ if (params.flags & 0x1) {
+ ret = nv_vgpu_bar_trap(vgpu, params.bar_index);
+ if (ret)
+ nv_vgpu_warn(vgpu, "Failed to trap BAR %d: %d", params.bar_index, ret);
+ else
+ vgpu->bars[params.bar_index].trapped = true;
+ }
+
+ mutex_unlock(&vgpu->lock);
+
+ if (copy_to_user(arg, &params, sizeof(params)))
+ return -EFAULT;
+
+ return 0;
+}
+
+int nv_vgpu_ioctl_unmap_bar(struct nv_vgpu_device *vgpu, void __user *arg)
+{
+ struct nv_vgpu_ioctl_unmap_bar params;
+ int ret;
+
+ if (copy_from_user(&params, arg, sizeof(params)))
+ return -EFAULT;
+
+ if (params.bar_index >= NV_VGPU_MAX_REGIONS)
+ return -EINVAL;
+
+ mutex_lock(&vgpu->lock);
+
+ if (vgpu->bars[params.bar_index].trapped) {
+ nv_vgpu_bar_untrap(vgpu, params.bar_index);
+ vgpu->bars[params.bar_index].trapped = false;
+ }
+
+ ret = nv_vgpu_memory_unmap(vgpu, params.iova, params.size);
+ if (ret) {
+ mutex_unlock(&vgpu->lock);
+ return ret;
+ }
+
+ nv_vgpu_memory_unpin(vgpu, vgpu->bars[params.bar_index].gpa, params.size);
+
+ vgpu->bars[params.bar_index].mapped = false;
+ vgpu->bars[params.bar_index].iova = 0;
+
+ mutex_unlock(&vgpu->lock);
+ return 0;
+}
+
+int nv_vgpu_ioctl_enable_irq(struct nv_vgpu_device *vgpu, void __user *arg)
+{
+ struct nv_vgpu_ioctl_irq params;
+ int ret;
+
+ if (copy_from_user(&params, arg, sizeof(params)))
+ return -EFAULT;
+
+ ret = nv_vgpu_irq_enable(vgpu, params.irq_index);
+ if (ret)
+ return ret;
+
+ if (params.fd >= 0) {
+ ret = nv_vgpu_irq_set_trigger(vgpu, params.irq_index, params.fd);
+ if (ret)
+ nv_vgpu_irq_disable(vgpu, params.irq_index);
+ }
+
+ return ret;
+}
+
+int nv_vgpu_ioctl_disable_irq(struct nv_vgpu_device *vgpu, void __user *arg)
+{
+ struct nv_vgpu_ioctl_irq params;
+ int ret;
+
+ if (copy_from_user(&params, arg, sizeof(params)))
+ return -EFAULT;
+
+ ret = nv_vgpu_irq_set_trigger(vgpu, params.irq_index, -1);
+ if (ret)
+ return ret;
+
+ return nv_vgpu_irq_disable(vgpu, params.irq_index);
+}
+
+int nv_vgpu_ioctl_gsp_rpc(struct nv_vgpu_device *vgpu, void __user *arg)
+{
+ struct nv_vgpu_ioctl_gsp_rpc params;
+ void *data = NULL;
+ int ret = 0;
+
+ if (copy_from_user(&params, arg, sizeof(params)))
+ return -EFAULT;
+
+ if (params.data_size > 0 && params.data_size <= NV_VGPU_GSP_RPC_MAX_SIZE) {
+ data = kmalloc(params.data_size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (copy_from_user(data, (void __user *)params.data_ptr, params.data_size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+ }
+
+ ret = nv_vgpu_gsp_send_rpc(vgpu, data, params.data_size);
+ params.result = ret;
+
+ if (data)
+ kfree(data);
+
+ if (copy_to_user(arg, &params, sizeof(params)))
+ return -EFAULT;
+
+ return 0;
+}
+
+int nv_vgpu_ioctl_migrate_save(struct nv_vgpu_device *vgpu, void __user *arg)
+{
+ struct nv_vgpu_ioctl_migrate params;
+ u8 *data = NULL;
+ size_t size = 0;
+ int ret;
+
+ if (copy_from_user(&params, arg, sizeof(params)))
+ return -EFAULT;
+
+ ret = nv_vgpu_migration_prepare(vgpu);
+ if (ret)
+ return ret;
+
+ ret = nv_vgpu_migration_complete(vgpu);
+ if (ret)
+ return ret;
+
+ data = nv_vgpu_migration_get_data(vgpu, &size);
+
+ if (params.data_ptr && params.data_size >= size) {
+ if (copy_to_user((void __user *)params.data_ptr, data, size)) {
+ ret = -EFAULT;
+ }
+ }
+
+ params.data_size = size;
+ params.result = ret;
+
+ if (copy_to_user(arg, &params, sizeof(params)))
+ return -EFAULT;
+
+ return ret;
+}
+
+int nv_vgpu_ioctl_migrate_restore(struct nv_vgpu_device *vgpu, void __user *arg)
+{
+ struct nv_vgpu_ioctl_migrate params;
+ u8 *data = NULL;
+ int ret;
+
+ if (copy_from_user(&params, arg, sizeof(params)))
+ return -EFAULT;
+
+ if (!params.data_ptr || params.data_size == 0)
+ return -EINVAL;
+
+ data = kmalloc(params.data_size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (copy_from_user(data, (void __user *)params.data_ptr, params.data_size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ ret = nv_vgpu_migration_restore_state(vgpu, data, params.data_size);
+ params.result = ret;
+
+ kfree(data);
+
+ if (copy_to_user(arg, &params, sizeof(params)))
+ return -EFAULT;
+
+ return ret;
+} \ No newline at end of file
diff --git a/kernel/irq.c b/kernel/irq.c
new file mode 100644
index 0000000..f4b77e2
--- /dev/null
+++ b/kernel/irq.c
@@ -0,0 +1,246 @@
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/msi.h>
+#include <linux/pci.h>
+#include <linux/eventfd.h>
+#include <linux/vfio.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "irq.h"
+
+static irqreturn_t nv_vgpu_msi_handler(int irq, void *data)
+{
+ struct nv_vgpu_msi_irq *msi = data;
+
+ if (msi->trigger)
+ eventfd_signal(msi->trigger);
+
+ return IRQ_HANDLED;
+}
+
+int nv_vgpu_irq_init(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_irq_ctx *ctx;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: IRQ init validation");
+ /* Check MSI/MSI-X capability */
+ if (pci_find_capability(vgpu->pdev, PCI_CAP_ID_MSIX)) {
+ nv_vgpu_info(vgpu, "DRY-RUN: MSI-X capability present");
+ } else if (pci_find_capability(vgpu->pdev, PCI_CAP_ID_MSI)) {
+ nv_vgpu_info(vgpu, "DRY-RUN: MSI capability present");
+ } else {
+ nv_vgpu_warn(vgpu, "DRY-RUN: No MSI/MSI-X capability found");
+ }
+ return 0;
+ }
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vgpu = vgpu;
+ mutex_init(&ctx->lock);
+ ctx->msi_count = 0;
+ ctx->msi_enabled = false;
+ ctx->msix_enabled = false;
+
+ vgpu->irq_ctx = ctx;
+ return 0;
+}
+
+void nv_vgpu_irq_cleanup(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_irq_ctx *ctx = vgpu->irq_ctx;
+
+ if (!ctx)
+ return;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: IRQ cleanup (no hardware resources to release)");
+ kfree(ctx);
+ vgpu->irq_ctx = NULL;
+ return;
+ }
+
+ nv_vgpu_irq_teardown_msix(vgpu);
+ nv_vgpu_irq_teardown_msi(vgpu);
+
+ kfree(ctx);
+ vgpu->irq_ctx = NULL;
+}
+
+int nv_vgpu_irq_setup_msi(struct nv_vgpu_device *vgpu, int num_vectors)
+{
+ struct nv_vgpu_irq_ctx *ctx = vgpu->irq_ctx;
+ struct pci_dev *pdev = vgpu->pdev;
+ int ret, i;
+
+ if (!ctx || num_vectors > NV_VGPU_MAX_MSI_VECTORS)
+ return -EINVAL;
+
+ ret = pci_alloc_irq_vectors(pdev, 1, num_vectors, PCI_IRQ_MSI);
+ if (ret < 0)
+ return ret;
+
+ ctx->msi_count = ret;
+ ctx->msi_enabled = true;
+
+ for (i = 0; i < ctx->msi_count; i++) {
+ ctx->msis[i].virq = pci_irq_vector(pdev, i);
+ ctx->msis[i].vector = i;
+ ctx->msis[i].enabled = false;
+ ctx->msis[i].trigger = NULL;
+
+ ret = request_irq(ctx->msis[i].virq, nv_vgpu_msi_handler,
+ 0, "nv-vgpu-msi", &ctx->msis[i]);
+ if (ret) {
+ nv_vgpu_warn(vgpu, "Failed to request MSI %d: %d", i, ret);
+ ctx->msis[i].virq = 0;
+ }
+ }
+
+ return 0;
+}
+
+void nv_vgpu_irq_teardown_msi(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_irq_ctx *ctx = vgpu->irq_ctx;
+ int i;
+
+ if (!ctx || !ctx->msi_enabled)
+ return;
+
+ for (i = 0; i < ctx->msi_count; i++) {
+ if (ctx->msis[i].virq)
+ free_irq(ctx->msis[i].virq, &ctx->msis[i]);
+ if (ctx->msis[i].trigger)
+ eventfd_ctx_put(ctx->msis[i].trigger);
+ }
+
+ pci_free_irq_vectors(vgpu->pdev);
+ ctx->msi_enabled = false;
+ ctx->msi_count = 0;
+}
+
+int nv_vgpu_irq_setup_msix(struct nv_vgpu_device *vgpu, int num_vectors)
+{
+ struct nv_vgpu_irq_ctx *ctx = vgpu->irq_ctx;
+ struct pci_dev *pdev = vgpu->pdev;
+ int ret, i;
+
+ if (!ctx || num_vectors > NV_VGPU_MAX_MSI_VECTORS)
+ return -EINVAL;
+
+ ret = pci_alloc_irq_vectors(pdev, 1, num_vectors, PCI_IRQ_MSIX);
+ if (ret < 0)
+ return ret;
+
+ ctx->msi_count = ret;
+ ctx->msix_enabled = true;
+
+ for (i = 0; i < ctx->msi_count; i++) {
+ ctx->msis[i].virq = pci_irq_vector(pdev, i);
+ ctx->msis[i].vector = i;
+ ctx->msis[i].enabled = false;
+ ctx->msis[i].trigger = NULL;
+
+ ret = request_irq(ctx->msis[i].virq, nv_vgpu_msi_handler,
+ 0, "nv-vgpu-msix", &ctx->msis[i]);
+ if (ret) {
+ nv_vgpu_warn(vgpu, "Failed to request MSIX %d: %d", i, ret);
+ ctx->msis[i].virq = 0;
+ }
+ }
+
+ return 0;
+}
+
+void nv_vgpu_irq_teardown_msix(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_irq_ctx *ctx = vgpu->irq_ctx;
+ int i;
+
+ if (!ctx || !ctx->msix_enabled)
+ return;
+
+ for (i = 0; i < ctx->msi_count; i++) {
+ if (ctx->msis[i].virq)
+ free_irq(ctx->msis[i].virq, &ctx->msis[i]);
+ if (ctx->msis[i].trigger)
+ eventfd_ctx_put(ctx->msis[i].trigger);
+ }
+
+ pci_free_irq_vectors(vgpu->pdev);
+ ctx->msix_enabled = false;
+ ctx->msi_count = 0;
+}
+
+int nv_vgpu_irq_enable(struct nv_vgpu_device *vgpu, int irq_idx)
+{
+ struct nv_vgpu_irq_ctx *ctx = vgpu->irq_ctx;
+
+ if (!ctx || irq_idx < 0 || irq_idx >= ctx->msi_count)
+ return -EINVAL;
+
+ if (!ctx->msis[irq_idx].virq)
+ return -ENODEV;
+
+ ctx->msis[irq_idx].enabled = true;
+ enable_irq(ctx->msis[irq_idx].virq);
+ return 0;
+}
+
+int nv_vgpu_irq_disable(struct nv_vgpu_device *vgpu, int irq_idx)
+{
+ struct nv_vgpu_irq_ctx *ctx = vgpu->irq_ctx;
+
+ if (!ctx || irq_idx < 0 || irq_idx >= ctx->msi_count)
+ return -EINVAL;
+
+ if (!ctx->msis[irq_idx].virq)
+ return -ENODEV;
+
+ disable_irq(ctx->msis[irq_idx].virq);
+ ctx->msis[irq_idx].enabled = false;
+ return 0;
+}
+
+int nv_vgpu_irq_set_trigger(struct nv_vgpu_device *vgpu, int irq_idx, int fd)
+{
+ struct nv_vgpu_irq_ctx *ctx = vgpu->irq_ctx;
+ struct eventfd_ctx *trigger;
+
+ if (!ctx || irq_idx < 0 || irq_idx >= ctx->msi_count)
+ return -EINVAL;
+
+ if (fd < 0) {
+ if (ctx->msis[irq_idx].trigger) {
+ eventfd_ctx_put(ctx->msis[irq_idx].trigger);
+ ctx->msis[irq_idx].trigger = NULL;
+ }
+ return 0;
+ }
+
+ trigger = eventfd_ctx_fdget(fd);
+ if (IS_ERR(trigger))
+ return PTR_ERR(trigger);
+
+ if (ctx->msis[irq_idx].trigger)
+ eventfd_ctx_put(ctx->msis[irq_idx].trigger);
+
+ ctx->msis[irq_idx].trigger = trigger;
+ return 0;
+}
+
+int nv_vgpu_irq_get_virq(struct nv_vgpu_device *vgpu, int irq_idx)
+{
+ struct nv_vgpu_irq_ctx *ctx = vgpu->irq_ctx;
+
+ if (!ctx || irq_idx < 0 || irq_idx >= ctx->msi_count)
+ return -EINVAL;
+
+ return ctx->msis[irq_idx].virq;
+} \ No newline at end of file
diff --git a/kernel/main.c b/kernel/main.c
new file mode 100644
index 0000000..7d12203
--- /dev/null
+++ b/kernel/main.c
@@ -0,0 +1,834 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vfio.h>
+#include <linux/mdev.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/iommu.h>
+#include <linux/vmalloc.h>
+#include <linux/version.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "bar_emulation.h"
+#include "gsp_comm.h"
+#include "memory.h"
+#include "irq.h"
+#include "migration.h"
+#include "ioctl.h"
+
+#include "nv-vgpu-vfio-interface.h"
+
+#define NV_VERSION_STRING "550.163.01"
+
+/* ============================================================
+ * Forward declarations
+ * ============================================================ */
+
+static int nv_vgpu_mdev_create(struct mdev_device *mdev);
+static void nv_vgpu_mdev_remove(struct mdev_device *mdev);
+static void nv_vgpu_device_init_work(struct work_struct *work);
+static void nv_vgpu_device_cleanup_work(struct work_struct *work);
+
+/* PCI Config Space handlers for VFIO mdev */
+static int nv_vgpu_config_space_init(struct nv_vgpu_device *vgpu, struct pci_dev *pdev);
+static ssize_t nv_vgpu_config_read(struct nv_vgpu_device *vgpu, char __user *buf, size_t count, loff_t *ppos);
+static ssize_t nv_vgpu_config_write(struct nv_vgpu_device *vgpu, const char __user *buf, size_t count, loff_t *ppos);
+
+static struct mdev_type *nv_vgpu_mdev_types[4];
+
+static struct mdev_driver nv_vgpu_mdev_driver = {
+ .device_api = VFIO_DEVICE_API_PCI_STRING,
+ .max_instances = 8,
+ .probe = nv_vgpu_mdev_create,
+ .remove = nv_vgpu_mdev_remove,
+ .driver = {
+ .name = "nvidia-vgpu-vfio-mdev",
+ },
+};
+
+/* ============================================================
+ * Symbols we provide to nvidia.ko
+ * ============================================================ */
+
+static vgpu_vfio_ops_t *g_vgpu_vfio_ops = NULL;
+static rm_vgpu_vfio_ops_t g_rm_vgpu_vfio_ops;
+
+/* mdev parent structure for our physical device */
+static struct mdev_parent *g_mdev_parent = NULL;
+static struct pci_dev *g_pdev = NULL;
+
+/* VFIO ops implementation */
+static NV_STATUS nv_vgpu_vfio_probe(struct pci_dev *pdev, NvU32 vgpu_type_id, NvU32 *vgpu_id);
+static void nv_vgpu_vfio_remove(struct pci_dev *pdev);
+static NV_STATUS nv_vgpu_vfio_inject_interrupt(void *vgpu_dev);
+
+static vgpu_vfio_ops_t nv_vgpu_vfio_ops = {
+ .probe = nv_vgpu_vfio_probe,
+ .remove = nv_vgpu_vfio_remove,
+ .inject_interrupt = nv_vgpu_vfio_inject_interrupt,
+};
+
+/* RM ops implementation - callbacks from nvidia.ko */
+static NV_STATUS nv_vgpu_create_callback(struct pci_dev *pdev, const NvU8 *uuid,
+ NvU32 type_id, NvU16 *vgpu_id,
+ NvU32 *gpu_pci_id, NvU32 gpu_pci_bdf)
+{
+ pr_info("nvidia-vgpu-vfio: vgpu_create callback: type_id=%u\n", type_id);
+ if (vgpu_id)
+ *vgpu_id = 1;
+ if (gpu_pci_id)
+ *gpu_pci_id = 0x10de1ed3;
+ return NV_OK;
+}
+
+static NV_STATUS nv_vgpu_delete_callback(const NvU8 *uuid, NvU16 vgpu_id)
+{
+ pr_info("nvidia-vgpu-vfio: vgpu_delete callback: vgpu_id=%u\n", vgpu_id);
+ return NV_OK;
+}
+
+static NV_STATUS nv_vgpu_bar_info_callback(struct pci_dev *pdev, const NvU8 *uuid,
+ NvU64 *size, NvU32 bar_index, void *vgpu_dev)
+{
+ pr_info("nvidia-vgpu-vfio: vgpu_bar_info callback: bar_index=%u\n", bar_index);
+ if (size) {
+ switch (bar_index) {
+ case 0:
+ *size = NV_VGPU_BAR0_SIZE; /* 16MB */
+ break;
+ case 1:
+ *size = NV_VGPU_BAR1_SIZE; /* 256MB */
+ break;
+ case 2:
+ *size = NV_VGPU_BAR2_SIZE; /* 32MB */
+ break;
+ case 3:
+ *size = NV_VGPU_BAR3_SIZE; /* 32MB */
+ break;
+ default:
+ *size = 0;
+ break;
+ }
+ }
+ return NV_OK;
+}
+
+static NV_STATUS nv_vgpu_get_name_callback(struct pci_dev *pdev, NvU32 type_id, char *buf)
+{
+ pr_info("nvidia-vgpu-vfio: get_name callback: type_id=%u\n", type_id);
+ if (buf) {
+ switch (type_id) {
+ case 0: /* Ampere */
+ strcpy(buf, "NVIDIA A100");
+ break;
+ case 1: /* Hopper */
+ strcpy(buf, "NVIDIA H100");
+ break;
+ case 2: /* Turing */
+ strcpy(buf, "NVIDIA T4");
+ break;
+ case 3: /* Pascal */
+ strcpy(buf, "NVIDIA P4");
+ break;
+ default:
+ strcpy(buf, "NVIDIA vGPU");
+ break;
+ }
+ }
+ return NV_OK;
+}
+
+static NV_STATUS nv_vgpu_get_description_callback(struct pci_dev *pdev, NvU32 type_id, char *buf)
+{
+ pr_info("nvidia-vgpu-vfio: get_description callback: type_id=%u\n", type_id);
+ if (buf) {
+ switch (type_id) {
+ case 0:
+ strcpy(buf, "NVIDIA Ampere Architecture vGPU");
+ break;
+ case 1:
+ strcpy(buf, "NVIDIA Hopper Architecture vGPU");
+ break;
+ case 2:
+ strcpy(buf, "NVIDIA Turing Architecture vGPU");
+ break;
+ case 3:
+ strcpy(buf, "NVIDIA Pascal Architecture vGPU");
+ break;
+ default:
+ strcpy(buf, "NVIDIA vGPU Description");
+ break;
+ }
+ }
+ return NV_OK;
+}
+
+static NV_STATUS nv_vgpu_get_instances_callback(struct pci_dev *pdev, NvU32 type_id, char *buf)
+{
+ pr_info("nvidia-vgpu-vfio: get_instances callback: type_id=%u\n", type_id);
+ if (buf)
+ strcpy(buf, "1");
+ return NV_OK;
+}
+
+static void nv_vgpu_update_request_callback(const NvU8 *uuid, NvU64 *vgpu_dev,
+ NvU64 *vgpu_vm, VGPU_DEVICE_STATE device_state,
+ const char *params)
+{
+ pr_info("nvidia-vgpu-vfio: update_request callback: state=%u\n", device_state);
+}
+
+static NV_STATUS nv_vgpu_start_callback(const NvU8 *uuid, void *wait_queue, NvS32 *return_status,
+ NvU8 *vm_name, NvU32 qemu_pid)
+{
+ pr_info("nvidia-vgpu-vfio: vgpu_start callback: qemu_pid=%u\n", qemu_pid);
+ if (return_status)
+ *return_status = 0;
+ return NV_OK;
+}
+
+static NV_STATUS nv_vgpu_sparse_mmap_callback(struct pci_dev *pdev, const NvU8 *uuid,
+ NvU64 **pages, NvU64 **offsets, NvU32 *count)
+{
+ pr_info("nvidia-vgpu-vfio: sparse_mmap callback\n");
+ if (count)
+ *count = 0;
+ return NV_OK;
+}
+
+/* Build RM ops structure */
+static void populate_rm_ops(void)
+{
+ memset(&g_rm_vgpu_vfio_ops, 0, sizeof(g_rm_vgpu_vfio_ops));
+ g_rm_vgpu_vfio_ops.version_string = NV_VERSION_STRING;
+ g_rm_vgpu_vfio_ops.vgpu_create = nv_vgpu_create_callback;
+ g_rm_vgpu_vfio_ops.vgpu_delete = nv_vgpu_delete_callback;
+ g_rm_vgpu_vfio_ops.vgpu_bar_info = nv_vgpu_bar_info_callback;
+ g_rm_vgpu_vfio_ops.vgpu_start = nv_vgpu_start_callback;
+ g_rm_vgpu_vfio_ops.get_name = nv_vgpu_get_name_callback;
+ g_rm_vgpu_vfio_ops.get_description = nv_vgpu_get_description_callback;
+ g_rm_vgpu_vfio_ops.get_instances = nv_vgpu_get_instances_callback;
+ g_rm_vgpu_vfio_ops.get_sparse_mmap = nv_vgpu_sparse_mmap_callback;
+ g_rm_vgpu_vfio_ops.update_request = nv_vgpu_update_request_callback;
+}
+
+/* get_ops: called by nvidia-vgpu-vfio to get RM ops */
+NV_STATUS nvidia_vgpu_vfio_get_ops(rm_vgpu_vfio_ops_t *ops)
+{
+ if (!ops)
+ return NV_ERR_INVALID_ARGUMENT;
+
+ populate_rm_ops();
+ memcpy(ops, &g_rm_vgpu_vfio_ops, sizeof(rm_vgpu_vfio_ops_t));
+ pr_info("nvidia-vgpu-vfio: get_ops provided RM ops\n");
+ return NV_OK;
+}
+
+/* set_ops: called by nvidia-vgpu-vfio to provide VFIO ops */
+NV_STATUS nvidia_vgpu_vfio_set_ops(vgpu_vfio_ops_t *ops)
+{
+ if (!ops)
+ return NV_ERR_INVALID_ARGUMENT;
+
+ g_vgpu_vfio_ops = ops;
+ pr_info("nvidia-vgpu-vfio: set_ops received VFIO ops\n");
+ return NV_OK;
+}
+
+extern NV_STATUS nv_vgpu_mdev_register_parent(struct pci_dev *pdev);
+extern void nv_vgpu_mdev_unregister_parent(struct pci_dev *pdev);
+extern int nv_vgpu_mdev_driver_register(void);
+extern void nv_vgpu_mdev_driver_unregister(void);
+
+MODULE_SOFTDEP("pre: nvidia");
+
+MODULE_AUTHOR("Open-IOV Project");
+MODULE_DESCRIPTION("Open-IOV vGPU VFIO Mediated Device Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(OPEN_IOV_VFIO_VERSION);
+
+struct nv_vgpu_driver_data nv_vgpu_driver = {
+ .lock = __MUTEX_INITIALIZER(nv_vgpu_driver.lock),
+ .devices = LIST_HEAD_INIT(nv_vgpu_driver.devices),
+ .device_count = 0,
+};
+
+static int nv_vgpu_mdev_create(struct mdev_device *mdev)
+{
+ struct nv_vgpu_device *vgpu;
+ struct pci_dev *pdev;
+ struct mdev_parent *parent;
+ int ret;
+
+ vgpu = kzalloc(sizeof(*vgpu), GFP_KERNEL);
+ if (!vgpu)
+ return -ENOMEM;
+
+ dev_set_drvdata(&mdev->dev, vgpu);
+ vgpu->mdev = mdev;
+ vgpu->state = NV_VGPU_STATE_UNINITIALIZED;
+ mutex_init(&vgpu->lock);
+ INIT_WORK(&vgpu->init_work, nv_vgpu_device_init_work);
+ INIT_WORK(&vgpu->cleanup_work, nv_vgpu_device_cleanup_work);
+
+ parent = mdev->type->parent;
+ if (!parent || !parent->dev) {
+ ret = -ENODEV;
+ goto err_free;
+ }
+ pdev = to_pci_dev(parent->dev);
+ if (!pdev) {
+ ret = -ENODEV;
+ goto err_free;
+ }
+ vgpu->pdev = pci_dev_get(pdev);
+
+ /* Initialize PCI config space for VFIO mdev */
+ ret = nv_vgpu_config_space_init(vgpu, pdev);
+ if (ret) {
+ nv_vgpu_err(vgpu, "PCI config space init failed: %d", ret);
+ goto err_put;
+ }
+
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_add_tail(&vgpu->list, &nv_vgpu_driver.devices);
+ nv_vgpu_driver.device_count++;
+ mutex_unlock(&nv_vgpu_driver.lock);
+
+ schedule_work(&vgpu->init_work);
+
+ return 0;
+
+err_put:
+ pci_dev_put(vgpu->pdev);
+err_free:
+ kfree(vgpu);
+ return ret;
+}
+
+static void nv_vgpu_mdev_remove(struct mdev_device *mdev)
+{
+ struct nv_vgpu_device *vgpu = dev_get_drvdata(&mdev->dev);
+
+ cancel_work_sync(&vgpu->init_work);
+ schedule_work(&vgpu->cleanup_work);
+ flush_work(&vgpu->cleanup_work);
+
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_del(&vgpu->list);
+ nv_vgpu_driver.device_count--;
+ mutex_unlock(&nv_vgpu_driver.lock);
+
+ pci_dev_put(vgpu->pdev);
+ kfree(vgpu);
+}
+
+static int __init nv_vgpu_init(void)
+{
+ int ret, i;
+ struct pci_dev *pdev = NULL;
+
+ nv_vgpu_driver.class = class_create("nvidia-vgpu");
+ if (IS_ERR(nv_vgpu_driver.class)) {
+ ret = PTR_ERR(nv_vgpu_driver.class);
+ pr_err("Failed to create nvidia-vgpu class: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < 4; i++) {
+ nv_vgpu_mdev_types[i] = kzalloc(sizeof(struct mdev_type), GFP_KERNEL);
+ if (!nv_vgpu_mdev_types[i]) {
+ ret = -ENOMEM;
+ goto err_types;
+ }
+ }
+
+ nv_vgpu_mdev_types[0]->sysfs_name = "nvidia-vgpu-ampere";
+ nv_vgpu_mdev_types[0]->pretty_name = "NVIDIA Ampere vGPU";
+ nv_vgpu_mdev_types[1]->sysfs_name = "nvidia-vgpu-hopper";
+ nv_vgpu_mdev_types[1]->pretty_name = "NVIDIA Hopper vGPU";
+ nv_vgpu_mdev_types[2]->sysfs_name = "nvidia-vgpu-turing";
+ nv_vgpu_mdev_types[2]->pretty_name = "NVIDIA Turing vGPU";
+ nv_vgpu_mdev_types[3]->sysfs_name = "nvidia-vgpu-pascal";
+ nv_vgpu_mdev_types[3]->pretty_name = "NVIDIA Pascal vGPU";
+
+ ret = mdev_register_driver(&nv_vgpu_mdev_driver);
+ if (ret) {
+ pr_err("Failed to register mdev driver: %d\n", ret);
+ goto err_types;
+ }
+
+ /* Find the parent PCI device to register mdev parent */
+ pdev = pci_get_device(0x10de, 0x1ed3, NULL);
+ if (!pdev) {
+ pr_err("Failed to find NVIDIA GPU (10de:1ed3) for mdev parent registration\n");
+ ret = -ENODEV;
+ goto err_driver;
+ }
+
+ /* Register mdev parent via nvidia.ko */
+ ret = nv_vgpu_mdev_register_parent(pdev);
+ pci_dev_put(pdev);
+ if (ret) {
+ pr_err("Failed to register mdev parent: %d\n", ret);
+ goto err_driver;
+ }
+
+ /* Provide VFIO ops to host driver (nvidia.ko) */
+ ret = nvidia_vgpu_vfio_set_ops(&nv_vgpu_vfio_ops);
+ if (ret) {
+ pr_err("Failed to set VFIO ops: %d\n", ret);
+ goto err_driver;
+ }
+
+ pr_info("NVIDIA vGPU VFIO driver loaded (version %s)\n", NVIDIA_VGPU_VFIO_VERSION);
+ return 0;
+
+err_driver:
+ mdev_unregister_driver(&nv_vgpu_mdev_driver);
+err_types:
+ for (i = 0; i < 4; i++) {
+ kfree(nv_vgpu_mdev_types[i]);
+ nv_vgpu_mdev_types[i] = NULL;
+ }
+ class_destroy(nv_vgpu_driver.class);
+ return ret;
+}
+
+static void __exit nv_vgpu_exit(void)
+{
+ int i;
+ struct pci_dev *pdev = NULL;
+
+ if (nv_vgpu_driver.device_count > 0) {
+ struct nv_vgpu_device *vgpu;
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_for_each_entry(vgpu, &nv_vgpu_driver.devices, list) {
+ if (vgpu->pdev) {
+ pdev = vgpu->pdev;
+ break;
+ }
+ }
+ mutex_unlock(&nv_vgpu_driver.lock);
+ }
+
+ if (!pdev) {
+ pdev = pci_get_device(0x10de, 0x1ed3, NULL);
+ }
+
+ /* Release host vGPU ops */
+ if (pdev) {
+ rm_vgpu_vfio_ops_t ops = {0};
+ ops.version_string = NV_VERSION_STRING;
+ nvidia_vgpu_vfio_get_ops(&ops);
+ }
+
+ /* Unregister mdev parent via nvidia.ko */
+ if (pdev) {
+ nv_vgpu_mdev_unregister_parent(pdev);
+ pr_info("nvidia-vgpu-vfio: mdev parent unregistered for %s\n", dev_name(&pdev->dev));
+ pci_dev_put(pdev);
+ }
+
+ mdev_unregister_driver(&nv_vgpu_mdev_driver);
+
+ class_destroy(nv_vgpu_driver.class);
+
+ for (i = 0; i < 4; i++) {
+ kfree(nv_vgpu_mdev_types[i]);
+ nv_vgpu_mdev_types[i] = NULL;
+ }
+
+ pr_info("NVIDIA vGPU VFIO driver unloaded\n");
+}
+
+static void nv_vgpu_device_init_work(struct work_struct *work)
+{
+ struct nv_vgpu_device *vgpu = container_of(work, struct nv_vgpu_device, init_work);
+ int ret;
+
+ mutex_lock(&vgpu->lock);
+ vgpu->state = NV_VGPU_STATE_INITIALIZING;
+ mutex_unlock(&vgpu->lock);
+
+ ret = nv_vgpu_bar_init(vgpu);
+ if (ret) {
+ nv_vgpu_err(vgpu, "BAR init failed: %d", ret);
+ goto err_state;
+ }
+
+ ret = nv_vgpu_irq_init(vgpu);
+ if (ret) {
+ nv_vgpu_err(vgpu, "IRQ init failed: %d", ret);
+ goto err_bar;
+ }
+
+ ret = nv_vgpu_memory_init(vgpu);
+ if (ret) {
+ nv_vgpu_err(vgpu, "Memory init failed: %d", ret);
+ goto err_irq;
+ }
+
+ ret = nv_vgpu_gsp_init(vgpu);
+ if (ret) {
+ nv_vgpu_err(vgpu, "GSP init failed: %d", ret);
+ goto err_memory;
+ }
+
+ ret = nv_vgpu_migration_init(vgpu);
+ if (ret) {
+ nv_vgpu_warn(vgpu, "Migration init failed: %d", ret);
+ }
+
+ mutex_lock(&vgpu->lock);
+ vgpu->state = NV_VGPU_STATE_READY;
+ mutex_unlock(&vgpu->lock);
+
+ nv_vgpu_info(vgpu, "vGPU device initialized successfully");
+ return;
+
+err_memory:
+ nv_vgpu_memory_cleanup(vgpu);
+err_irq:
+ nv_vgpu_irq_cleanup(vgpu);
+err_bar:
+ nv_vgpu_bar_cleanup(vgpu);
+err_state:
+ mutex_lock(&vgpu->lock);
+ vgpu->state = NV_VGPU_STATE_ERROR;
+ mutex_unlock(&vgpu->lock);
+}
+
+static void nv_vgpu_device_cleanup_work(struct work_struct *work)
+{
+ struct nv_vgpu_device *vgpu = container_of(work, struct nv_vgpu_device, cleanup_work);
+
+ nv_vgpu_migration_cleanup(vgpu);
+ nv_vgpu_gsp_cleanup(vgpu);
+ nv_vgpu_memory_cleanup(vgpu);
+ nv_vgpu_irq_cleanup(vgpu);
+ nv_vgpu_bar_cleanup(vgpu);
+
+ mutex_lock(&vgpu->lock);
+ vgpu->state = NV_VGPU_STATE_UNINITIALIZED;
+ mutex_unlock(&vgpu->lock);
+
+ nv_vgpu_info(vgpu, "vGPU device cleaned up");
+}
+
+static NV_STATUS nv_vgpu_vfio_probe(struct pci_dev *pdev, NvU32 vgpu_type_id, NvU32 *vgpu_id)
+{
+ pr_info("nvidia-vgpu-vfio: VFIO probe called for vGPU type %u\n", vgpu_type_id);
+ return NV_OK;
+}
+
+static void nv_vgpu_vfio_remove(struct pci_dev *pdev)
+{
+ pr_info("nvidia-vgpu-vfio: VFIO remove called\n");
+}
+
+static NV_STATUS nv_vgpu_vfio_inject_interrupt(void *vgpu_dev)
+{
+ pr_info("nvidia-vgpu-vfio: VFIO inject interrupt called\n");
+ return NV_OK;
+}
+
+/* ============================================================
+ * PCI Config Space for VFIO mdev
+ * ============================================================ */
+
+static int nv_vgpu_config_space_init(struct nv_vgpu_device *vgpu, struct pci_dev *pdev)
+{
+ u8 *cfg = vgpu->pci_config;
+ u16 vendor_id = 0x10de; /* NVIDIA */
+ u16 device_id = 0x1ed3; /* TU104 vGPU */
+ u16 subsys_vendor = 0x10de;
+ u16 subsys_device = 0x12b4;
+ u8 revision = 0xa1;
+ u8 prog_if = 0x00;
+ u8 subclass = 0x00;
+ u8 class_code = 0x03; /* Display controller */
+ u8 cache_line = 0x10;
+ u8 latency = 0x00;
+ u8 header_type = 0x00; /* Normal device */
+ u8 bist = 0x00;
+
+ memset(cfg, 0, 256);
+
+ /* PCI Header (offset 0x00-0x3F) */
+ cfg[0x00] = vendor_id & 0xff;
+ cfg[0x01] = (vendor_id >> 8) & 0xff;
+ cfg[0x02] = device_id & 0xff;
+ cfg[0x03] = (device_id >> 8) & 0xff;
+
+ /* Command register (0x04) - Memory + IO + BusMaster */
+ cfg[0x04] = 0x07;
+ cfg[0x05] = 0x00;
+
+ /* Status register (0x06) */
+ cfg[0x06] = 0x10;
+ cfg[0x07] = 0x00;
+
+ /* Revision ID (0x08) */
+ cfg[0x08] = revision;
+
+ /* Class code (0x09-0x0B) */
+ cfg[0x09] = prog_if;
+ cfg[0x0a] = subclass;
+ cfg[0x0b] = class_code;
+
+ /* Cache line size (0x0C) */
+ cfg[0x0c] = cache_line;
+
+ /* Latency timer (0x0D) */
+ cfg[0x0d] = latency;
+
+ /* Header type (0x0E) */
+ cfg[0x0e] = header_type;
+
+ /* BIST (0x0F) */
+ cfg[0x0f] = bist;
+
+ /* BAR0 - 16MB Memory space (prefetchable) */
+ cfg[0x10] = 0x04; /* 64-bit, prefetchable */
+ cfg[0x11] = 0x00;
+ cfg[0x12] = 0x00;
+ cfg[0x13] = 0x00;
+ cfg[0x14] = 0x00;
+ cfg[0x15] = 0x00;
+ cfg[0x16] = 0x00;
+ cfg[0x17] = 0x00;
+
+ /* BAR1 - 256MB Memory space (prefetchable) */
+ cfg[0x18] = 0x04;
+ cfg[0x19] = 0x00;
+ cfg[0x1a] = 0x00;
+ cfg[0x1b] = 0x00;
+ cfg[0x1c] = 0x00;
+ cfg[0x1d] = 0x00;
+ cfg[0x1e] = 0x00;
+ cfg[0x1f] = 0x00;
+
+ /* BAR2 - 32MB Memory space */
+ cfg[0x20] = 0x04;
+ cfg[0x21] = 0x00;
+ cfg[0x22] = 0x00;
+ cfg[0x23] = 0x00;
+ cfg[0x24] = 0x00;
+ cfg[0x25] = 0x00;
+ cfg[0x26] = 0x00;
+ cfg[0x27] = 0x00;
+
+ /* BAR3 - 32MB Memory space */
+ cfg[0x28] = 0x04;
+ cfg[0x29] = 0x00;
+ cfg[0x2a] = 0x00;
+ cfg[0x2b] = 0x00;
+ cfg[0x2c] = 0x00;
+ cfg[0x2d] = 0x00;
+ cfg[0x2e] = 0x00;
+ cfg[0x2f] = 0x00;
+
+ /* BAR4 - Not used */
+ cfg[0x30] = 0x00;
+ cfg[0x31] = 0x00;
+ cfg[0x32] = 0x00;
+ cfg[0x33] = 0x00;
+
+ /* BAR5 - Not used */
+ cfg[0x34] = 0x00;
+ cfg[0x35] = 0x00;
+ cfg[0x36] = 0x00;
+ cfg[0x37] = 0x00;
+
+ /* CardBus CIS Pointer (0x38-0x3B) - Not used */
+ cfg[0x38] = 0x00;
+ cfg[0x39] = 0x00;
+ cfg[0x3a] = 0x00;
+ cfg[0x3b] = 0x00;
+
+ /* Subsystem Vendor ID (0x3C) */
+ cfg[0x3c] = subsys_vendor & 0xff;
+ cfg[0x3d] = (subsys_vendor >> 8) & 0xff;
+
+ /* Subsystem ID (0x3E) */
+ cfg[0x3e] = subsys_device & 0xff;
+ cfg[0x3f] = (subsys_device >> 8) & 0xff;
+
+ /* Expansion ROM Base Address (0x40-0x43) - Not used */
+
+ /* Capabilities Pointer (0x34) - Point to Power Management at 0x40 */
+ cfg[0x34] = 0x40;
+ cfg[0x35] = 0x00;
+ cfg[0x36] = 0x00;
+ cfg[0x37] = 0x00;
+
+ /* Interrupt Line (0x3C) - Will be set by guest */
+ cfg[0x3c] = 0x00;
+ /* Interrupt Pin (0x3D) - INTA */
+ cfg[0x3d] = 0x01;
+ /* Min_Gnt (0x3E) */
+ cfg[0x3e] = 0x00;
+ /* Max_Lat (0x3F) */
+ cfg[0x3f] = 0x00;
+
+ /* Power Management Capability (0x40) */
+ cfg[0x40] = 0x01; /* Capability ID: Power Management */
+ cfg[0x41] = 0x50; /* Next capability: MSI at 0x50 */
+ cfg[0x42] = 0x03; /* PME support from D0, D3hot, D3cold */
+ cfg[0x43] = 0x00;
+ cfg[0x44] = 0x00; /* PM Control/Status */
+ cfg[0x45] = 0x00;
+ cfg[0x46] = 0x00;
+ cfg[0x47] = 0x00;
+
+ /* MSI Capability (0x50) */
+ cfg[0x50] = 0x05; /* Capability ID: MSI */
+ cfg[0x51] = 0x70; /* Next capability: MSI-X at 0x70 */
+ cfg[0x52] = 0x80; /* 64-bit capable, 1 vector */
+ cfg[0x53] = 0x00;
+ cfg[0x54] = 0x00; /* Message Address (low) - set by guest */
+ cfg[0x55] = 0x00;
+ cfg[0x56] = 0x00;
+ cfg[0x57] = 0x00;
+ cfg[0x58] = 0x00; /* Message Address (high) - 64-bit */
+ cfg[0x59] = 0x00;
+ cfg[0x5a] = 0x00;
+ cfg[0x5b] = 0x00;
+ cfg[0x5c] = 0x01; /* Message Data - set by guest */
+ cfg[0x5d] = 0x00;
+
+ /* MSI-X Capability (0x70) */
+ cfg[0x70] = 0x11; /* Capability ID: MSI-X */
+ cfg[0x71] = 0x00; /* Next capability: None */
+ cfg[0x72] = 0x00; /* Table size = 0 (1 vector), 64-bit */
+ cfg[0x73] = 0x00;
+ /* Table Offset/BIR (0x74) - Table at BAR0 offset 0x2000 */
+ cfg[0x74] = 0x00;
+ cfg[0x75] = 0x20;
+ cfg[0x76] = 0x00;
+ cfg[0x77] = 0x00;
+ /* PBA Offset/BIR (0x78) - PBA at BAR0 offset 0x3000 */
+ cfg[0x78] = 0x00;
+ cfg[0x79] = 0x30;
+ cfg[0x7a] = 0x00;
+ cfg[0x7b] = 0x00;
+
+ /* PCI Express Capability (0x80) */
+ cfg[0x80] = 0x10; /* Capability ID: PCI Express */
+ cfg[0x81] = 0x00; /* Next capability: None */
+ cfg[0x82] = 0x02; /* Device type: PCIe Endpoint, version 2 */
+ cfg[0x83] = 0x00;
+ cfg[0x84] = 0x00; /* Device Capabilities */
+ cfg[0x85] = 0x00;
+ cfg[0x86] = 0x00;
+ cfg[0x87] = 0x00;
+ cfg[0x88] = 0x00; /* Device Control */
+ cfg[0x89] = 0x00;
+ cfg[0x8a] = 0x10; /* Device Status */
+ cfg[0x8b] = 0x00;
+ cfg[0x8c] = 0x00; /* Link Capabilities */
+ cfg[0x8d] = 0x00;
+ cfg[0x8e] = 0x00;
+ cfg[0x8f] = 0x00;
+ cfg[0x90] = 0x00; /* Link Control */
+ cfg[0x91] = 0x00;
+ cfg[0x92] = 0x00; /* Link Status */
+ cfg[0x93] = 0x00;
+ cfg[0x94] = 0x00; /* Slot Capabilities */
+ cfg[0x95] = 0x00;
+ cfg[0x96] = 0x00;
+ cfg[0x97] = 0x00;
+ cfg[0x98] = 0x00; /* Slot Control */
+ cfg[0x99] = 0x00;
+ cfg[0x9a] = 0x00; /* Slot Status */
+ cfg[0x9b] = 0x00;
+ cfg[0x9c] = 0x00; /* Root Capabilities */
+ cfg[0x9d] = 0x00;
+ cfg[0x9e] = 0x00; /* Root Control */
+ cfg[0x9f] = 0x00;
+
+ vgpu->config_space_dirty = false;
+ nv_vgpu_info(vgpu, "PCI config space initialized (vendor=0x%04x, device=0x%04x)", vendor_id, device_id);
+ return 0;
+}
+
+static ssize_t nv_vgpu_config_read(struct nv_vgpu_device *vgpu, char __user *buf, size_t count, loff_t *ppos)
+{
+ size_t bytes_read = 0;
+ loff_t pos = *ppos;
+
+ if (pos >= 256)
+ return 0;
+
+ if (pos + count > 256)
+ count = 256 - pos;
+
+ if (copy_to_user(buf, vgpu->pci_config + pos, count))
+ return -EFAULT;
+
+ *ppos += count;
+ return count;
+}
+
+static ssize_t nv_vgpu_config_write(struct nv_vgpu_device *vgpu, const char __user *buf, size_t count, loff_t *ppos)
+{
+ loff_t pos = *ppos;
+ u8 val;
+
+ if (pos >= 256)
+ return -EINVAL;
+
+ if (pos + count > 256)
+ count = 256 - pos;
+
+ /* Handle specific writable registers */
+ while (count--) {
+ if (copy_from_user(&val, buf++, 1))
+ return -EFAULT;
+
+ switch (pos) {
+ case 0x04: /* Command register */
+ /* Allow Memory, IO, BusMaster */
+ vgpu->pci_config[pos] = val & 0x07;
+ break;
+ case 0x0c: /* Cache line size */
+ vgpu->pci_config[pos] = val;
+ break;
+ case 0x0d: /* Latency timer */
+ vgpu->pci_config[pos] = val;
+ break;
+ case 0x3c: /* Interrupt line */
+ vgpu->pci_config[pos] = val;
+ break;
+ case 0x54: case 0x55: case 0x56: case 0x57: /* MSI Message Address Low */
+ case 0x58: case 0x59: case 0x5a: case 0x5b: /* MSI Message Address High */
+ case 0x5c: case 0x5d: /* MSI Message Data */
+ vgpu->pci_config[pos] = val;
+ break;
+ case 0x88: case 0x89: /* Device Control */
+ case 0x90: case 0x91: /* Link Control */
+ case 0x98: case 0x99: /* Slot Control */
+ case 0x9e: case 0x9f: /* Root Control */
+ vgpu->pci_config[pos] = val;
+ break;
+ default:
+ /* Most config space is read-only for guest */
+ break;
+ }
+ pos++;
+ }
+
+ vgpu->config_space_dirty = true;
+ *ppos = pos;
+ return pos - *ppos + count;
+}
+
+module_init(nv_vgpu_init);
+module_exit(nv_vgpu_exit);
diff --git a/kernel/memory.c b/kernel/memory.c
new file mode 100644
index 0000000..710799d
--- /dev/null
+++ b/kernel/memory.c
@@ -0,0 +1,285 @@
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/iommu.h>
+#include <linux/vmalloc.h>
+#include <linux/highmem.h>
+#include <linux/pfn.h>
+#include <linux/scatterlist.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "memory.h"
+
+struct nv_vgpu_simple_mapping {
+ u64 gpa;
+ u64 size;
+ u64 iova;
+ struct page **pages;
+ int page_count;
+ bool pinned;
+ bool mapped;
+ struct list_head list;
+};
+
+static LIST_HEAD(simple_mappings);
+static DEFINE_MUTEX(simple_lock);
+
+int nv_vgpu_memory_init(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_mm_ctx *ctx;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: Memory init validation (simple fallback)");
+ return 0;
+ }
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vgpu = vgpu;
+ mutex_init(&ctx->lock);
+ INIT_LIST_HEAD(&ctx->mappings);
+ ctx->mapping_count = 0;
+ ctx->iommu_domain = NULL;
+ ctx->use_simple = true;
+
+ vgpu->mm_ctx = ctx;
+ nv_vgpu_info(vgpu, "Memory init: using simple fallback (no IOMMU)");
+ return 0;
+}
+
+void nv_vgpu_memory_cleanup(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_simple_mapping *mapping, *tmp;
+
+ if (!ctx)
+ return;
+
+ if (ctx->use_simple) {
+ mutex_lock(&simple_lock);
+ list_for_each_entry_safe(mapping, tmp, &simple_mappings, list) {
+ if (mapping->mapped) {
+ dma_unmap_page(&vgpu->pdev->dev, mapping->iova, mapping->size, DMA_BIDIRECTIONAL);
+ mapping->mapped = false;
+ }
+ if (mapping->pinned) {
+ for (int i = 0; i < mapping->page_count; i++) {
+ if (mapping->pages[i])
+ put_page(mapping->pages[i]);
+ }
+ mapping->pinned = false;
+ }
+ list_del(&mapping->list);
+ vfree(mapping->pages);
+ kfree(mapping);
+ }
+ mutex_unlock(&simple_lock);
+ } else if (ctx->iommu_domain) {
+ iommu_detach_device(ctx->iommu_domain, &vgpu->pdev->dev);
+ iommu_domain_free(ctx->iommu_domain);
+ }
+
+ kfree(ctx);
+ vgpu->mm_ctx = NULL;
+}
+
+int nv_vgpu_memory_pin(struct nv_vgpu_device *vgpu, u64 gpa, u64 size)
+{
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_simple_mapping *mapping;
+ u64 start_pfn = gpa >> PAGE_SHIFT;
+ u64 end_pfn = (gpa + size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ int page_count = end_pfn - start_pfn;
+ int i, ret = 0;
+
+ if (!ctx)
+ return -ENODEV;
+
+ if (!ctx->use_simple)
+ return -ENODEV;
+
+ mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
+ if (!mapping)
+ return -ENOMEM;
+
+ mapping->gpa = gpa;
+ mapping->size = size;
+ mapping->page_count = page_count;
+ mapping->pages = vzalloc(page_count * sizeof(struct page *));
+ if (!mapping->pages) {
+ kfree(mapping);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < page_count; i++) {
+ u64 pfn = start_pfn + i;
+ struct page *page = pfn_to_page(pfn);
+ if (!page) {
+ ret = -EFAULT;
+ goto err_unlock;
+ }
+ get_page(page);
+ mapping->pages[i] = page;
+ }
+
+ mapping->pinned = true;
+ mutex_lock(&simple_lock);
+ list_add_tail(&mapping->list, &simple_mappings);
+ mutex_unlock(&simple_lock);
+
+ return 0;
+
+err_unlock:
+ for (i = 0; i < page_count; i++) {
+ if (mapping->pages[i])
+ put_page(mapping->pages[i]);
+ }
+ vfree(mapping->pages);
+ kfree(mapping);
+ return ret;
+}
+
+int nv_vgpu_memory_unpin(struct nv_vgpu_device *vgpu, u64 gpa, u64 size)
+{
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_simple_mapping *mapping;
+
+ if (!ctx || !ctx->use_simple)
+ return -ENODEV;
+
+ mutex_lock(&simple_lock);
+ list_for_each_entry(mapping, &simple_mappings, list) {
+ if (mapping->gpa == gpa && mapping->size == size) {
+ if (mapping->mapped) {
+ mutex_unlock(&simple_lock);
+ return -EBUSY;
+ } else if (mapping->pinned) {
+ for (int i = 0; i < mapping->page_count; i++) {
+ if (mapping->pages[i])
+ put_page(mapping->pages[i]);
+ }
+ mapping->pinned = false;
+ }
+ list_del(&mapping->list);
+ vfree(mapping->pages);
+ kfree(mapping);
+ break;
+ }
+ }
+ mutex_unlock(&simple_lock);
+
+ return 0;
+}
+
+int nv_vgpu_memory_map(struct nv_vgpu_device *vgpu, u64 gpa, u64 size, u64 *iova)
+{
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_simple_mapping *mapping;
+ u64 map_iova;
+ int ret = 0;
+
+ if (!ctx || !ctx->use_simple)
+ return -ENODEV;
+
+ mutex_lock(&simple_lock);
+ list_for_each_entry(mapping, &simple_mappings, list) {
+ if (mapping->gpa == gpa && mapping->size == size)
+ break;
+ }
+ if (&mapping->list == &simple_mappings) {
+ mutex_unlock(&simple_lock);
+ return -ENOENT;
+ }
+
+ if (!mapping->pinned) {
+ mutex_unlock(&simple_lock);
+ return -EINVAL;
+ }
+
+ map_iova = dma_map_page(&vgpu->pdev->dev, mapping->pages[0], 0, size, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(&vgpu->pdev->dev, map_iova)) {
+ mutex_unlock(&simple_lock);
+ return -ENOMEM;
+ }
+
+ mapping->iova = map_iova;
+ mapping->mapped = true;
+ *iova = map_iova;
+ mutex_unlock(&simple_lock);
+
+ return ret;
+}
+
+int nv_vgpu_memory_unmap(struct nv_vgpu_device *vgpu, u64 iova, u64 size)
+{
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_simple_mapping *mapping;
+ int ret = 0;
+
+ if (!ctx || !ctx->use_simple)
+ return -ENODEV;
+
+ mutex_lock(&simple_lock);
+ list_for_each_entry(mapping, &simple_mappings, list) {
+ if (mapping->iova == iova && mapping->size == size) {
+ if (!mapping->mapped) {
+ mutex_unlock(&simple_lock);
+ return -ENOENT;
+ }
+ dma_unmap_page(&vgpu->pdev->dev, iova, size, DMA_BIDIRECTIONAL);
+ mapping->mapped = false;
+ mapping->iova = 0;
+ break;
+ }
+ }
+ mutex_unlock(&simple_lock);
+
+ return ret;
+}
+
+static vm_fault_t nv_vgpu_vm_fault(struct vm_fault *vmf)
+{
+ struct nv_vgpu_device *vgpu = vmf->vma->vm_private_data;
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_simple_mapping *mapping;
+ u64 offset = vmf->address - vmf->vma->vm_start + vmf->vma->vm_pgoff * PAGE_SIZE;
+ u64 pfn;
+ vm_fault_t ret = VM_FAULT_SIGBUS;
+
+ if (!ctx)
+ return VM_FAULT_SIGBUS;
+
+ if (!ctx->use_simple)
+ return VM_FAULT_SIGBUS;
+
+ mutex_lock(&simple_lock);
+ list_for_each_entry(mapping, &simple_mappings, list) {
+ if (mapping->mapped && offset >= mapping->iova &&
+ offset < mapping->iova + mapping->size) {
+ pfn = (mapping->gpa + offset - mapping->iova) >> PAGE_SHIFT;
+ vmf->page = pfn_to_page(pfn);
+ get_page(vmf->page);
+ ret = VM_FAULT_NOPAGE;
+ break;
+ }
+ }
+ mutex_unlock(&simple_lock);
+
+ return ret;
+}
+
+static const struct vm_operations_struct nv_vgpu_vm_ops = {
+ .fault = nv_vgpu_vm_fault,
+};
+
+int nv_vgpu_memory_mmap(struct nv_vgpu_device *vgpu, struct vm_area_struct *vma)
+{
+ vma->vm_ops = &nv_vgpu_vm_ops;
+ vma->vm_private_data = vgpu;
+ vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
+ return 0;
+}
diff --git a/kernel/memory.c.iommu b/kernel/memory.c.iommu
new file mode 100644
index 0000000..335446b
--- /dev/null
+++ b/kernel/memory.c.iommu
@@ -0,0 +1,335 @@
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/iommu.h>
+#include <linux/vmalloc.h>
+#include <linux/highmem.h>
+#include <linux/pfn.h>
+#include <linux/scatterlist.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "memory.h"
+
+int nv_vgpu_memory_init(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_mm_ctx *ctx;
+ int ret;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: Memory init validation");
+ /* Check IOMMU support */
+ if (iommu_present(&pci_bus_type)) {
+ nv_vgpu_info(vgpu, "DRY-RUN: IOMMU present");
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->iommu_domain = iommu_domain_alloc(&pci_bus_type);
+ if (ctx->iommu_domain) {
+ nv_vgpu_info(vgpu, "DRY-RUN: IOMMU domain allocatable");
+ iommu_domain_free(ctx->iommu_domain);
+ } else {
+ nv_vgpu_warn(vgpu, "DRY-RUN: IOMMU domain allocation failed");
+ }
+ kfree(ctx);
+ } else {
+ nv_vgpu_warn(vgpu, "DRY-RUN: No IOMMU present");
+ }
+ return 0;
+ }
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vgpu = vgpu;
+ mutex_init(&ctx->lock);
+ INIT_LIST_HEAD(&ctx->mappings);
+ ctx->mapping_count = 0;
+
+ ctx->iommu_domain = iommu_domain_alloc(&pci_bus_type);
+ if (!ctx->iommu_domain) {
+ ret = -ENODEV;
+ goto err_free;
+ }
+
+ ret = iommu_attach_device(ctx->iommu_domain, &vgpu->pdev->dev);
+ if (ret) {
+ nv_vgpu_warn(vgpu, "Failed to attach IOMMU domain: %d", ret);
+ }
+
+ vgpu->mm_ctx = ctx;
+ return 0;
+
+err_free:
+ kfree(ctx);
+ return ret;
+}
+
+void nv_vgpu_memory_cleanup(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_mapping *mapping, *tmp;
+
+ if (!ctx)
+ return;
+
+ if (vgpu->dry_run) {
+ nv_vgpu_info(vgpu, "DRY-RUN: Memory cleanup (no hardware resources to release)");
+ if (ctx->iommu_domain)
+ iommu_domain_free(ctx->iommu_domain);
+ kfree(ctx);
+ vgpu->mm_ctx = NULL;
+ return;
+ }
+
+ mutex_lock(&ctx->lock);
+ list_for_each_entry_safe(mapping, tmp, &ctx->mappings, list) {
+ if (mapping->mapped) {
+ iommu_unmap(ctx->iommu_domain, mapping->iova, mapping->size);
+ mapping->mapped = false;
+ }
+ if (mapping->pinned) {
+ int i;
+ for (i = 0; i < mapping->page_count; i++) {
+ if (mapping->pages[i])
+ put_page(mapping->pages[i]);
+ }
+ mapping->pinned = false;
+ }
+ list_del(&mapping->list);
+ vfree(mapping->pages);
+ kfree(mapping);
+ ctx->mapping_count--;
+ }
+ mutex_unlock(&ctx->lock);
+
+ if (ctx->iommu_domain) {
+ iommu_detach_device(ctx->iommu_domain, &vgpu->pdev->dev);
+ iommu_domain_free(ctx->iommu_domain);
+ }
+
+ kfree(ctx);
+ vgpu->mm_ctx = NULL;
+}
+
+static struct nv_vgpu_mapping *nv_vgpu_find_mapping(struct nv_vgpu_mm_ctx *ctx, u64 iova, u64 size)
+{
+ struct nv_vgpu_mapping *mapping;
+
+ list_for_each_entry(mapping, &ctx->mappings, list) {
+ if (mapping->iova == iova && mapping->size == size)
+ return mapping;
+ }
+ return NULL;
+}
+
+int nv_vgpu_memory_pin(struct nv_vgpu_device *vgpu, u64 gpa, u64 size)
+{
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_mapping *mapping;
+ u64 start_pfn = gpa >> PAGE_SHIFT;
+ u64 end_pfn = (gpa + size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ int page_count = end_pfn - start_pfn;
+ int i, ret = 0;
+
+ if (!ctx)
+ return -ENODEV;
+
+ mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
+ if (!mapping)
+ return -ENOMEM;
+
+ mapping->gpa = gpa;
+ mapping->size = size;
+ mapping->page_count = page_count;
+ mapping->pages = vzalloc(page_count * sizeof(struct page *));
+ if (!mapping->pages) {
+ kfree(mapping);
+ return -ENOMEM;
+ }
+
+ mutex_lock(&ctx->lock);
+
+ for (i = 0; i < page_count; i++) {
+ u64 pfn = start_pfn + i;
+ struct page *page = pfn_to_page(pfn);
+ if (!page) {
+ ret = -EFAULT;
+ goto err_unlock;
+ }
+ get_page(page);
+ mapping->pages[i] = page;
+ }
+
+ mapping->pinned = true;
+ list_add_tail(&mapping->list, &ctx->mappings);
+ ctx->mapping_count++;
+
+ mutex_unlock(&ctx->lock);
+ return 0;
+
+err_unlock:
+ for (i = 0; i < page_count; i++) {
+ if (mapping->pages[i])
+ put_page(mapping->pages[i]);
+ }
+ vfree(mapping->pages);
+ kfree(mapping);
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+int nv_vgpu_memory_unpin(struct nv_vgpu_device *vgpu, u64 gpa, u64 size)
+{
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_mapping *mapping;
+ u64 iova = 0;
+ int ret = 0;
+
+ if (!ctx)
+ return -ENODEV;
+
+ mutex_lock(&ctx->lock);
+ list_for_each_entry(mapping, &ctx->mappings, list) {
+ if (mapping->gpa == gpa && mapping->size == size) {
+ if (mapping->mapped) {
+ iova = mapping->iova;
+ ret = -EBUSY;
+ } else if (mapping->pinned) {
+ int i;
+ for (i = 0; i < mapping->page_count; i++) {
+ if (mapping->pages[i])
+ put_page(mapping->pages[i]);
+ }
+ mapping->pinned = false;
+ }
+ list_del(&mapping->list);
+ ctx->mapping_count--;
+ vfree(mapping->pages);
+ kfree(mapping);
+ break;
+ }
+ }
+ mutex_unlock(&ctx->lock);
+
+ return ret;
+}
+
+int nv_vgpu_memory_map(struct nv_vgpu_device *vgpu, u64 gpa, u64 size, u64 *iova)
+{
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_mapping *mapping;
+ u64 map_iova;
+ int ret;
+
+ if (!ctx || !ctx->iommu_domain)
+ return -ENODEV;
+
+ mutex_lock(&ctx->lock);
+ mapping = nv_vgpu_find_mapping(ctx, 0, 0);
+ if (!mapping || mapping->gpa != gpa || mapping->size != size) {
+ list_for_each_entry(mapping, &ctx->mappings, list) {
+ if (mapping->gpa == gpa && mapping->size == size)
+ break;
+ }
+ if (&mapping->list == &ctx->mappings) {
+ mutex_unlock(&ctx->lock);
+ return -ENOENT;
+ }
+ }
+
+ if (!mapping->pinned) {
+ mutex_unlock(&ctx->lock);
+ return -EINVAL;
+ }
+
+ map_iova = dma_map_page(&vgpu->pdev->dev, mapping->pages[0], 0, size, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(&vgpu->pdev->dev, map_iova)) {
+ mutex_unlock(&ctx->lock);
+ return -ENOMEM;
+ }
+
+ mapping->iova = map_iova;
+ mapping->mapped = true;
+ *iova = map_iova;
+
+ ret = iommu_map(ctx->iommu_domain, map_iova, gpa, size,
+ IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE, GFP_KERNEL);
+ if (ret) {
+ dma_unmap_page(&vgpu->pdev->dev, map_iova, size, DMA_BIDIRECTIONAL);
+ mapping->mapped = false;
+ mapping->iova = 0;
+ }
+
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+int nv_vgpu_memory_unmap(struct nv_vgpu_device *vgpu, u64 iova, u64 size)
+{
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_mapping *mapping;
+ int ret = 0;
+
+ if (!ctx || !ctx->iommu_domain)
+ return -ENODEV;
+
+ mutex_lock(&ctx->lock);
+ mapping = nv_vgpu_find_mapping(ctx, iova, size);
+ if (!mapping || !mapping->mapped) {
+ mutex_unlock(&ctx->lock);
+ return -ENOENT;
+ }
+
+ ret = iommu_unmap(ctx->iommu_domain, iova, size);
+ if (ret == 0) {
+ dma_unmap_page(&vgpu->pdev->dev, iova, size, DMA_BIDIRECTIONAL);
+ mapping->mapped = false;
+ mapping->iova = 0;
+ }
+
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+static vm_fault_t nv_vgpu_vm_fault(struct vm_fault *vmf)
+{
+ struct nv_vgpu_device *vgpu = vmf->vma->vm_private_data;
+ struct nv_vgpu_mm_ctx *ctx = vgpu->mm_ctx;
+ struct nv_vgpu_mapping *mapping;
+ u64 offset = vmf->address - vmf->vma->vm_start + vmf->vma->vm_pgoff * PAGE_SIZE;
+ u64 pfn;
+ vm_fault_t ret = VM_FAULT_SIGBUS;
+
+ if (!ctx)
+ return VM_FAULT_SIGBUS;
+
+ mutex_lock(&ctx->lock);
+ list_for_each_entry(mapping, &ctx->mappings, list) {
+ if (mapping->mapped && offset >= mapping->iova &&
+ offset < mapping->iova + mapping->size) {
+ pfn = (mapping->gpa + offset - mapping->iova) >> PAGE_SHIFT;
+ vmf->page = pfn_to_page(pfn);
+ get_page(vmf->page);
+ ret = VM_FAULT_NOPAGE;
+ break;
+ }
+ }
+ mutex_unlock(&ctx->lock);
+
+ return ret;
+}
+
+static const struct vm_operations_struct nv_vgpu_vm_ops = {
+ .fault = nv_vgpu_vm_fault,
+};
+
+int nv_vgpu_memory_mmap(struct nv_vgpu_device *vgpu, struct vm_area_struct *vma)
+{
+ vma->vm_ops = &nv_vgpu_vm_ops;
+ vma->vm_private_data = vgpu;
+ vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
+ return 0;
+} \ No newline at end of file
diff --git a/kernel/migration.c b/kernel/migration.c
new file mode 100644
index 0000000..c8814a2
--- /dev/null
+++ b/kernel/migration.c
@@ -0,0 +1,324 @@
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/crc32.h>
+#include <linux/kthread.h>
+#include <linux/completion.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "migration.h"
+
+static int nv_vgpu_migration_save_thread(void *data)
+{
+ struct nv_vgpu_device *vgpu = data;
+ struct nv_vgpu_migration_ctx *ctx = vgpu->migration.ctx;
+ u8 *buffer;
+ size_t size;
+ int ret;
+
+ ret = nv_vgpu_migration_save_state(vgpu, &buffer, &size);
+ if (ret) {
+ ctx->state.result = ret;
+ } else {
+ ctx->state.data = buffer;
+ ctx->state.data_size = size;
+ ctx->state.result = 0;
+ }
+
+ complete(&ctx->state.done);
+ return 0;
+}
+
+static int nv_vgpu_migration_restore_thread(void *data)
+{
+ struct nv_vgpu_device *vgpu = data;
+ struct nv_vgpu_migration_ctx *ctx = vgpu->migration.ctx;
+ int ret;
+
+ ret = nv_vgpu_migration_restore_state(vgpu, ctx->state.data, ctx->state.data_size);
+ ctx->state.result = ret;
+ complete(&ctx->state.done);
+ return 0;
+}
+
+int nv_vgpu_migration_init(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_migration_ctx *ctx;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vgpu = vgpu;
+ mutex_init(&ctx->lock);
+ init_completion(&ctx->state.done);
+ ctx->in_progress = false;
+
+ vgpu->migration.ctx = ctx;
+ return 0;
+}
+
+void nv_vgpu_migration_cleanup(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_migration_ctx *ctx = vgpu->migration.ctx;
+
+ if (!ctx)
+ return;
+
+ if (ctx->save_thread) {
+ kthread_stop(ctx->save_thread);
+ ctx->save_thread = NULL;
+ }
+ if (ctx->restore_thread) {
+ kthread_stop(ctx->restore_thread);
+ ctx->restore_thread = NULL;
+ }
+
+ if (ctx->state.data) {
+ kfree(ctx->state.data);
+ ctx->state.data = NULL;
+ }
+
+ kfree(ctx);
+ vgpu->migration.ctx = NULL;
+}
+
+int nv_vgpu_migration_save_state(struct nv_vgpu_device *vgpu, u8 **data, size_t *size)
+{
+ struct nv_vgpu_migration_header *hdr;
+ struct nv_vgpu_migration_section *sections;
+ struct nv_vgpu_migration_device_state *dev_state;
+ struct nv_vgpu_migration_bar_state *bar_state;
+ u8 *buffer;
+ size_t total_size = 0;
+ int i, num_sections = 0;
+
+ mutex_lock(&vgpu->lock);
+
+ total_size = sizeof(struct nv_vgpu_migration_header) +
+ (8 * sizeof(struct nv_vgpu_migration_section));
+
+ for (i = 0; i < NV_VGPU_MAX_REGIONS; i++) {
+ if (vgpu->bars[i].mapped) {
+ total_size += sizeof(struct nv_vgpu_migration_bar_state) + vgpu->bars[i].size;
+ num_sections++;
+ }
+ }
+
+ buffer = kmalloc(total_size, GFP_KERNEL);
+ if (!buffer) {
+ mutex_unlock(&vgpu->lock);
+ return -ENOMEM;
+ }
+
+ hdr = (struct nv_vgpu_migration_header *)buffer;
+ hdr->magic = NV_VGPU_MIGRATION_MAGIC;
+ hdr->version = NV_VGPU_MIGRATION_VERSION;
+ hdr->flags = 0;
+ hdr->num_sections = num_sections + 1;
+ hdr->total_size = total_size;
+ hdr->timestamp = ktime_get_ns();
+
+ sections = (struct nv_vgpu_migration_section *)(buffer + sizeof(*hdr));
+
+ dev_state = (struct nv_vgpu_migration_device_state *)(buffer + sizeof(*hdr) +
+ (hdr->num_sections * sizeof(*sections)));
+
+ dev_state->vgpu_type = vgpu->type;
+ dev_state->vgpu_id = vgpu->vgpu_id;
+ dev_state->gpu_instance_id = vgpu->gpu_instance_id;
+ dev_state->compute_instance_id = vgpu->compute_instance_id;
+ dev_state->state = vgpu->state;
+ dev_state->fb_base = vgpu->fb_base;
+ dev_state->fb_size = vgpu->fb_size;
+ dev_state->bar0_config_base = vgpu->bar0_config_base;
+
+ sections[0].type = NV_VGPU_MIG_SECTION_DEVICE_STATE;
+ sections[0].flags = 0;
+ sections[0].offset = (u8 *)dev_state - buffer;
+ sections[0].size = sizeof(*dev_state);
+ sections[0].checksum = crc32(0, dev_state, sizeof(*dev_state));
+
+ u8 *data_ptr = (u8 *)dev_state + sizeof(*dev_state);
+ for (i = 0; i < NV_VGPU_MAX_REGIONS; i++) {
+ if (!vgpu->bars[i].mapped)
+ continue;
+
+ bar_state = (struct nv_vgpu_migration_bar_state *)data_ptr;
+ bar_state->bar_index = i;
+ bar_state->gpa = vgpu->bars[i].gpa;
+ bar_state->size = vgpu->bars[i].size;
+ bar_state->iova = vgpu->bars[i].iova;
+
+ if (vgpu->bars[i].vaddr && vgpu->bars[i].size > 0) {
+ memcpy(bar_state->data, vgpu->bars[i].vaddr, vgpu->bars[i].size);
+ }
+
+ sections[num_sections].type = NV_VGPU_MIG_SECTION_BAR_STATE;
+ sections[num_sections].flags = 0;
+ sections[num_sections].offset = data_ptr - buffer;
+ sections[num_sections].size = sizeof(*bar_state) + vgpu->bars[i].size;
+ sections[num_sections].checksum = crc32(0, bar_state, sections[num_sections].size);
+
+ data_ptr += sections[num_sections].size;
+ num_sections++;
+ }
+
+ hdr->num_sections = num_sections;
+ hdr->total_size = data_ptr - buffer;
+
+ *data = buffer;
+ *size = hdr->total_size;
+
+ mutex_unlock(&vgpu->lock);
+ return 0;
+}
+
+int nv_vgpu_migration_restore_state(struct nv_vgpu_device *vgpu, u8 *data, size_t size)
+{
+ struct nv_vgpu_migration_header *hdr = (struct nv_vgpu_migration_header *)data;
+ struct nv_vgpu_migration_section *sections;
+ struct nv_vgpu_migration_device_state *dev_state;
+ struct nv_vgpu_migration_bar_state *bar_state;
+ int i;
+
+ if (!data || size < sizeof(*hdr))
+ return -EINVAL;
+
+ if (hdr->magic != NV_VGPU_MIGRATION_MAGIC)
+ return -EINVAL;
+
+ if (hdr->version > NV_VGPU_MIGRATION_VERSION)
+ return -EINVAL;
+
+ sections = (struct nv_vgpu_migration_section *)(data + sizeof(*hdr));
+
+ mutex_lock(&vgpu->lock);
+
+ for (i = 0; i < hdr->num_sections; i++) {
+ u32 checksum;
+ u8 *section_data = data + sections[i].offset;
+
+ checksum = crc32(0, section_data, sections[i].size);
+ if (checksum != sections[i].checksum) {
+ nv_vgpu_err(vgpu, "Migration checksum mismatch section %d", i);
+ mutex_unlock(&vgpu->lock);
+ return -EIO;
+ }
+
+ switch (sections[i].type) {
+ case NV_VGPU_MIG_SECTION_DEVICE_STATE:
+ dev_state = (struct nv_vgpu_migration_device_state *)section_data;
+ vgpu->type = dev_state->vgpu_type;
+ vgpu->vgpu_id = dev_state->vgpu_id;
+ vgpu->gpu_instance_id = dev_state->gpu_instance_id;
+ vgpu->compute_instance_id = dev_state->compute_instance_id;
+ vgpu->state = dev_state->state;
+ vgpu->fb_base = dev_state->fb_base;
+ vgpu->fb_size = dev_state->fb_size;
+ vgpu->bar0_config_base = dev_state->bar0_config_base;
+ break;
+
+ case NV_VGPU_MIG_SECTION_BAR_STATE:
+ bar_state = (struct nv_vgpu_migration_bar_state *)section_data;
+ if (bar_state->bar_index < NV_VGPU_MAX_REGIONS) {
+ vgpu->bars[bar_state->bar_index].gpa = bar_state->gpa;
+ vgpu->bars[bar_state->bar_index].size = bar_state->size;
+ vgpu->bars[bar_state->bar_index].iova = bar_state->iova;
+ if (vgpu->bars[bar_state->bar_index].vaddr && bar_state->size > 0) {
+ memcpy(vgpu->bars[bar_state->bar_index].vaddr, bar_state->data, bar_state->size);
+ }
+ }
+ break;
+
+ default:
+ nv_vgpu_warn(vgpu, "Unknown migration section type: %d", sections[i].type);
+ break;
+ }
+ }
+
+ mutex_unlock(&vgpu->lock);
+ return 0;
+}
+
+int nv_vgpu_migration_prepare(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_migration_ctx *ctx = vgpu->migration.ctx;
+
+ if (!ctx)
+ return -ENODEV;
+
+ mutex_lock(&ctx->lock);
+ if (ctx->in_progress) {
+ mutex_unlock(&ctx->lock);
+ return -EBUSY;
+ }
+ ctx->in_progress = true;
+ ctx->state.version = NV_VGPU_MIGRATION_VERSION;
+ ctx->state.flags = 0;
+ ctx->state.timestamp = ktime_get_ns();
+ reinit_completion(&ctx->state.done);
+ mutex_unlock(&ctx->lock);
+
+ ctx->save_thread = kthread_run(nv_vgpu_migration_save_thread, vgpu, "nv-vgpu-mig-save");
+ if (IS_ERR(ctx->save_thread)) {
+ ctx->in_progress = false;
+ return PTR_ERR(ctx->save_thread);
+ }
+
+ return 0;
+}
+
+int nv_vgpu_migration_complete(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_migration_ctx *ctx = vgpu->migration.ctx;
+ int ret;
+
+ if (!ctx)
+ return -ENODEV;
+
+ wait_for_completion(&ctx->state.done);
+ ret = ctx->state.result;
+
+ mutex_lock(&ctx->lock);
+ ctx->in_progress = false;
+ ctx->save_thread = NULL;
+ mutex_unlock(&ctx->lock);
+
+ return ret;
+}
+
+int nv_vgpu_migration_cancel(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_migration_ctx *ctx = vgpu->migration.ctx;
+
+ if (!ctx)
+ return -ENODEV;
+
+ if (ctx->save_thread) {
+ kthread_stop(ctx->save_thread);
+ ctx->save_thread = NULL;
+ }
+ if (ctx->restore_thread) {
+ kthread_stop(ctx->restore_thread);
+ ctx->restore_thread = NULL;
+ }
+
+ mutex_lock(&ctx->lock);
+ ctx->in_progress = false;
+ mutex_unlock(&ctx->lock);
+
+ return 0;
+}
+
+u8 *nv_vgpu_migration_get_data(struct nv_vgpu_device *vgpu, size_t *size)
+{
+ struct nv_vgpu_migration_ctx *ctx = vgpu->migration.ctx;
+
+ if (!ctx || !ctx->state.data)
+ return NULL;
+
+ if (size)
+ *size = ctx->state.data_size;
+
+ return ctx->state.data;
+} \ No newline at end of file
diff --git a/kernel/tests/Makefile b/kernel/tests/Makefile
new file mode 100644
index 0000000..ce44670
--- /dev/null
+++ b/kernel/tests/Makefile
@@ -0,0 +1,40 @@
+# GSP vGPU-IO Driver Test Makefile
+
+# Comprehensive test module
+obj-m += gsp_vgpu_comprehensive_test.o
+
+# KUnit test module (requires CONFIG_KUNIT)
+obj-$(CONFIG_KUNIT) += gsp_vgpu_kunit_test.o
+
+# Include driver headers
+ccflags-y := -I$OPEN_IOV_ROOT/driver/include
+
+# Build against the kernel source
+KDIR ?= /lib/modules/$(shell uname -r)/build
+
+all:
+ $(MAKE) -C $(KDIR) M=$(PWD) modules
+
+clean:
+ $(MAKE) -C $(KDIR) M=$(PWD) clean
+
+install:
+ $(MAKE) -C $(KDIR) M=$(PWD) modules_install
+
+test: all
+ @echo "Loading comprehensive test module..."
+ sudo insmod gsp_vgpu_comprehensive_test.ko
+ @echo "Test module loaded, checking dmesg..."
+ dmesg -T | tail -50
+
+test-kunit: all
+ @echo "Loading KUnit test module..."
+ sudo insmod gsp_vgpu_kunit_test.ko
+ @echo "KUnit test module loaded, checking dmesg..."
+ dmesg -T | grep -E "(kunit|KUNIT|PASS|FAIL)" | tail -50
+
+test-unload:
+ sudo rmmod gsp_vgpu_comprehensive_test gsp_vgpu_kunit_test 2>/dev/null || true
+ dmesg -T | tail -20
+
+.PHONY: all clean install test test-kunit test-unload \ No newline at end of file
diff --git a/kernel/tests/Module.symvers b/kernel/tests/Module.symvers
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/kernel/tests/Module.symvers
diff --git a/kernel/tests/gsp_rpc_test.c b/kernel/tests/gsp_rpc_test.c
new file mode 100644
index 0000000..f609066
--- /dev/null
+++ b/kernel/tests/gsp_rpc_test.c
@@ -0,0 +1,425 @@
+/*
+ * GSP RPC Client Test/Validation Harness
+ * Tests message encoding/decoding, RPC structure validation
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include "../include/gsp_rpc_client.h"
+#include "../include/nvidia-vgpu-vfio.h"
+
+#define TEST_PASS 0
+#define TEST_FAIL -1
+
+static int test_count = 0;
+static int pass_count = 0;
+static int fail_count = 0;
+
+#define TEST_ASSERT(cond, msg) \
+ do { \
+ test_count++; \
+ if (cond) { \
+ pass_count++; \
+ pr_info("PASS: %s\n", msg); \
+ } else { \
+ fail_count++; \
+ pr_err("FAIL: %s\n", msg); \
+ } \
+ } while (0)
+
+#define TEST_ASSERT_EQ(a, b, msg) \
+ TEST_ASSERT((a) == (b), msg " (expected " #b ", got " #a ")")
+
+/* ============================================================
+ * Structure Size/Alignment Tests
+ * ============================================================ */
+
+static int test_structure_sizes(void)
+{
+ pr_info("=== Testing Structure Sizes ===\n");
+
+ TEST_ASSERT_EQ(sizeof(struct gsp_rpc_msg), 72, "gsp_rpc_msg size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_rpc_response), 64, "gsp_rpc_response size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_alloc_req), 40, "gsp_alloc_req size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_alloc_rsp), 24, "gsp_alloc_rsp size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_map_req), 48, "gsp_map_req size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_map_rsp), 8, "gsp_map_rsp size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_pin_req), 40, "gsp_pin_req size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_pin_rsp), 16, "gsp_pin_rsp size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_sched_req), 40, "gsp_sched_req size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_bar_trap_req), 32, "gsp_bar_trap_req size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_bar_trap_rsp), 16, "gsp_bar_trap_rsp size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_migrate_req), 56, "gsp_migrate_req size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_migrate_rsp), 16, "gsp_migrate_rsp size");
+
+ return TEST_PASS;
+}
+
+static int test_structure_alignment(void)
+{
+ pr_info("=== Testing Structure Alignment ===\n");
+
+ TEST_ASSERT_EQ(__alignof__(struct gsp_rpc_msg), 8, "gsp_rpc_msg alignment");
+ TEST_ASSERT_EQ(__alignof__(struct gsp_rpc_response), 8, "gsp_rpc_response alignment");
+ TEST_ASSERT_EQ(__alignof__(struct gsp_alloc_req), 8, "gsp_alloc_req alignment");
+ TEST_ASSERT_EQ(__alignof__(struct gsp_map_req), 8, "gsp_map_req alignment");
+ TEST_ASSERT_EQ(__alignof__(struct gsp_pin_req), 8, "gsp_pin_req alignment");
+ TEST_ASSERT_EQ(__alignof__(struct gsp_sched_req), 8, "gsp_sched_req alignment");
+ TEST_ASSERT_EQ(__alignof__(struct gsp_bar_trap_req), 8, "gsp_bar_trap_req alignment");
+ TEST_ASSERT_EQ(__alignof__(struct gsp_migrate_req), 8, "gsp_migrate_req alignment");
+
+ return TEST_PASS;
+}
+
+/* ============================================================
+ * Message Encoding/Decoding Tests
+ * ============================================================ */
+
+static int test_rpc_msg_encoding(void)
+{
+ struct gsp_rpc_msg msg = {0};
+ struct gsp_alloc_req *alloc_req = (struct gsp_alloc_req *)msg.payload;
+
+ pr_info("=== Testing RPC Message Encoding ===\n");
+
+ /* Test ALLOC message encoding */
+ msg.type = cpu_to_le32(GSP_RPC_ALLOC);
+ msg.subcmd = cpu_to_le32(GSP_ALLOC_SUBCMD_DEFAULT);
+ msg.seq_id = cpu_to_le32(0x12345678);
+ msg.flags = cpu_to_le32(0x00000001);
+
+ alloc_req->size = cpu_to_le64(0x100000);
+ alloc_req->alignment = cpu_to_le64(0x1000);
+ alloc_req->mem_type = cpu_to_le32(0x2);
+ alloc_req->flags = cpu_to_le32(0x3);
+ alloc_req->addr_hint = cpu_to_le64(0x0);
+ alloc_req->pid = cpu_to_le32(0x456);
+ alloc_req->reserved = 0;
+
+ TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_ALLOC, "RPC type field");
+ TEST_ASSERT_EQ(le32_to_cpu(msg.subcmd), GSP_ALLOC_SUBCMD_DEFAULT, "RPC subcmd field");
+ TEST_ASSERT_EQ(le32_to_cpu(msg.seq_id), 0x12345678, "RPC seq_id field");
+ TEST_ASSERT_EQ(le32_to_cpu(msg.flags), 0x00000001, "RPC flags field");
+ TEST_ASSERT_EQ(le64_to_cpu(alloc_req->size), 0x100000, "ALLOC size field");
+ TEST_ASSERT_EQ(le64_to_cpu(alloc_req->alignment), 0x1000, "ALLOC alignment field");
+ TEST_ASSERT_EQ(le32_to_cpu(alloc_req->mem_type), 0x2, "ALLOC mem_type field");
+ TEST_ASSERT_EQ(le32_to_cpu(alloc_req->flags), 0x3, "ALLOC flags field");
+ TEST_ASSERT_EQ(le64_to_cpu(alloc_req->addr_hint), 0x0, "ALLOC addr_hint field");
+ TEST_ASSERT_EQ(le32_to_cpu(alloc_req->pid), 0x456, "ALLOC pid field");
+
+ return TEST_PASS;
+}
+
+static int test_map_msg_encoding(void)
+{
+ struct gsp_rpc_msg msg = {0};
+ struct gsp_map_req *map_req = (struct gsp_map_req *)msg.payload;
+
+ pr_info("=== Testing MAP Message Encoding ===\n");
+
+ msg.type = cpu_to_le32(GSP_RPC_MAP);
+ msg.subcmd = cpu_to_le32(0);
+ msg.seq_id = cpu_to_le32(0xABCDEF00);
+
+ map_req->handle = cpu_to_le64(0xDEADBEEF);
+ map_req->gpu_va = cpu_to_le64(0x80000000);
+ map_req->size = cpu_to_le64(0x200000);
+ map_req->prot = cpu_to_le32(0x7);
+ map_req->flags = cpu_to_le32(0x1);
+ map_req->offset = cpu_to_le64(0x0);
+ map_req->reserved = 0;
+
+ TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_MAP, "MAP RPC type");
+ TEST_ASSERT_EQ(le64_to_cpu(map_req->handle), 0xDEADBEEF, "MAP handle");
+ TEST_ASSERT_EQ(le64_to_cpu(map_req->gpu_va), 0x80000000, "MAP gpu_va");
+ TEST_ASSERT_EQ(le64_to_cpu(map_req->size), 0x200000, "MAP size");
+ TEST_ASSERT_EQ(le32_to_cpu(map_req->prot), 0x7, "MAP prot");
+ TEST_ASSERT_EQ(le32_to_cpu(map_req->flags), 0x1, "MAP flags");
+ TEST_ASSERT_EQ(le64_to_cpu(map_req->offset), 0x0, "MAP offset");
+
+ return TEST_PASS;
+}
+
+static int test_pin_msg_encoding(void)
+{
+ struct gsp_rpc_msg msg = {0};
+ struct gsp_pin_req *pin_req = (struct gsp_pin_req *)msg.payload;
+
+ pr_info("=== Testing PIN Message Encoding ===\n");
+
+ msg.type = cpu_to_le32(GSP_RPC_PIN);
+ msg.subcmd = cpu_to_le32(GSP_PIN_SUBCMD_DEFAULT);
+
+ pin_req->handle = cpu_to_le64(0xCAFEBABE);
+ pin_req->offset = cpu_to_le64(0x1000);
+ pin_req->size = cpu_to_le64(0x10000);
+ pin_req->flags = cpu_to_le32(0x3);
+ pin_req->reserved[0] = 0;
+ pin_req->reserved[1] = 0;
+
+ TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_PIN, "PIN RPC type");
+ TEST_ASSERT_EQ(le32_to_cpu(msg.subcmd), GSP_PIN_SUBCMD_DEFAULT, "PIN subcmd");
+ TEST_ASSERT_EQ(le64_to_cpu(pin_req->handle), 0xCAFEBABE, "PIN handle");
+ TEST_ASSERT_EQ(le64_to_cpu(pin_req->offset), 0x1000, "PIN offset");
+ TEST_ASSERT_EQ(le64_to_cpu(pin_req->size), 0x10000, "PIN size");
+ TEST_ASSERT_EQ(le32_to_cpu(pin_req->flags), 0x3, "PIN flags");
+
+ return TEST_PASS;
+}
+
+static int test_sched_msg_encoding(void)
+{
+ struct gsp_rpc_msg msg = {0};
+ struct gsp_sched_req *sched_req = (struct gsp_sched_req *)msg.payload;
+
+ pr_info("=== Testing SCHEDULE Message Encoding ===\n");
+
+ msg.type = cpu_to_le32(GSP_RPC_SCHEDULE);
+ msg.subcmd = cpu_to_le32(GSP_SCHED_SUBCMD_DEFAULT);
+
+ sched_req->work_handle = cpu_to_le64(0x11111111);
+ sched_req->callback = cpu_to_le64(0x22222222);
+ sched_req->arg = cpu_to_le64(0x33333333);
+ sched_req->priority = cpu_to_le32(0x5);
+ sched_req->flags = cpu_to_le32(0x0);
+ sched_req->reserved[0] = 0;
+ sched_req->reserved[1] = 0;
+
+ TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_SCHEDULE, "SCHED RPC type");
+ TEST_ASSERT_EQ(le64_to_cpu(sched_req->work_handle), 0x11111111, "SCHED work_handle");
+ TEST_ASSERT_EQ(le64_to_cpu(sched_req->callback), 0x22222222, "SCHED callback");
+ TEST_ASSERT_EQ(le64_to_cpu(sched_req->arg), 0x33333333, "SCHED arg");
+ TEST_ASSERT_EQ(le32_to_cpu(sched_req->priority), 0x5, "SCHED priority");
+
+ return TEST_PASS;
+}
+
+static int test_bar_trap_msg_encoding(void)
+{
+ struct gsp_rpc_msg msg = {0};
+ struct gsp_bar_trap_req *bar_req = (struct gsp_bar_trap_req *)msg.payload;
+
+ pr_info("=== Testing BAR_TRAP Message Encoding ===\n");
+
+ msg.type = cpu_to_le32(GSP_RPC_BAR_TRAP);
+ msg.subcmd = cpu_to_le32(GSP_BAR_TRAP_SUBCMD_DEFAULT);
+
+ bar_req->bar_addr = cpu_to_le64(0x00100000);
+ bar_req->access_type = cpu_to_le32(0x1);
+ bar_req->size = cpu_to_le32(0x4);
+ bar_req->value = cpu_to_le64(0xDEADBEEF);
+ bar_req->bar_num = cpu_to_le32(0x1);
+
+ TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_BAR_TRAP, "BAR_TRAP RPC type");
+ TEST_ASSERT_EQ(le64_to_cpu(bar_req->bar_addr), 0x00100000, "BAR_TRAP bar_addr");
+ TEST_ASSERT_EQ(le32_to_cpu(bar_req->access_type), 0x1, "BAR_TRAP access_type");
+ TEST_ASSERT_EQ(le32_to_cpu(bar_req->size), 0x4, "BAR_TRAP size");
+ TEST_ASSERT_EQ(le64_to_cpu(bar_req->value), 0xDEADBEEF, "BAR_TRAP value");
+ TEST_ASSERT_EQ(le32_to_cpu(bar_req->bar_num), 0x1, "BAR_TRAP bar_num");
+
+ return TEST_PASS;
+}
+
+static int test_migrate_msg_encoding(void)
+{
+ struct gsp_rpc_msg msg = {0};
+ struct gsp_migrate_req *mig_req = (struct gsp_migrate_req *)msg.payload;
+
+ pr_info("=== Testing MIGRATE Message Encoding ===\n");
+
+ msg.type = cpu_to_le32(GSP_RPC_MIGRATE);
+ msg.subcmd = cpu_to_le32(GSP_MIGRATE_SUBCMD_SAVE);
+
+ mig_req->src_gpu_id = cpu_to_le64(0x1);
+ mig_req->dst_gpu_id = cpu_to_le64(0x2);
+ mig_req->memory_bitmap = cpu_to_le64(0xFFFFFFFFFFFFFFFF);
+ mig_req->page_table_ptr = cpu_to_le64(0x80000000);
+ mig_req->state = cpu_to_le32(GSP_MIGRATE_SUBCMD_SAVE);
+ mig_req->flags = cpu_to_le32(0);
+ mig_req->context = cpu_to_le64(0);
+ mig_req->reserved = 0;
+
+ TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_MIGRATE, "MIGRATE RPC type");
+ TEST_ASSERT_EQ(le32_to_cpu(msg.subcmd), GSP_MIGRATE_SUBCMD_SAVE, "MIGRATE subcmd");
+ TEST_ASSERT_EQ(le64_to_cpu(mig_req->src_gpu_id), 0x1, "MIGRATE src_gpu_id");
+ TEST_ASSERT_EQ(le64_to_cpu(mig_req->dst_gpu_id), 0x2, "MIGRATE dst_gpu_id");
+ TEST_ASSERT_EQ(le64_to_cpu(mig_req->memory_bitmap), 0xFFFFFFFFFFFFFFFF, "MIGRATE memory_bitmap");
+ TEST_ASSERT_EQ(le64_to_cpu(mig_req->page_table_ptr), 0x80000000, "MIGRATE page_table_ptr");
+
+ /* Test RESTORE subcmd */
+ msg.subcmd = cpu_to_le32(GSP_MIGRATE_SUBCMD_RESTORE);
+ mig_req->state = cpu_to_le32(GSP_MIGRATE_SUBCMD_RESTORE);
+ mig_req->flags = cpu_to_le32(GSP_MIGRATE_SUBCMD_RESTORE);
+ mig_req->context = cpu_to_le64(0xDEADBEEF);
+
+ TEST_ASSERT_EQ(le32_to_cpu(msg.subcmd), GSP_MIGRATE_SUBCMD_RESTORE, "MIGRATE restore subcmd");
+ TEST_ASSERT_EQ(le64_to_cpu(mig_req->context), 0xDEADBEEF, "MIGRATE checkpoint context");
+
+ return TEST_PASS;
+}
+
+/* ============================================================
+ * Firmware Address Validation Tests
+ * ============================================================ */
+
+static int test_firmware_addresses(void)
+{
+ pr_info("=== Testing Firmware Address Constants ===\n");
+
+ TEST_ASSERT_EQ(GSP_FW_BASE, 0x04000000UL, "GSP_FW_BASE");
+ TEST_ASSERT_EQ(GSP_RPC_DISPATCH_PRIMARY, 0x057a1ac0UL, "Primary Dispatcher");
+ TEST_ASSERT_EQ(GSP_RPC_JUMP_TABLE, 0x057a3ad8UL, "Jump Table");
+ TEST_ASSERT_EQ(GSP_RPC_DISPATCH_SECONDARY, 0x04ed39dcUL, "Secondary Dispatcher");
+ TEST_ASSERT_EQ(GSP_RPC_DISPATCH_TERTIARY, 0x04cc58f8UL, "Tertiary Dispatcher");
+ TEST_ASSERT_EQ(GSP_RPC_DISPATCH_QUATERNARY, 0x05723ef4UL, "Quaternary Dispatcher");
+ TEST_ASSERT_EQ(GSP_ALLOC_MAIN, 0x057e94ecUL, "ALLOC Main");
+ TEST_ASSERT_EQ(GSP_SCHEDULE_MAIN, 0x051efbf4UL, "SCHEDULE Main");
+ TEST_ASSERT_EQ(GSP_BAR_TRAP_HANDLER_JT, 0x057675b7UL, "BAR_TRAP Handler (JT)");
+ TEST_ASSERT_EQ(GSP_BAR_TRAP_HANDLER_PRIM, 0x04cb8189UL, "BAR_TRAP Handler (Primary)");
+ TEST_ASSERT_EQ(GSP_PIN_HANDLER_SEC, 0x04ed34b8UL, "PIN Handler (Secondary)");
+ TEST_ASSERT_EQ(GSP_MAP_HANDLER_A, 0x05766ab7UL, "MAP Handler A");
+ TEST_ASSERT_EQ(GSP_MAP_HANDLER_B, 0x05778a37UL, "MAP Handler B");
+ TEST_ASSERT_EQ(GSP_UNMAP_HANDLER_A, 0x057669b7UL, "UNMAP Handler A");
+ TEST_ASSERT_EQ(GSP_UNMAP_HANDLER_B, 0x05779937UL, "UNMAP Handler B");
+ TEST_ASSERT_EQ(GSP_PIN_HANDLER_A, 0x057664b7UL, "PIN Handler A");
+ TEST_ASSERT_EQ(GSP_PIN_HANDLER_B, 0x0577a3b7UL, "PIN Handler B");
+ TEST_ASSERT_EQ(GSP_UNPIN_HANDLER_A, 0x057682b7UL, "UNPIN Handler A");
+ TEST_ASSERT_EQ(GSP_UNPIN_HANDLER_B, 0x05779fb7UL, "UNPIN Handler B");
+ TEST_ASSERT_EQ(GSP_SCHEDULE_HANDLER_A, 0x05767f37UL, "SCHEDULE Handler A");
+ TEST_ASSERT_EQ(GSP_SCHEDULE_HANDLER_B, 0x0575aeb7UL, "SCHEDULE Handler B");
+ TEST_ASSERT_EQ(GSP_YIELD_HANDLER_A, 0x0575ae37UL, "YIELD Handler A");
+ TEST_ASSERT_EQ(GSP_YIELD_HANDLER_B, 0x05767337UL, "YIELD Handler B");
+ TEST_ASSERT_EQ(GSP_NOTIFY_HANDLER_A, 0x0577c8b7UL, "NOTIFY Handler A");
+ TEST_ASSERT_EQ(GSP_NOTIFY_HANDLER_B, 0x05767837UL, "NOTIFY Handler B");
+ TEST_ASSERT_EQ(GSP_ENTRY_POINT, 0x04009000UL, "Entry Point");
+
+ return TEST_PASS;
+}
+
+/* ============================================================
+ * RPC Type/Subcommand Validation Tests
+ * ============================================================ */
+
+static int test_rpc_types(void)
+{
+ pr_info("=== Testing RPC Type Constants ===\n");
+
+ TEST_ASSERT_EQ(GSP_RPC_ALLOC, 0x01, "RPC_ALLOC");
+ TEST_ASSERT_EQ(GSP_RPC_FREE, 0x02, "RPC_FREE");
+ TEST_ASSERT_EQ(GSP_RPC_MAP, 0x03, "RPC_MAP");
+ TEST_ASSERT_EQ(GSP_RPC_UNMAP, 0x04, "RPC_UNMAP");
+ TEST_ASSERT_EQ(GSP_RPC_PIN, 0x05, "RPC_PIN");
+ TEST_ASSERT_EQ(GSP_RPC_UNPIN, 0x06, "RPC_UNPIN");
+ TEST_ASSERT_EQ(GSP_RPC_SCHEDULE, 0x07, "RPC_SCHEDULE");
+ TEST_ASSERT_EQ(GSP_RPC_YIELD, 0x08, "RPC_YIELD");
+ TEST_ASSERT_EQ(GSP_RPC_NOTIFY, 0x09, "RPC_NOTIFY");
+ TEST_ASSERT_EQ(GSP_RPC_BAR_TRAP, 0x0A, "RPC_BAR_TRAP");
+ TEST_ASSERT_EQ(GSP_RPC_MIGRATE, 0x0B, "RPC_MIGRATE");
+ TEST_ASSERT_EQ(GSP_RPC_DEBUG, 0xFF, "RPC_DEBUG");
+
+ return TEST_PASS;
+}
+
+static int test_alloc_subcmds(void)
+{
+ pr_info("=== Testing ALLOC Sub-commands ===\n");
+
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_DEFAULT, 0, "ALLOC_SUBCMD_DEFAULT");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE1, 1, "ALLOC_SUBCMD_TYPE1");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE2, 3, "ALLOC_SUBCMD_TYPE2");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE3, 4, "ALLOC_SUBCMD_TYPE3");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE4, 5, "ALLOC_SUBCMD_TYPE4");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE5, 8, "ALLOC_SUBCMD_TYPE5");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE6, 10, "ALLOC_SUBCMD_TYPE6");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE7, 12, "ALLOC_SUBCMD_TYPE7");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE8, 17, "ALLOC_SUBCMD_TYPE8");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE9, 19, "ALLOC_SUBCMD_TYPE9");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE10, 20, "ALLOC_SUBCMD_TYPE10");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE11, 21, "ALLOC_SUBCMD_TYPE11");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE12, 22, "ALLOC_SUBCMD_TYPE12");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE13, 23, "ALLOC_SUBCMD_TYPE13");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE14, 24, "ALLOC_SUBCMD_TYPE14");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE15, 25, "ALLOC_SUBCMD_TYPE15");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE16, 26, "ALLOC_SUBCMD_TYPE16");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE17, 27, "ALLOC_SUBCMD_TYPE17");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE18, 28, "ALLOC_SUBCMD_TYPE18");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE19, 29, "ALLOC_SUBCMD_TYPE19");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE20, 30, "ALLOC_SUBCMD_TYPE20");
+ TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE21, 31, "ALLOC_SUBCMD_TYPE21");
+
+ return TEST_PASS;
+}
+
+/* ============================================================
+ * Payload Size Validation
+ * ============================================================ */
+
+static int test_payload_capacity(void)
+{
+ pr_info("=== Testing Payload Capacity ===\n");
+
+ /* gsp_rpc_msg has 6 x u64 payload = 48 bytes */
+ TEST_ASSERT(sizeof(struct gsp_alloc_req) <= 48, "ALLOC req fits in payload");
+ TEST_ASSERT(sizeof(struct gsp_map_req) <= 48, "MAP req fits in payload");
+ TEST_ASSERT(sizeof(struct gsp_pin_req) <= 48, "PIN req fits in payload");
+ TEST_ASSERT(sizeof(struct gsp_sched_req) <= 48, "SCHED req fits in payload");
+ TEST_ASSERT(sizeof(struct gsp_bar_trap_req) <= 48, "BAR_TRAP req fits in payload");
+ /* MIGRATE req is 56 bytes - uses extended payload format */
+ TEST_ASSERT(sizeof(struct gsp_migrate_req) == 56, "MIGRATE req size is 56 bytes");
+
+ return TEST_PASS;
+}
+
+/* ============================================================
+ * Module Init/Exit
+ * ============================================================ */
+
+static int __init gsp_rpc_test_init(void)
+{
+ pr_info("========================================\n");
+ pr_info("GSP RPC Client Test Suite Starting\n");
+ pr_info("========================================\n");
+
+ test_structure_sizes();
+ test_structure_alignment();
+ test_rpc_msg_encoding();
+ test_map_msg_encoding();
+ test_pin_msg_encoding();
+ test_sched_msg_encoding();
+ test_bar_trap_msg_encoding();
+ test_migrate_msg_encoding();
+ test_firmware_addresses();
+ test_rpc_types();
+ test_alloc_subcmds();
+ test_payload_capacity();
+
+ pr_info("========================================\n");
+ pr_info("Test Results: %d total, %d passed, %d failed\n",
+ test_count, pass_count, fail_count);
+ pr_info("========================================\n");
+
+ if (fail_count > 0) {
+ pr_err("TEST SUITE FAILED\n");
+ return -EINVAL;
+ }
+
+ pr_info("TEST SUITE PASSED\n");
+ return 0;
+}
+
+static void __exit gsp_rpc_test_exit(void)
+{
+ pr_info("GSP RPC Client Test Suite Exited\n");
+}
+
+module_init(gsp_rpc_test_init);
+module_exit(gsp_rpc_test_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Open-IOV Project");
+MODULE_DESCRIPTION("GSP RPC Client Test/Validation Harness");
+MODULE_VERSION("1.0");
diff --git a/kernel/tests/gsp_vgpu_comprehensive_test.c b/kernel/tests/gsp_vgpu_comprehensive_test.c
new file mode 100644
index 0000000..7918beb
--- /dev/null
+++ b/kernel/tests/gsp_vgpu_comprehensive_test.c
@@ -0,0 +1,432 @@
+/*
+ * GSP vGPU-IO Driver - Comprehensive Test Suite
+ * Tests all driver components: RPC, VF, BAR, WPR, Migration
+ * Compiles against kernel headers only (no driver dependency)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ktime.h>
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/kref.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/vmalloc.h>
+
+#include <gsp_core.h>
+
+static int test_count = 0;
+static int pass_count = 0;
+static int fail_count = 0;
+
+#define TEST_ASSERT(cond, msg) \
+ do { \
+ test_count++; \
+ if (cond) { \
+ pass_count++; \
+ pr_info("PASS: %s\n", msg); \
+ } else { \
+ fail_count++; \
+ pr_err("FAIL: %s\n", msg); \
+ } \
+ } while (0)
+
+#define TEST_ASSERT_EQ(a, b, msg) \
+ TEST_ASSERT((a) == (b), msg " (expected " #b ", got " #a ")")
+
+#define TEST_ASSERT_NE(a, b, msg) \
+ TEST_ASSERT((a) != (b), msg " (expected not " #b ", got " #a ")")
+
+/* ============================================================
+ * Test: Kernel API Availability
+ * ============================================================ */
+
+static int test_kernel_apis(void)
+{
+ pr_info("=== Testing Kernel API Availability ===\n");
+
+ /* Test kref */
+ struct kref *ref = kmalloc(sizeof(*ref), GFP_KERNEL);
+ TEST_ASSERT(ref != NULL, "kref allocation");
+ kref_init(ref);
+ TEST_ASSERT(kref_read(ref) == 1, "kref_init sets refcount to 1");
+
+ kref_get(ref);
+ TEST_ASSERT(kref_read(ref) == 2, "kref_get increments refcount");
+
+ kref_put(ref, (void (*)(struct kref *))kfree);
+ TEST_ASSERT(kref_read(ref) == 1, "kref_put decrements refcount");
+
+ kref_put(ref, (void (*)(struct kref *))kfree);
+ TEST_ASSERT(kref_read(ref) == 0, "kref_put reaches zero");
+
+ /* Test completion */
+ struct completion comp;
+ init_completion(&comp);
+ TEST_ASSERT(!completion_done(&comp), "completion initially not done");
+ complete(&comp);
+ TEST_ASSERT(completion_done(&comp), "completion marked done");
+
+ init_completion(&comp);
+ TEST_ASSERT(wait_for_completion_timeout(&comp, msecs_to_jiffies(100)) > 0,
+ "wait_for_completion_timeout works");
+
+ /* Test atomic64 */
+ atomic64_t a64 = ATOMIC64_INIT(0);
+ atomic64_inc(&a64);
+ TEST_ASSERT_EQ(atomic64_read(&a64), 1, "atomic64_inc");
+ atomic64_add(5, &a64);
+ TEST_ASSERT_EQ(atomic64_read(&a64), 6, "atomic64_add");
+ atomic64_sub(3, &a64);
+ TEST_ASSERT_EQ(atomic64_read(&a64), 3, "atomic64_sub");
+
+ /* Test mutex */
+ struct mutex mtx;
+ mutex_init(&mtx);
+ mutex_lock(&mtx);
+ TEST_ASSERT(mutex_is_locked(&mtx), "mutex_is_locked after lock");
+ mutex_unlock(&mtx);
+ TEST_ASSERT(!mutex_is_locked(&mtx), "mutex_is_locked after unlock");
+
+ /* Test spinlock */
+ spinlock_t sl = __SPIN_LOCK_UNLOCKED(sl);
+ unsigned long flags;
+ spin_lock_irqsave(&sl, flags);
+ TEST_ASSERT(spin_is_locked(&sl), "spin_is_locked after lock");
+ spin_unlock_irqrestore(&sl, flags);
+ TEST_ASSERT(!spin_is_locked(&sl), "spin_is_locked after unlock");
+
+ /* Test idr */
+ struct idr idr;
+ idr_init(&idr);
+ int id = idr_alloc(&idr, (void *)0xdeadbeef, 1, 100, GFP_KERNEL);
+ TEST_ASSERT(id >= 1 && id < 100, "idr_alloc returns valid id");
+ void *ptr = idr_find(&idr, id);
+ TEST_ASSERT(ptr == (void *)0xdeadbeef, "idr_find returns correct pointer");
+ idr_remove(&idr, id);
+ TEST_ASSERT(idr_find(&idr, id) == NULL, "idr_remove works");
+ idr_destroy(&idr);
+
+ /* Test list */
+ struct list_head head;
+ INIT_LIST_HEAD(&head);
+ TEST_ASSERT(list_empty(&head), "list_empty on new list");
+
+ struct list_head item;
+ INIT_LIST_HEAD(&item);
+ list_add(&item, &head);
+ TEST_ASSERT(!list_empty(&head), "list not empty after add");
+ list_del(&item);
+ TEST_ASSERT(list_empty(&head), "list empty after del");
+
+ /* Test ktime */
+ ktime_t start = ktime_get();
+ ktime_t end = ktime_get();
+ s64 diff = ktime_to_ns(ktime_sub(end, start));
+ TEST_ASSERT(diff >= 0, "ktime_get and ktime_to_ns work");
+
+ return 0;
+}
+
+/* ============================================================
+ * Test: Structure Sizes (from driver header)
+ * ============================================================ */
+
+static int test_structure_sizes(void)
+{
+ pr_info("=== Testing Structure Sizes ===\n");
+
+ /* These sizes must match driver/include/gsp_core.h */
+ TEST_ASSERT_EQ(sizeof(struct gsp_rpc_header), 64, "gsp_rpc_header size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_rpc_message), 256, "gsp_rpc_message size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_rpc_response), 32, "gsp_rpc_response size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_alloc_payload), 40, "gsp_alloc_payload size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_alloc_response), 24, "gsp_alloc_response size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_pin_payload), 40, "gsp_pin_payload size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_pin_response), 8, "gsp_pin_response size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_bar_trap_payload), 40, "gsp_bar_trap_payload size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_bar_trap_response), 8, "gsp_bar_trap_response size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_migrate_payload), 56, "gsp_migrate_payload size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_subcmd_desc), 8, "gsp_subcmd_desc size");
+ TEST_ASSERT_EQ(sizeof(struct gsp_vf_config), 56, "gsp_vf_config size");
+
+ return 0;
+}
+
+/* ============================================================
+ * Test: Enum Values (from driver header)
+ * ============================================================ */
+
+static int test_enum_values(void)
+{
+ pr_info("=== Testing Enum Values ===\n");
+
+ TEST_ASSERT_EQ(GSP_RPC_ALLOC, 0x01, "RPC_ALLOC");
+ TEST_ASSERT_EQ(GSP_RPC_FREE, 0x02, "RPC_FREE");
+ TEST_ASSERT_EQ(GSP_RPC_MAP, 0x03, "RPC_MAP");
+ TEST_ASSERT_EQ(GSP_RPC_UNMAP, 0x04, "RPC_UNMAP");
+ TEST_ASSERT_EQ(GSP_RPC_PIN, 0x05, "RPC_PIN");
+ TEST_ASSERT_EQ(GSP_RPC_UNPIN, 0x06, "RPC_UNPIN");
+ TEST_ASSERT_EQ(GSP_RPC_SCHEDULE, 0x07, "RPC_SCHEDULE");
+ TEST_ASSERT_EQ(GSP_RPC_YIELD, 0x08, "RPC_YIELD");
+ TEST_ASSERT_EQ(GSP_RPC_NOTIFY, 0x09, "RPC_NOTIFY");
+ TEST_ASSERT_EQ(GSP_RPC_BAR_TRAP, 0x0A, "RPC_BAR_TRAP");
+ TEST_ASSERT_EQ(GSP_RPC_MIGRATE, 0x0B, "RPC_MIGRATE");
+ TEST_ASSERT_EQ(GSP_RPC_DEBUG, 0x0C, "RPC_DEBUG");
+
+ TEST_ASSERT_EQ(GSP_MEM_VRAM, 0, "MEM_VRAM");
+ TEST_ASSERT_EQ(GSP_MEM_SYSMEM, 1, "MEM_SYSMEM");
+ TEST_ASSERT_EQ(GSP_MEM_UNCACHED, 2, "MEM_UNCACHED");
+
+ TEST_ASSERT_EQ(GSP_PIN_READ, (1 << 0), "PIN_READ");
+ TEST_ASSERT_EQ(GSP_PIN_WRITE, (1 << 1), "PIN_WRITE");
+ TEST_ASSERT_EQ(GSP_PIN_LONGTERM, (1 << 2), "PIN_LONGTERM");
+
+ TEST_ASSERT_EQ(GSP_MIG_QUERY, 0, "MIG_QUERY");
+ TEST_ASSERT_EQ(GSP_MIG_PREPARE, 1, "MIG_PREPARE");
+ TEST_ASSERT_EQ(GSP_MIG_TRANSFER, 2, "MIG_TRANSFER");
+ TEST_ASSERT_EQ(GSP_MIG_COMMIT, 3, "MIG_COMMIT");
+ TEST_ASSERT_EQ(GSP_MIG_CLEANUP, 4, "MIG_CLEANUP");
+
+ TEST_ASSERT_EQ(GSP_MIG_LIVE, (1 << 0), "MIG_LIVE");
+ TEST_ASSERT_EQ(GSP_MIG_POSTCOPY, (1 << 1), "MIG_POSTCOPY");
+ TEST_ASSERT_EQ(GSP_MIG_AUTO_COMMIT, (1 << 2), "MIG_AUTO_COMMIT");
+ TEST_ASSERT_EQ(GSP_MIG_VERIFY, (1 << 3), "MIG_VERIFY");
+
+ TEST_ASSERT_EQ(GSP_VF_CREATING, 0, "VF_CREATING");
+ TEST_ASSERT_EQ(GSP_VF_ACTIVE, 1, "VF_ACTIVE");
+ TEST_ASSERT_EQ(GSP_VF_MIGRATING, 2, "VF_MIGRATING");
+ TEST_ASSERT_EQ(GSP_VF_SUSPENDED, 3, "VF_SUSPENDED");
+ TEST_ASSERT_EQ(GSP_VF_DESTROYING, 4, "VF_DESTROYING");
+
+ return 0;
+}
+
+/* ============================================================
+ * Test: Error Code Mapping
+ * ============================================================ */
+
+static int test_error_codes(void)
+{
+ pr_info("=== Testing Error Codes ===\n");
+
+ TEST_ASSERT_EQ(GSP_OK, 0, "GSP_OK");
+ TEST_ASSERT_EQ(GSP_EPERM, 0xFFFFFFFF, "GSP_EPERM");
+ TEST_ASSERT_EQ(GSP_ENOENT, 0xFFFFFFFE, "GSP_ENOENT");
+ TEST_ASSERT_EQ(GSP_EINVAL, 0xFFFFFFFD, "GSP_EINVAL");
+ TEST_ASSERT_EQ(GSP_ENODEV, 0xFFFFFFFA, "GSP_ENODEV");
+ TEST_ASSERT_EQ(GSP_ENOMEM, 0xFFFFFFF4, "GSP_ENOMEM");
+ TEST_ASSERT_EQ(GSP_EBUSY, 0xFFFFFFEE, "GSP_EBUSY");
+ TEST_ASSERT_EQ(GSP_ENOSPC, 0xFFFFFFE4, "GSP_ENOSPC");
+ TEST_ASSERT_EQ(GSP_ETIMEDOUT, 0x0000001E, "GSP_ETIMEDOUT");
+ TEST_ASSERT_EQ(GSP_EOVERFLOW, 0x00000025, "GSP_EOVERFLOW");
+ TEST_ASSERT_EQ(GSP_EDEV, 0x0000001F, "GSP_EDEV");
+
+ return 0;
+}
+
+/* ============================================================
+ * Test: Firmware Constants
+ * ============================================================ */
+
+static int test_firmware_constants(void)
+{
+ pr_info("=== Testing Firmware Constants ===\n");
+
+ TEST_ASSERT_EQ(GSP_FW_BASE_ADDR, 0x04000000ULL, "FW_BASE_ADDR");
+ TEST_ASSERT_EQ(GSP_FW_SIZE, 0x02000000ULL, "FW_SIZE");
+ TEST_ASSERT_EQ(GSP_ENTRY_POINT, 0x04009000ULL, "ENTRY_POINT");
+ TEST_ASSERT_EQ(GSP_PRIMARY_DISPATCHER, 0x057A1AC0ULL, "PRIMARY_DISPATCHER");
+ TEST_ASSERT_EQ(GSP_SECONDARY_DISPATCHER, 0x04ED39DCULL, "SECONDARY_DISPATCHER");
+ TEST_ASSERT_EQ(GSP_RPC_JUMP_TABLE, 0x059BFEB0ULL, "RPC_JUMP_TABLE");
+ TEST_ASSERT_EQ(GSP_BAR0_DOORBELL, 0x1000, "BAR0_DOORBELL");
+ TEST_ASSERT_EQ(GSP_BAR0_MSIX, 0x2000, "BAR0_MSIX");
+ TEST_ASSERT_EQ(GSP_MSG_RING_SIZE, 256, "MSG_RING_SIZE");
+ TEST_ASSERT_EQ(GSP_MSG_MAX_SIZE, 256, "MSG_MAX_SIZE");
+ TEST_ASSERT_EQ(GSP_MAX_PENDING, 64, "MAX_PENDING");
+
+ return 0;
+}
+
+/* ============================================================
+ * Test: Migration State Enum (internal)
+ * ============================================================ */
+
+static int test_migration_states(void)
+{
+ pr_info("=== Testing Migration States ===\n");
+
+ TEST_ASSERT_EQ(GSP_MIG_S_IDLE, 0, "MIG_S_IDLE");
+ TEST_ASSERT_EQ(GSP_MIG_S_QUERY, 1, "MIG_S_QUERY");
+ TEST_ASSERT_EQ(GSP_MIG_S_PREPARE, 2, "MIG_S_PREPARE");
+ TEST_ASSERT_EQ(GSP_MIG_S_TRANSFER, 3, "MIG_S_TRANSFER");
+ TEST_ASSERT_EQ(GSP_MIG_S_COMMIT, 4, "MIG_S_COMMIT");
+ TEST_ASSERT_EQ(GSP_MIG_S_CLEANUP, 5, "MIG_S_CLEANUP");
+ TEST_ASSERT_EQ(GSP_MIG_S_FAILED, 6, "MIG_S_FAILED");
+ TEST_ASSERT_EQ(GSP_MIG_S_COMPLETED, 7, "MIG_S_COMPLETED");
+
+ return 0;
+}
+
+/* ============================================================
+ * Test: WPR Flags and Events
+ * ============================================================ */
+
+static int test_wpr_flags(void)
+{
+ pr_info("=== Testing WPR Flags ===\n");
+
+ TEST_ASSERT_EQ(GSP_WPR_WRITE, (1 << 0), "WPR_WRITE");
+ TEST_ASSERT_EQ(GSP_WPR_READ, (1 << 1), "WPR_READ");
+ TEST_ASSERT_EQ(GSP_WPR_EXECUTE, (1 << 2), "WPR_EXECUTE");
+
+ TEST_ASSERT_EQ(GSP_WPR_WRITE_COMPLETE, 0, "WPR_WRITE_COMPLETE");
+ TEST_ASSERT_EQ(GSP_WPR_READ_COMPLETE, 1, "WPR_READ_COMPLETE");
+ TEST_ASSERT_EQ(GSP_WPR_WRITE_TIMEOUT, 2, "WPR_WRITE_TIMEOUT");
+ TEST_ASSERT_EQ(GSP_WPR_READ_TIMEOUT, 3, "WPR_READ_TIMEOUT");
+
+ return 0;
+}
+
+/* ============================================================
+ * Test: Message Flags
+ * ============================================================ */
+
+static int test_message_flags(void)
+{
+ pr_info("=== Testing Message Flags ===\n");
+
+ TEST_ASSERT_EQ(GSP_MSG_SYNC, (1 << 0), "MSG_SYNC");
+ TEST_ASSERT_EQ(GSP_MSG_ASYNC, (1 << 1), "MSG_ASYNC");
+ TEST_ASSERT_EQ(GSP_MSG_NOREPLY, (1 << 2), "MSG_NOREPLY");
+
+ return 0;
+}
+
+/* ============================================================
+ * Test: BAR Trap Types
+ * ============================================================ */
+
+static int test_bar_trap_types(void)
+{
+ pr_info("=== Testing BAR Trap Types ===\n");
+
+ TEST_ASSERT_EQ(GSP_BAR_READ, 0, "BAR_READ");
+ TEST_ASSERT_EQ(GSP_BAR_WRITE, 1, "BAR_WRITE");
+ TEST_ASSERT_EQ(GSP_BAR_ATOMIC, 2, "BAR_ATOMIC");
+
+ return 0;
+}
+
+/* ============================================================
+ * Test: Memory Allocation Patterns
+ * ============================================================ */
+
+static int test_memory_patterns(void)
+{
+ pr_info("=== Testing Memory Allocation Patterns ===\n");
+
+ /* Test vzalloc/vfree */
+ void *ptr = vzalloc(4096);
+ TEST_ASSERT(ptr != NULL, "vzalloc allocates memory");
+ vfree(ptr);
+
+ /* Test kmalloc/kfree */
+ ptr = kmalloc(1024, GFP_KERNEL);
+ TEST_ASSERT(ptr != NULL, "kmalloc allocates memory");
+ kfree(ptr);
+
+ /* Test kcalloc */
+ ptr = kcalloc(10, 64, GFP_KERNEL);
+ TEST_ASSERT(ptr != NULL, "kcalloc allocates zeroed memory");
+ kfree(ptr);
+
+ return 0;
+}
+
+/* ============================================================
+ * Test: String Operations
+ * ============================================================ */
+
+static int test_string_ops(void)
+{
+ pr_info("=== Testing String Operations ===\n");
+
+ char buf[64];
+ int len = snprintf(buf, sizeof(buf), "test_%d", 42);
+ TEST_ASSERT(len > 0 && len < sizeof(buf), "snprintf works");
+ TEST_ASSERT(strcmp(buf, "test_42") == 0, "snprintf formats correctly");
+
+ char *dup = kstrdup("test_string", GFP_KERNEL);
+ TEST_ASSERT(dup != NULL, "kstrdup works");
+ TEST_ASSERT(strcmp(dup, "test_string") == 0, "kstrdup copies correctly");
+ kfree(dup);
+
+ return 0;
+}
+
+/* ============================================================
+ * Test Suite Runner
+ * ============================================================ */
+
+static int run_all_tests(void)
+{
+ pr_info("\n============================================================\n");
+ pr_info("GSP vGPU-IO Comprehensive Test Suite Starting\n");
+ pr_info("============================================================\n\n");
+
+ test_kernel_apis();
+ test_structure_sizes();
+ test_enum_values();
+ test_error_codes();
+ test_firmware_constants();
+ test_migration_states();
+ test_wpr_flags();
+ test_message_flags();
+ test_bar_trap_types();
+ test_memory_patterns();
+ test_string_ops();
+
+ pr_info("\n============================================================\n");
+ pr_info("TEST RESULTS: %d total, %d passed, %d failed\n",
+ test_count, pass_count, fail_count);
+ pr_info("============================================================\n");
+
+ if (fail_count == 0) {
+ pr_info("TEST SUITE PASSED\n");
+ return 0;
+ } else {
+ pr_err("TEST SUITE FAILED\n");
+ return -1;
+ }
+}
+
+/* ============================================================
+ * Module Init/Exit
+ * ============================================================ */
+
+static int __init gsp_test_init(void)
+{
+ return run_all_tests();
+}
+
+static void __exit gsp_test_exit(void)
+{
+ pr_info("GSP vGPU-IO test module unloaded\n");
+}
+
+module_init(gsp_test_init);
+module_exit(gsp_test_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("NVIDIA vGPU-IO Team");
+MODULE_DESCRIPTION("GSP vGPU-IO Comprehensive Test Suite");
+MODULE_VERSION("1.0.0"); \ No newline at end of file
diff --git a/kernel/tests/gsp_vgpu_kunit_test.c b/kernel/tests/gsp_vgpu_kunit_test.c
new file mode 100644
index 0000000..c661f91
--- /dev/null
+++ b/kernel/tests/gsp_vgpu_kunit_test.c
@@ -0,0 +1,327 @@
+/*
+ * GSP vGPU-IO KUnit Tests
+ * In-kernel unit tests for the driver
+ */
+
+#include <kunit/test.h>
+#include <kunit/mock.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/kref.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+
+#include <gsp_core.h>
+
+/* ============================================================
+ * Test: Structure Sizes and Alignments
+ * ============================================================ */
+
+static void test_structure_sizes(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_rpc_header), 64);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_rpc_message), 256);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_rpc_response), 32);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_subcmd_desc), 8);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_vf_config), 64);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_alloc_payload), 48);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_alloc_response), 24);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_pin_payload), 40);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_pin_response), 8);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_bar_trap_payload), 40);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_bar_trap_response), 8);
+ KUNIT_EXPECT_EQ(test, sizeof(struct gsp_migrate_payload), 64);
+}
+
+static void test_structure_alignments(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_rpc_header), 8);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_rpc_message), 8);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_rpc_response), 8);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_subcmd_desc), 4);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_vf_config), 8);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_alloc_payload), 8);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_alloc_response), 8);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_pin_payload), 8);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_pin_response), 8);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_bar_trap_payload), 8);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_bar_trap_response), 8);
+ KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_migrate_payload), 8);
+}
+
+/* ============================================================
+ * Test: Payload Encoding/Decoding
+ * ============================================================ */
+
+static void test_alloc_payload_encoding(struct kunit *test)
+{
+ struct gsp_alloc_payload payload = {0};
+
+ payload.size = cpu_to_le64(0x100000);
+ payload.type = cpu_to_le32(GSP_MEM_VRAM);
+ payload.alignment = cpu_to_le32(0x1000);
+ payload.va_hint = cpu_to_le64(0x0);
+ payload.flags = cpu_to_le32(0x3);
+ payload.pid = cpu_to_le32(0x456);
+ payload.vf_id = cpu_to_le32(0x1);
+
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.size), 0x100000);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.type), GSP_MEM_VRAM);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.alignment), 0x1000);
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.va_hint), 0x0);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.flags), 0x3);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.pid), 0x456);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.vf_id), 0x1);
+}
+
+static void test_pin_payload_encoding(struct kunit *test)
+{
+ struct gsp_pin_payload payload = {0};
+
+ payload.handle = cpu_to_le64(0xCAFEBABE);
+ payload.offset = cpu_to_le64(0x1000);
+ payload.size = cpu_to_le64(0x10000);
+ payload.vf_id = cpu_to_le32(0x2);
+ payload.flags = cpu_to_le32(GSP_PIN_READ | GSP_PIN_WRITE);
+
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.handle), 0xCAFEBABE);
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.offset), 0x1000);
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.size), 0x10000);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.vf_id), 0x2);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.flags), GSP_PIN_READ | GSP_PIN_WRITE);
+}
+
+static void test_bar_trap_payload_encoding(struct kunit *test)
+{
+ struct gsp_bar_trap_payload payload = {0};
+
+ payload.bar_index = cpu_to_le32(1);
+ payload.trap_type = cpu_to_le32(GSP_BAR_WRITE);
+ payload.offset = cpu_to_le64(0x1000);
+ payload.value = cpu_to_le64(0xDEADBEEF);
+ payload.size = cpu_to_le32(4);
+ payload.vf_id = cpu_to_le32(0x1);
+
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.bar_index), 1);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.trap_type), GSP_BAR_WRITE);
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.offset), 0x1000);
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.value), 0xDEADBEEF);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.size), 4);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.vf_id), 0x1);
+}
+
+static void test_migrate_payload_encoding(struct kunit *test)
+{
+ struct gsp_migrate_payload payload = {0};
+
+ payload.phase = cpu_to_le32(GSP_MIG_PREPARE);
+ payload.subcmd = cpu_to_le32(3);
+ payload.src_vf = cpu_to_le64(0x1);
+ payload.dst_vf = cpu_to_le64(0x2);
+ payload.data_ptr = cpu_to_le64(0x80000000);
+ payload.data_size = cpu_to_le64(0x1000);
+ payload.flags = cpu_to_le32(GSP_MIG_LIVE | GSP_MIG_VERIFY);
+ payload.token = cpu_to_le64(0xABCDEF1234567890);
+
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.phase), GSP_MIG_PREPARE);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.subcmd), 3);
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.src_vf), 0x1);
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.dst_vf), 0x2);
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.data_ptr), 0x80000000);
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.data_size), 0x1000);
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.flags), GSP_MIG_LIVE | GSP_MIG_VERIFY);
+ KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.token), 0xABCDEF1234567890);
+}
+
+static void test_subcmd_desc_encoding(struct kunit *test)
+{
+ struct gsp_subcmd_desc desc = {0};
+
+ desc.subcmd_id = cpu_to_le32(0x14);
+ desc.echo_flags_b = 0xAA;
+ desc.echo_flags_a = 0xBB;
+ desc.echo_flags_c = 0xCC;
+
+ KUNIT_EXPECT_EQ(test, le32_to_cpu(desc.subcmd_id), 0x14);
+ KUNIT_EXPECT_EQ(test, desc.echo_flags_b, 0xAA);
+ KUNIT_EXPECT_EQ(test, desc.echo_flags_a, 0xBB);
+ KUNIT_EXPECT_EQ(test, desc.echo_flags_c, 0xCC);
+}
+
+/* ============================================================
+ * Test: Firmware Address Constants
+ * ============================================================ */
+
+static void test_firmware_addresses(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, GSP_FW_BASE_ADDR, 0x04000000ULL);
+ KUNIT_EXPECT_EQ(test, GSP_ENTRY_POINT, 0x04009000ULL);
+ KUNIT_EXPECT_EQ(test, GSP_PRIMARY_DISPATCHER, 0x057A1AC0ULL);
+ KUNIT_EXPECT_EQ(test, GSP_SECONDARY_DISPATCHER, 0x04ED39DCULL);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_JUMP_TABLE, 0x059BFEB0ULL);
+ KUNIT_EXPECT_EQ(test, 0x04179ED8ULL, 0x04179ED8ULL); /* Migration Table 1 */
+ KUNIT_EXPECT_EQ(test, 0x04179FBCULL, 0x04179FBCULL); /* Migration Table 2 */
+ KUNIT_EXPECT_EQ(test, 0x04CB8188ULL, 0x04CB8188ULL); /* BAR Handler */
+ KUNIT_EXPECT_EQ(test, 0x04F00B0CULL, 0x04F00B0CULL); /* WPR Handler 1 */
+ KUNIT_EXPECT_EQ(test, 0x04F00C78ULL, 0x04F00C78ULL); /* WPR Handler 2 */
+ KUNIT_EXPECT_EQ(test, 0x057E94ECULL, 0x057E94ECULL); /* ALLOC Main */
+ KUNIT_EXPECT_EQ(test, 0x051EFBF4ULL, 0x051EFBF4ULL); /* SCHEDULE Main */
+}
+
+/* ============================================================
+ * Test: RPC Type Constants
+ * ============================================================ */
+
+static void test_rpc_types(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, GSP_RPC_ALLOC, 0x01);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_FREE, 0x02);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_MAP, 0x03);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_UNMAP, 0x04);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_PIN, 0x05);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_UNPIN, 0x06);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_SCHEDULE, 0x07);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_YIELD, 0x08);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_NOTIFY, 0x09);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_BAR_TRAP, 0x0A);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_MIGRATE, 0x0B);
+ KUNIT_EXPECT_EQ(test, GSP_RPC_DEBUG, 0x0C);
+}
+
+static void test_migration_constants(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, GSP_MIG_QUERY, 0);
+ KUNIT_EXPECT_EQ(test, GSP_MIG_PREPARE, 1);
+ KUNIT_EXPECT_EQ(test, GSP_MIG_TRANSFER, 2);
+ KUNIT_EXPECT_EQ(test, GSP_MIG_COMMIT, 3);
+ KUNIT_EXPECT_EQ(test, GSP_MIG_CLEANUP, 4);
+
+ KUNIT_EXPECT_EQ(test, GSP_MIG_LIVE, (1 << 0));
+ KUNIT_EXPECT_EQ(test, GSP_MIG_POSTCOPY, (1 << 1));
+ KUNIT_EXPECT_EQ(test, GSP_MIG_AUTO_COMMIT, (1 << 2));
+ KUNIT_EXPECT_EQ(test, GSP_MIG_VERIFY, (1 << 3));
+}
+
+static void test_bar_trap_types(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, GSP_BAR_READ, 0);
+ KUNIT_EXPECT_EQ(test, GSP_BAR_WRITE, 1);
+ KUNIT_EXPECT_EQ(test, GSP_BAR_ATOMIC, 2);
+}
+
+static void test_pin_flags(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, GSP_PIN_READ, (1 << 0));
+ KUNIT_EXPECT_EQ(test, GSP_PIN_WRITE, (1 << 1));
+ KUNIT_EXPECT_EQ(test, GSP_PIN_LONGTERM, (1 << 2));
+}
+
+static void test_wpr_flags(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, GSP_WPR_WRITE, (1 << 0));
+ KUNIT_EXPECT_EQ(test, GSP_WPR_READ, (1 << 1));
+ KUNIT_EXPECT_EQ(test, GSP_WPR_EXECUTE, (1 << 2));
+}
+
+static void test_vf_states(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, GSP_VF_CREATING, 0);
+ KUNIT_EXPECT_EQ(test, GSP_VF_ACTIVE, 1);
+ KUNIT_EXPECT_EQ(test, GSP_VF_MIGRATING, 2);
+ KUNIT_EXPECT_EQ(test, GSP_VF_SUSPENDED, 3);
+ KUNIT_EXPECT_EQ(test, GSP_VF_DESTROYING, 4);
+}
+
+static void test_msg_flags(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, GSP_MSG_SYNC, (1 << 0));
+ KUNIT_EXPECT_EQ(test, GSP_MSG_ASYNC, (1 << 1));
+ KUNIT_EXPECT_EQ(test, GSP_MSG_NOREPLY, (1 << 2));
+}
+
+/* ============================================================
+ * Test: Error Code Translation
+ * ============================================================ */
+
+static void test_error_codes(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_OK), 0);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_EPERM), -EPERM);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_ENOENT), -ENOENT);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_EINVAL), -EINVAL);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_ENODEV), -ENODEV);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_ENOMEM), -ENOMEM);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_EBUSY), -EBUSY);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_ENOSPC), -ENOSPC);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_ETIMEDOUT), -ETIMEDOUT);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_EOVERFLOW), -EOVERFLOW);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_EDEV), -EIO);
+ KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(0xDEADBEEF), -EIO);
+}
+
+/* ============================================================
+ * Test: Memory Types
+ * ============================================================ */
+
+static void test_memory_types(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, GSP_MEM_VRAM, 0);
+ KUNIT_EXPECT_EQ(test, GSP_MEM_SYSMEM, 1);
+ KUNIT_EXPECT_EQ(test, GSP_MEM_UNCACHED, 2);
+}
+
+/* ============================================================
+ * Test: Payload Capacity
+ * ============================================================ */
+
+static void test_payload_capacity(struct kunit *test)
+{
+ KUNIT_EXPECT_TRUE(test, sizeof(struct gsp_alloc_payload) <= 192);
+ KUNIT_EXPECT_TRUE(test, sizeof(struct gsp_pin_payload) <= 192);
+ KUNIT_EXPECT_TRUE(test, sizeof(struct gsp_bar_trap_payload) <= 192);
+ KUNIT_EXPECT_TRUE(test, sizeof(struct gsp_migrate_payload) <= 192);
+ KUNIT_EXPECT_TRUE(test, sizeof(struct gsp_subcmd_desc) <= 192);
+}
+
+/* ============================================================
+ * Test Suite Registration
+ * ============================================================ */
+
+static struct kunit_case gsp_vgpu_test_cases[] = {
+ KUNIT_CASE(test_structure_sizes),
+ KUNIT_CASE(test_structure_alignments),
+ KUNIT_CASE(test_alloc_payload_encoding),
+ KUNIT_CASE(test_pin_payload_encoding),
+ KUNIT_CASE(test_bar_trap_payload_encoding),
+ KUNIT_CASE(test_migrate_payload_encoding),
+ KUNIT_CASE(test_subcmd_desc_encoding),
+ KUNIT_CASE(test_firmware_addresses),
+ KUNIT_CASE(test_rpc_types),
+ KUNIT_CASE(test_migration_constants),
+ KUNIT_CASE(test_bar_trap_types),
+ KUNIT_CASE(test_pin_flags),
+ KUNIT_CASE(test_wpr_flags),
+ KUNIT_CASE(test_vf_states),
+ KUNIT_CASE(test_msg_flags),
+ KUNIT_CASE(test_error_codes),
+ KUNIT_CASE(test_memory_types),
+ KUNIT_CASE(test_payload_capacity),
+ {}
+};
+
+static struct kunit_suite gsp_vgpu_test_suite = {
+ .name = "gsp-vgpu-io",
+ .test_cases = gsp_vgpu_test_cases,
+};
+
+kunit_test_suites(&gsp_vgpu_test_suite);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Open-IOV Project");
+MODULE_DESCRIPTION("GSP vGPU-IO KUnit Tests"); \ No newline at end of file
diff --git a/kernel/tests/gsp_vgpu_mock_test.c b/kernel/tests/gsp_vgpu_mock_test.c
new file mode 100644
index 0000000..b9a46df
--- /dev/null
+++ b/kernel/tests/gsp_vgpu_mock_test.c
@@ -0,0 +1,624 @@
+/*
+ * GSP vGPU-IO Driver Mock Test
+ * Tests driver logic with mocked hardware interfaces
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/idr.h>
+#include <linux/kref.h>
+#include <linux/completion.h>
+#include <linux/atomic.h>
+
+#define TEST_PASS 0
+#define TEST_FAIL -1
+
+static int test_count = 0;
+static int pass_count = 0;
+static int fail_count = 0;
+
+#define TEST_ASSERT(cond, msg) \
+ do { \
+ test_count++; \
+ if (cond) { \
+ pass_count++; \
+ pr_info("PASS: %s\n", msg); \
+ } else { \
+ fail_count++; \
+ pr_err("FAIL: %s\n", msg); \
+ } \
+ } while (0)
+
+#define TEST_ASSERT_EQ(a, b, msg) \
+ TEST_ASSERT((a) == (b), msg " (expected " #b ", got " #a ")")
+
+/* ============================================================
+ * Mock Hardware Abstraction
+ * ============================================================ */
+
+struct mock_hw {
+ u64 bar0_regs[256];
+ u64 bar1_regs[256];
+ u8 *fw_memory;
+ size_t fw_size;
+ bool doorbell_triggered;
+ u32 doorbell_count;
+ spinlock_t lock;
+};
+
+static struct mock_hw *mock_hw_create(void)
+{
+ struct mock_hw *hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+ if (!hw)
+ return NULL;
+
+ hw->fw_memory = vmalloc(32 * 1024 * 1024); // 32MB
+ if (!hw->fw_memory) {
+ kfree(hw);
+ return NULL;
+ }
+ hw->fw_size = 32 * 1024 * 1024;
+ spin_lock_init(&hw->lock);
+ return hw;
+}
+
+static void mock_hw_destroy(struct mock_hw *hw)
+{
+ if (!hw)
+ return;
+ vfree(hw->fw_memory);
+ kfree(hw);
+}
+
+static void mock_hw_write_bar0(struct mock_hw *hw, u32 offset, u32 value)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&hw->lock, flags);
+ if (offset / 4 < 256)
+ hw->bar0_regs[offset / 4] = value;
+ if (offset == 0x1000) { // Doorbell
+ hw->doorbell_triggered = true;
+ hw->doorbell_count++;
+ }
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static u32 mock_hw_read_bar0(struct mock_hw *hw, u32 offset)
+{
+ unsigned long flags;
+ u32 value = 0;
+ spin_lock_irqsave(&hw->lock, flags);
+ if (offset / 4 < 256)
+ value = hw->bar0_regs[offset / 4];
+ spin_unlock_irqrestore(&hw->lock, flags);
+ return value;
+}
+
+static void mock_hw_write_bar1(struct mock_hw *hw, u64 offset, u64 value)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&hw->lock, flags);
+ if (offset < hw->fw_size)
+ *(u64 *)(hw->fw_memory + offset) = value;
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static u64 mock_hw_read_bar1(struct mock_hw *hw, u64 offset)
+{
+ unsigned long flags;
+ u64 value = 0;
+ spin_lock_irqsave(&hw->lock, flags);
+ if (offset < hw->fw_size)
+ value = *(u64 *)(hw->fw_memory + offset);
+ spin_unlock_irqrestore(&hw->lock, flags);
+ return value;
+}
+
+/* ============================================================
+ * Mock VF Operations
+ * ============================================================ */
+
+struct mock_vf {
+ u32 id;
+ u64 bar1_offset;
+ u64 bar1_size;
+ u64 pt_base;
+ u64 pt_size;
+ u64 cmd_base;
+ u64 cmd_size;
+ bool active;
+ struct {
+ u64 aperture_offset;
+ u64 aperture_size;
+ bool force_access;
+ u32 force_vf;
+ } bar_state;
+ struct list_head wpr_list;
+ struct mutex lock;
+};
+
+struct mock_wpr_region {
+ struct list_head list;
+ u64 base;
+ u64 size;
+ u32 flags;
+};
+
+static struct mock_vf *mock_vf_create(u32 id, u64 bar1_offset, u64 bar1_size,
+ u64 pt_base, u64 pt_size,
+ u64 cmd_base, u64 cmd_size)
+{
+ struct mock_vf *vf = kzalloc(sizeof(*vf), GFP_KERNEL);
+ if (!vf)
+ return NULL;
+
+ vf->id = id;
+ vf->bar1_offset = bar1_offset;
+ vf->bar1_size = bar1_size;
+ vf->pt_base = pt_base;
+ vf->pt_size = pt_size;
+ vf->cmd_base = cmd_base;
+ vf->cmd_size = cmd_size;
+ vf->active = true;
+ INIT_LIST_HEAD(&vf->wpr_list);
+ mutex_init(&vf->lock);
+
+ vf->bar_state.aperture_offset = bar1_offset;
+ vf->bar_state.aperture_size = bar1_size;
+ vf->bar_state.force_access = false;
+ vf->bar_state.force_vf = 0;
+
+ return vf;
+}
+
+static void mock_vf_destroy(struct mock_vf *vf)
+{
+ if (!vf)
+ return;
+
+ struct mock_wpr_region *rgn, *tmp;
+ mutex_lock(&vf->lock);
+ list_for_each_entry_safe(rgn, tmp, &vf->wpr_list, list) {
+ list_del(&rgn->list);
+ kfree(rgn);
+ }
+ mutex_unlock(&vf->lock);
+
+ kfree(vf);
+}
+
+static int mock_vf_wpr_add(struct mock_vf *vf, u64 base, u64 size, u32 flags)
+{
+ struct mock_wpr_region *rgn = kzalloc(sizeof(*rgn), GFP_KERNEL);
+ if (!rgn)
+ return -ENOMEM;
+
+ rgn->base = base;
+ rgn->size = size;
+ rgn->flags = flags;
+
+ mutex_lock(&vf->lock);
+ list_add(&rgn->list, &vf->wpr_list);
+ mutex_unlock(&vf->lock);
+
+ return 0;
+}
+
+static int mock_vf_wpr_remove(struct mock_vf *vf, u64 base)
+{
+ struct mock_wpr_region *rgn, *tmp;
+ bool found = false;
+
+ mutex_lock(&vf->lock);
+ list_for_each_entry_safe(rgn, tmp, &vf->wpr_list, list) {
+ if (rgn->base == base) {
+ list_del(&rgn->list);
+ kfree(rgn);
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&vf->lock);
+
+ return found ? 0 : -ENOENT;
+}
+
+/* ============================================================
+ * Mock RPC Client
+ * ============================================================ */
+
+struct mock_rpc_client {
+ u32 seq;
+ u32 pending_seq[64];
+ u32 pending_count;
+ struct mutex lock;
+};
+
+static struct mock_rpc_client *mock_rpc_create(void)
+{
+ struct mock_rpc_client *rpc = kzalloc(sizeof(*rpc), GFP_KERNEL);
+ if (!rpc)
+ return NULL;
+ mutex_init(&rpc->lock);
+ return rpc;
+}
+
+static void mock_rpc_destroy(struct mock_rpc_client *rpc)
+{
+ if (!rpc)
+ return;
+ kfree(rpc);
+}
+
+static int mock_rpc_send(struct mock_rpc_client *rpc, u32 rpc_type,
+ void *payload, size_t payload_size,
+ void *response, size_t *response_size)
+{
+ if (!rpc)
+ return -EINVAL;
+
+ mutex_lock(&rpc->lock);
+ rpc->seq++;
+ rpc->pending_seq[rpc->pending_count++] = rpc->seq;
+ mutex_unlock(&rpc->lock);
+
+ /* Simulate successful response */
+ if (response && response_size) {
+ struct gsp_rpc_response *resp = response;
+ resp->status = 0;
+ resp->rpc_type = rpc_type;
+ resp->sequence = rpc->seq;
+ *response_size = sizeof(*resp);
+ }
+
+ return 0;
+}
+
+/* ============================================================
+ * Driver Logic Tests with Mocks
+ * ============================================================ */
+
+static int test_mock_hardware(void)
+{
+ struct mock_hw *hw = mock_hw_create();
+ TEST_ASSERT(hw != NULL, "Mock HW create");
+
+ /* Test BAR0 write/read */
+ mock_hw_write_bar0(hw, 0x1000, 0x1);
+ TEST_ASSERT(hw->doorbell_triggered, "Doorbell triggered");
+ TEST_ASSERT_EQ(hw->doorbell_count, 1, "Doorbell count");
+
+ u32 val = mock_hw_read_bar0(hw, 0x1000);
+ TEST_ASSERT_EQ(val, 0x1, "BAR0 read back");
+
+ /* Test BAR1 write/read */
+ mock_hw_write_bar1(hw, 0x1000, 0xDEADBEEFCAFEBABE);
+ u64 val64 = mock_hw_read_bar1(hw, 0x1000);
+ TEST_ASSERT_EQ(val64, 0xDEADBEEFCAFEBABE, "BAR1 read back");
+
+ mock_hw_destroy(hw);
+ return TEST_PASS;
+}
+
+static int test_mock_vf_lifecycle(void)
+{
+ struct mock_vf *vf = mock_vf_create(1, 0x10000000, 0x100000,
+ 0x20000000, 0x100000,
+ 0x30000000, 0x10000);
+ TEST_ASSERT(vf != NULL, "Mock VF create");
+
+ TEST_ASSERT(vf->active, "VF active after create");
+ TEST_ASSERT_EQ(vf->id, 1, "VF ID");
+ TEST_ASSERT_EQ(vf->bar1_offset, 0x10000000, "BAR1 offset");
+ TEST_ASSERT_EQ(vf->bar1_size, 0x100000, "BAR1 size");
+ TEST_ASSERT_EQ(vf->pt_base, 0x20000000, "Page table base");
+ TEST_ASSERT_EQ(vf->pt_size, 0x100000, "Page table size");
+
+ /* Test BAR state */
+ TEST_ASSERT_EQ(vf->bar_state.aperture_offset, 0x10000000, "Aperture offset");
+ TEST_ASSERT_EQ(vf->bar_state.aperture_size, 0x100000, "Aperture size");
+ TEST_ASSERT(!vf->bar_state.force_access, "Force access initially false");
+
+ /* Test WPR list empty */
+ TEST_ASSERT(list_empty(&vf->wpr_list), "WPR list empty initially");
+
+ mock_vf_destroy(vf);
+ return TEST_PASS;
+}
+
+static int test_mock_vf_wpr(void)
+{
+ struct mock_vf *vf = mock_vf_create(1, 0x10000000, 0x100000,
+ 0x20000000, 0x100000,
+ 0x30000000, 0x10000);
+ TEST_ASSERT(vf != NULL, "Mock VF create");
+
+ /* Add WPR region */
+ int ret = mock_vf_wpr_add(vf, 0x10000000, 0x10000, 3);
+ TEST_ASSERT_EQ(ret, 0, "WPR add");
+
+ ret = mock_vf_wpr_add(vf, 0x20000000, 0x20000, 1);
+ TEST_ASSERT_EQ(ret, 0, "WPR add second");
+
+ /* Verify list */
+ int count = 0;
+ struct mock_wpr_region *rgn;
+ mutex_lock(&vf->lock);
+ list_for_each_entry(rgn, &vf->wpr_list, list) {
+ count++;
+ }
+ mutex_unlock(&vf->lock);
+ TEST_ASSERT_EQ(count, 2, "Two WPR regions");
+
+ /* Remove one */
+ ret = mock_vf_wpr_remove(vf, 0x10000000);
+ TEST_ASSERT_EQ(ret, 0, "WPR remove");
+
+ count = 0;
+ mutex_lock(&vf->lock);
+ list_for_each_entry(rgn, &vf->wpr_list, list) {
+ count++;
+ }
+ mutex_unlock(&vf->lock);
+ TEST_ASSERT_EQ(count, 1, "One WPR region remaining");
+
+ /* Remove non-existent */
+ ret = mock_vf_wpr_remove(vf, 0x30000000);
+ TEST_ASSERT_EQ(ret, -ENOENT, "WPR remove non-existent returns ENOENT");
+
+ mock_vf_destroy(vf);
+ return TEST_PASS;
+}
+
+static int test_mock_rpc_client(void)
+{
+ struct mock_rpc_client *rpc = mock_rpc_create();
+ TEST_ASSERT(rpc != NULL, "Mock RPC create");
+
+ struct gsp_rpc_response resp;
+ size_t resp_size = sizeof(resp);
+ struct gsp_alloc_payload payload = {
+ .size = cpu_to_le64(0x100000),
+ .type = cpu_to_le32(GSP_MEM_VRAM),
+ };
+
+ int ret = mock_rpc_send(rpc, GSP_RPC_ALLOC, &payload, sizeof(payload), &resp, &resp_size);
+ TEST_ASSERT_EQ(ret, 0, "RPC send");
+
+ TEST_ASSERT_EQ(resp.status, 0, "RPC response status");
+ TEST_ASSERT_EQ(resp.rpc_type, GSP_RPC_ALLOC, "RPC response type");
+ TEST_ASSERT_EQ(resp.sequence, 1, "RPC response sequence");
+
+ /* Second call */
+ ret = mock_rpc_send(rpc, GSP_RPC_PIN, &payload, sizeof(payload), &resp, &resp_size);
+ TEST_ASSERT_EQ(ret, 0, "RPC send second");
+ TEST_ASSERT_EQ(resp.sequence, 2, "RPC response sequence 2");
+
+ mock_rpc_destroy(rpc);
+ return TEST_PASS;
+}
+
+static int test_driver_vf_creation_logic(void)
+{
+ /* Simulate driver VF creation logic */
+ struct mock_hw *hw = mock_hw_create();
+ struct mock_rpc_client *rpc = mock_rpc_create();
+ struct mock_vf *vfs[8] = {0};
+
+ /* Create VFs 1-4 */
+ for (int i = 1; i <= 4; i++) {
+ vfs[i] = mock_vf_create(i, 0x10000000 * i, 0x100000,
+ 0x20000000 * i, 0x100000,
+ 0x30000000 * i, 0x10000);
+ TEST_ASSERT(vfs[i] != NULL, "VF creation in loop");
+ TEST_ASSERT(vfs[i]->active, "VF active in loop");
+ }
+
+ /* Initialize BAR for each VF */
+ for (int i = 1; i <= 4; i++) {
+ vfs[i]->bar_state.aperture_offset = vfs[i]->bar1_offset;
+ vfs[i]->bar_state.aperture_size = vfs[i]->bar1_size;
+ TEST_ASSERT_EQ(vfs[i]->bar_state.aperture_offset, 0x10000000 * i, "VF BAR offset");
+ TEST_ASSERT_EQ(vfs[i]->bar_state.aperture_size, 0x100000, "VF BAR size");
+ }
+
+ /* Add WPR for each VF */
+ for (int i = 1; i <= 4; i++) {
+ int ret = mock_vf_wpr_add(vfs[i], vfs[i]->pt_base, vfs[i]->pt_size, 3);
+ TEST_ASSERT_EQ(ret, 0, "VF WPR add in loop");
+ }
+
+ /* Simulate RPC calls for each VF */
+ for (int i = 1; i <= 4; i++) {
+ struct gsp_rpc_response resp;
+ size_t resp_size = sizeof(resp);
+ struct gsp_alloc_payload payload = {
+ .size = cpu_to_le64(0x100000),
+ .type = cpu_to_le32(GSP_MEM_VRAM),
+ .vf_id = cpu_to_le32(i),
+ };
+ int ret = mock_rpc_send(rpc, GSP_RPC_ALLOC, &payload, sizeof(payload), &resp, &resp_size);
+ TEST_ASSERT_EQ(ret, 0, "VF RPC alloc");
+ TEST_ASSERT_EQ(resp.status, 0, "VF RPC response OK");
+ }
+
+ /* Cleanup */
+ for (int i = 1; i <= 4; i++) {
+ mock_vf_destroy(vfs[i]);
+ }
+ mock_rpc_destroy(rpc);
+ mock_hw_destroy(hw);
+
+ return TEST_PASS;
+}
+
+static int test_migration_progress_tracking(void)
+{
+ struct {
+ u64 pages_transferred;
+ u64 bytes_transferred;
+ u64 dirty_pages;
+ u64 *page_list;
+ u32 page_count;
+ } progress = {0};
+
+ progress.page_count = 1000;
+ progress.page_list = kcalloc(progress.page_count, sizeof(u64), GFP_KERNEL);
+ TEST_ASSERT(progress.page_list != NULL, "Progress page list alloc");
+
+ /* Simulate transfer loop */
+ for (int iter = 0; iter < 10; iter++) {
+ progress.pages_transferred += 100;
+ progress.bytes_transferred += 100 * 4096;
+ progress.dirty_pages = 50 - (iter * 5);
+
+ TEST_ASSERT(progress.pages_transferred <= 1000, "Pages not exceeded");
+ TEST_ASSERT(progress.bytes_transferred <= 1000 * 4096, "Bytes not exceeded");
+ }
+
+ TEST_ASSERT_EQ(progress.pages_transferred, 1000, "Final pages");
+ TEST_ASSERT_EQ(progress.bytes_transferred, 1000 * 4096, "Final bytes");
+ TEST_ASSERT_EQ(progress.dirty_pages, 0, "Final dirty pages");
+
+ kfree(progress.page_list);
+ return TEST_PASS;
+}
+
+static int test_bar_state_machine(void)
+{
+ struct {
+ u64 aperture_offset;
+ u64 aperture_size;
+ bool force_access;
+ u32 force_vf;
+ } bar_state = {0};
+
+ /* Initial state */
+ bar_state.aperture_offset = 0x10000000;
+ bar_state.aperture_size = 0x100000;
+ TEST_ASSERT_EQ(bar_state.aperture_offset, 0x10000000, "Initial aperture offset");
+ TEST_ASSERT(!bar_state.force_access, "Force access initially false");
+
+ /* Enable force access */
+ bar_state.force_access = true;
+ bar_state.force_vf = 1;
+ TEST_ASSERT(bar_state.force_access, "Force access enabled");
+ TEST_ASSERT_EQ(bar_state.force_vf, 1, "Force VF set");
+
+ /* Disable force access */
+ bar_state.force_access = false;
+ bar_state.force_vf = 0;
+ TEST_ASSERT(!bar_state.force_access, "Force access disabled");
+ TEST_ASSERT_EQ(bar_state.force_vf, 0, "Force VF cleared");
+
+ return TEST_PASS;
+}
+
+static int test_concurrent_vf_operations(void)
+{
+ struct mock_vf *vf1 = mock_vf_create(1, 0x10000000, 0x100000, 0x20000000, 0x100000, 0x30000000, 0x10000);
+ struct mock_vf *vf2 = mock_vf_create(2, 0x20000000, 0x100000, 0x40000000, 0x100000, 0x60000000, 0x10000);
+ struct mock_rpc_client *rpc = mock_rpc_create();
+
+ TEST_ASSERT(vf1 && vf2 && rpc, "Create two VFs and RPC");
+
+ /* Simulate concurrent operations */
+ struct gsp_rpc_response resp;
+ size_t resp_size = sizeof(resp);
+
+ /* VF1 alloc */
+ struct gsp_alloc_payload payload1 = {
+ .size = cpu_to_le64(0x100000),
+ .type = cpu_to_le32(GSP_MEM_VRAM),
+ .vf_id = cpu_to_le32(1),
+ };
+ int ret = mock_rpc_send(rpc, GSP_RPC_ALLOC, &payload1, sizeof(payload1), &resp, &resp_size);
+ TEST_ASSERT_EQ(ret, 0, "VF1 alloc");
+ TEST_ASSERT_EQ(resp.status, 0, "VF1 alloc response");
+
+ /* VF2 alloc */
+ struct gsp_alloc_payload payload2 = {
+ .size = cpu_to_le64(0x200000),
+ .type = cpu_to_le32(GSP_MEM_VRAM),
+ .vf_id = cpu_to_le32(2),
+ };
+ ret = mock_rpc_send(rpc, GSP_RPC_ALLOC, &payload2, sizeof(payload2), &resp, &resp_size);
+ TEST_ASSERT_EQ(ret, 0, "VF2 alloc");
+ TEST_ASSERT_EQ(resp.status, 0, "VF2 alloc response");
+
+ /* VF1 pin */
+ struct gsp_pin_payload pin_payload = {
+ .handle = cpu_to_le64(0x1000),
+ .vf_id = cpu_to_le32(1),
+ };
+ ret = mock_rpc_send(rpc, GSP_RPC_PIN, &pin_payload, sizeof(pin_payload), &resp, &resp_size);
+ TEST_ASSERT_EQ(ret, 0, "VF1 pin");
+
+ /* VF2 BAR trap */
+ struct gsp_bar_trap_payload bar_payload = {
+ .vf_id = cpu_to_le32(2),
+ .bar_index = cpu_to_le32(1),
+ };
+ ret = mock_rpc_send(rpc, GSP_RPC_BAR_TRAP, &bar_payload, sizeof(bar_payload), &resp, &resp_size);
+ TEST_ASSERT_EQ(ret, 0, "VF2 BAR trap");
+
+ /* Verify sequence numbers */
+ TEST_ASSERT_EQ(resp.sequence, 4, "Sequence number 4");
+
+ mock_vf_destroy(vf1);
+ mock_vf_destroy(vf2);
+ mock_rpc_destroy(rpc);
+
+ return TEST_PASS;
+}
+
+/* ============================================================
+ * Module Init/Exit
+ * ============================================================ */
+
+static int __init gsp_mock_test_init(void)
+{
+ pr_info("========================================\n");
+ pr_info("GSP vGPU-IO Mock Driver Test Suite\n");
+ pr_info("========================================\n");
+
+ test_mock_hardware();
+ test_mock_vf_lifecycle();
+ test_mock_vf_wpr();
+ test_mock_rpc_client();
+ test_driver_vf_creation_logic();
+ test_migration_progress_tracking();
+ test_bar_state_machine();
+ test_concurrent_vf_operations();
+
+ pr_info("========================================\n");
+ pr_info("Mock Test Results: %d total, %d passed, %d failed\n",
+ test_count, pass_count, fail_count);
+ pr_info("========================================\n");
+
+ if (fail_count > 0) {
+ pr_err("MOCK TEST SUITE FAILED\n");
+ return -EINVAL;
+ }
+
+ pr_info("MOCK TEST SUITE PASSED\n");
+ return 0;
+}
+
+static void __exit gsp_mock_test_exit(void)
+{
+ pr_info("GSP vGPU-IO Mock Test Suite Exited\n");
+}
+
+module_init(gsp_mock_test_init);
+module_exit(gsp_mock_test_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Open-IOV Project");
+MODULE_DESCRIPTION("GSP vGPU-IO Mock Driver Test");
+MODULE_VERSION("1.0"); \ No newline at end of file