diff options
| author | Open-IOV <dev@open-iov.org> | 2026-09-23 05:44:08 -0400 |
|---|---|---|
| committer | Open-IOV <dev@open-iov.org> | 2026-09-23 05:46:43 -0400 |
| commit | 68cdd7728a7cc6715bbbff7decfe2b11c3bdf558 (patch) | |
| tree | 3dc8ce2e6af313a97ec6852f5c663a9a93665d2e /kernel/tests | |
| download | open-iov-68cdd7728a7cc6715bbbff7decfe2b11c3bdf558.tar.gz open-iov-68cdd7728a7cc6715bbbff7decfe2b11c3bdf558.zip | |
Open-IOV: initial public release
Open-IOV virtualizes NVIDIA GPUs so that unmodified guest drivers run with
real GPU functionality - CUDA first, 3D and video to follow - on unmodified
hosts, without a proprietary vGPU license and without patching any driver,
firmware or hypervisor.
Core principle: emulate the interface, pass through the work.
kernel/ open-iov-vfio.ko - host VFIO mdev driver: virtual GPU device
(PCI config space, BAR shadows, MSI-X), GSP boot emulation,
GSP-RM message-queue/RPC emulation, memory model (GPU-VA to
guest-PA discovery) and the semaphore-completion service that
advances guest UVM progress.
userspace/ libiov.so management API and examples.
tools/ guest diagnostics, firmware analysis wrappers, RM probes.
scripts/ stack manager, VM harness, build/load/test automation.
config/ hardware/version profiles (the portability surface).
docs/ documentation tree - start at docs/README.md.
wiki/ long-form firmware and protocol deep dives.
Status: initialization works end to end for a stock 570.144 Linux guest on
Turing (TU104): driver probe, nvidia-smi, the full GSP-RM control battery,
object creation and the early CUDA/UVM start-up path. GPU work forwarding -
the step that makes CUDA kernels execute on real hardware - is the active
work item; see docs/04-implementation/01-known-knowns-and-unknowns.md.
License: BSD-3-Clause for the project; GPL-2.0 for kernel modules that link
GPL-only kernel interfaces. See LICENSE and docs/09-reference/legal-and-licensing.md.
Diffstat (limited to 'kernel/tests')
| -rw-r--r-- | kernel/tests/Makefile | 24 | ||||
| -rw-r--r-- | kernel/tests/gsp_emulation_test.c | 350 | ||||
| -rw-r--r-- | kernel/tests/gsp_rpc_test.c | 425 |
3 files changed, 799 insertions, 0 deletions
diff --git a/kernel/tests/Makefile b/kernel/tests/Makefile new file mode 100644 index 0000000..56c819a --- /dev/null +++ b/kernel/tests/Makefile @@ -0,0 +1,24 @@ +obj-m += gsp_emulation_test.o + +ccflags-y := -I$(PWD)/../ -I$(PWD)/../include + +# Resolve open-iov-vfio.ko exports (gsp_emu_process_rpc, +# gsp_client_init_emulation, gsp_rpc_emulation_set, ...). +# Requires 'make kernel' (or 'make -C kernel') to have run first. +KBUILD_EXTRA_SYMBOLS := $(abspath $(PWD)/../Module.symvers) + +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean + +# Requires open-iov-vfio.ko loaded first (symbol dependency) +install: + sudo insmod gsp_emulation_test.ko + +uninstall: + sudo rmmod gsp_emulation_test diff --git a/kernel/tests/gsp_emulation_test.c b/kernel/tests/gsp_emulation_test.c new file mode 100644 index 0000000..d63fcdb --- /dev/null +++ b/kernel/tests/gsp_emulation_test.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GSP Emulation Unit Tests + * + * Drives gsp_emu_process_rpc() end-to-end with crafted shared-memory + * messages and validates dispatch, response layout, and protocol handling. + * + * Standalone-testable RPC paths (no physical GPU dependency): + * SCHEDULE (emulated workqueue), YIELD (no-op), NOTIFY (log-only), + * DEBUG (log-only), protocol validation (seq_id, unknown type). + * + * NOT covered here: ALLOC/FREE/MAP/UNMAP/PIN/UNPIN emulation is coupled + * to a physical device (client->dev->driver_data -> vgpu -> pdev + + * dma_map_page), and BAR_TRAP/MIGRATE submit raw RPCs to real firmware. + * Those paths are exercised by the live QEMU guest integration test + * (scripts/test_vgpu_stack.sh). + * + * Requires open-iov-vfio.ko loaded first (links against its exports). + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include "gsp_emulation.h" +#include "gsp_rpc_client.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Open-IOV"); +MODULE_DESCRIPTION("GSP Emulation Unit Tests"); +MODULE_SOFTDEP("pre: open_iov_vfio"); + +#define SHMEM_SIZE 4096 +#define RSP_OFFSET 0x80 +#define UNKNOWN_TYPE 0x77 /* not a valid GSP_RPC_* type */ + +static struct gsp_emu_context *test_ctx; +static struct gsp_client *test_client; +static void *test_shmem; +static u32 test_seq; + +static int test_failures; + +#define T_CHECK(cond, name, ...) do { \ + if (!(cond)) { \ + pr_err("GSP_EMU_TEST: FAIL %s: " #name ": " __VA_ARGS__); \ + test_failures++; \ + return -1; \ + } \ +} while (0) + +/* + * run_rpc - place a request in fake shared memory, run one dispatch, + * and copy back the response. Returns the dispatch return code. + */ +static int run_rpc(u32 type, u32 subcmd, const void *payload, size_t plen, + struct gsp_rpc_response *rsp) +{ + struct gsp_rpc_msg *msg = test_shmem; + struct gsp_rpc_response *r = test_shmem + RSP_OFFSET; + int ret; + + memset(test_shmem, 0, SHMEM_SIZE); + msg->type = type; + msg->subcmd = subcmd; + if (payload && plen) + memcpy(msg->payload, payload, min(plen, sizeof(msg->payload))); + msg->seq_id = ++test_seq; + + ret = gsp_emu_process_rpc(test_ctx); + + memcpy(rsp, r, sizeof(*rsp)); + if (rsp->seq_id != msg->seq_id) { + pr_err("GSP_EMU_TEST: response seq mismatch (got %u, want %u)\n", + rsp->seq_id, msg->seq_id); + return -EBADMSG; + } + return ret; +} + +/* seq_id == 0 must be rejected by gsp_emu_process_rpc itself */ +static int test_seq_id_zero(void) +{ + struct gsp_rpc_msg *msg = test_shmem; + int ret; + + pr_info("GSP_EMU_TEST: Testing seq_id=0 rejection...\n"); + + memset(test_shmem, 0, SHMEM_SIZE); + msg->type = GSP_RPC_YIELD; + msg->seq_id = 0; + + ret = gsp_emu_process_rpc(test_ctx); + T_CHECK(ret == -EINVAL, seq_zero, "expected -EINVAL, got %d\n", ret); + + pr_info("GSP_EMU_TEST: seq_id=0 correctly rejected\n"); + return 0; +} + +/* uninitialized context must be rejected */ +static int test_uninitialized_ctx(void) +{ + struct gsp_emu_context *bad; + int ret; + + pr_info("GSP_EMU_TEST: Testing uninitialized ctx rejection...\n"); + + bad = kzalloc(sizeof(*bad), GFP_KERNEL); + T_CHECK(bad != NULL, alloc, "out of memory\n"); + bad->initialized = false; + + ret = gsp_emu_process_rpc(bad); + kfree(bad); + T_CHECK(ret == -ENODEV, uninit, "expected -ENODEV, got %d\n", ret); + + pr_info("GSP_EMU_TEST: uninitialized ctx correctly rejected\n"); + return 0; +} + +/* unknown RPC type must be rejected with -ENOTSUPP in ret and status */ +static int test_unknown_type(void) +{ + struct gsp_rpc_response rsp; + int ret; + + pr_info("GSP_EMU_TEST: Testing unknown type rejection...\n"); + + ret = run_rpc(UNKNOWN_TYPE, 0, NULL, 0, &rsp); + T_CHECK(ret == -ENOTSUPP, unkret, "expected -ENOTSUPP, got %d\n", ret); + T_CHECK((s32)rsp.status == -ENOTSUPP, unkstatus, + "expected status -ENOTSUPP, got %d\n", (s32)rsp.status); + T_CHECK(rsp.type == UNKNOWN_TYPE, unktype, + "expected type echo 0x%x, got 0x%x\n", UNKNOWN_TYPE, rsp.type); + + pr_info("GSP_EMU_TEST: unknown type correctly rejected\n"); + return 0; +} + +static int test_schedule(void) +{ + struct gsp_sched_req sreq = {0}; + struct gsp_rpc_response rsp; + int ret; + + pr_info("GSP_EMU_TEST: Testing SCHEDULE...\n"); + + sreq.work_handle = 0x1234; + sreq.callback = 0; /* emulation worker never invokes callback */ + sreq.arg = 0; + sreq.priority = 10; + sreq.flags = 0; + + ret = run_rpc(GSP_RPC_SCHEDULE, 0, &sreq, sizeof(sreq), &rsp); + T_CHECK(ret == 0, schedret, "dispatch returned %d\n", ret); + T_CHECK((s32)rsp.status == 0, schedstatus, + "status %d\n", (s32)rsp.status); + T_CHECK(rsp.type == GSP_RPC_SCHEDULE, schedtype, + "type echo 0x%x\n", rsp.type); + + pr_info("GSP_EMU_TEST: SCHEDULE succeeded\n"); + return 0; +} + +static int test_yield(void) +{ + struct gsp_rpc_response rsp; + int ret; + + pr_info("GSP_EMU_TEST: Testing YIELD...\n"); + + ret = run_rpc(GSP_RPC_YIELD, 0, NULL, 0, &rsp); + T_CHECK(ret == 0, yieldret, "dispatch returned %d\n", ret); + T_CHECK((s32)rsp.status == 0, yieldstatus, + "status %d\n", (s32)rsp.status); + + pr_info("GSP_EMU_TEST: YIELD succeeded\n"); + return 0; +} + +static int test_notify(void) +{ + struct gsp_rpc_response rsp; + int ret; + + pr_info("GSP_EMU_TEST: Testing NOTIFY...\n"); + + /* subcmd carries the event type; dispatch is log-only, must succeed + * even with a NULL vgpu (exercises NULL-safe logging macros). */ + ret = run_rpc(GSP_RPC_NOTIFY, 1, NULL, 0, &rsp); + T_CHECK(ret == 0, notifyret, "dispatch returned %d\n", ret); + T_CHECK((s32)rsp.status == 0, notifystatus, + "status %d\n", (s32)rsp.status); + + pr_info("GSP_EMU_TEST: NOTIFY succeeded\n"); + return 0; +} + +static int test_debug(void) +{ + struct gsp_rpc_response rsp; + int ret; + + pr_info("GSP_EMU_TEST: Testing DEBUG...\n"); + + ret = run_rpc(GSP_RPC_DEBUG, 0, NULL, 0, &rsp); + T_CHECK(ret == 0, dbgret, "dispatch returned %d\n", ret); + T_CHECK((s32)rsp.status == 0, dbgstatus, + "status %d\n", (s32)rsp.status); + + pr_info("GSP_EMU_TEST: DEBUG succeeded\n"); + return 0; +} + +/* hammer the dispatch loop with mixed SCHEDULE/YIELD traffic */ +static int test_dispatch_stress(void) +{ + struct gsp_sched_req sreq = {0}; + struct gsp_rpc_response rsp; + int i, ret; + + pr_info("GSP_EMU_TEST: Testing dispatch stress (100 iterations)...\n"); + + sreq.work_handle = 0xBEEF; + sreq.priority = 5; + + for (i = 0; i < 100; i++) { + u32 type = (i & 1) ? GSP_RPC_YIELD : GSP_RPC_SCHEDULE; + + ret = run_rpc(type, 0, &sreq, + (type == GSP_RPC_SCHEDULE) ? sizeof(sreq) : 0, + &rsp); + T_CHECK(ret == 0, stress, "iteration %d returned %d\n", i, ret); + T_CHECK((s32)rsp.status == 0, stressstatus, + "iteration %d status %d\n", i, (s32)rsp.status); + } + + pr_info("GSP_EMU_TEST: dispatch stress passed (100/100)\n"); + return 0; +} + +static int init_test_context(void) +{ + int ret; + + /* Route all RPC paths through their emulation implementations */ + gsp_rpc_emulation_set(true); + + test_shmem = vzalloc(SHMEM_SIZE); + if (!test_shmem) + return -ENOMEM; + + /* NULL fw/doorbell virt addrs: emulation paths never touch them */ + test_client = gsp_client_init_emulation(NULL, NULL, NULL); + if (IS_ERR(test_client)) { + ret = PTR_ERR(test_client); + test_client = NULL; + goto err_shmem; + } + + test_ctx = kzalloc(sizeof(*test_ctx), GFP_KERNEL); + if (!test_ctx) { + ret = -ENOMEM; + goto err_client; + } + + test_ctx->vgpu = NULL; /* NULL-safe logging macros handle this */ + test_ctx->bar0_vaddr = NULL; /* PBA write is guarded */ + test_ctx->shared_mem_virt = test_shmem; + test_ctx->shared_mem_size = SHMEM_SIZE; + test_ctx->rpc_client = test_client; + spin_lock_init(&test_ctx->lock); + test_ctx->running = true; + test_ctx->initialized = true; + /* msix_trigger[] zeroed by kzalloc; eventfd_signal is guarded */ + + pr_info("GSP_EMU_TEST: Test context initialized\n"); + return 0; + +err_client: + gsp_client_fini(test_client); + test_client = NULL; +err_shmem: + vfree(test_shmem); + test_shmem = NULL; + gsp_rpc_emulation_set(false); + return ret; +} + +static void cleanup_test_context(void) +{ + if (test_ctx) { + test_ctx->running = false; + kfree(test_ctx); + test_ctx = NULL; + } + if (test_client) { + gsp_client_fini(test_client); + test_client = NULL; + } + if (test_shmem) { + vfree(test_shmem); + test_shmem = NULL; + } + gsp_rpc_emulation_set(false); + pr_info("GSP_EMU_TEST: Test context cleaned up\n"); +} + +static int run_all_tests(void) +{ + test_failures = 0; + + if (init_test_context()) { + pr_err("GSP_EMU_TEST: Failed to initialize test context\n"); + return -1; + } + + test_seq_id_zero(); + test_uninitialized_ctx(); + test_unknown_type(); + test_schedule(); + test_yield(); + test_notify(); + test_debug(); + test_dispatch_stress(); + + cleanup_test_context(); + + if (test_failures == 0) { + pr_info("GSP_EMU_TEST: ALL TESTS PASSED\n"); + return 0; + } + + pr_err("GSP_EMU_TEST: %d TEST(S) FAILED\n", test_failures); + return -1; +} + +static int __init gsp_emulation_test_init(void) +{ + pr_info("GSP_EMU_TEST: Starting GSP emulation unit tests...\n"); + return run_all_tests(); +} + +static void __exit gsp_emulation_test_exit(void) +{ + pr_info("GSP_EMU_TEST: Module unloaded\n"); +} + +module_init(gsp_emulation_test_init); +module_exit(gsp_emulation_test_exit); 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"); |
