diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-13 19:28:03 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-13 19:28:03 -0400 |
| commit | bfd269ee74d166be4f386640f8b5daad31d5d6ed (patch) | |
| tree | 82da0efeeea759d44605ff69b0f20c8f248623e2 /kernel | |
| parent | 7adef330df18691acabda9f5c988742f23a2917d (diff) | |
| download | open-iov-bfd269ee74d166be4f386640f8b5daad31d5d6ed.tar.gz open-iov-bfd269ee74d166be4f386640f8b5daad31d5d6ed.zip | |
Phase 4: Portable Architecture Implementation
- Firmware Analyzer: Runtime firmware signature computation (CRC32, SHA256) and pattern-based RPC handler address discovery
- Dynamic RPC Dispatcher: Version-aware dispatch with native firmware handlers (550/560/570/580) and emulation fallback
- Hardware Architecture Layer: Turing, Ampere, Blackwell configs with BAR layouts, doorbell offsets, MSI-X vectors
- Driver Version Adaptation Layer: Feature detection per driver version
- Declarative YAML configs for hardware (turing/ampere/blackwell) and versions (550/560/570/580)
- Kernel unit tests for GSP emulation (gsp_emulation_test.c)
- Test infrastructure scripts (test_gsp_emu.sh, test_guest_driver.sh, test_cuda_workloads.sh, test_graphics_workloads.sh, test_windows_guest.sh, test_vgpu_stack.sh)
- Updated kernel Makefile with new source files
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/Makefile | 8 | ||||
| -rw-r--r-- | kernel/architecture/architecture.c | 176 | ||||
| -rw-r--r-- | kernel/architecture/architecture.h | 49 | ||||
| -rw-r--r-- | kernel/dynamic_rpc_dispatcher.c | 462 | ||||
| -rw-r--r-- | kernel/dynamic_rpc_dispatcher.h | 66 | ||||
| -rw-r--r-- | kernel/firmware_analyzer.c | 224 | ||||
| -rw-r--r-- | kernel/firmware_analyzer.h | 36 | ||||
| -rw-r--r-- | kernel/tests/Makefile | 36 | ||||
| -rw-r--r-- | kernel/tests/gsp_emulation_test.c | 705 | ||||
| -rw-r--r-- | kernel/version/version.c | 137 | ||||
| -rw-r--r-- | kernel/version/version.h | 37 |
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 |
