summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile9
-rw-r--r--kernel/gsp_comm.c5
-rw-r--r--kernel/gsp_emulation.c71
-rw-r--r--kernel/gsp_rpc_client.c40
-rw-r--r--kernel/include/nvidia-vgpu-vfio.h8
-rw-r--r--kernel/main.c85
-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
9 files changed, 154 insertions, 1447 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
index aef7129..98adfd4 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -25,14 +25,17 @@ KBUILD_MODPOST_WARN := 1
# Use symbols from host nvidia.ko (path resolved relative to this repo)
KBUILD_EXTRA_SYMBOLS := $(abspath $(src)/../nvidia-open-gpu-kernel-modules/kernel-open/Module.symvers)
+# Use $(CURDIR), NOT $(PWD): when invoked via the top-level Makefile
+# (make -C kernel), $(PWD) still points at the caller's directory and kbuild
+# silently builds nothing (the "stale .ko" trap).
all:
- $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+ $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(CURDIR) modules
clean:
- $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
+ $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(CURDIR) clean
install:
- $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules_install
+ $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(CURDIR) modules_install
depmod -a
.PHONY: all clean install \ No newline at end of file
diff --git a/kernel/gsp_comm.c b/kernel/gsp_comm.c
index a8d0664..094fcfb 100644
--- a/kernel/gsp_comm.c
+++ b/kernel/gsp_comm.c
@@ -37,7 +37,6 @@ static void nv_vgpu_gsp_rpc_work(struct work_struct *work)
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)
@@ -221,7 +220,7 @@ int nv_vgpu_gsp_boot(struct nv_vgpu_device *vgpu)
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);
+ (u64)NV_VGPU_GSP_DOORBELL_OFFSET);
return 0;
}
@@ -267,7 +266,7 @@ int nv_vgpu_gsp_init(struct nv_vgpu_device *vgpu)
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);
+ bar->vaddr, (u64)NV_VGPU_GSP_DOORBELL_OFFSET, (u64)NV_VGPU_GSP_SHARED_MEM_OFFSET);
/* Validate firmware files exist */
ret = request_firmware(NULL, NV_VGPU_GSP_BOOTLOADER_FILE, &vgpu->pdev->dev);
if (ret) {
diff --git a/kernel/gsp_emulation.c b/kernel/gsp_emulation.c
index 60805eb..bfd8be7 100644
--- a/kernel/gsp_emulation.c
+++ b/kernel/gsp_emulation.c
@@ -239,16 +239,23 @@ static int gsp_emu_dispatch_rpc(struct gsp_emu_context *ctx,
void gsp_emu_inject_completion_interrupt(struct gsp_emu_context *ctx, u32 seq_id)
{
+ struct eventfd_ctx *trigger = NULL;
+
/* Write to MSI-X PBA (Pending Bit Array) at BAR0+0x3000 */
if (ctx->bar0_vaddr) {
u32 *pba = (u32 *)(ctx->bar0_vaddr + GSP_EMU_MSIX_PBA_OFFSET);
writel(1, pba); /* Set pending bit for vector 0 */
}
+ /* Look up the vector-0 trigger LIVE: QEMU wires eventfds via
+ * VFIO_DEVICE_SET_IRQS after gsp_emu_init runs, so an init-time copy
+ * would be stale (NULL). */
+ if (ctx->vgpu)
+ trigger = ctx->vgpu->irqs[0].trigger;
+
/* Signal eventfd for VFIO interrupt delivery */
- if (ctx->msix_trigger[0]) {
- eventfd_signal(ctx->msix_trigger[0]);
- }
+ if (trigger)
+ eventfd_signal(trigger);
}
/* ============================================================
@@ -321,6 +328,7 @@ static int gsp_emu_thread_fn(void *data)
int gsp_emu_init(struct nv_vgpu_device *vgpu)
{
+ struct nv_vgpu_bar_emul_ctx *bar_ctx;
struct gsp_emu_context *ctx;
int ret = 0;
@@ -337,46 +345,47 @@ int gsp_emu_init(struct nv_vgpu_device *vgpu)
init_completion(&ctx->thread_stopped);
spin_lock_init(&ctx->lock);
- /* Use already-mapped BAR0 virtual address (mapped by nv_vgpu_bar_init) */
- if (!vgpu->bars[0].vaddr) {
- nv_vgpu_err(vgpu, "BAR0 virtual address not available");
+ /* The guest's BAR0 is the emulation shadow (vzalloc'd by
+ * nv_vgpu_bar_emul_init, mapped to the guest via VFIO mmap) - NOT the
+ * host physical BAR0 (vgpu->bars[0].vaddr, which belongs to the real GPU
+ * and the host nvidia driver). All guest doorbell writes, RPC messages,
+ * MSI-X table writes and PBA reads land in the shadow, so the emulation
+ * engine must operate on the shadow exclusively. Polling the physical
+ * BAR0 would never see guest traffic and would corrupt host GPU state. */
+ bar_ctx = (struct nv_vgpu_bar_emul_ctx *)vgpu->bar_emul_ctx;
+ if (!bar_ctx || !bar_ctx->bar0_shadow) {
+ nv_vgpu_err(vgpu, "BAR0 emulation shadow not available");
ret = -ENODEV;
goto err_free_ctx;
}
- ctx->bar0_vaddr = vgpu->bars[0].vaddr;
+ ctx->bar0_vaddr = (void __iomem *)bar_ctx->bar0_shadow;
- /* Locate doorbell register at BAR0+0x110C00 */
+ /* Doorbell register at BAR0+0x110C00 (guest-visible shadow) */
ctx->doorbell_reg = ctx->bar0_vaddr + GSP_EMU_DOORBELL_OFFSET;
- /* Allocate shared memory (4KB, page-aligned, WC mapping) */
+ /* RPC shared memory at BAR0+0x1000. This ALIASES the guest-visible
+ * shadow - it is NOT a separate allocation. shared_mem_dma stays 0 to
+ * mark it as aliased so cleanup does not dma_free it. */
ctx->shared_mem_size = GSP_EMU_SHARED_MEM_SIZE;
- ctx->shared_mem_virt = dma_alloc_wc(&vgpu->pdev->dev, ctx->shared_mem_size,
- &ctx->shared_mem_dma, GFP_KERNEL);
- if (!ctx->shared_mem_virt) {
- nv_vgpu_err(vgpu, "Failed to allocate shared memory");
- ret = -ENOMEM;
- goto err_free_ctx;
- }
-
- /* Map shared memory into guest BAR0 at offset 0x1000 */
ctx->shared_mem = ctx->bar0_vaddr + GSP_EMU_SHARED_MEM_OFFSET;
+ ctx->shared_mem_virt = (void *)ctx->shared_mem;
+ ctx->shared_mem_dma = 0;
- /* Initialize RPC client with already-mapped BAR0 virtual address and doorbell */
+ /* Initialize RPC client with the shadow BAR0 base and doorbell */
ctx->rpc_client = gsp_client_init_emulation(&vgpu->dev, ctx->bar0_vaddr, ctx->doorbell_reg);
if (IS_ERR(ctx->rpc_client)) {
ret = PTR_ERR(ctx->rpc_client);
nv_vgpu_err(vgpu, "Failed to init GSP RPC client: %d", ret);
- goto err_free_shared_mem;
+ goto err_free_ctx;
}
/* Enable emulation mode in RPC client */
gsp_rpc_emulation_set(true);
- /* Get MSI-X eventfd triggers from IRQ context */
- for (int i = 0; i < NV_VGPU_MAX_IRQS; i++) {
- ctx->msix_trigger[i] = vgpu->irqs[i].trigger;
- }
+ /* NOTE: MSI-X eventfd triggers are NOT copied here - QEMU wires them
+ * via VFIO_DEVICE_SET_IRQS later, so gsp_emu_inject_completion_interrupt
+ * looks them up live from vgpu->irqs[]. */
/* Start emulation thread */
ctx->thread = kthread_run(gsp_emu_thread_fn, ctx, "gsp_emu_%u", vgpu->vgpu_id);
@@ -395,10 +404,9 @@ int gsp_emu_init(struct nv_vgpu_device *vgpu)
err_fini_client:
gsp_client_fini(ctx->rpc_client);
-err_free_shared_mem:
- dma_free_wc(&vgpu->pdev->dev, ctx->shared_mem_size,
- ctx->shared_mem_virt, ctx->shared_mem_dma);
err_free_ctx:
+ /* shared_mem_virt aliases the BAR0 shadow (owned by bar_emulation),
+ * nothing to free here */
kfree(ctx);
return ret;
}
@@ -433,14 +441,15 @@ void gsp_emu_cleanup(struct nv_vgpu_device *vgpu)
ctx->rpc_client = NULL;
}
- /* Free shared memory */
- if (ctx->shared_mem_virt) {
+ /* Shared memory aliases the BAR0 shadow (owned by bar_emulation) -
+ * only free it if it was a standalone DMA allocation (dma != 0) */
+ if (ctx->shared_mem_virt && ctx->shared_mem_dma) {
dma_free_wc(&vgpu->pdev->dev, ctx->shared_mem_size,
ctx->shared_mem_virt, ctx->shared_mem_dma);
- ctx->shared_mem_virt = NULL;
}
+ ctx->shared_mem_virt = NULL;
- /* BAR0 is managed by nv_vgpu_bar_init/cleanup, don't unmap here */
+ /* BAR0 shadow is managed by nv_vgpu_bar_emul_init/cleanup */
ctx->bar0_vaddr = NULL;
vgpu->gsp_comm_ctx = NULL;
diff --git a/kernel/gsp_rpc_client.c b/kernel/gsp_rpc_client.c
index ae45513..93c1876 100644
--- a/kernel/gsp_rpc_client.c
+++ b/kernel/gsp_rpc_client.c
@@ -375,8 +375,8 @@ int gsp_rpc_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size)
/* 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)
+ 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};
@@ -384,6 +384,17 @@ int gsp_rpc_bar_trap(struct gsp_client *client,
u32 seq = gsp_next_seq(client);
int ret;
+ /* In emulation mode there is no real firmware to submit to, and
+ * submit_raw would ring the very doorbell the emulation thread polls
+ * (self-triggered RPC livelock + timeout stall). Guest BAR access is
+ * synchronous MMIO via the shadow mapping and never needs this RPC.
+ * Real shadow-register handlers are TODO; fail fast and loudly. */
+ if (gsp_rpc_emulation_is_enabled()) {
+ pr_warn_ratelimited("gsp_rpc_bar_trap: not supported in emulation mode (bar=%u addr=0x%llx)\n",
+ bar_num, bar_addr);
+ return -ENOTSUPP;
+ }
+
req.bar_addr = bar_addr;
req.access_type = access_type;
req.size = size;
@@ -413,9 +424,19 @@ int gsp_rpc_migrate_save(struct gsp_client *client,
struct gsp_migrate_req req = {0};
struct gsp_migrate_rsp rsp = {0};
struct gsp_rpc_response response = {0};
- u32 seq = gsp_next_seq(client);
+ u32 seq;
int ret;
+ /* Emulation mode: no real firmware; submit_raw would ring the doorbell
+ * the emulation thread polls (self-triggered livelock + 30s stall).
+ * Live migration over the emulated vGPU is not yet implemented;
+ * see kernel/migration.c for the 4-phase host-side protocol work. */
+ if (gsp_rpc_emulation_is_enabled()) {
+ pr_warn_ratelimited("gsp_rpc_migrate_save: not supported in emulation mode\n");
+ return -ENOTSUPP;
+ }
+
+ seq = gsp_next_seq(client);
req.src_gpu_id = src_gpu_id;
req.dst_gpu_id = dst_gpu_id;
req.memory_bitmap = memory_bitmap;
@@ -444,9 +465,16 @@ int gsp_rpc_migrate_restore(struct gsp_client *client,
{
struct gsp_migrate_req req = {0};
struct gsp_rpc_response response = {0};
- u32 seq = gsp_next_seq(client);
+ u32 seq;
int ret;
+ /* Emulation mode: no real firmware; see gsp_rpc_migrate_save note. */
+ if (gsp_rpc_emulation_is_enabled()) {
+ pr_warn_ratelimited("gsp_rpc_migrate_restore: not supported in emulation mode\n");
+ return -ENOTSUPP;
+ }
+
+ seq = gsp_next_seq(client);
req.src_gpu_id = src_gpu_id;
req.dst_gpu_id = dst_gpu_id;
req.memory_bitmap = memory_bitmap;
@@ -777,7 +805,7 @@ int gsp_rpc_schedule(struct gsp_client *client,
u32 seq = gsp_next_seq(client);
int ret;
- if (gsp_emulation_enabled) {
+ if (gsp_rpc_emulation_is_enabled()) {
struct gsp_sched_work *sched = kzalloc(sizeof(*sched), GFP_KERNEL);
if (!sched)
return -ENOMEM;
@@ -827,7 +855,7 @@ int gsp_rpc_yield(struct gsp_client *client)
u32 seq = gsp_next_seq(client);
int ret;
- if (gsp_emulation_enabled) {
+ if (gsp_rpc_emulation_is_enabled()) {
/* In emulation, yield is a no-op success */
return 0;
}
diff --git a/kernel/include/nvidia-vgpu-vfio.h b/kernel/include/nvidia-vgpu-vfio.h
index 02c2c13..961bba2 100644
--- a/kernel/include/nvidia-vgpu-vfio.h
+++ b/kernel/include/nvidia-vgpu-vfio.h
@@ -177,6 +177,14 @@ 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);
+/* mdev driver/parent registration (main.c) */
+int nv_vgpu_create_mdev_types(void);
+void nv_vgpu_free_type_info(void);
+int nv_vgpu_mdev_driver_register(void);
+void nv_vgpu_mdev_driver_unregister(void);
+int nv_vgpu_mdev_register_parent(struct pci_dev *pdev);
+void nv_vgpu_mdev_unregister_parent(struct pci_dev *pdev);
+
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);
diff --git a/kernel/main.c b/kernel/main.c
index e0e823b..3da7dc6 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -671,8 +671,19 @@ static int nv_vgpu_vfio_dev_open(struct vfio_device *vdev)
static void nv_vgpu_vfio_dev_close(struct vfio_device *vdev)
{
struct nv_vgpu_device *vgpu = container_of(vdev, struct nv_vgpu_device, vfio_dev);
-
+ int i;
+
pr_info("nvidia-vgpu-vfio: VFIO device close\n");
+
+ /* Release eventfd trigger references wired via SET_IRQS; the next
+ * opener re-wires its own. Prevents fd/ctx leaks across VM restarts. */
+ for (i = 0; i < NV_VGPU_MAX_IRQS; i++) {
+ if (vgpu->irqs[i].trigger) {
+ eventfd_ctx_put(vgpu->irqs[i].trigger);
+ vgpu->irqs[i].trigger = NULL;
+ }
+ }
+
atomic_dec(&vgpu->vfio_dev_open_count);
wake_up(&vgpu->vfio_dev_close_wait);
}
@@ -712,14 +723,17 @@ static ssize_t nv_vgpu_vfio_dev_write(struct vfio_device *vdev, const char __use
pr_info("nvidia-vgpu-vfio: VFIO device write count=%zu pos=%lld (0x%llx)\n", count, pos, pos);
- /* Config space region */
+ /* Config space region - route through the per-register filter so the
+ * guest can only modify writable registers (not vendor/device IDs,
+ * BAR addresses, capabilities structures, etc.) */
if (pos >= 0x70000000000ULL && pos < 0x70000000000ULL + 4096) {
loff_t cfg_pos = pos - 0x70000000000ULL;
- size_t to_write = min(count, (size_t)(4096 - cfg_pos));
- if (copy_from_user(vgpu->pci_config + cfg_pos, buf, to_write))
- return -EFAULT;
- *ppos += to_write;
- return to_write;
+ ssize_t ret;
+
+ ret = nv_vgpu_config_write(vgpu, buf, count, &cfg_pos);
+ if (ret > 0)
+ *ppos += ret;
+ return ret;
}
return 0;
@@ -961,11 +975,21 @@ static long nv_vgpu_vfio_dev_ioctl(struct vfio_device *vdev, unsigned int cmd,
/* Handle DATA_EVENTFD */
if (irq_set_ptr->flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+ int i, idx, wired = 0;
+
if (irq_set_ptr->count == 0) {
kfree(irq_set_ptr);
return -EINVAL;
}
+ if (irq_set_ptr->index > VFIO_PCI_REQ_IRQ_INDEX ||
+ irq_set_ptr->start + irq_set_ptr->count > NV_VGPU_MAX_IRQS) {
+ pr_err("nvidia-vgpu-vfio: SET_IRQS out of range: index=%u start=%u count=%u\n",
+ irq_set_ptr->index, irq_set_ptr->start, irq_set_ptr->count);
+ kfree(irq_set_ptr);
+ return -EINVAL;
+ }
+
fds = kcalloc(irq_set_ptr->count, sizeof(int), GFP_KERNEL);
if (!fds) {
kfree(irq_set_ptr);
@@ -980,21 +1004,38 @@ static long nv_vgpu_vfio_dev_ioctl(struct vfio_device *vdev, unsigned int cmd,
return -EFAULT;
}
- /* For now, just store the eventfds for INTx and MSI-X */
- if (irq_set_ptr->index == VFIO_PCI_INTX_IRQ_INDEX) {
- pr_info("nvidia-vgpu-vfio: SET_IRQS INTx trigger fds[%d]\n", irq_set_ptr->count);
- /* TODO: Store eventfds for INTx */
- } else if (irq_set_ptr->index == VFIO_PCI_MSIX_IRQ_INDEX) {
- pr_info("nvidia-vgpu-vfio: SET_IRQS MSI-X trigger fds[%d] start=%d\n", irq_set_ptr->count, irq_set_ptr->start);
- /* TODO: Store eventfds for MSI-X vectors */
- } else if (irq_set_ptr->index == VFIO_PCI_MSI_IRQ_INDEX) {
- pr_info("nvidia-vgpu-vfio: SET_IRQS MSI trigger fds[%d]\n", irq_set_ptr->count);
- /* TODO: Store eventfds for MSI */
- } else if (irq_set_ptr->index == VFIO_PCI_ERR_IRQ_INDEX) {
- pr_info("nvidia-vgpu-vfio: SET_IRQS ERR trigger fds[%d]\n", irq_set_ptr->count);
- /* TODO: Store eventfds for ERR */
+ /* Wire each eventfd to its interrupt vector trigger.
+ * fd >= 0: take a reference and store; fd == -1: clear.
+ * The GSP emulation engine fires vector 0 via
+ * gsp_emu_inject_completion_interrupt(). */
+ for (i = 0; i < irq_set_ptr->count; i++) {
+ idx = irq_set_ptr->start + i;
+
+ if (fds[i] >= 0) {
+ struct eventfd_ctx *trigger;
+
+ trigger = eventfd_ctx_fdget(fds[i]);
+ if (IS_ERR(trigger)) {
+ pr_err("nvidia-vgpu-vfio: SET_IRQS bad eventfd %d for vector %d\n",
+ fds[i], idx);
+ continue;
+ }
+ if (vgpu->irqs[idx].trigger)
+ eventfd_ctx_put(vgpu->irqs[idx].trigger);
+ vgpu->irqs[idx].trigger = trigger;
+ vgpu->irqs[idx].type = irq_set_ptr->index;
+ wired++;
+ } else if (fds[i] == -1) {
+ if (vgpu->irqs[idx].trigger) {
+ eventfd_ctx_put(vgpu->irqs[idx].trigger);
+ vgpu->irqs[idx].trigger = NULL;
+ }
+ }
}
+ pr_info("nvidia-vgpu-vfio: SET_IRQS wired %d/%d eventfds (index=%d start=%d)\n",
+ wired, irq_set_ptr->count, irq_set_ptr->index, irq_set_ptr->start);
+
kfree(fds);
kfree(irq_set_ptr);
return 0;
@@ -1685,6 +1726,7 @@ static ssize_t nv_vgpu_config_read(struct nv_vgpu_device *vgpu, char __user *buf
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;
+ size_t written = 0;
u8 val;
if (pos >= 256)
@@ -1737,11 +1779,12 @@ static ssize_t nv_vgpu_config_write(struct nv_vgpu_device *vgpu, const char __us
break;
}
pos++;
+ written++;
}
vgpu->config_space_dirty = true;
*ppos = pos;
- return pos - *ppos + count;
+ return written;
}
/* ============================================================
diff --git a/kernel/tests/gsp_vgpu_comprehensive_test.c b/kernel/tests/gsp_vgpu_comprehensive_test.c
deleted file mode 100644
index 7918beb..0000000
--- a/kernel/tests/gsp_vgpu_comprehensive_test.c
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * 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
deleted file mode 100644
index c661f91..0000000
--- a/kernel/tests/gsp_vgpu_kunit_test.c
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * 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
deleted file mode 100644
index b9a46df..0000000
--- a/kernel/tests/gsp_vgpu_mock_test.c
+++ /dev/null
@@ -1,624 +0,0 @@
-/*
- * 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