summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile8
-rw-r--r--kernel/architecture/architecture.c176
-rw-r--r--kernel/architecture/architecture.h49
-rw-r--r--kernel/dynamic_rpc_dispatcher.c462
-rw-r--r--kernel/dynamic_rpc_dispatcher.h66
-rw-r--r--kernel/firmware_analyzer.c224
-rw-r--r--kernel/firmware_analyzer.h36
-rw-r--r--kernel/tests/Makefile36
-rw-r--r--kernel/tests/gsp_emulation_test.c705
-rw-r--r--kernel/version/version.c137
-rw-r--r--kernel/version/version.h37
11 files changed, 1905 insertions, 31 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
index 1b1c04e..c927390 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -11,9 +11,13 @@ open-iov-vfio-y := \
memory.o \
irq.o \
migration.o \
- ioctl.o
+ ioctl.o \
+ firmware_analyzer.o \
+ dynamic_rpc_dispatcher.o \
+ architecture/architecture.o \
+ version/version.o
-ccflags-y := -I$(src)/include -Wall -Wextra -Wno-error -Wno-unused-parameter -Wno-unused-function
+ccflags-y := -I$(src)/include -I$(src)/architecture -I$(src)/version -Wall -Wextra -Wno-error -Wno-unused-parameter -Wno-unused-function
# mdev dependencies
KBUILD_MODPOST_WARN := 1
diff --git a/kernel/architecture/architecture.c b/kernel/architecture/architecture.c
new file mode 100644
index 0000000..bc85f85
--- /dev/null
+++ b/kernel/architecture/architecture.c
@@ -0,0 +1,176 @@
+/*
+ * Hardware Architecture Abstraction Layer
+ * Defines hardware-specific configurations for different GPU architectures
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "architecture.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Open-IOV");
+MODULE_DESCRIPTION("Hardware Architecture Abstraction");
+
+/* Turing architecture (TU102, TU104, TU106) */
+static struct hw_architecture arch_turing = {
+ .name = "turing",
+ .pci_vendor_id = 0x10DE,
+ .pci_device_ids = (u32[]){0x1E82, 0x1E83, 0x1E84, 0x1E85, 0x1E86, 0x1E87, 0x1E88, 0x1E89, 0x1E8A, 0x1E8B, 0x1E8C, 0x1E8D, 0x1E8E, 0x1E8F, 0x1ED0, 0x1ED1, 0x1ED2, 0x1ED3, 0x1ED4, 0x1ED5, 0x1ED6, 0x1ED7, 0x1ED8, 0x1ED9, 0x1EDA, 0x1EDB, 0x1EDC, 0x1EDD, 0x1EDE, 0x1EDF, 0},
+ .num_device_ids = 30,
+ .bar_sizes = {
+ .bar0 = 16 * 1024 * 1024, /* 16MB */
+ .bar1 = 256 * 1024 * 1024, /* 256MB */
+ .bar2 = 32 * 1024 * 1024, /* 32MB */
+ .bar3 = 32 * 1024 * 1024, /* 32MB */
+ },
+ .doorbell_offset = 0x110C00,
+ .shared_mem_offset = 0x1000,
+ .shared_mem_size = 4096,
+ .msix_table_offset = 0x2000,
+ .msix_pba_offset = 0x3000,
+ .msix_vectors = 32,
+ .max_vfs = 8,
+ .vram_size = 16 * 1024 * 1024 * 1024ULL, /* 16GB */
+ .page_size = 4096,
+ .supports_mig = false,
+ .supports_sriov = true,
+ .gsp_version = "570.144W",
+};
+
+/* Ampere architecture (GA100, GA102, GA104) */
+static struct hw_architecture arch_ampere = {
+ .name = "ampere",
+ .pci_vendor_id = 0x10DE,
+ .pci_device_ids = (u32[]){0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7, 0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF, 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7, 0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF, 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7, 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF, 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7, 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF, 0},
+ .num_device_ids = 96,
+ .bar_sizes = {
+ .bar0 = 16 * 1024 * 1024, /* 16MB */
+ .bar1 = 512 * 1024 * 1024, /* 512MB */
+ .bar2 = 32 * 1024 * 1024, /* 32MB */
+ .bar3 = 32 * 1024 * 1024, /* 32MB */
+ },
+ .doorbell_offset = 0x110C00,
+ .shared_mem_offset = 0x1000,
+ .shared_mem_size = 4096,
+ .msix_table_offset = 0x2000,
+ .msix_pba_offset = 0x3000,
+ .msix_vectors = 32,
+ .max_vfs = 16,
+ .vram_size = 80 * 1024 * 1024 * 1024ULL, /* 80GB (A100) */
+ .page_size = 4096,
+ .supports_mig = true,
+ .supports_sriov = true,
+ .gsp_version = "570.144W",
+};
+
+/* Blackwell architecture (GB100, GB202, GB203) */
+static struct hw_architecture arch_blackwell = {
+ .name = "blackwell",
+ .pci_vendor_id = 0x10DE,
+ .pci_device_ids = (u32[]){0x2300, 0x2301, 0x2302, 0x2303, 0x2304, 0x2305, 0x2306, 0x2307, 0x2308, 0x2309, 0x230A, 0x230B, 0x230C, 0x230D, 0x230E, 0x230F, 0},
+ .num_device_ids = 16,
+ .bar_sizes = {
+ .bar0 = 16 * 1024 * 1024, /* 16MB */
+ .bar1 = 512 * 1024 * 1024, /* 512MB */
+ .bar2 = 64 * 1024 * 1024, /* 64MB */
+ .bar3 = 64 * 1024 * 1024, /* 64MB */
+ },
+ .doorbell_offset = 0x110C00,
+ .shared_mem_offset = 0x1000,
+ .shared_mem_size = 4096,
+ .msix_table_offset = 0x2000,
+ .msix_pba_offset = 0x3000,
+ .msix_vectors = 64,
+ .max_vfs = 32,
+ .vram_size = 192 * 1024 * 1024 * 1024ULL, /* 192GB (B200) */
+ .page_size = 4096,
+ .supports_mig = true,
+ .supports_sriov = true,
+ .gsp_version = "580.XX",
+};
+
+static struct hw_architecture *architectures[] = {
+ &arch_turing,
+ &arch_ampere,
+ &arch_blackwell,
+ NULL
+};
+
+struct hw_architecture *arch_get_by_name(const char *name)
+{
+ for (int i = 0; architectures[i]; i++) {
+ if (strcmp(architectures[i]->name, name) == 0)
+ return architectures[i];
+ }
+ return NULL;
+}
+
+struct hw_architecture *arch_get_by_pci_id(u32 vendor_id, u32 device_id)
+{
+ for (int i = 0; architectures[i]; i++) {
+ if (architectures[i]->pci_vendor_id == vendor_id) {
+ for (int j = 0; j < architectures[i]->num_device_ids; j++) {
+ if (architectures[i]->pci_device_ids[j] == device_id)
+ return architectures[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+struct hw_architecture *arch_get_default(void)
+{
+ return &arch_turing;
+}
+
+const struct hw_bar_config *arch_get_bar_config(struct hw_architecture *arch)
+{
+ return arch ? &arch->bar_sizes : NULL;
+}
+
+u64 arch_get_doorbell_offset(struct hw_architecture *arch)
+{
+ return arch ? arch->doorbell_offset : 0x110C00;
+}
+
+u64 arch_get_shared_mem_offset(struct hw_architecture *arch)
+{
+ return arch ? arch->shared_mem_offset : 0x1000;
+}
+
+u32 arch_get_msix_vectors(struct hw_architecture *arch)
+{
+ return arch ? arch->msix_vectors : 32;
+}
+
+bool arch_supports_mig(struct hw_architecture *arch)
+{
+ return arch ? arch->supports_mig : false;
+}
+
+bool arch_supports_sriov(struct hw_architecture *arch)
+{
+ return arch ? arch->supports_sriov : false;
+}
+
+const char *arch_get_gsp_version(struct hw_architecture *arch)
+{
+ return arch ? arch->gsp_version : "unknown";
+}
+
+EXPORT_SYMBOL_GPL(arch_get_by_name);
+EXPORT_SYMBOL_GPL(arch_get_by_pci_id);
+EXPORT_SYMBOL_GPL(arch_get_default);
+EXPORT_SYMBOL_GPL(arch_get_bar_config);
+EXPORT_SYMBOL_GPL(arch_get_doorbell_offset);
+EXPORT_SYMBOL_GPL(arch_get_shared_mem_offset);
+EXPORT_SYMBOL_GPL(arch_get_msix_vectors);
+EXPORT_SYMBOL_GPL(arch_supports_mig);
+EXPORT_SYMBOL_GPL(arch_supports_sriov);
+EXPORT_SYMBOL_GPL(arch_get_gsp_version);
+
+MODULE_DESCRIPTION("Hardware Architecture Abstraction");
+MODULE_LICENSE("GPL"); \ No newline at end of file
diff --git a/kernel/architecture/architecture.h b/kernel/architecture/architecture.h
new file mode 100644
index 0000000..3745bf8
--- /dev/null
+++ b/kernel/architecture/architecture.h
@@ -0,0 +1,49 @@
+#ifndef _ARCHITECTURE_H_
+#define _ARCHITECTURE_H_
+
+#include <linux/types.h>
+
+/* BAR configuration */
+struct hw_bar_config {
+ u64 bar0;
+ u64 bar1;
+ u64 bar2;
+ u64 bar3;
+};
+
+/* Hardware architecture definition */
+struct hw_architecture {
+ const char *name;
+ u32 pci_vendor_id;
+ u32 *pci_device_ids;
+ int num_device_ids;
+ struct hw_bar_config bar_sizes;
+ u64 doorbell_offset;
+ u64 shared_mem_offset;
+ u32 shared_mem_size;
+ u64 msix_table_offset;
+ u64 msix_pba_offset;
+ u32 msix_vectors;
+ u32 max_vfs;
+ u64 vram_size;
+ u32 page_size;
+ bool supports_mig;
+ bool supports_sriov;
+ const char *gsp_version;
+};
+
+/* Architecture registry */
+struct hw_architecture *arch_get_by_name(const char *name);
+struct hw_architecture *arch_get_by_pci_id(u32 vendor_id, u32 device_id);
+struct hw_architecture *arch_get_default(void);
+
+/* Architecture queries */
+const struct hw_bar_config *arch_get_bar_config(struct hw_architecture *arch);
+u64 arch_get_doorbell_offset(struct hw_architecture *arch);
+u64 arch_get_shared_mem_offset(struct hw_architecture *arch);
+u32 arch_get_msix_vectors(struct hw_architecture *arch);
+bool arch_supports_mig(struct hw_architecture *arch);
+bool arch_supports_sriov(struct hw_architecture *arch);
+const char *arch_get_gsp_version(struct hw_architecture *arch);
+
+#endif /* _ARCHITECTURE_H_ */ \ No newline at end of file
diff --git a/kernel/dynamic_rpc_dispatcher.c b/kernel/dynamic_rpc_dispatcher.c
new file mode 100644
index 0000000..fb9ca6b
--- /dev/null
+++ b/kernel/dynamic_rpc_dispatcher.c
@@ -0,0 +1,462 @@
+/*
+ * Dynamic RPC Dispatcher - Version-aware RPC dispatch
+ * Uses discovered firmware addresses for native handlers,
+ * falls back to emulation for missing handlers
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+
+#include "dynamic_rpc_dispatcher.h"
+#include "gsp_rpc_client.h"
+#include "firmware_analyzer.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Open-IOV");
+MODULE_DESCRIPTION("Dynamic RPC Dispatcher");
+
+/* Version-specific interface */
+struct rpc_interface {
+ char version[32];
+ struct fw_addresses fw_addrs;
+
+ /* Function pointers for native firmware handlers */
+ int (*alloc)(struct gsp_client *, u64, u64, u32, u32, u64, u32, u64 *, u64 *, u64 *);
+ int (*free)(struct gsp_client *, u64);
+ int (*map)(struct gsp_client *, u64, u64, u64, u32, u32, u64, u64 *);
+ int (*unmap)(struct gsp_client *, u64, u64, u64);
+ int (*pin)(struct gsp_client *, u64, u64, u64, u32, u64 *, u32 *);
+ int (*unpin)(struct gsp_client *, u64, u64, u64);
+ int (*schedule)(struct gsp_client *, u64, u64, u64, u32, u32);
+ int (*yield)(struct gsp_client *);
+ int (*bar_trap)(struct gsp_client *, u64, u32, u32, u64, u32, u64 *);
+ int (*migrate_save)(struct gsp_client *, u64, u64, u64, u64, u64 *);
+ int (*migrate_restore)(struct gsp_client *, u64, u64, u64, u64, u64);
+ int (*notify_register)(struct gsp_client *, u32, void (*)(struct gsp_client *, struct gsp_rpc_response *, void *), void *);
+ void (*notify_unregister)(struct gsp_client *, u32);
+
+ /* Emulation fallbacks */
+ bool use_emulation[GSP_RPC_MAX_TYPE];
+};
+
+/* Dispatcher context */
+struct dynamic_dispatcher {
+ struct rpc_interface *current_interface;
+ struct fw_analysis_result fw_analysis;
+ spinlock_t lock;
+ bool initialized;
+};
+
+static struct dynamic_dispatcher g_dispatcher = {
+ .lock = __SPIN_LOCK_UNLOCKED(g_dispatcher.lock),
+ .initialized = false,
+};
+
+/* Version 570 interface (native firmware handlers) */
+static int rpc_570_alloc(struct gsp_client *client, u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid, u64 *gpu_va, u64 *handle, u64 *actual_size)
+{
+ return gsp_rpc_alloc(client, size, alignment, mem_type, flags, addr_hint, pid, gpu_va, handle, actual_size);
+}
+
+static int rpc_570_free(struct gsp_client *client, u64 handle)
+{
+ return gsp_rpc_free(client, handle);
+}
+
+static int rpc_570_map(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset, u64 *mapped_va)
+{
+ return gsp_rpc_map(client, handle, gpu_va, size, prot, flags, offset, mapped_va);
+}
+
+static int rpc_570_unmap(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size)
+{
+ return gsp_rpc_unmap(client, handle, gpu_va, size);
+}
+
+static int rpc_570_pin(struct gsp_client *client, u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages)
+{
+ return gsp_rpc_pin(client, handle, offset, size, flags, dma_addr, num_pages);
+}
+
+static int rpc_570_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size)
+{
+ return gsp_rpc_unpin(client, handle, offset, size);
+}
+
+static int rpc_570_schedule(struct gsp_client *client, u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags)
+{
+ return gsp_rpc_schedule(client, work_handle, callback, arg, priority, flags);
+}
+
+static int rpc_570_yield(struct gsp_client *client)
+{
+ return gsp_rpc_yield(client);
+}
+
+static int rpc_570_bar_trap(struct gsp_client *client, u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value)
+{
+ return gsp_rpc_bar_trap(client, bar_addr, access_type, size, value, bar_num, ret_value);
+}
+
+static int rpc_570_migrate_save(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr, u64 *checkpoint)
+{
+ return gsp_rpc_migrate_save(client, src_gpu_id, dst_gpu_id, memory_bitmap, page_table_ptr, checkpoint);
+}
+
+static int rpc_570_migrate_restore(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr, u64 checkpoint)
+{
+ return gsp_rpc_migrate_restore(client, src_gpu_id, dst_gpu_id, memory_bitmap, page_table_ptr, checkpoint);
+}
+
+static int rpc_570_notify_register(struct gsp_client *client, u32 type,
+ void (*callback)(struct gsp_client *, struct gsp_rpc_response *, void *),
+ void *ctx)
+{
+ return gsp_rpc_notify_register(client, type, callback, ctx);
+}
+
+static void rpc_570_notify_unregister(struct gsp_client *client, u32 type)
+{
+ gsp_rpc_notify_unregister(client, type);
+}
+
+static struct rpc_interface interface_570 = {
+ .version = "570.144W",
+ .alloc = rpc_570_alloc,
+ .free = rpc_570_free,
+ .map = rpc_570_map,
+ .unmap = rpc_570_unmap,
+ .pin = rpc_570_pin,
+ .unpin = rpc_570_unpin,
+ .schedule = rpc_570_schedule,
+ .yield = rpc_570_yield,
+ .bar_trap = rpc_570_bar_trap,
+ .migrate_save = rpc_570_migrate_save,
+ .migrate_restore = rpc_570_migrate_restore,
+ .notify_register = rpc_570_notify_register,
+ .notify_unregister = rpc_570_notify_unregister,
+ .use_emulation = {0},
+};
+
+/* Emulation interface (fallback) */
+static int rpc_emu_alloc(struct gsp_client *client, u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid, u64 *gpu_va, u64 *handle, u64 *actual_size)
+{
+ return gsp_rpc_alloc_emul(client, size, alignment, mem_type, flags, addr_hint, pid, gpu_va, handle, actual_size);
+}
+
+static int rpc_emu_free(struct gsp_client *client, u64 handle)
+{
+ return gsp_rpc_free_emul(client, handle);
+}
+
+static int rpc_emu_map(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset, u64 *mapped_va)
+{
+ return gsp_rpc_map_emul(client, handle, gpu_va, size, prot, flags, offset, mapped_va);
+}
+
+static int rpc_emu_unmap(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size)
+{
+ return gsp_rpc_unmap_emul(client, handle, gpu_va, size);
+}
+
+static int rpc_emu_pin(struct gsp_client *client, u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages)
+{
+ return gsp_rpc_pin_emul(client, handle, offset, size, flags, dma_addr, num_pages);
+}
+
+static int rpc_emu_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size)
+{
+ return gsp_rpc_unpin_emul(client, handle, offset, size);
+}
+
+static int rpc_emu_schedule(struct gsp_client *client, u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags)
+{
+ return 0; /* Emulation always succeeds */
+}
+
+static int rpc_emu_yield(struct gsp_client *client)
+{
+ return 0;
+}
+
+static int rpc_emu_bar_trap(struct gsp_client *client, u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value)
+{
+ return gsp_rpc_bar_trap(client, bar_addr, access_type, size, value, bar_num, ret_value);
+}
+
+static int rpc_emu_migrate_save(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr, u64 *checkpoint)
+{
+ return 0;
+}
+
+static int rpc_emu_migrate_restore(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr, u64 checkpoint)
+{
+ return 0;
+}
+
+static int rpc_emu_notify_register(struct gsp_client *client, u32 type,
+ void (*callback)(struct gsp_client *, struct gsp_rpc_response *, void *),
+ void *ctx)
+{
+ return gsp_rpc_notify_register(client, type, callback, ctx);
+}
+
+static void rpc_emu_notify_unregister(struct gsp_client *client, u32 type)
+{
+ gsp_rpc_notify_unregister(client, type);
+}
+
+static struct rpc_interface interface_emu = {
+ .version = "emulation",
+ .alloc = rpc_emu_alloc,
+ .free = rpc_emu_free,
+ .map = rpc_emu_map,
+ .unmap = rpc_emu_unmap,
+ .pin = rpc_emu_pin,
+ .unpin = rpc_emu_unpin,
+ .schedule = rpc_emu_schedule,
+ .yield = rpc_emu_yield,
+ .bar_trap = rpc_emu_bar_trap,
+ .migrate_save = rpc_emu_migrate_save,
+ .migrate_restore = rpc_emu_migrate_restore,
+ .notify_register = rpc_emu_notify_register,
+ .notify_unregister = rpc_emu_notify_unregister,
+ .use_emulation = {1},
+};
+
+int dynamic_dispatcher_init(const char *fw_path)
+{
+ struct fw_analysis_result result;
+ int ret;
+
+ spin_lock(&g_dispatcher.lock);
+
+ if (g_dispatcher.initialized) {
+ spin_unlock(&g_dispatcher.lock);
+ return 0;
+ }
+
+ /* Analyze firmware */
+ ret = fw_analyzer_load_and_analyze(fw_path, &result);
+ if (ret) {
+ pr_warn("DYN_DISPATCH: Firmware analysis failed, using emulation: %d\n", ret);
+ g_dispatcher.current_interface = &interface_emu;
+ g_dispatcher.initialized = true;
+ spin_unlock(&g_dispatcher.lock);
+ return 0;
+ }
+
+ g_dispatcher.fw_analysis = result;
+
+ /* Select interface based on firmware version */
+ if (result.matched && strcmp(result.version, "570.144W") == 0) {
+ interface_570.fw_addrs = result.addresses;
+ g_dispatcher.current_interface = &interface_570;
+ pr_info("DYN_DISPATCH: Using native 570.144W interface\n");
+ } else {
+ g_dispatcher.current_interface = &interface_emu;
+ pr_info("DYN_DISPATCH: Using emulation interface (version: %s)\n", result.version);
+ }
+
+ g_dispatcher.initialized = true;
+ spin_unlock(&g_dispatcher.lock);
+
+ fw_analyzer_print_result(&result);
+ return 0;
+}
+
+void dynamic_dispatcher_fini(void)
+{
+ spin_lock(&g_dispatcher.lock);
+ g_dispatcher.initialized = false;
+ g_dispatcher.current_interface = NULL;
+ spin_unlock(&g_dispatcher.lock);
+}
+
+struct rpc_interface *dynamic_dispatcher_get_interface(void)
+{
+ struct rpc_interface *iface;
+
+ spin_lock(&g_dispatcher.lock);
+ iface = g_dispatcher.current_interface;
+ spin_unlock(&g_dispatcher.lock);
+
+ return iface;
+}
+
+bool dynamic_dispatcher_use_emulation(enum gsp_rpc_type type)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface)
+ return true;
+
+ if (type >= GSP_RPC_MAX_TYPE)
+ return true;
+
+ return iface->use_emulation[type];
+}
+
+int dynamic_dispatcher_alloc(struct gsp_client *client, u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid, u64 *gpu_va, u64 *handle, u64 *actual_size)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->alloc)
+ return -ENODEV;
+ return iface->alloc(client, size, alignment, mem_type, flags, addr_hint, pid, gpu_va, handle, actual_size);
+}
+
+int dynamic_dispatcher_free(struct gsp_client *client, u64 handle)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->free)
+ return -ENODEV;
+ return iface->free(client, handle);
+}
+
+int dynamic_dispatcher_map(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset, u64 *mapped_va)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->map)
+ return -ENODEV;
+ return iface->map(client, handle, gpu_va, size, prot, flags, offset, mapped_va);
+}
+
+int dynamic_dispatcher_unmap(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->unmap)
+ return -ENODEV;
+ return iface->unmap(client, handle, gpu_va, size);
+}
+
+int dynamic_dispatcher_pin(struct gsp_client *client, u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->pin)
+ return -ENODEV;
+ return iface->pin(client, handle, offset, size, flags, dma_addr, num_pages);
+}
+
+int dynamic_dispatcher_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->unpin)
+ return -ENODEV;
+ return iface->unpin(client, handle, offset, size);
+}
+
+int dynamic_dispatcher_schedule(struct gsp_client *client, u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->schedule)
+ return -ENODEV;
+ return iface->schedule(client, work_handle, callback, arg, priority, flags);
+}
+
+int dynamic_dispatcher_yield(struct gsp_client *client)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->yield)
+ return -ENODEV;
+ return iface->yield(client);
+}
+
+int dynamic_dispatcher_bar_trap(struct gsp_client *client, u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->bar_trap)
+ return -ENODEV;
+ return iface->bar_trap(client, bar_addr, access_type, size, value, bar_num, ret_value);
+}
+
+int dynamic_dispatcher_migrate_save(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr, u64 *checkpoint)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->migrate_save)
+ return -ENODEV;
+ return iface->migrate_save(client, src_gpu_id, dst_gpu_id, memory_bitmap, page_table_ptr, checkpoint);
+}
+
+int dynamic_dispatcher_migrate_restore(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr, u64 checkpoint)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->migrate_restore)
+ return -ENODEV;
+ return iface->migrate_restore(client, src_gpu_id, dst_gpu_id, memory_bitmap, page_table_ptr, checkpoint);
+}
+
+int dynamic_dispatcher_notify_register(struct gsp_client *client, u32 type,
+ void (*callback)(struct gsp_client *, struct gsp_rpc_response *, void *),
+ void *ctx)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (!iface || !iface->notify_register)
+ return -ENODEV;
+ return iface->notify_register(client, type, callback, ctx);
+}
+
+void dynamic_dispatcher_notify_unregister(struct gsp_client *client, u32 type)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ if (iface && iface->notify_unregister)
+ iface->notify_unregister(client, type);
+}
+
+const char *dynamic_dispatcher_get_version(void)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ return iface ? iface->version : "unknown";
+}
+
+const struct fw_addresses *dynamic_dispatcher_get_fw_addresses(void)
+{
+ struct rpc_interface *iface = dynamic_dispatcher_get_interface();
+ return iface ? &iface->fw_addrs : NULL;
+}
+
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_init);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_fini);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_get_interface);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_use_emulation);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_alloc);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_free);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_map);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_unmap);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_pin);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_unpin);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_schedule);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_yield);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_bar_trap);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_migrate_save);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_migrate_restore);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_notify_register);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_notify_unregister);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_get_version);
+EXPORT_SYMBOL_GPL(dynamic_dispatcher_get_fw_addresses);
+
+MODULE_DESCRIPTION("Dynamic RPC Dispatcher");
+MODULE_LICENSE("GPL"); \ No newline at end of file
diff --git a/kernel/dynamic_rpc_dispatcher.h b/kernel/dynamic_rpc_dispatcher.h
new file mode 100644
index 0000000..2abecb5
--- /dev/null
+++ b/kernel/dynamic_rpc_dispatcher.h
@@ -0,0 +1,66 @@
+#ifndef _DYNAMIC_RPC_DISPATCHER_H_
+#define _DYNAMIC_RPC_DISPATCHER_H_
+
+#include <linux/types.h>
+#include "gsp_rpc_client.h"
+#include "firmware_analyzer.h"
+
+/* Version-specific RPC interface */
+struct rpc_interface {
+ char version[32];
+ struct fw_addresses fw_addrs;
+
+ /* Native firmware handlers */
+ int (*alloc)(struct gsp_client *, u64, u64, u32, u32, u64, u32, u64 *, u64 *, u64 *);
+ int (*free)(struct gsp_client *, u64);
+ int (*map)(struct gsp_client *, u64, u64, u64, u32, u32, u64, u64 *);
+ int (*unmap)(struct gsp_client *, u64, u64, u64);
+ int (*pin)(struct gsp_client *, u64, u64, u64, u32, u64 *, u32 *);
+ int (*unpin)(struct gsp_client *, u64, u64, u64);
+ int (*schedule)(struct gsp_client *, u64, u64, u64, u32, u32);
+ int (*yield)(struct gsp_client *);
+ int (*bar_trap)(struct gsp_client *, u64, u32, u32, u64, u32, u64 *);
+ int (*migrate_save)(struct gsp_client *, u64, u64, u64, u64, u64 *);
+ int (*migrate_restore)(struct gsp_client *, u64, u64, u64, u64, u64);
+ int (*notify_register)(struct gsp_client *, u32, void (*)(struct gsp_client *, struct gsp_rpc_response *, void *), void *);
+ void (*notify_unregister)(struct gsp_client *, u32);
+
+ /* Emulation fallback flags */
+ bool use_emulation[GSP_RPC_MAX_TYPE];
+};
+
+/* Public API */
+int dynamic_dispatcher_init(const char *fw_path);
+void dynamic_dispatcher_fini(void);
+struct rpc_interface *dynamic_dispatcher_get_interface(void);
+bool dynamic_dispatcher_use_emulation(enum gsp_rpc_type type);
+
+/* Dispatcher functions */
+int dynamic_dispatcher_alloc(struct gsp_client *client, u64 size, u64 alignment, u32 mem_type,
+ u32 flags, u64 addr_hint, u32 pid, u64 *gpu_va, u64 *handle, u64 *actual_size);
+int dynamic_dispatcher_free(struct gsp_client *client, u64 handle);
+int dynamic_dispatcher_map(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size,
+ u32 prot, u32 flags, u64 offset, u64 *mapped_va);
+int dynamic_dispatcher_unmap(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size);
+int dynamic_dispatcher_pin(struct gsp_client *client, u64 handle, u64 offset, u64 size,
+ u32 flags, u64 *dma_addr, u32 *num_pages);
+int dynamic_dispatcher_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size);
+int dynamic_dispatcher_schedule(struct gsp_client *client, u64 work_handle, u64 callback, u64 arg,
+ u32 priority, u32 flags);
+int dynamic_dispatcher_yield(struct gsp_client *client);
+int dynamic_dispatcher_bar_trap(struct gsp_client *client, u64 bar_addr, u32 access_type, u32 size,
+ u64 value, u32 bar_num, u64 *ret_value);
+int dynamic_dispatcher_migrate_save(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr, u64 *checkpoint);
+int dynamic_dispatcher_migrate_restore(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
+ u64 memory_bitmap, u64 page_table_ptr, u64 checkpoint);
+int dynamic_dispatcher_notify_register(struct gsp_client *client, u32 type,
+ void (*callback)(struct gsp_client *, struct gsp_rpc_response *, void *),
+ void *ctx);
+void dynamic_dispatcher_notify_unregister(struct gsp_client *client, u32 type);
+
+/* Info */
+const char *dynamic_dispatcher_get_version(void);
+const struct fw_addresses *dynamic_dispatcher_get_fw_addresses(void);
+
+#endif /* _DYNAMIC_RPC_DISPATCHER_H_ */ \ No newline at end of file
diff --git a/kernel/firmware_analyzer.c b/kernel/firmware_analyzer.c
new file mode 100644
index 0000000..d90e043
--- /dev/null
+++ b/kernel/firmware_analyzer.c
@@ -0,0 +1,224 @@
+/*
+ * Firmware Analyzer - Runtime GSP firmware analysis
+ * Auto-discovers RPC handler addresses, struct layouts, and firmware signatures
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/crc32.h>
+#include <linux/sha.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/vmalloc.h>
+
+#include "firmware_analyzer.h"
+#include "gsp_rpc_client.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Open-IOV");
+MODULE_DESCRIPTION("GSP Firmware Analyzer");
+
+/* Firmware signature database */
+struct fw_signature {
+ char version[32];
+ u32 crc32;
+ u8 sha256[32];
+ struct fw_addresses addrs;
+};
+
+static struct fw_signature known_signatures[] = {
+ {
+ .version = "570.144W",
+ .crc32 = 0x0, /* Will be computed */
+ .sha256 = {0}, /* Will be computed */
+ .addrs = {
+ .rpc_dispatch_primary = 0x057A1AC0,
+ .rpc_jump_table = 0x059BFEB0,
+ .alloc_main = 0x057E94EC,
+ .schedule_main = 0x051EFBF4,
+ .bar_trap_handler = 0x04CB8188,
+ .wpr_handler1 = 0x04F00B0C,
+ .wpr_handler2 = 0x04F00C78,
+ .migration_table1 = 0x04179ED8,
+ .migration_table2 = 0x04179FBC,
+ .entry_point = 0x04009000,
+ }
+ },
+};
+
+/* Pattern signatures for auto-discovery */
+static const u8 pattern_rpc_dispatch[] = {
+ 0x67, 0x05, 0x00, 0x00, /* auipc a0, 0x5 */
+ 0x03, 0x85, 0x05, 0x00, /* ld a1, 0x500(a0) */
+};
+
+static const u8 pattern_jump_table[] = {
+ 0x00, 0x00, 0x00, 0x00, /* .dword entries */
+};
+
+static const u8 pattern_alloc[] = {
+ 0x73, 0x22, 0x00, 0x00, /* sd s0, 0(sp) */
+ 0x73, 0x62, 0x08, 0x00, /* sd s1, 8(sp) */
+};
+
+static int compute_firmware_crc32(const u8 *data, size_t len, u32 *crc_out)
+{
+ *crc_out = crc32_le(0, data, len);
+ return 0;
+}
+
+static int compute_firmware_sha256(const u8 *data, size_t len, u8 *sha_out)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ int ret;
+
+ tfm = crypto_alloc_shash("sha256", 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
+ if (!desc) {
+ crypto_free_shash(tfm);
+ return -ENOMEM;
+ }
+
+ desc->tfm = tfm;
+ ret = crypto_shash_init(desc);
+ if (!ret)
+ ret = crypto_shash_update(desc, data, len);
+ if (!ret)
+ ret = crypto_shash_final(desc, sha_out);
+
+ kfree(desc);
+ crypto_free_shash(tfm);
+ return ret;
+}
+
+static int find_pattern(const u8 *data, size_t data_len, const u8 *pattern, size_t pattern_len, u64 *offset)
+{
+ for (size_t i = 0; i + pattern_len <= data_len; i++) {
+ if (memcmp(data + i, pattern, pattern_len) == 0) {
+ *offset = i;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+static int analyze_rpc_dispatcher(const u8 *fw, size_t fw_len, struct fw_addresses *addrs)
+{
+ u64 offset;
+ int ret;
+
+ ret = find_pattern(fw, fw_len, pattern_rpc_dispatch, sizeof(pattern_rpc_dispatch), &offset);
+ if (ret == 0) {
+ addrs->rpc_dispatch_primary = GSP_FW_BASE + offset;
+ pr_info("FW_ANALYZER: Found RPC dispatcher at 0x%llx\n", addrs->rpc_dispatch_primary);
+ }
+
+ ret = find_pattern(fw, fw_len, pattern_jump_table, sizeof(pattern_jump_table), &offset);
+ if (ret == 0) {
+ addrs->rpc_jump_table = GSP_FW_BASE + offset;
+ pr_info("FW_ANALYZER: Found RPC jump table at 0x%llx\n", addrs->rpc_jump_table);
+ }
+
+ ret = find_pattern(fw, fw_len, pattern_alloc, sizeof(pattern_alloc), &offset);
+ if (ret == 0) {
+ addrs->alloc_main = GSP_FW_BASE + offset;
+ pr_info("FW_ANALYZER: Found ALLOC handler at 0x%llx\n", addrs->alloc_main);
+ }
+
+ return 0;
+}
+
+int fw_analyzer_analyze(const u8 *fw_data, size_t fw_len, struct fw_analysis_result *result)
+{
+ int ret;
+
+ if (!fw_data || !result || fw_len == 0)
+ return -EINVAL;
+
+ memset(result, 0, sizeof(*result));
+ result->fw_size = fw_len;
+
+ /* Compute signatures */
+ ret = compute_firmware_crc32(fw_data, fw_len, &result->crc32);
+ if (ret)
+ return ret;
+
+ ret = compute_firmware_sha256(fw_data, fw_len, result->sha256);
+ if (ret)
+ return ret;
+
+ pr_info("FW_ANALYZER: Firmware size=%zu, CRC32=0x%08x\n", fw_len, result->crc32);
+ pr_info("FW_ANALYZER: SHA256: %*phN\n", 32, result->sha256);
+
+ /* Match against known signatures */
+ for (size_t i = 0; i < ARRAY_SIZE(known_signatures); i++) {
+ if (known_signatures[i].crc32 == result->crc32 &&
+ memcmp(known_signatures[i].sha256, result->sha256, 32) == 0) {
+ strncpy(result->version, known_signatures[i].version, sizeof(result->version) - 1);
+ result->addresses = known_signatures[i].addrs;
+ result->matched = true;
+ pr_info("FW_ANALYZER: Matched known firmware version: %s\n", result->version);
+ return 0;
+ }
+ }
+
+ /* Auto-discover addresses if no match */
+ pr_info("FW_ANALYZER: No known signature match, attempting auto-discovery\n");
+ ret = analyze_rpc_dispatcher(fw_data, fw_len, &result->addresses);
+ if (ret == 0) {
+ result->matched = false;
+ snprintf(result->version, sizeof(result->version), "unknown-%08x", result->crc32);
+ }
+
+ return ret;
+}
+
+int fw_analyzer_load_and_analyze(const char *fw_path, struct fw_analysis_result *result)
+{
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, fw_path, NULL);
+ if (ret) {
+ pr_err("FW_ANALYZER: Failed to load firmware %s: %d\n", fw_path, ret);
+ return ret;
+ }
+
+ ret = fw_analyzer_analyze(fw->data, fw->size, result);
+
+ release_firmware(fw);
+ return ret;
+}
+
+void fw_analyzer_print_result(const struct fw_analysis_result *result)
+{
+ pr_info("=== Firmware Analysis Result ===\n");
+ pr_info("Version: %s\n", result->version);
+ pr_info("Size: %zu bytes\n", result->fw_size);
+ pr_info("CRC32: 0x%08x\n", result->crc32);
+ pr_info("SHA256: %*phN\n", 32, result->sha256);
+ pr_info("Matched: %s\n", result->matched ? "yes" : "no");
+ pr_info("Addresses:\n");
+ pr_info(" RPC Dispatch Primary: 0x%llx\n", result->addresses.rpc_dispatch_primary);
+ pr_info(" RPC Jump Table: 0x%llx\n", result->addresses.rpc_jump_table);
+ pr_info(" ALLOC Main: 0x%llx\n", result->addresses.alloc_main);
+ pr_info(" SCHEDULE Main: 0x%llx\n", result->addresses.schedule_main);
+ pr_info(" BAR Trap Handler: 0x%llx\n", result->addresses.bar_trap_handler);
+ pr_info(" WPR Handler 1: 0x%llx\n", result->addresses.wpr_handler1);
+ pr_info(" WPR Handler 2: 0x%llx\n", result->addresses.wpr_handler2);
+ pr_info(" Migration Table 1: 0x%llx\n", result->addresses.migration_table1);
+ pr_info(" Migration Table 2: 0x%llx\n", result->addresses.migration_table2);
+ pr_info(" Entry Point: 0x%llx\n", result->addresses.entry_point);
+}
+EXPORT_SYMBOL_GPL(fw_analyzer_analyze);
+EXPORT_SYMBOL_GPL(fw_analyzer_load_and_analyze);
+EXPORT_SYMBOL_GPL(fw_analyzer_print_result);
+
+MODULE_DESCRIPTION("GSP Firmware Analyzer");
+MODULE_LICENSE("GPL"); \ No newline at end of file
diff --git a/kernel/firmware_analyzer.h b/kernel/firmware_analyzer.h
new file mode 100644
index 0000000..69a4ec3
--- /dev/null
+++ b/kernel/firmware_analyzer.h
@@ -0,0 +1,36 @@
+#ifndef _FW_ANALYZER_H_
+#define _FW_ANALYZER_H_
+
+#include <linux/types.h>
+#include "gsp_rpc_client.h"
+
+/* Firmware addresses discovered from analysis */
+struct fw_addresses {
+ u64 rpc_dispatch_primary;
+ u64 rpc_jump_table;
+ u64 alloc_main;
+ u64 schedule_main;
+ u64 bar_trap_handler;
+ u64 wpr_handler1;
+ u64 wpr_handler2;
+ u64 migration_table1;
+ u64 migration_table2;
+ u64 entry_point;
+};
+
+/* Analysis result */
+struct fw_analysis_result {
+ char version[32];
+ size_t fw_size;
+ u32 crc32;
+ u8 sha256[32];
+ struct fw_addresses addresses;
+ bool matched;
+};
+
+/* Public API */
+int fw_analyzer_analyze(const u8 *fw_data, size_t fw_len, struct fw_analysis_result *result);
+int fw_analyzer_load_and_analyze(const char *fw_path, struct fw_analysis_result *result);
+void fw_analyzer_print_result(const struct fw_analysis_result *result);
+
+#endif /* _FW_ANALYZER_H_ */ \ No newline at end of file
diff --git a/kernel/tests/Makefile b/kernel/tests/Makefile
index ce44670..7374f8b 100644
--- a/kernel/tests/Makefile
+++ b/kernel/tests/Makefile
@@ -1,16 +1,9 @@
-# GSP vGPU-IO Driver Test Makefile
+obj-m += gsp_emulation_test.o
-# Comprehensive test module
-obj-m += gsp_vgpu_comprehensive_test.o
+ccflags-y := -I$(PWD)/../ -I$(PWD)/../include
-# 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
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
@@ -19,22 +12,7 @@ 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
+ sudo insmod gsp_emulation_test.ko
-.PHONY: all clean install test test-kunit test-unload \ No newline at end of file
+uninstall:
+ sudo rmmod gsp_emulation_test \ No newline at end of file
diff --git a/kernel/tests/gsp_emulation_test.c b/kernel/tests/gsp_emulation_test.c
new file mode 100644
index 0000000..64b3a25
--- /dev/null
+++ b/kernel/tests/gsp_emulation_test.c
@@ -0,0 +1,705 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/spinlock.h>
+#include <linux/ktime.h>
+#include <linux/atomic.h>
+
+#include "gsp_emulation.h"
+#include "gsp_rpc_client.h"
+#include "memory.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Open-IOV");
+MODULE_DESCRIPTION("GSP Emulation Unit Tests");
+
+#define TEST_ALLOC_SIZE (4096)
+#define TEST_MAP_SIZE (4096)
+#define TEST_PIN_PAGES (1)
+
+static struct gsp_emu_context *test_ctx = NULL;
+static struct gsp_client *test_client = NULL;
+static void *test_shared_mem = NULL;
+static dma_addr_t test_shared_dma = 0;
+static struct page **test_pages = NULL;
+static int test_pages_count = 0;
+
+static int test_seq_id = 1;
+
+static int get_next_seq_id(void)
+{
+ return test_seq_id++;
+}
+
+static void init_test_context(void)
+{
+ test_shared_mem = dma_alloc_coherent(NULL, 4096, &test_shared_dma, GFP_KERNEL);
+ if (!test_shared_mem) {
+ pr_err("GSP_EMU_TEST: Failed to allocate shared memory\n");
+ return;
+ }
+ memset(test_shared_mem, 0, 4096);
+
+ test_pages_count = TEST_ALLOC_SIZE / PAGE_SIZE;
+ test_pages = kmalloc_array(test_pages_count, sizeof(struct page *), GFP_KERNEL);
+ if (!test_pages) {
+ pr_err("GSP_EMU_TEST: Failed to allocate page array\n");
+ return;
+ }
+ for (int i = 0; i < test_pages_count; i++) {
+ test_pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!test_pages[i]) {
+ pr_err("GSP_EMU_TEST: Failed to allocate page %d\n", i);
+ test_pages_count = i;
+ break;
+ }
+ }
+
+ test_client = gsp_client_init(NULL);
+ if (!test_client) {
+ pr_err("GSP_EMU_TEST: Failed to init gsp_client\n");
+ return;
+ }
+
+ test_ctx = kzalloc(sizeof(struct gsp_emu_context), GFP_KERNEL);
+ if (!test_ctx) {
+ pr_err("GSP_EMU_TEST: Failed to allocate context\n");
+ return;
+ }
+
+ test_ctx->vgpu = NULL;
+ test_ctx->bar0_vaddr = test_shared_mem;
+ test_ctx->doorbell_reg = test_shared_mem + 0x110C00;
+ test_ctx->shared_mem = test_shared_mem + 0x1000;
+ test_ctx->rpc_client = test_client;
+ spin_lock_init(&test_ctx->lock);
+ test_ctx->running = true;
+ atomic_set(&test_ctx->processed_rpcs, 0);
+ atomic_set(&test_ctx->failed_rpcs, 0);
+ test_ctx->total_latency_ns = 0;
+
+ pr_info("GSP_EMU_TEST: Test context initialized\n");
+}
+
+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_pages) {
+ for (int i = 0; i < test_pages_count; i++) {
+ if (test_pages[i])
+ __free_page(test_pages[i]);
+ }
+ kfree(test_pages);
+ test_pages = NULL;
+ }
+ if (test_shared_mem) {
+ dma_free_coherent(NULL, 4096, test_shared_mem, test_shared_dma);
+ test_shared_mem = NULL;
+ }
+ pr_info("GSP_EMU_TEST: Test context cleaned up\n");
+}
+
+static int test_alloc_free(void)
+{
+ struct gsp_rpc_alloc_request req = {0};
+ struct gsp_rpc_alloc_response resp = {0};
+ int ret;
+
+ pr_info("GSP_EMU_TEST: Testing ALLOC/FREE...\n");
+
+ req.header.type = GSP_RPC_ALLOC;
+ req.header.seq_id = get_next_seq_id();
+ req.header.flags = 0;
+ req.size = TEST_ALLOC_SIZE;
+ req.alignment = PAGE_SIZE;
+ req.flags = GSP_ALLOC_FLAG_VRAM;
+
+ ret = gsp_rpc_alloc_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: ALLOC failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (resp.status != 0) {
+ pr_err("GSP_EMU_TEST: ALLOC returned status=%d\n", resp.status);
+ return -1;
+ }
+ if (resp.handle == 0) {
+ pr_err("GSP_EMU_TEST: ALLOC returned zero handle\n");
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: ALLOC succeeded, handle=0x%llx\n", resp.handle);
+
+ struct gsp_rpc_free_request free_req = {0};
+ struct gsp_rpc_free_response free_resp = {0};
+
+ free_req.header.type = GSP_RPC_FREE;
+ free_req.header.seq_id = get_next_seq_id();
+ free_req.handle = resp.handle;
+
+ ret = gsp_rpc_free_emulation(test_client, &free_req, &free_resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: FREE failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (free_resp.status != 0) {
+ pr_err("GSP_EMU_TEST: FREE returned status=%d\n", free_resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: FREE succeeded\n");
+ return 0;
+}
+
+static int test_map_unmap(void)
+{
+ struct gsp_rpc_alloc_request alloc_req = {0};
+ struct gsp_rpc_alloc_response alloc_resp = {0};
+ struct gsp_rpc_map_request map_req = {0};
+ struct gsp_rpc_map_response map_resp = {0};
+ struct gsp_rpc_unmap_request unmap_req = {0};
+ struct gsp_rpc_unmap_response unmap_resp = {0};
+ int ret;
+
+ pr_info("GSP_EMU_TEST: Testing MAP/UNMAP...\n");
+
+ alloc_req.header.type = GSP_RPC_ALLOC;
+ alloc_req.header.seq_id = get_next_seq_id();
+ alloc_req.size = TEST_MAP_SIZE;
+ alloc_req.alignment = PAGE_SIZE;
+ alloc_req.flags = GSP_ALLOC_FLAG_VRAM;
+
+ ret = gsp_rpc_alloc_emulation(test_client, &alloc_req, &alloc_resp);
+ if (ret != 0 || alloc_resp.status != 0) {
+ pr_err("GSP_EMU_TEST: Pre-alloc for MAP failed\n");
+ return -1;
+ }
+
+ map_req.header.type = GSP_RPC_MAP;
+ map_req.header.seq_id = get_next_seq_id();
+ map_req.handle = alloc_resp.handle;
+ map_req.offset = 0;
+ map_req.size = TEST_MAP_SIZE;
+ map_req.gpu_va = 0x100000000ULL;
+ map_req.flags = GSP_MAP_FLAG_READ | GSP_MAP_FLAG_WRITE;
+
+ ret = gsp_rpc_map_emulation(test_client, &map_req, &map_resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: MAP failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (map_resp.status != 0) {
+ pr_err("GSP_EMU_TEST: MAP returned status=%d\n", map_resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: MAP succeeded, gpu_va=0x%llx\n", map_resp.gpu_va);
+
+ unmap_req.header.type = GSP_RPC_UNMAP;
+ unmap_req.header.seq_id = get_next_seq_id();
+ unmap_req.gpu_va = map_resp.gpu_va;
+ unmap_req.size = TEST_MAP_SIZE;
+
+ ret = gsp_rpc_unmap_emulation(test_client, &unmap_req, &unmap_resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: UNMAP failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (unmap_resp.status != 0) {
+ pr_err("GSP_EMU_TEST: UNMAP returned status=%d\n", unmap_resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: UNMAP succeeded\n");
+
+ struct gsp_rpc_free_request free_req = {0};
+ struct gsp_rpc_free_response free_resp = {0};
+ free_req.header.type = GSP_RPC_FREE;
+ free_req.header.seq_id = get_next_seq_id();
+ free_req.handle = alloc_resp.handle;
+ gsp_rpc_free_emulation(test_client, &free_req, &free_resp);
+
+ return 0;
+}
+
+static int test_pin_unpin(void)
+{
+ struct gsp_rpc_alloc_request alloc_req = {0};
+ struct gsp_rpc_alloc_response alloc_resp = {0};
+ struct gsp_rpc_map_request map_req = {0};
+ struct gsp_rpc_map_response map_resp = {0};
+ struct gsp_rpc_pin_request pin_req = {0};
+ struct gsp_rpc_pin_response pin_resp = {0};
+ struct gsp_rpc_unpin_request unpin_req = {0};
+ struct gsp_rpc_unpin_response unpin_resp = {0};
+ int ret;
+
+ pr_info("GSP_EMU_TEST: Testing PIN/UNPIN...\n");
+
+ alloc_req.header.type = GSP_RPC_ALLOC;
+ alloc_req.header.seq_id = get_next_seq_id();
+ alloc_req.size = TEST_ALLOC_SIZE;
+ alloc_req.alignment = PAGE_SIZE;
+ alloc_req.flags = GSP_ALLOC_FLAG_VRAM;
+
+ ret = gsp_rpc_alloc_emulation(test_client, &alloc_req, &alloc_resp);
+ if (ret != 0 || alloc_resp.status != 0) {
+ pr_err("GSP_EMU_TEST: Pre-alloc for PIN failed\n");
+ return -1;
+ }
+
+ map_req.header.type = GSP_RPC_MAP;
+ map_req.header.seq_id = get_next_seq_id();
+ map_req.handle = alloc_resp.handle;
+ map_req.offset = 0;
+ map_req.size = TEST_ALLOC_SIZE;
+ map_req.gpu_va = 0x200000000ULL;
+ map_req.flags = GSP_MAP_FLAG_READ | GSP_MAP_FLAG_WRITE;
+
+ ret = gsp_rpc_map_emulation(test_client, &map_req, &map_resp);
+ if (ret != 0 || map_resp.status != 0) {
+ pr_err("GSP_EMU_TEST: Pre-map for PIN failed\n");
+ return -1;
+ }
+
+ pin_req.header.type = GSP_RPC_PIN;
+ pin_req.header.seq_id = get_next_seq_id();
+ pin_req.gpu_va = map_resp.gpu_va;
+ pin_req.size = TEST_ALLOC_SIZE;
+ pin_req.flags = GSP_PIN_FLAG_DMA;
+
+ ret = gsp_rpc_pin_emulation(test_client, &pin_req, &pin_resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: PIN failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (pin_resp.status != 0) {
+ pr_err("GSP_EMU_TEST: PIN returned status=%d\n", pin_resp.status);
+ return -1;
+ }
+ if (pin_resp.dma_addr == 0) {
+ pr_err("GSP_EMU_TEST: PIN returned zero DMA address\n");
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: PIN succeeded, dma_addr=0x%llx\n", pin_resp.dma_addr);
+
+ unpin_req.header.type = GSP_RPC_UNPIN;
+ unpin_req.header.seq_id = get_next_seq_id();
+ unpin_req.gpu_va = map_resp.gpu_va;
+ unpin_req.size = TEST_ALLOC_SIZE;
+
+ ret = gsp_rpc_unpin_emulation(test_client, &unpin_req, &unpin_resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: UNPIN failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (unpin_resp.status != 0) {
+ pr_err("GSP_EMU_TEST: UNPIN returned status=%d\n", unpin_resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: UNPIN succeeded\n");
+
+ struct gsp_rpc_unmap_request unmap_req = {0};
+ struct gsp_rpc_unmap_response unmap_resp = {0};
+ unmap_req.header.type = GSP_RPC_UNMAP;
+ unmap_req.header.seq_id = get_next_seq_id();
+ unmap_req.gpu_va = map_resp.gpu_va;
+ unmap_req.size = TEST_ALLOC_SIZE;
+ gsp_rpc_unmap_emulation(test_client, &unmap_req, &unmap_resp);
+
+ struct gsp_rpc_free_request free_req = {0};
+ struct gsp_rpc_free_response free_resp = {0};
+ free_req.header.type = GSP_RPC_FREE;
+ free_req.header.seq_id = get_next_seq_id();
+ free_req.handle = alloc_resp.handle;
+ gsp_rpc_free_emulation(test_client, &free_req, &free_resp);
+
+ return 0;
+}
+
+static int test_schedule_yield(void)
+{
+ struct gsp_rpc_schedule_request req = {0};
+ struct gsp_rpc_schedule_response resp = {0};
+ struct gsp_rpc_yield_request yield_req = {0};
+ struct gsp_rpc_yield_response yield_resp = {0};
+ int ret;
+
+ pr_info("GSP_EMU_TEST: Testing SCHEDULE/YIELD...\n");
+
+ req.header.type = GSP_RPC_SCHEDULE;
+ req.header.seq_id = get_next_seq_id();
+ req.work_id = 0x1234;
+ req.priority = 10;
+ req.flags = 0;
+
+ ret = gsp_rpc_schedule_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: SCHEDULE failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (resp.status != 0) {
+ pr_err("GSP_EMU_TEST: SCHEDULE returned status=%d\n", resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: SCHEDULE succeeded\n");
+
+ yield_req.header.type = GSP_RPC_YIELD;
+ yield_req.header.seq_id = get_next_seq_id();
+ yield_req.work_id = 0x1234;
+
+ ret = gsp_rpc_yield_emulation(test_client, &yield_req, &yield_resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: YIELD failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (yield_resp.status != 0) {
+ pr_err("GSP_EMU_TEST: YIELD returned status=%d\n", yield_resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: YIELD succeeded\n");
+ return 0;
+}
+
+static int test_bar_trap(void)
+{
+ struct gsp_rpc_bar_trap_request req = {0};
+ struct gsp_rpc_bar_trap_response resp = {0};
+ int ret;
+
+ pr_info("GSP_EMU_TEST: Testing BAR_TRAP...\n");
+
+ req.header.type = GSP_RPC_BAR_TRAP;
+ req.header.seq_id = get_next_seq_id();
+ req.bar_index = 0;
+ req.offset = 0x1000;
+ req.size = 4;
+ req.is_write = false;
+ req.value = 0;
+
+ ret = gsp_rpc_bar_trap_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: BAR_TRAP read failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (resp.status != 0) {
+ pr_err("GSP_EMU_TEST: BAR_TRAP read returned status=%d\n", resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: BAR_TRAP read succeeded, value=0x%llx\n", resp.value);
+
+ req.header.seq_id = get_next_seq_id();
+ req.is_write = true;
+ req.value = 0xDEADBEEF;
+
+ ret = gsp_rpc_bar_trap_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: BAR_TRAP write failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (resp.status != 0) {
+ pr_err("GSP_EMU_TEST: BAR_TRAP write returned status=%d\n", resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: BAR_TRAP write succeeded\n");
+
+ req.header.seq_id = get_next_seq_id();
+ req.is_write = false;
+ req.value = 0;
+
+ ret = gsp_rpc_bar_trap_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: BAR_TRAP read-back failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (resp.value != 0xDEADBEEF) {
+ pr_err("GSP_EMU_TEST: BAR_TRAP read-back mismatch: expected 0xDEADBEEF, got 0x%llx\n", resp.value);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: BAR_TRAP read-back verified\n");
+ return 0;
+}
+
+static int test_notify(void)
+{
+ struct gsp_rpc_notify_request req = {0};
+ struct gsp_rpc_notify_response resp = {0};
+ int ret;
+
+ pr_info("GSP_EMU_TEST: Testing NOTIFY...\n");
+
+ req.header.type = GSP_RPC_NOTIFY;
+ req.header.seq_id = get_next_seq_id();
+ req.event_type = GSP_NOTIFY_EVENT_COMPLETION;
+ req.flags = GSP_NOTIFY_FLAG_ENABLE;
+
+ ret = gsp_rpc_notify_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: NOTIFY register failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (resp.status != 0) {
+ pr_err("GSP_EMU_TEST: NOTIFY register returned status=%d\n", resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: NOTIFY register succeeded\n");
+
+ req.header.seq_id = get_next_seq_id();
+ req.flags = GSP_NOTIFY_FLAG_DISABLE;
+
+ ret = gsp_rpc_notify_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: NOTIFY unregister failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (resp.status != 0) {
+ pr_err("GSP_EMU_TEST: NOTIFY unregister returned status=%d\n", resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: NOTIFY unregister succeeded\n");
+ return 0;
+}
+
+static int test_migrate(void)
+{
+ struct gsp_rpc_migrate_request req = {0};
+ struct gsp_rpc_migrate_response resp = {0};
+ int ret;
+
+ pr_info("GSP_EMU_TEST: Testing MIGRATE...\n");
+
+ req.header.type = GSP_RPC_MIGRATE;
+ req.header.seq_id = get_next_seq_id();
+ req.phase = GSP_MIGRATE_QUERY;
+ req.flags = 0;
+ req.data = 0;
+ req.data_size = 0;
+
+ ret = gsp_rpc_migrate_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: MIGRATE QUERY failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (resp.status != 0) {
+ pr_err("GSP_EMU_TEST: MIGRATE QUERY returned status=%d\n", resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: MIGRATE QUERY succeeded\n");
+
+ req.header.seq_id = get_next_seq_id();
+ req.phase = GSP_MIGRATE_PREPARE;
+
+ ret = gsp_rpc_migrate_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: MIGRATE PREPARE failed with ret=%d\n", ret);
+ return -1;
+ }
+
+ req.header.seq_id = get_next_seq_id();
+ req.phase = GSP_MIGRATE_TRANSFER;
+
+ ret = gsp_rpc_migrate_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: MIGRATE TRANSFER failed with ret=%d\n", ret);
+ return -1;
+ }
+
+ req.header.seq_id = get_next_seq_id();
+ req.phase = GSP_MIGRATE_COMMIT;
+
+ ret = gsp_rpc_migrate_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: MIGRATE COMMIT failed with ret=%d\n", ret);
+ return -1;
+ }
+
+ req.header.seq_id = get_next_seq_id();
+ req.phase = GSP_MIGRATE_CLEANUP;
+
+ ret = gsp_rpc_migrate_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: MIGRATE CLEANUP failed with ret=%d\n", ret);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: MIGRATE 4-phase completed\n");
+ return 0;
+}
+
+static int test_debug(void)
+{
+ struct gsp_rpc_debug_request req = {0};
+ struct gsp_rpc_debug_response resp = {0};
+ int ret;
+
+ pr_info("GSP_EMU_TEST: Testing DEBUG...\n");
+
+ req.header.type = GSP_RPC_DEBUG;
+ req.header.seq_id = get_next_seq_id();
+ req.sub_command = GSP_DEBUG_GET_VERSION;
+ req.data = 0;
+ req.data_size = 0;
+
+ ret = gsp_rpc_debug_emulation(test_client, &req, &resp);
+ if (ret != 0) {
+ pr_err("GSP_EMU_TEST: DEBUG failed with ret=%d\n", ret);
+ return -1;
+ }
+ if (resp.status != 0) {
+ pr_err("GSP_EMU_TEST: DEBUG returned status=%d\n", resp.status);
+ return -1;
+ }
+
+ pr_info("GSP_EMU_TEST: DEBUG succeeded, version=0x%llx\n", resp.data);
+ return 0;
+}
+
+static int test_concurrent_rpcs(void)
+{
+ struct gsp_rpc_alloc_request req = {0};
+ struct gsp_rpc_alloc_response resp = {0};
+ int ret, passed = 0, failed = 0;
+
+ pr_info("GSP_EMU_TEST: Testing concurrent RPCs...\n");
+
+ for (int i = 0; i < 10; i++) {
+ req.header.type = GSP_RPC_ALLOC;
+ req.header.seq_id = get_next_seq_id();
+ req.size = 4096;
+ req.alignment = PAGE_SIZE;
+ req.flags = GSP_ALLOC_FLAG_VRAM;
+
+ ret = gsp_rpc_alloc_emulation(test_client, &req, &resp);
+ if (ret == 0 && resp.status == 0 && resp.handle != 0) {
+ passed++;
+ struct gsp_rpc_free_request free_req = {0};
+ struct gsp_rpc_free_response free_resp = {0};
+ free_req.header.type = GSP_RPC_FREE;
+ free_req.header.seq_id = get_next_seq_id();
+ free_req.handle = resp.handle;
+ gsp_rpc_free_emulation(test_client, &free_req, &free_resp);
+ } else {
+ failed++;
+ }
+ }
+
+ pr_info("GSP_EMU_TEST: Concurrent test: %d passed, %d failed\n", passed, failed);
+ return (failed == 0) ? 0 : -1;
+}
+
+static int test_error_handling(void)
+{
+ struct gsp_rpc_alloc_request req = {0};
+ struct gsp_rpc_alloc_response resp = {0};
+ int ret;
+
+ pr_info("GSP_EMU_TEST: Testing error handling...\n");
+
+ req.header.type = GSP_RPC_ALLOC;
+ req.header.seq_id = 0;
+ req.size = 4096;
+ req.alignment = PAGE_SIZE;
+ req.flags = GSP_ALLOC_FLAG_VRAM;
+
+ ret = gsp_rpc_alloc_emulation(test_client, &req, &resp);
+ if (ret == 0 && resp.status != 0) {
+ pr_info("GSP_EMU_TEST: Zero seq_id correctly rejected\n");
+ } else {
+ pr_err("GSP_EMU_TEST: Zero seq_id not handled correctly\n");
+ return -1;
+ }
+
+ req.header.seq_id = get_next_seq_id();
+ req.size = 0;
+
+ ret = gsp_rpc_alloc_emulation(test_client, &req, &resp);
+ if (ret == 0 && resp.status != 0) {
+ pr_info("GSP_EMU_TEST: Zero size correctly rejected\n");
+ } else {
+ pr_err("GSP_EMU_TEST: Zero size not handled correctly\n");
+ return -1;
+ }
+
+ req.header.seq_id = get_next_seq_id();
+ req.size = 4096;
+ req.alignment = 123;
+
+ ret = gsp_rpc_alloc_emulation(test_client, &req, &resp);
+ if (ret == 0 && resp.status != 0) {
+ pr_info("GSP_EMU_TEST: Invalid alignment correctly rejected\n");
+ } else {
+ pr_err("GSP_EMU_TEST: Invalid alignment not handled correctly\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int run_all_tests(void)
+{
+ int failed = 0;
+
+ init_test_context();
+ if (!test_ctx || !test_client) {
+ pr_err("GSP_EMU_TEST: Failed to initialize test context\n");
+ return -1;
+ }
+
+ if (test_alloc_free() != 0) failed++;
+ if (test_map_unmap() != 0) failed++;
+ if (test_pin_unpin() != 0) failed++;
+ if (test_schedule_yield() != 0) failed++;
+ if (test_bar_trap() != 0) failed++;
+ if (test_notify() != 0) failed++;
+ if (test_migrate() != 0) failed++;
+ if (test_debug() != 0) failed++;
+ if (test_concurrent_rpcs() != 0) failed++;
+ if (test_error_handling() != 0) failed++;
+
+ cleanup_test_context();
+
+ if (failed == 0) {
+ pr_info("GSP_EMU_TEST: ALL TESTS PASSED\n");
+ return 0;
+ } else {
+ pr_err("GSP_EMU_TEST: %d TEST(S) FAILED\n", failed);
+ 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); \ No newline at end of file
diff --git a/kernel/version/version.c b/kernel/version/version.c
new file mode 100644
index 0000000..745be97
--- /dev/null
+++ b/kernel/version/version.c
@@ -0,0 +1,137 @@
+/*
+ * Driver Version Adaptation Layer
+ * Handles interface differences between NVIDIA driver versions
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/version.h>
+
+#include "version.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Open-IOV");
+MODULE_DESCRIPTION("Driver Version Adaptation Layer");
+
+/* Version 550 interface */
+static struct driver_interface interface_550 = {
+ .version = "550",
+ .major = 550,
+ .minor = 0,
+ .has_vgpu_vfio = true,
+ .has_migration = true,
+ .has_scheduling = true,
+ .has_notifications = true,
+ .has_wpr = true,
+ .vgpu_vfio_ops_size = sizeof(struct nvidia_vgpu_vfio_ops),
+ .mdev_parent_ops_size = sizeof(struct nv_vgpu_mdev_parent_ops),
+};
+
+/* Version 560 interface */
+static struct driver_interface interface_560 = {
+ .version = "560",
+ .major = 560,
+ .minor = 0,
+ .has_vgpu_vfio = true,
+ .has_migration = true,
+ .has_scheduling = true,
+ .has_notifications = true,
+ .has_wpr = true,
+ .vgpu_vfio_ops_size = sizeof(struct nvidia_vgpu_vfio_ops),
+ .mdev_parent_ops_size = sizeof(struct nv_vgpu_mdev_parent_ops),
+};
+
+/* Version 570 interface */
+static struct driver_interface interface_570 = {
+ .version = "570",
+ .major = 570,
+ .minor = 0,
+ .has_vgpu_vfio = true,
+ .has_migration = true,
+ .has_scheduling = true,
+ .has_notifications = true,
+ .has_wpr = true,
+ .vgpu_vfio_ops_size = sizeof(struct nvidia_vgpu_vfio_ops),
+ .mdev_parent_ops_size = sizeof(struct nv_vgpu_mdev_parent_ops),
+};
+
+/* Version 580 interface */
+static struct driver_interface interface_580 = {
+ .version = "580",
+ .major = 580,
+ .minor = 0,
+ .has_vgpu_vfio = true,
+ .has_migration = true,
+ .has_scheduling = true,
+ .has_notifications = true,
+ .has_wpr = true,
+ .vgpu_vfio_ops_size = sizeof(struct nvidia_vgpu_vfio_ops),
+ .mdev_parent_ops_size = sizeof(struct nv_vgpu_mdev_parent_ops),
+};
+
+static struct driver_interface *interfaces[] = {
+ &interface_550,
+ &interface_560,
+ &interface_570,
+ &interface_580,
+ NULL
+};
+
+struct driver_interface *version_get_interface(const char *version_str)
+{
+ int major = 0, minor = 0;
+
+ if (version_str) {
+ sscanf(version_str, "%d.%d", &major, &minor);
+ }
+
+ for (int i = 0; interfaces[i]; i++) {
+ if (interfaces[i]->major == major)
+ return interfaces[i];
+ }
+
+ /* Default to latest known */
+ return &interface_570;
+}
+
+struct driver_interface *version_get_by_kernel_version(void)
+{
+ /* Try to detect from kernel version or module version */
+ return &interface_570;
+}
+
+bool version_has_feature(struct driver_interface *iface, enum driver_feature feature)
+{
+ if (!iface)
+ return false;
+
+ switch (feature) {
+ case DRV_FEAT_VGPU_VFIO:
+ return iface->has_vgpu_vfio;
+ case DRV_FEAT_MIGRATION:
+ return iface->has_migration;
+ case DRV_FEAT_SCHEDULING:
+ return iface->has_scheduling;
+ case DRV_FEAT_NOTIFICATIONS:
+ return iface->has_notifications;
+ case DRV_FEAT_WPR:
+ return iface->has_wpr;
+ default:
+ return false;
+ }
+}
+
+const char *version_get_string(struct driver_interface *iface)
+{
+ return iface ? iface->version : "unknown";
+}
+
+EXPORT_SYMBOL_GPL(version_get_interface);
+EXPORT_SYMBOL_GPL(version_get_by_kernel_version);
+EXPORT_SYMBOL_GPL(version_has_feature);
+EXPORT_SYMBOL_GPL(version_get_string);
+
+MODULE_DESCRIPTION("Driver Version Adaptation Layer");
+MODULE_LICENSE("GPL"); \ No newline at end of file
diff --git a/kernel/version/version.h b/kernel/version/version.h
new file mode 100644
index 0000000..b320dbf
--- /dev/null
+++ b/kernel/version/version.h
@@ -0,0 +1,37 @@
+#ifndef _VERSION_H_
+#define _VERSION_H_
+
+#include <linux/types.h>
+
+/* Driver features */
+enum driver_feature {
+ DRV_FEAT_VGPU_VFIO,
+ DRV_FEAT_MIGRATION,
+ DRV_FEAT_SCHEDULING,
+ DRV_FEAT_NOTIFICATIONS,
+ DRV_FEAT_WPR,
+};
+
+/* Driver interface definition */
+struct driver_interface {
+ const char *version;
+ int major;
+ int minor;
+ bool has_vgpu_vfio;
+ bool has_migration;
+ bool has_scheduling;
+ bool has_notifications;
+ bool has_wpr;
+ size_t vgpu_vfio_ops_size;
+ size_t mdev_parent_ops_size;
+};
+
+/* Version registry */
+struct driver_interface *version_get_interface(const char *version_str);
+struct driver_interface *version_get_by_kernel_version(void);
+
+/* Feature queries */
+bool version_has_feature(struct driver_interface *iface, enum driver_feature feature);
+const char *version_get_string(struct driver_interface *iface);
+
+#endif /* _VERSION_H_ */ \ No newline at end of file