diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/Makefile | 9 | ||||
| -rw-r--r-- | kernel/gsp_comm.c | 5 | ||||
| -rw-r--r-- | kernel/gsp_emulation.c | 71 | ||||
| -rw-r--r-- | kernel/gsp_rpc_client.c | 40 | ||||
| -rw-r--r-- | kernel/include/nvidia-vgpu-vfio.h | 8 | ||||
| -rw-r--r-- | kernel/main.c | 85 | ||||
| -rw-r--r-- | kernel/tests/gsp_vgpu_comprehensive_test.c | 432 | ||||
| -rw-r--r-- | kernel/tests/gsp_vgpu_kunit_test.c | 327 | ||||
| -rw-r--r-- | kernel/tests/gsp_vgpu_mock_test.c | 624 |
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 |
