diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-13 18:36:21 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-13 18:36:21 -0400 |
| commit | 00e54c13c7c9030c4d5f7151467f2d85a3730e29 (patch) | |
| tree | 17a45239df8d2e4912ff3697ed06e85a10b969de /kernel/tests | |
| download | open-iov-00e54c13c7c9030c4d5f7151467f2d85a3730e29.tar.gz open-iov-00e54c13c7c9030c4d5f7151467f2d85a3730e29.zip | |
Initial commit: Open-IOV vGPU-IO complete stack
- Firmware decompilation: GSP 570.144W (28.5MB ELF, RV64IMAC), 132 decompiled C files
- Kernel modules: nvidia.ko (34MB, OpenRM), open-iov-vfio.ko (2.8MB, VFIO mdev), gsp-vgpu.ko (1.8MB, guest driver)
- Userspace: libiov.so/libvgpu.so (76KB), example_vgpu (28KB), rpc_emulator.c
- PCI config space emulation (256B with PM, MSI, MSI-X, PCIe caps)
- 12 RPC types fully mapped from firmware decompilation
- BAR trap, WPR, scheduling, notification, migration emulation
- Helper scripts: load_vgpu_stack, create_mdev, launch_qemu_vgpu, test_vgpu_stack, setup_guest_driver, unload_vgpu_stack
- Architecture documentation
- Wiki: 10 technical documents
- Git LFS for large binaries (.ko, .bin, .iso, .img, .o, ghidra repos)
- nvidia-open-gpu-kernel-modules as submodule
Diffstat (limited to 'kernel/tests')
| -rw-r--r-- | kernel/tests/Makefile | 40 | ||||
| -rw-r--r-- | kernel/tests/Module.symvers | 0 | ||||
| -rw-r--r-- | kernel/tests/gsp_rpc_test.c | 425 | ||||
| -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 |
6 files changed, 1848 insertions, 0 deletions
diff --git a/kernel/tests/Makefile b/kernel/tests/Makefile new file mode 100644 index 0000000..ce44670 --- /dev/null +++ b/kernel/tests/Makefile @@ -0,0 +1,40 @@ +# GSP vGPU-IO Driver Test Makefile + +# Comprehensive test module +obj-m += gsp_vgpu_comprehensive_test.o + +# KUnit test module (requires CONFIG_KUNIT) +obj-$(CONFIG_KUNIT) += gsp_vgpu_kunit_test.o + +# Include driver headers +ccflags-y := -I$OPEN_IOV_ROOT/driver/include + +# Build against the kernel source +KDIR ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean + +install: + $(MAKE) -C $(KDIR) M=$(PWD) modules_install + +test: all + @echo "Loading comprehensive test module..." + sudo insmod gsp_vgpu_comprehensive_test.ko + @echo "Test module loaded, checking dmesg..." + dmesg -T | tail -50 + +test-kunit: all + @echo "Loading KUnit test module..." + sudo insmod gsp_vgpu_kunit_test.ko + @echo "KUnit test module loaded, checking dmesg..." + dmesg -T | grep -E "(kunit|KUNIT|PASS|FAIL)" | tail -50 + +test-unload: + sudo rmmod gsp_vgpu_comprehensive_test gsp_vgpu_kunit_test 2>/dev/null || true + dmesg -T | tail -20 + +.PHONY: all clean install test test-kunit test-unload
\ No newline at end of file diff --git a/kernel/tests/Module.symvers b/kernel/tests/Module.symvers new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/kernel/tests/Module.symvers diff --git a/kernel/tests/gsp_rpc_test.c b/kernel/tests/gsp_rpc_test.c new file mode 100644 index 0000000..f609066 --- /dev/null +++ b/kernel/tests/gsp_rpc_test.c @@ -0,0 +1,425 @@ +/* + * GSP RPC Client Test/Validation Harness + * Tests message encoding/decoding, RPC structure validation + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/errno.h> + +#include "../include/gsp_rpc_client.h" +#include "../include/nvidia-vgpu-vfio.h" + +#define TEST_PASS 0 +#define TEST_FAIL -1 + +static int test_count = 0; +static int pass_count = 0; +static int fail_count = 0; + +#define TEST_ASSERT(cond, msg) \ + do { \ + test_count++; \ + if (cond) { \ + pass_count++; \ + pr_info("PASS: %s\n", msg); \ + } else { \ + fail_count++; \ + pr_err("FAIL: %s\n", msg); \ + } \ + } while (0) + +#define TEST_ASSERT_EQ(a, b, msg) \ + TEST_ASSERT((a) == (b), msg " (expected " #b ", got " #a ")") + +/* ============================================================ + * Structure Size/Alignment Tests + * ============================================================ */ + +static int test_structure_sizes(void) +{ + pr_info("=== Testing Structure Sizes ===\n"); + + TEST_ASSERT_EQ(sizeof(struct gsp_rpc_msg), 72, "gsp_rpc_msg size"); + TEST_ASSERT_EQ(sizeof(struct gsp_rpc_response), 64, "gsp_rpc_response size"); + TEST_ASSERT_EQ(sizeof(struct gsp_alloc_req), 40, "gsp_alloc_req size"); + TEST_ASSERT_EQ(sizeof(struct gsp_alloc_rsp), 24, "gsp_alloc_rsp size"); + TEST_ASSERT_EQ(sizeof(struct gsp_map_req), 48, "gsp_map_req size"); + TEST_ASSERT_EQ(sizeof(struct gsp_map_rsp), 8, "gsp_map_rsp size"); + TEST_ASSERT_EQ(sizeof(struct gsp_pin_req), 40, "gsp_pin_req size"); + TEST_ASSERT_EQ(sizeof(struct gsp_pin_rsp), 16, "gsp_pin_rsp size"); + TEST_ASSERT_EQ(sizeof(struct gsp_sched_req), 40, "gsp_sched_req size"); + TEST_ASSERT_EQ(sizeof(struct gsp_bar_trap_req), 32, "gsp_bar_trap_req size"); + TEST_ASSERT_EQ(sizeof(struct gsp_bar_trap_rsp), 16, "gsp_bar_trap_rsp size"); + TEST_ASSERT_EQ(sizeof(struct gsp_migrate_req), 56, "gsp_migrate_req size"); + TEST_ASSERT_EQ(sizeof(struct gsp_migrate_rsp), 16, "gsp_migrate_rsp size"); + + return TEST_PASS; +} + +static int test_structure_alignment(void) +{ + pr_info("=== Testing Structure Alignment ===\n"); + + TEST_ASSERT_EQ(__alignof__(struct gsp_rpc_msg), 8, "gsp_rpc_msg alignment"); + TEST_ASSERT_EQ(__alignof__(struct gsp_rpc_response), 8, "gsp_rpc_response alignment"); + TEST_ASSERT_EQ(__alignof__(struct gsp_alloc_req), 8, "gsp_alloc_req alignment"); + TEST_ASSERT_EQ(__alignof__(struct gsp_map_req), 8, "gsp_map_req alignment"); + TEST_ASSERT_EQ(__alignof__(struct gsp_pin_req), 8, "gsp_pin_req alignment"); + TEST_ASSERT_EQ(__alignof__(struct gsp_sched_req), 8, "gsp_sched_req alignment"); + TEST_ASSERT_EQ(__alignof__(struct gsp_bar_trap_req), 8, "gsp_bar_trap_req alignment"); + TEST_ASSERT_EQ(__alignof__(struct gsp_migrate_req), 8, "gsp_migrate_req alignment"); + + return TEST_PASS; +} + +/* ============================================================ + * Message Encoding/Decoding Tests + * ============================================================ */ + +static int test_rpc_msg_encoding(void) +{ + struct gsp_rpc_msg msg = {0}; + struct gsp_alloc_req *alloc_req = (struct gsp_alloc_req *)msg.payload; + + pr_info("=== Testing RPC Message Encoding ===\n"); + + /* Test ALLOC message encoding */ + msg.type = cpu_to_le32(GSP_RPC_ALLOC); + msg.subcmd = cpu_to_le32(GSP_ALLOC_SUBCMD_DEFAULT); + msg.seq_id = cpu_to_le32(0x12345678); + msg.flags = cpu_to_le32(0x00000001); + + alloc_req->size = cpu_to_le64(0x100000); + alloc_req->alignment = cpu_to_le64(0x1000); + alloc_req->mem_type = cpu_to_le32(0x2); + alloc_req->flags = cpu_to_le32(0x3); + alloc_req->addr_hint = cpu_to_le64(0x0); + alloc_req->pid = cpu_to_le32(0x456); + alloc_req->reserved = 0; + + TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_ALLOC, "RPC type field"); + TEST_ASSERT_EQ(le32_to_cpu(msg.subcmd), GSP_ALLOC_SUBCMD_DEFAULT, "RPC subcmd field"); + TEST_ASSERT_EQ(le32_to_cpu(msg.seq_id), 0x12345678, "RPC seq_id field"); + TEST_ASSERT_EQ(le32_to_cpu(msg.flags), 0x00000001, "RPC flags field"); + TEST_ASSERT_EQ(le64_to_cpu(alloc_req->size), 0x100000, "ALLOC size field"); + TEST_ASSERT_EQ(le64_to_cpu(alloc_req->alignment), 0x1000, "ALLOC alignment field"); + TEST_ASSERT_EQ(le32_to_cpu(alloc_req->mem_type), 0x2, "ALLOC mem_type field"); + TEST_ASSERT_EQ(le32_to_cpu(alloc_req->flags), 0x3, "ALLOC flags field"); + TEST_ASSERT_EQ(le64_to_cpu(alloc_req->addr_hint), 0x0, "ALLOC addr_hint field"); + TEST_ASSERT_EQ(le32_to_cpu(alloc_req->pid), 0x456, "ALLOC pid field"); + + return TEST_PASS; +} + +static int test_map_msg_encoding(void) +{ + struct gsp_rpc_msg msg = {0}; + struct gsp_map_req *map_req = (struct gsp_map_req *)msg.payload; + + pr_info("=== Testing MAP Message Encoding ===\n"); + + msg.type = cpu_to_le32(GSP_RPC_MAP); + msg.subcmd = cpu_to_le32(0); + msg.seq_id = cpu_to_le32(0xABCDEF00); + + map_req->handle = cpu_to_le64(0xDEADBEEF); + map_req->gpu_va = cpu_to_le64(0x80000000); + map_req->size = cpu_to_le64(0x200000); + map_req->prot = cpu_to_le32(0x7); + map_req->flags = cpu_to_le32(0x1); + map_req->offset = cpu_to_le64(0x0); + map_req->reserved = 0; + + TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_MAP, "MAP RPC type"); + TEST_ASSERT_EQ(le64_to_cpu(map_req->handle), 0xDEADBEEF, "MAP handle"); + TEST_ASSERT_EQ(le64_to_cpu(map_req->gpu_va), 0x80000000, "MAP gpu_va"); + TEST_ASSERT_EQ(le64_to_cpu(map_req->size), 0x200000, "MAP size"); + TEST_ASSERT_EQ(le32_to_cpu(map_req->prot), 0x7, "MAP prot"); + TEST_ASSERT_EQ(le32_to_cpu(map_req->flags), 0x1, "MAP flags"); + TEST_ASSERT_EQ(le64_to_cpu(map_req->offset), 0x0, "MAP offset"); + + return TEST_PASS; +} + +static int test_pin_msg_encoding(void) +{ + struct gsp_rpc_msg msg = {0}; + struct gsp_pin_req *pin_req = (struct gsp_pin_req *)msg.payload; + + pr_info("=== Testing PIN Message Encoding ===\n"); + + msg.type = cpu_to_le32(GSP_RPC_PIN); + msg.subcmd = cpu_to_le32(GSP_PIN_SUBCMD_DEFAULT); + + pin_req->handle = cpu_to_le64(0xCAFEBABE); + pin_req->offset = cpu_to_le64(0x1000); + pin_req->size = cpu_to_le64(0x10000); + pin_req->flags = cpu_to_le32(0x3); + pin_req->reserved[0] = 0; + pin_req->reserved[1] = 0; + + TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_PIN, "PIN RPC type"); + TEST_ASSERT_EQ(le32_to_cpu(msg.subcmd), GSP_PIN_SUBCMD_DEFAULT, "PIN subcmd"); + TEST_ASSERT_EQ(le64_to_cpu(pin_req->handle), 0xCAFEBABE, "PIN handle"); + TEST_ASSERT_EQ(le64_to_cpu(pin_req->offset), 0x1000, "PIN offset"); + TEST_ASSERT_EQ(le64_to_cpu(pin_req->size), 0x10000, "PIN size"); + TEST_ASSERT_EQ(le32_to_cpu(pin_req->flags), 0x3, "PIN flags"); + + return TEST_PASS; +} + +static int test_sched_msg_encoding(void) +{ + struct gsp_rpc_msg msg = {0}; + struct gsp_sched_req *sched_req = (struct gsp_sched_req *)msg.payload; + + pr_info("=== Testing SCHEDULE Message Encoding ===\n"); + + msg.type = cpu_to_le32(GSP_RPC_SCHEDULE); + msg.subcmd = cpu_to_le32(GSP_SCHED_SUBCMD_DEFAULT); + + sched_req->work_handle = cpu_to_le64(0x11111111); + sched_req->callback = cpu_to_le64(0x22222222); + sched_req->arg = cpu_to_le64(0x33333333); + sched_req->priority = cpu_to_le32(0x5); + sched_req->flags = cpu_to_le32(0x0); + sched_req->reserved[0] = 0; + sched_req->reserved[1] = 0; + + TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_SCHEDULE, "SCHED RPC type"); + TEST_ASSERT_EQ(le64_to_cpu(sched_req->work_handle), 0x11111111, "SCHED work_handle"); + TEST_ASSERT_EQ(le64_to_cpu(sched_req->callback), 0x22222222, "SCHED callback"); + TEST_ASSERT_EQ(le64_to_cpu(sched_req->arg), 0x33333333, "SCHED arg"); + TEST_ASSERT_EQ(le32_to_cpu(sched_req->priority), 0x5, "SCHED priority"); + + return TEST_PASS; +} + +static int test_bar_trap_msg_encoding(void) +{ + struct gsp_rpc_msg msg = {0}; + struct gsp_bar_trap_req *bar_req = (struct gsp_bar_trap_req *)msg.payload; + + pr_info("=== Testing BAR_TRAP Message Encoding ===\n"); + + msg.type = cpu_to_le32(GSP_RPC_BAR_TRAP); + msg.subcmd = cpu_to_le32(GSP_BAR_TRAP_SUBCMD_DEFAULT); + + bar_req->bar_addr = cpu_to_le64(0x00100000); + bar_req->access_type = cpu_to_le32(0x1); + bar_req->size = cpu_to_le32(0x4); + bar_req->value = cpu_to_le64(0xDEADBEEF); + bar_req->bar_num = cpu_to_le32(0x1); + + TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_BAR_TRAP, "BAR_TRAP RPC type"); + TEST_ASSERT_EQ(le64_to_cpu(bar_req->bar_addr), 0x00100000, "BAR_TRAP bar_addr"); + TEST_ASSERT_EQ(le32_to_cpu(bar_req->access_type), 0x1, "BAR_TRAP access_type"); + TEST_ASSERT_EQ(le32_to_cpu(bar_req->size), 0x4, "BAR_TRAP size"); + TEST_ASSERT_EQ(le64_to_cpu(bar_req->value), 0xDEADBEEF, "BAR_TRAP value"); + TEST_ASSERT_EQ(le32_to_cpu(bar_req->bar_num), 0x1, "BAR_TRAP bar_num"); + + return TEST_PASS; +} + +static int test_migrate_msg_encoding(void) +{ + struct gsp_rpc_msg msg = {0}; + struct gsp_migrate_req *mig_req = (struct gsp_migrate_req *)msg.payload; + + pr_info("=== Testing MIGRATE Message Encoding ===\n"); + + msg.type = cpu_to_le32(GSP_RPC_MIGRATE); + msg.subcmd = cpu_to_le32(GSP_MIGRATE_SUBCMD_SAVE); + + mig_req->src_gpu_id = cpu_to_le64(0x1); + mig_req->dst_gpu_id = cpu_to_le64(0x2); + mig_req->memory_bitmap = cpu_to_le64(0xFFFFFFFFFFFFFFFF); + mig_req->page_table_ptr = cpu_to_le64(0x80000000); + mig_req->state = cpu_to_le32(GSP_MIGRATE_SUBCMD_SAVE); + mig_req->flags = cpu_to_le32(0); + mig_req->context = cpu_to_le64(0); + mig_req->reserved = 0; + + TEST_ASSERT_EQ(le32_to_cpu(msg.type), GSP_RPC_MIGRATE, "MIGRATE RPC type"); + TEST_ASSERT_EQ(le32_to_cpu(msg.subcmd), GSP_MIGRATE_SUBCMD_SAVE, "MIGRATE subcmd"); + TEST_ASSERT_EQ(le64_to_cpu(mig_req->src_gpu_id), 0x1, "MIGRATE src_gpu_id"); + TEST_ASSERT_EQ(le64_to_cpu(mig_req->dst_gpu_id), 0x2, "MIGRATE dst_gpu_id"); + TEST_ASSERT_EQ(le64_to_cpu(mig_req->memory_bitmap), 0xFFFFFFFFFFFFFFFF, "MIGRATE memory_bitmap"); + TEST_ASSERT_EQ(le64_to_cpu(mig_req->page_table_ptr), 0x80000000, "MIGRATE page_table_ptr"); + + /* Test RESTORE subcmd */ + msg.subcmd = cpu_to_le32(GSP_MIGRATE_SUBCMD_RESTORE); + mig_req->state = cpu_to_le32(GSP_MIGRATE_SUBCMD_RESTORE); + mig_req->flags = cpu_to_le32(GSP_MIGRATE_SUBCMD_RESTORE); + mig_req->context = cpu_to_le64(0xDEADBEEF); + + TEST_ASSERT_EQ(le32_to_cpu(msg.subcmd), GSP_MIGRATE_SUBCMD_RESTORE, "MIGRATE restore subcmd"); + TEST_ASSERT_EQ(le64_to_cpu(mig_req->context), 0xDEADBEEF, "MIGRATE checkpoint context"); + + return TEST_PASS; +} + +/* ============================================================ + * Firmware Address Validation Tests + * ============================================================ */ + +static int test_firmware_addresses(void) +{ + pr_info("=== Testing Firmware Address Constants ===\n"); + + TEST_ASSERT_EQ(GSP_FW_BASE, 0x04000000UL, "GSP_FW_BASE"); + TEST_ASSERT_EQ(GSP_RPC_DISPATCH_PRIMARY, 0x057a1ac0UL, "Primary Dispatcher"); + TEST_ASSERT_EQ(GSP_RPC_JUMP_TABLE, 0x057a3ad8UL, "Jump Table"); + TEST_ASSERT_EQ(GSP_RPC_DISPATCH_SECONDARY, 0x04ed39dcUL, "Secondary Dispatcher"); + TEST_ASSERT_EQ(GSP_RPC_DISPATCH_TERTIARY, 0x04cc58f8UL, "Tertiary Dispatcher"); + TEST_ASSERT_EQ(GSP_RPC_DISPATCH_QUATERNARY, 0x05723ef4UL, "Quaternary Dispatcher"); + TEST_ASSERT_EQ(GSP_ALLOC_MAIN, 0x057e94ecUL, "ALLOC Main"); + TEST_ASSERT_EQ(GSP_SCHEDULE_MAIN, 0x051efbf4UL, "SCHEDULE Main"); + TEST_ASSERT_EQ(GSP_BAR_TRAP_HANDLER_JT, 0x057675b7UL, "BAR_TRAP Handler (JT)"); + TEST_ASSERT_EQ(GSP_BAR_TRAP_HANDLER_PRIM, 0x04cb8189UL, "BAR_TRAP Handler (Primary)"); + TEST_ASSERT_EQ(GSP_PIN_HANDLER_SEC, 0x04ed34b8UL, "PIN Handler (Secondary)"); + TEST_ASSERT_EQ(GSP_MAP_HANDLER_A, 0x05766ab7UL, "MAP Handler A"); + TEST_ASSERT_EQ(GSP_MAP_HANDLER_B, 0x05778a37UL, "MAP Handler B"); + TEST_ASSERT_EQ(GSP_UNMAP_HANDLER_A, 0x057669b7UL, "UNMAP Handler A"); + TEST_ASSERT_EQ(GSP_UNMAP_HANDLER_B, 0x05779937UL, "UNMAP Handler B"); + TEST_ASSERT_EQ(GSP_PIN_HANDLER_A, 0x057664b7UL, "PIN Handler A"); + TEST_ASSERT_EQ(GSP_PIN_HANDLER_B, 0x0577a3b7UL, "PIN Handler B"); + TEST_ASSERT_EQ(GSP_UNPIN_HANDLER_A, 0x057682b7UL, "UNPIN Handler A"); + TEST_ASSERT_EQ(GSP_UNPIN_HANDLER_B, 0x05779fb7UL, "UNPIN Handler B"); + TEST_ASSERT_EQ(GSP_SCHEDULE_HANDLER_A, 0x05767f37UL, "SCHEDULE Handler A"); + TEST_ASSERT_EQ(GSP_SCHEDULE_HANDLER_B, 0x0575aeb7UL, "SCHEDULE Handler B"); + TEST_ASSERT_EQ(GSP_YIELD_HANDLER_A, 0x0575ae37UL, "YIELD Handler A"); + TEST_ASSERT_EQ(GSP_YIELD_HANDLER_B, 0x05767337UL, "YIELD Handler B"); + TEST_ASSERT_EQ(GSP_NOTIFY_HANDLER_A, 0x0577c8b7UL, "NOTIFY Handler A"); + TEST_ASSERT_EQ(GSP_NOTIFY_HANDLER_B, 0x05767837UL, "NOTIFY Handler B"); + TEST_ASSERT_EQ(GSP_ENTRY_POINT, 0x04009000UL, "Entry Point"); + + return TEST_PASS; +} + +/* ============================================================ + * RPC Type/Subcommand Validation Tests + * ============================================================ */ + +static int test_rpc_types(void) +{ + pr_info("=== Testing RPC Type Constants ===\n"); + + TEST_ASSERT_EQ(GSP_RPC_ALLOC, 0x01, "RPC_ALLOC"); + TEST_ASSERT_EQ(GSP_RPC_FREE, 0x02, "RPC_FREE"); + TEST_ASSERT_EQ(GSP_RPC_MAP, 0x03, "RPC_MAP"); + TEST_ASSERT_EQ(GSP_RPC_UNMAP, 0x04, "RPC_UNMAP"); + TEST_ASSERT_EQ(GSP_RPC_PIN, 0x05, "RPC_PIN"); + TEST_ASSERT_EQ(GSP_RPC_UNPIN, 0x06, "RPC_UNPIN"); + TEST_ASSERT_EQ(GSP_RPC_SCHEDULE, 0x07, "RPC_SCHEDULE"); + TEST_ASSERT_EQ(GSP_RPC_YIELD, 0x08, "RPC_YIELD"); + TEST_ASSERT_EQ(GSP_RPC_NOTIFY, 0x09, "RPC_NOTIFY"); + TEST_ASSERT_EQ(GSP_RPC_BAR_TRAP, 0x0A, "RPC_BAR_TRAP"); + TEST_ASSERT_EQ(GSP_RPC_MIGRATE, 0x0B, "RPC_MIGRATE"); + TEST_ASSERT_EQ(GSP_RPC_DEBUG, 0xFF, "RPC_DEBUG"); + + return TEST_PASS; +} + +static int test_alloc_subcmds(void) +{ + pr_info("=== Testing ALLOC Sub-commands ===\n"); + + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_DEFAULT, 0, "ALLOC_SUBCMD_DEFAULT"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE1, 1, "ALLOC_SUBCMD_TYPE1"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE2, 3, "ALLOC_SUBCMD_TYPE2"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE3, 4, "ALLOC_SUBCMD_TYPE3"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE4, 5, "ALLOC_SUBCMD_TYPE4"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE5, 8, "ALLOC_SUBCMD_TYPE5"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE6, 10, "ALLOC_SUBCMD_TYPE6"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE7, 12, "ALLOC_SUBCMD_TYPE7"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE8, 17, "ALLOC_SUBCMD_TYPE8"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE9, 19, "ALLOC_SUBCMD_TYPE9"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE10, 20, "ALLOC_SUBCMD_TYPE10"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE11, 21, "ALLOC_SUBCMD_TYPE11"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE12, 22, "ALLOC_SUBCMD_TYPE12"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE13, 23, "ALLOC_SUBCMD_TYPE13"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE14, 24, "ALLOC_SUBCMD_TYPE14"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE15, 25, "ALLOC_SUBCMD_TYPE15"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE16, 26, "ALLOC_SUBCMD_TYPE16"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE17, 27, "ALLOC_SUBCMD_TYPE17"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE18, 28, "ALLOC_SUBCMD_TYPE18"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE19, 29, "ALLOC_SUBCMD_TYPE19"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE20, 30, "ALLOC_SUBCMD_TYPE20"); + TEST_ASSERT_EQ(GSP_ALLOC_SUBCMD_TYPE21, 31, "ALLOC_SUBCMD_TYPE21"); + + return TEST_PASS; +} + +/* ============================================================ + * Payload Size Validation + * ============================================================ */ + +static int test_payload_capacity(void) +{ + pr_info("=== Testing Payload Capacity ===\n"); + + /* gsp_rpc_msg has 6 x u64 payload = 48 bytes */ + TEST_ASSERT(sizeof(struct gsp_alloc_req) <= 48, "ALLOC req fits in payload"); + TEST_ASSERT(sizeof(struct gsp_map_req) <= 48, "MAP req fits in payload"); + TEST_ASSERT(sizeof(struct gsp_pin_req) <= 48, "PIN req fits in payload"); + TEST_ASSERT(sizeof(struct gsp_sched_req) <= 48, "SCHED req fits in payload"); + TEST_ASSERT(sizeof(struct gsp_bar_trap_req) <= 48, "BAR_TRAP req fits in payload"); + /* MIGRATE req is 56 bytes - uses extended payload format */ + TEST_ASSERT(sizeof(struct gsp_migrate_req) == 56, "MIGRATE req size is 56 bytes"); + + return TEST_PASS; +} + +/* ============================================================ + * Module Init/Exit + * ============================================================ */ + +static int __init gsp_rpc_test_init(void) +{ + pr_info("========================================\n"); + pr_info("GSP RPC Client Test Suite Starting\n"); + pr_info("========================================\n"); + + test_structure_sizes(); + test_structure_alignment(); + test_rpc_msg_encoding(); + test_map_msg_encoding(); + test_pin_msg_encoding(); + test_sched_msg_encoding(); + test_bar_trap_msg_encoding(); + test_migrate_msg_encoding(); + test_firmware_addresses(); + test_rpc_types(); + test_alloc_subcmds(); + test_payload_capacity(); + + pr_info("========================================\n"); + pr_info("Test Results: %d total, %d passed, %d failed\n", + test_count, pass_count, fail_count); + pr_info("========================================\n"); + + if (fail_count > 0) { + pr_err("TEST SUITE FAILED\n"); + return -EINVAL; + } + + pr_info("TEST SUITE PASSED\n"); + return 0; +} + +static void __exit gsp_rpc_test_exit(void) +{ + pr_info("GSP RPC Client Test Suite Exited\n"); +} + +module_init(gsp_rpc_test_init); +module_exit(gsp_rpc_test_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Open-IOV Project"); +MODULE_DESCRIPTION("GSP RPC Client Test/Validation Harness"); +MODULE_VERSION("1.0"); diff --git a/kernel/tests/gsp_vgpu_comprehensive_test.c b/kernel/tests/gsp_vgpu_comprehensive_test.c new file mode 100644 index 0000000..7918beb --- /dev/null +++ b/kernel/tests/gsp_vgpu_comprehensive_test.c @@ -0,0 +1,432 @@ +/* + * GSP vGPU-IO Driver - Comprehensive Test Suite + * Tests all driver components: RPC, VF, BAR, WPR, Migration + * Compiles against kernel headers only (no driver dependency) + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ktime.h> +#include <linux/atomic.h> +#include <linux/list.h> +#include <linux/idr.h> +#include <linux/mutex.h> +#include <linux/kref.h> +#include <linux/completion.h> +#include <linux/wait.h> +#include <linux/spinlock.h> +#include <linux/debugfs.h> +#include <linux/vmalloc.h> + +#include <gsp_core.h> + +static int test_count = 0; +static int pass_count = 0; +static int fail_count = 0; + +#define TEST_ASSERT(cond, msg) \ + do { \ + test_count++; \ + if (cond) { \ + pass_count++; \ + pr_info("PASS: %s\n", msg); \ + } else { \ + fail_count++; \ + pr_err("FAIL: %s\n", msg); \ + } \ + } while (0) + +#define TEST_ASSERT_EQ(a, b, msg) \ + TEST_ASSERT((a) == (b), msg " (expected " #b ", got " #a ")") + +#define TEST_ASSERT_NE(a, b, msg) \ + TEST_ASSERT((a) != (b), msg " (expected not " #b ", got " #a ")") + +/* ============================================================ + * Test: Kernel API Availability + * ============================================================ */ + +static int test_kernel_apis(void) +{ + pr_info("=== Testing Kernel API Availability ===\n"); + + /* Test kref */ + struct kref *ref = kmalloc(sizeof(*ref), GFP_KERNEL); + TEST_ASSERT(ref != NULL, "kref allocation"); + kref_init(ref); + TEST_ASSERT(kref_read(ref) == 1, "kref_init sets refcount to 1"); + + kref_get(ref); + TEST_ASSERT(kref_read(ref) == 2, "kref_get increments refcount"); + + kref_put(ref, (void (*)(struct kref *))kfree); + TEST_ASSERT(kref_read(ref) == 1, "kref_put decrements refcount"); + + kref_put(ref, (void (*)(struct kref *))kfree); + TEST_ASSERT(kref_read(ref) == 0, "kref_put reaches zero"); + + /* Test completion */ + struct completion comp; + init_completion(&comp); + TEST_ASSERT(!completion_done(&comp), "completion initially not done"); + complete(&comp); + TEST_ASSERT(completion_done(&comp), "completion marked done"); + + init_completion(&comp); + TEST_ASSERT(wait_for_completion_timeout(&comp, msecs_to_jiffies(100)) > 0, + "wait_for_completion_timeout works"); + + /* Test atomic64 */ + atomic64_t a64 = ATOMIC64_INIT(0); + atomic64_inc(&a64); + TEST_ASSERT_EQ(atomic64_read(&a64), 1, "atomic64_inc"); + atomic64_add(5, &a64); + TEST_ASSERT_EQ(atomic64_read(&a64), 6, "atomic64_add"); + atomic64_sub(3, &a64); + TEST_ASSERT_EQ(atomic64_read(&a64), 3, "atomic64_sub"); + + /* Test mutex */ + struct mutex mtx; + mutex_init(&mtx); + mutex_lock(&mtx); + TEST_ASSERT(mutex_is_locked(&mtx), "mutex_is_locked after lock"); + mutex_unlock(&mtx); + TEST_ASSERT(!mutex_is_locked(&mtx), "mutex_is_locked after unlock"); + + /* Test spinlock */ + spinlock_t sl = __SPIN_LOCK_UNLOCKED(sl); + unsigned long flags; + spin_lock_irqsave(&sl, flags); + TEST_ASSERT(spin_is_locked(&sl), "spin_is_locked after lock"); + spin_unlock_irqrestore(&sl, flags); + TEST_ASSERT(!spin_is_locked(&sl), "spin_is_locked after unlock"); + + /* Test idr */ + struct idr idr; + idr_init(&idr); + int id = idr_alloc(&idr, (void *)0xdeadbeef, 1, 100, GFP_KERNEL); + TEST_ASSERT(id >= 1 && id < 100, "idr_alloc returns valid id"); + void *ptr = idr_find(&idr, id); + TEST_ASSERT(ptr == (void *)0xdeadbeef, "idr_find returns correct pointer"); + idr_remove(&idr, id); + TEST_ASSERT(idr_find(&idr, id) == NULL, "idr_remove works"); + idr_destroy(&idr); + + /* Test list */ + struct list_head head; + INIT_LIST_HEAD(&head); + TEST_ASSERT(list_empty(&head), "list_empty on new list"); + + struct list_head item; + INIT_LIST_HEAD(&item); + list_add(&item, &head); + TEST_ASSERT(!list_empty(&head), "list not empty after add"); + list_del(&item); + TEST_ASSERT(list_empty(&head), "list empty after del"); + + /* Test ktime */ + ktime_t start = ktime_get(); + ktime_t end = ktime_get(); + s64 diff = ktime_to_ns(ktime_sub(end, start)); + TEST_ASSERT(diff >= 0, "ktime_get and ktime_to_ns work"); + + return 0; +} + +/* ============================================================ + * Test: Structure Sizes (from driver header) + * ============================================================ */ + +static int test_structure_sizes(void) +{ + pr_info("=== Testing Structure Sizes ===\n"); + + /* These sizes must match driver/include/gsp_core.h */ + TEST_ASSERT_EQ(sizeof(struct gsp_rpc_header), 64, "gsp_rpc_header size"); + TEST_ASSERT_EQ(sizeof(struct gsp_rpc_message), 256, "gsp_rpc_message size"); + TEST_ASSERT_EQ(sizeof(struct gsp_rpc_response), 32, "gsp_rpc_response size"); + TEST_ASSERT_EQ(sizeof(struct gsp_alloc_payload), 40, "gsp_alloc_payload size"); + TEST_ASSERT_EQ(sizeof(struct gsp_alloc_response), 24, "gsp_alloc_response size"); + TEST_ASSERT_EQ(sizeof(struct gsp_pin_payload), 40, "gsp_pin_payload size"); + TEST_ASSERT_EQ(sizeof(struct gsp_pin_response), 8, "gsp_pin_response size"); + TEST_ASSERT_EQ(sizeof(struct gsp_bar_trap_payload), 40, "gsp_bar_trap_payload size"); + TEST_ASSERT_EQ(sizeof(struct gsp_bar_trap_response), 8, "gsp_bar_trap_response size"); + TEST_ASSERT_EQ(sizeof(struct gsp_migrate_payload), 56, "gsp_migrate_payload size"); + TEST_ASSERT_EQ(sizeof(struct gsp_subcmd_desc), 8, "gsp_subcmd_desc size"); + TEST_ASSERT_EQ(sizeof(struct gsp_vf_config), 56, "gsp_vf_config size"); + + return 0; +} + +/* ============================================================ + * Test: Enum Values (from driver header) + * ============================================================ */ + +static int test_enum_values(void) +{ + pr_info("=== Testing Enum Values ===\n"); + + TEST_ASSERT_EQ(GSP_RPC_ALLOC, 0x01, "RPC_ALLOC"); + TEST_ASSERT_EQ(GSP_RPC_FREE, 0x02, "RPC_FREE"); + TEST_ASSERT_EQ(GSP_RPC_MAP, 0x03, "RPC_MAP"); + TEST_ASSERT_EQ(GSP_RPC_UNMAP, 0x04, "RPC_UNMAP"); + TEST_ASSERT_EQ(GSP_RPC_PIN, 0x05, "RPC_PIN"); + TEST_ASSERT_EQ(GSP_RPC_UNPIN, 0x06, "RPC_UNPIN"); + TEST_ASSERT_EQ(GSP_RPC_SCHEDULE, 0x07, "RPC_SCHEDULE"); + TEST_ASSERT_EQ(GSP_RPC_YIELD, 0x08, "RPC_YIELD"); + TEST_ASSERT_EQ(GSP_RPC_NOTIFY, 0x09, "RPC_NOTIFY"); + TEST_ASSERT_EQ(GSP_RPC_BAR_TRAP, 0x0A, "RPC_BAR_TRAP"); + TEST_ASSERT_EQ(GSP_RPC_MIGRATE, 0x0B, "RPC_MIGRATE"); + TEST_ASSERT_EQ(GSP_RPC_DEBUG, 0x0C, "RPC_DEBUG"); + + TEST_ASSERT_EQ(GSP_MEM_VRAM, 0, "MEM_VRAM"); + TEST_ASSERT_EQ(GSP_MEM_SYSMEM, 1, "MEM_SYSMEM"); + TEST_ASSERT_EQ(GSP_MEM_UNCACHED, 2, "MEM_UNCACHED"); + + TEST_ASSERT_EQ(GSP_PIN_READ, (1 << 0), "PIN_READ"); + TEST_ASSERT_EQ(GSP_PIN_WRITE, (1 << 1), "PIN_WRITE"); + TEST_ASSERT_EQ(GSP_PIN_LONGTERM, (1 << 2), "PIN_LONGTERM"); + + TEST_ASSERT_EQ(GSP_MIG_QUERY, 0, "MIG_QUERY"); + TEST_ASSERT_EQ(GSP_MIG_PREPARE, 1, "MIG_PREPARE"); + TEST_ASSERT_EQ(GSP_MIG_TRANSFER, 2, "MIG_TRANSFER"); + TEST_ASSERT_EQ(GSP_MIG_COMMIT, 3, "MIG_COMMIT"); + TEST_ASSERT_EQ(GSP_MIG_CLEANUP, 4, "MIG_CLEANUP"); + + TEST_ASSERT_EQ(GSP_MIG_LIVE, (1 << 0), "MIG_LIVE"); + TEST_ASSERT_EQ(GSP_MIG_POSTCOPY, (1 << 1), "MIG_POSTCOPY"); + TEST_ASSERT_EQ(GSP_MIG_AUTO_COMMIT, (1 << 2), "MIG_AUTO_COMMIT"); + TEST_ASSERT_EQ(GSP_MIG_VERIFY, (1 << 3), "MIG_VERIFY"); + + TEST_ASSERT_EQ(GSP_VF_CREATING, 0, "VF_CREATING"); + TEST_ASSERT_EQ(GSP_VF_ACTIVE, 1, "VF_ACTIVE"); + TEST_ASSERT_EQ(GSP_VF_MIGRATING, 2, "VF_MIGRATING"); + TEST_ASSERT_EQ(GSP_VF_SUSPENDED, 3, "VF_SUSPENDED"); + TEST_ASSERT_EQ(GSP_VF_DESTROYING, 4, "VF_DESTROYING"); + + return 0; +} + +/* ============================================================ + * Test: Error Code Mapping + * ============================================================ */ + +static int test_error_codes(void) +{ + pr_info("=== Testing Error Codes ===\n"); + + TEST_ASSERT_EQ(GSP_OK, 0, "GSP_OK"); + TEST_ASSERT_EQ(GSP_EPERM, 0xFFFFFFFF, "GSP_EPERM"); + TEST_ASSERT_EQ(GSP_ENOENT, 0xFFFFFFFE, "GSP_ENOENT"); + TEST_ASSERT_EQ(GSP_EINVAL, 0xFFFFFFFD, "GSP_EINVAL"); + TEST_ASSERT_EQ(GSP_ENODEV, 0xFFFFFFFA, "GSP_ENODEV"); + TEST_ASSERT_EQ(GSP_ENOMEM, 0xFFFFFFF4, "GSP_ENOMEM"); + TEST_ASSERT_EQ(GSP_EBUSY, 0xFFFFFFEE, "GSP_EBUSY"); + TEST_ASSERT_EQ(GSP_ENOSPC, 0xFFFFFFE4, "GSP_ENOSPC"); + TEST_ASSERT_EQ(GSP_ETIMEDOUT, 0x0000001E, "GSP_ETIMEDOUT"); + TEST_ASSERT_EQ(GSP_EOVERFLOW, 0x00000025, "GSP_EOVERFLOW"); + TEST_ASSERT_EQ(GSP_EDEV, 0x0000001F, "GSP_EDEV"); + + return 0; +} + +/* ============================================================ + * Test: Firmware Constants + * ============================================================ */ + +static int test_firmware_constants(void) +{ + pr_info("=== Testing Firmware Constants ===\n"); + + TEST_ASSERT_EQ(GSP_FW_BASE_ADDR, 0x04000000ULL, "FW_BASE_ADDR"); + TEST_ASSERT_EQ(GSP_FW_SIZE, 0x02000000ULL, "FW_SIZE"); + TEST_ASSERT_EQ(GSP_ENTRY_POINT, 0x04009000ULL, "ENTRY_POINT"); + TEST_ASSERT_EQ(GSP_PRIMARY_DISPATCHER, 0x057A1AC0ULL, "PRIMARY_DISPATCHER"); + TEST_ASSERT_EQ(GSP_SECONDARY_DISPATCHER, 0x04ED39DCULL, "SECONDARY_DISPATCHER"); + TEST_ASSERT_EQ(GSP_RPC_JUMP_TABLE, 0x059BFEB0ULL, "RPC_JUMP_TABLE"); + TEST_ASSERT_EQ(GSP_BAR0_DOORBELL, 0x1000, "BAR0_DOORBELL"); + TEST_ASSERT_EQ(GSP_BAR0_MSIX, 0x2000, "BAR0_MSIX"); + TEST_ASSERT_EQ(GSP_MSG_RING_SIZE, 256, "MSG_RING_SIZE"); + TEST_ASSERT_EQ(GSP_MSG_MAX_SIZE, 256, "MSG_MAX_SIZE"); + TEST_ASSERT_EQ(GSP_MAX_PENDING, 64, "MAX_PENDING"); + + return 0; +} + +/* ============================================================ + * Test: Migration State Enum (internal) + * ============================================================ */ + +static int test_migration_states(void) +{ + pr_info("=== Testing Migration States ===\n"); + + TEST_ASSERT_EQ(GSP_MIG_S_IDLE, 0, "MIG_S_IDLE"); + TEST_ASSERT_EQ(GSP_MIG_S_QUERY, 1, "MIG_S_QUERY"); + TEST_ASSERT_EQ(GSP_MIG_S_PREPARE, 2, "MIG_S_PREPARE"); + TEST_ASSERT_EQ(GSP_MIG_S_TRANSFER, 3, "MIG_S_TRANSFER"); + TEST_ASSERT_EQ(GSP_MIG_S_COMMIT, 4, "MIG_S_COMMIT"); + TEST_ASSERT_EQ(GSP_MIG_S_CLEANUP, 5, "MIG_S_CLEANUP"); + TEST_ASSERT_EQ(GSP_MIG_S_FAILED, 6, "MIG_S_FAILED"); + TEST_ASSERT_EQ(GSP_MIG_S_COMPLETED, 7, "MIG_S_COMPLETED"); + + return 0; +} + +/* ============================================================ + * Test: WPR Flags and Events + * ============================================================ */ + +static int test_wpr_flags(void) +{ + pr_info("=== Testing WPR Flags ===\n"); + + TEST_ASSERT_EQ(GSP_WPR_WRITE, (1 << 0), "WPR_WRITE"); + TEST_ASSERT_EQ(GSP_WPR_READ, (1 << 1), "WPR_READ"); + TEST_ASSERT_EQ(GSP_WPR_EXECUTE, (1 << 2), "WPR_EXECUTE"); + + TEST_ASSERT_EQ(GSP_WPR_WRITE_COMPLETE, 0, "WPR_WRITE_COMPLETE"); + TEST_ASSERT_EQ(GSP_WPR_READ_COMPLETE, 1, "WPR_READ_COMPLETE"); + TEST_ASSERT_EQ(GSP_WPR_WRITE_TIMEOUT, 2, "WPR_WRITE_TIMEOUT"); + TEST_ASSERT_EQ(GSP_WPR_READ_TIMEOUT, 3, "WPR_READ_TIMEOUT"); + + return 0; +} + +/* ============================================================ + * Test: Message Flags + * ============================================================ */ + +static int test_message_flags(void) +{ + pr_info("=== Testing Message Flags ===\n"); + + TEST_ASSERT_EQ(GSP_MSG_SYNC, (1 << 0), "MSG_SYNC"); + TEST_ASSERT_EQ(GSP_MSG_ASYNC, (1 << 1), "MSG_ASYNC"); + TEST_ASSERT_EQ(GSP_MSG_NOREPLY, (1 << 2), "MSG_NOREPLY"); + + return 0; +} + +/* ============================================================ + * Test: BAR Trap Types + * ============================================================ */ + +static int test_bar_trap_types(void) +{ + pr_info("=== Testing BAR Trap Types ===\n"); + + TEST_ASSERT_EQ(GSP_BAR_READ, 0, "BAR_READ"); + TEST_ASSERT_EQ(GSP_BAR_WRITE, 1, "BAR_WRITE"); + TEST_ASSERT_EQ(GSP_BAR_ATOMIC, 2, "BAR_ATOMIC"); + + return 0; +} + +/* ============================================================ + * Test: Memory Allocation Patterns + * ============================================================ */ + +static int test_memory_patterns(void) +{ + pr_info("=== Testing Memory Allocation Patterns ===\n"); + + /* Test vzalloc/vfree */ + void *ptr = vzalloc(4096); + TEST_ASSERT(ptr != NULL, "vzalloc allocates memory"); + vfree(ptr); + + /* Test kmalloc/kfree */ + ptr = kmalloc(1024, GFP_KERNEL); + TEST_ASSERT(ptr != NULL, "kmalloc allocates memory"); + kfree(ptr); + + /* Test kcalloc */ + ptr = kcalloc(10, 64, GFP_KERNEL); + TEST_ASSERT(ptr != NULL, "kcalloc allocates zeroed memory"); + kfree(ptr); + + return 0; +} + +/* ============================================================ + * Test: String Operations + * ============================================================ */ + +static int test_string_ops(void) +{ + pr_info("=== Testing String Operations ===\n"); + + char buf[64]; + int len = snprintf(buf, sizeof(buf), "test_%d", 42); + TEST_ASSERT(len > 0 && len < sizeof(buf), "snprintf works"); + TEST_ASSERT(strcmp(buf, "test_42") == 0, "snprintf formats correctly"); + + char *dup = kstrdup("test_string", GFP_KERNEL); + TEST_ASSERT(dup != NULL, "kstrdup works"); + TEST_ASSERT(strcmp(dup, "test_string") == 0, "kstrdup copies correctly"); + kfree(dup); + + return 0; +} + +/* ============================================================ + * Test Suite Runner + * ============================================================ */ + +static int run_all_tests(void) +{ + pr_info("\n============================================================\n"); + pr_info("GSP vGPU-IO Comprehensive Test Suite Starting\n"); + pr_info("============================================================\n\n"); + + test_kernel_apis(); + test_structure_sizes(); + test_enum_values(); + test_error_codes(); + test_firmware_constants(); + test_migration_states(); + test_wpr_flags(); + test_message_flags(); + test_bar_trap_types(); + test_memory_patterns(); + test_string_ops(); + + pr_info("\n============================================================\n"); + pr_info("TEST RESULTS: %d total, %d passed, %d failed\n", + test_count, pass_count, fail_count); + pr_info("============================================================\n"); + + if (fail_count == 0) { + pr_info("TEST SUITE PASSED\n"); + return 0; + } else { + pr_err("TEST SUITE FAILED\n"); + return -1; + } +} + +/* ============================================================ + * Module Init/Exit + * ============================================================ */ + +static int __init gsp_test_init(void) +{ + return run_all_tests(); +} + +static void __exit gsp_test_exit(void) +{ + pr_info("GSP vGPU-IO test module unloaded\n"); +} + +module_init(gsp_test_init); +module_exit(gsp_test_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("NVIDIA vGPU-IO Team"); +MODULE_DESCRIPTION("GSP vGPU-IO Comprehensive Test Suite"); +MODULE_VERSION("1.0.0");
\ No newline at end of file diff --git a/kernel/tests/gsp_vgpu_kunit_test.c b/kernel/tests/gsp_vgpu_kunit_test.c new file mode 100644 index 0000000..c661f91 --- /dev/null +++ b/kernel/tests/gsp_vgpu_kunit_test.c @@ -0,0 +1,327 @@ +/* + * GSP vGPU-IO KUnit Tests + * In-kernel unit tests for the driver + */ + +#include <kunit/test.h> +#include <kunit/mock.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/atomic.h> +#include <linux/list.h> +#include <linux/idr.h> +#include <linux/mutex.h> +#include <linux/kref.h> +#include <linux/completion.h> +#include <linux/wait.h> +#include <linux/spinlock.h> + +#include <gsp_core.h> + +/* ============================================================ + * Test: Structure Sizes and Alignments + * ============================================================ */ + +static void test_structure_sizes(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_rpc_header), 64); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_rpc_message), 256); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_rpc_response), 32); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_subcmd_desc), 8); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_vf_config), 64); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_alloc_payload), 48); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_alloc_response), 24); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_pin_payload), 40); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_pin_response), 8); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_bar_trap_payload), 40); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_bar_trap_response), 8); + KUNIT_EXPECT_EQ(test, sizeof(struct gsp_migrate_payload), 64); +} + +static void test_structure_alignments(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_rpc_header), 8); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_rpc_message), 8); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_rpc_response), 8); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_subcmd_desc), 4); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_vf_config), 8); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_alloc_payload), 8); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_alloc_response), 8); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_pin_payload), 8); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_pin_response), 8); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_bar_trap_payload), 8); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_bar_trap_response), 8); + KUNIT_EXPECT_EQ(test, __alignof__(struct gsp_migrate_payload), 8); +} + +/* ============================================================ + * Test: Payload Encoding/Decoding + * ============================================================ */ + +static void test_alloc_payload_encoding(struct kunit *test) +{ + struct gsp_alloc_payload payload = {0}; + + payload.size = cpu_to_le64(0x100000); + payload.type = cpu_to_le32(GSP_MEM_VRAM); + payload.alignment = cpu_to_le32(0x1000); + payload.va_hint = cpu_to_le64(0x0); + payload.flags = cpu_to_le32(0x3); + payload.pid = cpu_to_le32(0x456); + payload.vf_id = cpu_to_le32(0x1); + + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.size), 0x100000); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.type), GSP_MEM_VRAM); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.alignment), 0x1000); + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.va_hint), 0x0); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.flags), 0x3); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.pid), 0x456); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.vf_id), 0x1); +} + +static void test_pin_payload_encoding(struct kunit *test) +{ + struct gsp_pin_payload payload = {0}; + + payload.handle = cpu_to_le64(0xCAFEBABE); + payload.offset = cpu_to_le64(0x1000); + payload.size = cpu_to_le64(0x10000); + payload.vf_id = cpu_to_le32(0x2); + payload.flags = cpu_to_le32(GSP_PIN_READ | GSP_PIN_WRITE); + + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.handle), 0xCAFEBABE); + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.offset), 0x1000); + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.size), 0x10000); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.vf_id), 0x2); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.flags), GSP_PIN_READ | GSP_PIN_WRITE); +} + +static void test_bar_trap_payload_encoding(struct kunit *test) +{ + struct gsp_bar_trap_payload payload = {0}; + + payload.bar_index = cpu_to_le32(1); + payload.trap_type = cpu_to_le32(GSP_BAR_WRITE); + payload.offset = cpu_to_le64(0x1000); + payload.value = cpu_to_le64(0xDEADBEEF); + payload.size = cpu_to_le32(4); + payload.vf_id = cpu_to_le32(0x1); + + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.bar_index), 1); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.trap_type), GSP_BAR_WRITE); + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.offset), 0x1000); + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.value), 0xDEADBEEF); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.size), 4); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.vf_id), 0x1); +} + +static void test_migrate_payload_encoding(struct kunit *test) +{ + struct gsp_migrate_payload payload = {0}; + + payload.phase = cpu_to_le32(GSP_MIG_PREPARE); + payload.subcmd = cpu_to_le32(3); + payload.src_vf = cpu_to_le64(0x1); + payload.dst_vf = cpu_to_le64(0x2); + payload.data_ptr = cpu_to_le64(0x80000000); + payload.data_size = cpu_to_le64(0x1000); + payload.flags = cpu_to_le32(GSP_MIG_LIVE | GSP_MIG_VERIFY); + payload.token = cpu_to_le64(0xABCDEF1234567890); + + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.phase), GSP_MIG_PREPARE); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.subcmd), 3); + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.src_vf), 0x1); + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.dst_vf), 0x2); + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.data_ptr), 0x80000000); + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.data_size), 0x1000); + KUNIT_EXPECT_EQ(test, le32_to_cpu(payload.flags), GSP_MIG_LIVE | GSP_MIG_VERIFY); + KUNIT_EXPECT_EQ(test, le64_to_cpu(payload.token), 0xABCDEF1234567890); +} + +static void test_subcmd_desc_encoding(struct kunit *test) +{ + struct gsp_subcmd_desc desc = {0}; + + desc.subcmd_id = cpu_to_le32(0x14); + desc.echo_flags_b = 0xAA; + desc.echo_flags_a = 0xBB; + desc.echo_flags_c = 0xCC; + + KUNIT_EXPECT_EQ(test, le32_to_cpu(desc.subcmd_id), 0x14); + KUNIT_EXPECT_EQ(test, desc.echo_flags_b, 0xAA); + KUNIT_EXPECT_EQ(test, desc.echo_flags_a, 0xBB); + KUNIT_EXPECT_EQ(test, desc.echo_flags_c, 0xCC); +} + +/* ============================================================ + * Test: Firmware Address Constants + * ============================================================ */ + +static void test_firmware_addresses(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, GSP_FW_BASE_ADDR, 0x04000000ULL); + KUNIT_EXPECT_EQ(test, GSP_ENTRY_POINT, 0x04009000ULL); + KUNIT_EXPECT_EQ(test, GSP_PRIMARY_DISPATCHER, 0x057A1AC0ULL); + KUNIT_EXPECT_EQ(test, GSP_SECONDARY_DISPATCHER, 0x04ED39DCULL); + KUNIT_EXPECT_EQ(test, GSP_RPC_JUMP_TABLE, 0x059BFEB0ULL); + KUNIT_EXPECT_EQ(test, 0x04179ED8ULL, 0x04179ED8ULL); /* Migration Table 1 */ + KUNIT_EXPECT_EQ(test, 0x04179FBCULL, 0x04179FBCULL); /* Migration Table 2 */ + KUNIT_EXPECT_EQ(test, 0x04CB8188ULL, 0x04CB8188ULL); /* BAR Handler */ + KUNIT_EXPECT_EQ(test, 0x04F00B0CULL, 0x04F00B0CULL); /* WPR Handler 1 */ + KUNIT_EXPECT_EQ(test, 0x04F00C78ULL, 0x04F00C78ULL); /* WPR Handler 2 */ + KUNIT_EXPECT_EQ(test, 0x057E94ECULL, 0x057E94ECULL); /* ALLOC Main */ + KUNIT_EXPECT_EQ(test, 0x051EFBF4ULL, 0x051EFBF4ULL); /* SCHEDULE Main */ +} + +/* ============================================================ + * Test: RPC Type Constants + * ============================================================ */ + +static void test_rpc_types(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, GSP_RPC_ALLOC, 0x01); + KUNIT_EXPECT_EQ(test, GSP_RPC_FREE, 0x02); + KUNIT_EXPECT_EQ(test, GSP_RPC_MAP, 0x03); + KUNIT_EXPECT_EQ(test, GSP_RPC_UNMAP, 0x04); + KUNIT_EXPECT_EQ(test, GSP_RPC_PIN, 0x05); + KUNIT_EXPECT_EQ(test, GSP_RPC_UNPIN, 0x06); + KUNIT_EXPECT_EQ(test, GSP_RPC_SCHEDULE, 0x07); + KUNIT_EXPECT_EQ(test, GSP_RPC_YIELD, 0x08); + KUNIT_EXPECT_EQ(test, GSP_RPC_NOTIFY, 0x09); + KUNIT_EXPECT_EQ(test, GSP_RPC_BAR_TRAP, 0x0A); + KUNIT_EXPECT_EQ(test, GSP_RPC_MIGRATE, 0x0B); + KUNIT_EXPECT_EQ(test, GSP_RPC_DEBUG, 0x0C); +} + +static void test_migration_constants(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, GSP_MIG_QUERY, 0); + KUNIT_EXPECT_EQ(test, GSP_MIG_PREPARE, 1); + KUNIT_EXPECT_EQ(test, GSP_MIG_TRANSFER, 2); + KUNIT_EXPECT_EQ(test, GSP_MIG_COMMIT, 3); + KUNIT_EXPECT_EQ(test, GSP_MIG_CLEANUP, 4); + + KUNIT_EXPECT_EQ(test, GSP_MIG_LIVE, (1 << 0)); + KUNIT_EXPECT_EQ(test, GSP_MIG_POSTCOPY, (1 << 1)); + KUNIT_EXPECT_EQ(test, GSP_MIG_AUTO_COMMIT, (1 << 2)); + KUNIT_EXPECT_EQ(test, GSP_MIG_VERIFY, (1 << 3)); +} + +static void test_bar_trap_types(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, GSP_BAR_READ, 0); + KUNIT_EXPECT_EQ(test, GSP_BAR_WRITE, 1); + KUNIT_EXPECT_EQ(test, GSP_BAR_ATOMIC, 2); +} + +static void test_pin_flags(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, GSP_PIN_READ, (1 << 0)); + KUNIT_EXPECT_EQ(test, GSP_PIN_WRITE, (1 << 1)); + KUNIT_EXPECT_EQ(test, GSP_PIN_LONGTERM, (1 << 2)); +} + +static void test_wpr_flags(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, GSP_WPR_WRITE, (1 << 0)); + KUNIT_EXPECT_EQ(test, GSP_WPR_READ, (1 << 1)); + KUNIT_EXPECT_EQ(test, GSP_WPR_EXECUTE, (1 << 2)); +} + +static void test_vf_states(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, GSP_VF_CREATING, 0); + KUNIT_EXPECT_EQ(test, GSP_VF_ACTIVE, 1); + KUNIT_EXPECT_EQ(test, GSP_VF_MIGRATING, 2); + KUNIT_EXPECT_EQ(test, GSP_VF_SUSPENDED, 3); + KUNIT_EXPECT_EQ(test, GSP_VF_DESTROYING, 4); +} + +static void test_msg_flags(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, GSP_MSG_SYNC, (1 << 0)); + KUNIT_EXPECT_EQ(test, GSP_MSG_ASYNC, (1 << 1)); + KUNIT_EXPECT_EQ(test, GSP_MSG_NOREPLY, (1 << 2)); +} + +/* ============================================================ + * Test: Error Code Translation + * ============================================================ */ + +static void test_error_codes(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_OK), 0); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_EPERM), -EPERM); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_ENOENT), -ENOENT); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_EINVAL), -EINVAL); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_ENODEV), -ENODEV); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_ENOMEM), -ENOMEM); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_EBUSY), -EBUSY); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_ENOSPC), -ENOSPC); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_ETIMEDOUT), -ETIMEDOUT); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_EOVERFLOW), -EOVERFLOW); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(GSP_EDEV), -EIO); + KUNIT_EXPECT_EQ(test, gsp_errno_to_errno(0xDEADBEEF), -EIO); +} + +/* ============================================================ + * Test: Memory Types + * ============================================================ */ + +static void test_memory_types(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, GSP_MEM_VRAM, 0); + KUNIT_EXPECT_EQ(test, GSP_MEM_SYSMEM, 1); + KUNIT_EXPECT_EQ(test, GSP_MEM_UNCACHED, 2); +} + +/* ============================================================ + * Test: Payload Capacity + * ============================================================ */ + +static void test_payload_capacity(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, sizeof(struct gsp_alloc_payload) <= 192); + KUNIT_EXPECT_TRUE(test, sizeof(struct gsp_pin_payload) <= 192); + KUNIT_EXPECT_TRUE(test, sizeof(struct gsp_bar_trap_payload) <= 192); + KUNIT_EXPECT_TRUE(test, sizeof(struct gsp_migrate_payload) <= 192); + KUNIT_EXPECT_TRUE(test, sizeof(struct gsp_subcmd_desc) <= 192); +} + +/* ============================================================ + * Test Suite Registration + * ============================================================ */ + +static struct kunit_case gsp_vgpu_test_cases[] = { + KUNIT_CASE(test_structure_sizes), + KUNIT_CASE(test_structure_alignments), + KUNIT_CASE(test_alloc_payload_encoding), + KUNIT_CASE(test_pin_payload_encoding), + KUNIT_CASE(test_bar_trap_payload_encoding), + KUNIT_CASE(test_migrate_payload_encoding), + KUNIT_CASE(test_subcmd_desc_encoding), + KUNIT_CASE(test_firmware_addresses), + KUNIT_CASE(test_rpc_types), + KUNIT_CASE(test_migration_constants), + KUNIT_CASE(test_bar_trap_types), + KUNIT_CASE(test_pin_flags), + KUNIT_CASE(test_wpr_flags), + KUNIT_CASE(test_vf_states), + KUNIT_CASE(test_msg_flags), + KUNIT_CASE(test_error_codes), + KUNIT_CASE(test_memory_types), + KUNIT_CASE(test_payload_capacity), + {} +}; + +static struct kunit_suite gsp_vgpu_test_suite = { + .name = "gsp-vgpu-io", + .test_cases = gsp_vgpu_test_cases, +}; + +kunit_test_suites(&gsp_vgpu_test_suite); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Open-IOV Project"); +MODULE_DESCRIPTION("GSP vGPU-IO KUnit Tests");
\ No newline at end of file diff --git a/kernel/tests/gsp_vgpu_mock_test.c b/kernel/tests/gsp_vgpu_mock_test.c new file mode 100644 index 0000000..b9a46df --- /dev/null +++ b/kernel/tests/gsp_vgpu_mock_test.c @@ -0,0 +1,624 @@ +/* + * GSP vGPU-IO Driver Mock Test + * Tests driver logic with mocked hardware interfaces + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/list.h> +#include <linux/idr.h> +#include <linux/kref.h> +#include <linux/completion.h> +#include <linux/atomic.h> + +#define TEST_PASS 0 +#define TEST_FAIL -1 + +static int test_count = 0; +static int pass_count = 0; +static int fail_count = 0; + +#define TEST_ASSERT(cond, msg) \ + do { \ + test_count++; \ + if (cond) { \ + pass_count++; \ + pr_info("PASS: %s\n", msg); \ + } else { \ + fail_count++; \ + pr_err("FAIL: %s\n", msg); \ + } \ + } while (0) + +#define TEST_ASSERT_EQ(a, b, msg) \ + TEST_ASSERT((a) == (b), msg " (expected " #b ", got " #a ")") + +/* ============================================================ + * Mock Hardware Abstraction + * ============================================================ */ + +struct mock_hw { + u64 bar0_regs[256]; + u64 bar1_regs[256]; + u8 *fw_memory; + size_t fw_size; + bool doorbell_triggered; + u32 doorbell_count; + spinlock_t lock; +}; + +static struct mock_hw *mock_hw_create(void) +{ + struct mock_hw *hw = kzalloc(sizeof(*hw), GFP_KERNEL); + if (!hw) + return NULL; + + hw->fw_memory = vmalloc(32 * 1024 * 1024); // 32MB + if (!hw->fw_memory) { + kfree(hw); + return NULL; + } + hw->fw_size = 32 * 1024 * 1024; + spin_lock_init(&hw->lock); + return hw; +} + +static void mock_hw_destroy(struct mock_hw *hw) +{ + if (!hw) + return; + vfree(hw->fw_memory); + kfree(hw); +} + +static void mock_hw_write_bar0(struct mock_hw *hw, u32 offset, u32 value) +{ + unsigned long flags; + spin_lock_irqsave(&hw->lock, flags); + if (offset / 4 < 256) + hw->bar0_regs[offset / 4] = value; + if (offset == 0x1000) { // Doorbell + hw->doorbell_triggered = true; + hw->doorbell_count++; + } + spin_unlock_irqrestore(&hw->lock, flags); +} + +static u32 mock_hw_read_bar0(struct mock_hw *hw, u32 offset) +{ + unsigned long flags; + u32 value = 0; + spin_lock_irqsave(&hw->lock, flags); + if (offset / 4 < 256) + value = hw->bar0_regs[offset / 4]; + spin_unlock_irqrestore(&hw->lock, flags); + return value; +} + +static void mock_hw_write_bar1(struct mock_hw *hw, u64 offset, u64 value) +{ + unsigned long flags; + spin_lock_irqsave(&hw->lock, flags); + if (offset < hw->fw_size) + *(u64 *)(hw->fw_memory + offset) = value; + spin_unlock_irqrestore(&hw->lock, flags); +} + +static u64 mock_hw_read_bar1(struct mock_hw *hw, u64 offset) +{ + unsigned long flags; + u64 value = 0; + spin_lock_irqsave(&hw->lock, flags); + if (offset < hw->fw_size) + value = *(u64 *)(hw->fw_memory + offset); + spin_unlock_irqrestore(&hw->lock, flags); + return value; +} + +/* ============================================================ + * Mock VF Operations + * ============================================================ */ + +struct mock_vf { + u32 id; + u64 bar1_offset; + u64 bar1_size; + u64 pt_base; + u64 pt_size; + u64 cmd_base; + u64 cmd_size; + bool active; + struct { + u64 aperture_offset; + u64 aperture_size; + bool force_access; + u32 force_vf; + } bar_state; + struct list_head wpr_list; + struct mutex lock; +}; + +struct mock_wpr_region { + struct list_head list; + u64 base; + u64 size; + u32 flags; +}; + +static struct mock_vf *mock_vf_create(u32 id, u64 bar1_offset, u64 bar1_size, + u64 pt_base, u64 pt_size, + u64 cmd_base, u64 cmd_size) +{ + struct mock_vf *vf = kzalloc(sizeof(*vf), GFP_KERNEL); + if (!vf) + return NULL; + + vf->id = id; + vf->bar1_offset = bar1_offset; + vf->bar1_size = bar1_size; + vf->pt_base = pt_base; + vf->pt_size = pt_size; + vf->cmd_base = cmd_base; + vf->cmd_size = cmd_size; + vf->active = true; + INIT_LIST_HEAD(&vf->wpr_list); + mutex_init(&vf->lock); + + vf->bar_state.aperture_offset = bar1_offset; + vf->bar_state.aperture_size = bar1_size; + vf->bar_state.force_access = false; + vf->bar_state.force_vf = 0; + + return vf; +} + +static void mock_vf_destroy(struct mock_vf *vf) +{ + if (!vf) + return; + + struct mock_wpr_region *rgn, *tmp; + mutex_lock(&vf->lock); + list_for_each_entry_safe(rgn, tmp, &vf->wpr_list, list) { + list_del(&rgn->list); + kfree(rgn); + } + mutex_unlock(&vf->lock); + + kfree(vf); +} + +static int mock_vf_wpr_add(struct mock_vf *vf, u64 base, u64 size, u32 flags) +{ + struct mock_wpr_region *rgn = kzalloc(sizeof(*rgn), GFP_KERNEL); + if (!rgn) + return -ENOMEM; + + rgn->base = base; + rgn->size = size; + rgn->flags = flags; + + mutex_lock(&vf->lock); + list_add(&rgn->list, &vf->wpr_list); + mutex_unlock(&vf->lock); + + return 0; +} + +static int mock_vf_wpr_remove(struct mock_vf *vf, u64 base) +{ + struct mock_wpr_region *rgn, *tmp; + bool found = false; + + mutex_lock(&vf->lock); + list_for_each_entry_safe(rgn, tmp, &vf->wpr_list, list) { + if (rgn->base == base) { + list_del(&rgn->list); + kfree(rgn); + found = true; + break; + } + } + mutex_unlock(&vf->lock); + + return found ? 0 : -ENOENT; +} + +/* ============================================================ + * Mock RPC Client + * ============================================================ */ + +struct mock_rpc_client { + u32 seq; + u32 pending_seq[64]; + u32 pending_count; + struct mutex lock; +}; + +static struct mock_rpc_client *mock_rpc_create(void) +{ + struct mock_rpc_client *rpc = kzalloc(sizeof(*rpc), GFP_KERNEL); + if (!rpc) + return NULL; + mutex_init(&rpc->lock); + return rpc; +} + +static void mock_rpc_destroy(struct mock_rpc_client *rpc) +{ + if (!rpc) + return; + kfree(rpc); +} + +static int mock_rpc_send(struct mock_rpc_client *rpc, u32 rpc_type, + void *payload, size_t payload_size, + void *response, size_t *response_size) +{ + if (!rpc) + return -EINVAL; + + mutex_lock(&rpc->lock); + rpc->seq++; + rpc->pending_seq[rpc->pending_count++] = rpc->seq; + mutex_unlock(&rpc->lock); + + /* Simulate successful response */ + if (response && response_size) { + struct gsp_rpc_response *resp = response; + resp->status = 0; + resp->rpc_type = rpc_type; + resp->sequence = rpc->seq; + *response_size = sizeof(*resp); + } + + return 0; +} + +/* ============================================================ + * Driver Logic Tests with Mocks + * ============================================================ */ + +static int test_mock_hardware(void) +{ + struct mock_hw *hw = mock_hw_create(); + TEST_ASSERT(hw != NULL, "Mock HW create"); + + /* Test BAR0 write/read */ + mock_hw_write_bar0(hw, 0x1000, 0x1); + TEST_ASSERT(hw->doorbell_triggered, "Doorbell triggered"); + TEST_ASSERT_EQ(hw->doorbell_count, 1, "Doorbell count"); + + u32 val = mock_hw_read_bar0(hw, 0x1000); + TEST_ASSERT_EQ(val, 0x1, "BAR0 read back"); + + /* Test BAR1 write/read */ + mock_hw_write_bar1(hw, 0x1000, 0xDEADBEEFCAFEBABE); + u64 val64 = mock_hw_read_bar1(hw, 0x1000); + TEST_ASSERT_EQ(val64, 0xDEADBEEFCAFEBABE, "BAR1 read back"); + + mock_hw_destroy(hw); + return TEST_PASS; +} + +static int test_mock_vf_lifecycle(void) +{ + struct mock_vf *vf = mock_vf_create(1, 0x10000000, 0x100000, + 0x20000000, 0x100000, + 0x30000000, 0x10000); + TEST_ASSERT(vf != NULL, "Mock VF create"); + + TEST_ASSERT(vf->active, "VF active after create"); + TEST_ASSERT_EQ(vf->id, 1, "VF ID"); + TEST_ASSERT_EQ(vf->bar1_offset, 0x10000000, "BAR1 offset"); + TEST_ASSERT_EQ(vf->bar1_size, 0x100000, "BAR1 size"); + TEST_ASSERT_EQ(vf->pt_base, 0x20000000, "Page table base"); + TEST_ASSERT_EQ(vf->pt_size, 0x100000, "Page table size"); + + /* Test BAR state */ + TEST_ASSERT_EQ(vf->bar_state.aperture_offset, 0x10000000, "Aperture offset"); + TEST_ASSERT_EQ(vf->bar_state.aperture_size, 0x100000, "Aperture size"); + TEST_ASSERT(!vf->bar_state.force_access, "Force access initially false"); + + /* Test WPR list empty */ + TEST_ASSERT(list_empty(&vf->wpr_list), "WPR list empty initially"); + + mock_vf_destroy(vf); + return TEST_PASS; +} + +static int test_mock_vf_wpr(void) +{ + struct mock_vf *vf = mock_vf_create(1, 0x10000000, 0x100000, + 0x20000000, 0x100000, + 0x30000000, 0x10000); + TEST_ASSERT(vf != NULL, "Mock VF create"); + + /* Add WPR region */ + int ret = mock_vf_wpr_add(vf, 0x10000000, 0x10000, 3); + TEST_ASSERT_EQ(ret, 0, "WPR add"); + + ret = mock_vf_wpr_add(vf, 0x20000000, 0x20000, 1); + TEST_ASSERT_EQ(ret, 0, "WPR add second"); + + /* Verify list */ + int count = 0; + struct mock_wpr_region *rgn; + mutex_lock(&vf->lock); + list_for_each_entry(rgn, &vf->wpr_list, list) { + count++; + } + mutex_unlock(&vf->lock); + TEST_ASSERT_EQ(count, 2, "Two WPR regions"); + + /* Remove one */ + ret = mock_vf_wpr_remove(vf, 0x10000000); + TEST_ASSERT_EQ(ret, 0, "WPR remove"); + + count = 0; + mutex_lock(&vf->lock); + list_for_each_entry(rgn, &vf->wpr_list, list) { + count++; + } + mutex_unlock(&vf->lock); + TEST_ASSERT_EQ(count, 1, "One WPR region remaining"); + + /* Remove non-existent */ + ret = mock_vf_wpr_remove(vf, 0x30000000); + TEST_ASSERT_EQ(ret, -ENOENT, "WPR remove non-existent returns ENOENT"); + + mock_vf_destroy(vf); + return TEST_PASS; +} + +static int test_mock_rpc_client(void) +{ + struct mock_rpc_client *rpc = mock_rpc_create(); + TEST_ASSERT(rpc != NULL, "Mock RPC create"); + + struct gsp_rpc_response resp; + size_t resp_size = sizeof(resp); + struct gsp_alloc_payload payload = { + .size = cpu_to_le64(0x100000), + .type = cpu_to_le32(GSP_MEM_VRAM), + }; + + int ret = mock_rpc_send(rpc, GSP_RPC_ALLOC, &payload, sizeof(payload), &resp, &resp_size); + TEST_ASSERT_EQ(ret, 0, "RPC send"); + + TEST_ASSERT_EQ(resp.status, 0, "RPC response status"); + TEST_ASSERT_EQ(resp.rpc_type, GSP_RPC_ALLOC, "RPC response type"); + TEST_ASSERT_EQ(resp.sequence, 1, "RPC response sequence"); + + /* Second call */ + ret = mock_rpc_send(rpc, GSP_RPC_PIN, &payload, sizeof(payload), &resp, &resp_size); + TEST_ASSERT_EQ(ret, 0, "RPC send second"); + TEST_ASSERT_EQ(resp.sequence, 2, "RPC response sequence 2"); + + mock_rpc_destroy(rpc); + return TEST_PASS; +} + +static int test_driver_vf_creation_logic(void) +{ + /* Simulate driver VF creation logic */ + struct mock_hw *hw = mock_hw_create(); + struct mock_rpc_client *rpc = mock_rpc_create(); + struct mock_vf *vfs[8] = {0}; + + /* Create VFs 1-4 */ + for (int i = 1; i <= 4; i++) { + vfs[i] = mock_vf_create(i, 0x10000000 * i, 0x100000, + 0x20000000 * i, 0x100000, + 0x30000000 * i, 0x10000); + TEST_ASSERT(vfs[i] != NULL, "VF creation in loop"); + TEST_ASSERT(vfs[i]->active, "VF active in loop"); + } + + /* Initialize BAR for each VF */ + for (int i = 1; i <= 4; i++) { + vfs[i]->bar_state.aperture_offset = vfs[i]->bar1_offset; + vfs[i]->bar_state.aperture_size = vfs[i]->bar1_size; + TEST_ASSERT_EQ(vfs[i]->bar_state.aperture_offset, 0x10000000 * i, "VF BAR offset"); + TEST_ASSERT_EQ(vfs[i]->bar_state.aperture_size, 0x100000, "VF BAR size"); + } + + /* Add WPR for each VF */ + for (int i = 1; i <= 4; i++) { + int ret = mock_vf_wpr_add(vfs[i], vfs[i]->pt_base, vfs[i]->pt_size, 3); + TEST_ASSERT_EQ(ret, 0, "VF WPR add in loop"); + } + + /* Simulate RPC calls for each VF */ + for (int i = 1; i <= 4; i++) { + struct gsp_rpc_response resp; + size_t resp_size = sizeof(resp); + struct gsp_alloc_payload payload = { + .size = cpu_to_le64(0x100000), + .type = cpu_to_le32(GSP_MEM_VRAM), + .vf_id = cpu_to_le32(i), + }; + int ret = mock_rpc_send(rpc, GSP_RPC_ALLOC, &payload, sizeof(payload), &resp, &resp_size); + TEST_ASSERT_EQ(ret, 0, "VF RPC alloc"); + TEST_ASSERT_EQ(resp.status, 0, "VF RPC response OK"); + } + + /* Cleanup */ + for (int i = 1; i <= 4; i++) { + mock_vf_destroy(vfs[i]); + } + mock_rpc_destroy(rpc); + mock_hw_destroy(hw); + + return TEST_PASS; +} + +static int test_migration_progress_tracking(void) +{ + struct { + u64 pages_transferred; + u64 bytes_transferred; + u64 dirty_pages; + u64 *page_list; + u32 page_count; + } progress = {0}; + + progress.page_count = 1000; + progress.page_list = kcalloc(progress.page_count, sizeof(u64), GFP_KERNEL); + TEST_ASSERT(progress.page_list != NULL, "Progress page list alloc"); + + /* Simulate transfer loop */ + for (int iter = 0; iter < 10; iter++) { + progress.pages_transferred += 100; + progress.bytes_transferred += 100 * 4096; + progress.dirty_pages = 50 - (iter * 5); + + TEST_ASSERT(progress.pages_transferred <= 1000, "Pages not exceeded"); + TEST_ASSERT(progress.bytes_transferred <= 1000 * 4096, "Bytes not exceeded"); + } + + TEST_ASSERT_EQ(progress.pages_transferred, 1000, "Final pages"); + TEST_ASSERT_EQ(progress.bytes_transferred, 1000 * 4096, "Final bytes"); + TEST_ASSERT_EQ(progress.dirty_pages, 0, "Final dirty pages"); + + kfree(progress.page_list); + return TEST_PASS; +} + +static int test_bar_state_machine(void) +{ + struct { + u64 aperture_offset; + u64 aperture_size; + bool force_access; + u32 force_vf; + } bar_state = {0}; + + /* Initial state */ + bar_state.aperture_offset = 0x10000000; + bar_state.aperture_size = 0x100000; + TEST_ASSERT_EQ(bar_state.aperture_offset, 0x10000000, "Initial aperture offset"); + TEST_ASSERT(!bar_state.force_access, "Force access initially false"); + + /* Enable force access */ + bar_state.force_access = true; + bar_state.force_vf = 1; + TEST_ASSERT(bar_state.force_access, "Force access enabled"); + TEST_ASSERT_EQ(bar_state.force_vf, 1, "Force VF set"); + + /* Disable force access */ + bar_state.force_access = false; + bar_state.force_vf = 0; + TEST_ASSERT(!bar_state.force_access, "Force access disabled"); + TEST_ASSERT_EQ(bar_state.force_vf, 0, "Force VF cleared"); + + return TEST_PASS; +} + +static int test_concurrent_vf_operations(void) +{ + struct mock_vf *vf1 = mock_vf_create(1, 0x10000000, 0x100000, 0x20000000, 0x100000, 0x30000000, 0x10000); + struct mock_vf *vf2 = mock_vf_create(2, 0x20000000, 0x100000, 0x40000000, 0x100000, 0x60000000, 0x10000); + struct mock_rpc_client *rpc = mock_rpc_create(); + + TEST_ASSERT(vf1 && vf2 && rpc, "Create two VFs and RPC"); + + /* Simulate concurrent operations */ + struct gsp_rpc_response resp; + size_t resp_size = sizeof(resp); + + /* VF1 alloc */ + struct gsp_alloc_payload payload1 = { + .size = cpu_to_le64(0x100000), + .type = cpu_to_le32(GSP_MEM_VRAM), + .vf_id = cpu_to_le32(1), + }; + int ret = mock_rpc_send(rpc, GSP_RPC_ALLOC, &payload1, sizeof(payload1), &resp, &resp_size); + TEST_ASSERT_EQ(ret, 0, "VF1 alloc"); + TEST_ASSERT_EQ(resp.status, 0, "VF1 alloc response"); + + /* VF2 alloc */ + struct gsp_alloc_payload payload2 = { + .size = cpu_to_le64(0x200000), + .type = cpu_to_le32(GSP_MEM_VRAM), + .vf_id = cpu_to_le32(2), + }; + ret = mock_rpc_send(rpc, GSP_RPC_ALLOC, &payload2, sizeof(payload2), &resp, &resp_size); + TEST_ASSERT_EQ(ret, 0, "VF2 alloc"); + TEST_ASSERT_EQ(resp.status, 0, "VF2 alloc response"); + + /* VF1 pin */ + struct gsp_pin_payload pin_payload = { + .handle = cpu_to_le64(0x1000), + .vf_id = cpu_to_le32(1), + }; + ret = mock_rpc_send(rpc, GSP_RPC_PIN, &pin_payload, sizeof(pin_payload), &resp, &resp_size); + TEST_ASSERT_EQ(ret, 0, "VF1 pin"); + + /* VF2 BAR trap */ + struct gsp_bar_trap_payload bar_payload = { + .vf_id = cpu_to_le32(2), + .bar_index = cpu_to_le32(1), + }; + ret = mock_rpc_send(rpc, GSP_RPC_BAR_TRAP, &bar_payload, sizeof(bar_payload), &resp, &resp_size); + TEST_ASSERT_EQ(ret, 0, "VF2 BAR trap"); + + /* Verify sequence numbers */ + TEST_ASSERT_EQ(resp.sequence, 4, "Sequence number 4"); + + mock_vf_destroy(vf1); + mock_vf_destroy(vf2); + mock_rpc_destroy(rpc); + + return TEST_PASS; +} + +/* ============================================================ + * Module Init/Exit + * ============================================================ */ + +static int __init gsp_mock_test_init(void) +{ + pr_info("========================================\n"); + pr_info("GSP vGPU-IO Mock Driver Test Suite\n"); + pr_info("========================================\n"); + + test_mock_hardware(); + test_mock_vf_lifecycle(); + test_mock_vf_wpr(); + test_mock_rpc_client(); + test_driver_vf_creation_logic(); + test_migration_progress_tracking(); + test_bar_state_machine(); + test_concurrent_vf_operations(); + + pr_info("========================================\n"); + pr_info("Mock Test Results: %d total, %d passed, %d failed\n", + test_count, pass_count, fail_count); + pr_info("========================================\n"); + + if (fail_count > 0) { + pr_err("MOCK TEST SUITE FAILED\n"); + return -EINVAL; + } + + pr_info("MOCK TEST SUITE PASSED\n"); + return 0; +} + +static void __exit gsp_mock_test_exit(void) +{ + pr_info("GSP vGPU-IO Mock Test Suite Exited\n"); +} + +module_init(gsp_mock_test_init); +module_exit(gsp_mock_test_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Open-IOV Project"); +MODULE_DESCRIPTION("GSP vGPU-IO Mock Driver Test"); +MODULE_VERSION("1.0");
\ No newline at end of file |
