diff options
| author | Open-IOV <dev@open-iov.org> | 2026-09-23 05:44:08 -0400 |
|---|---|---|
| committer | Open-IOV <dev@open-iov.org> | 2026-09-23 05:46:43 -0400 |
| commit | 2302fc09080bad51c73f293294e19fd4db464eb0 (patch) | |
| tree | c722d186347fdef5a34131e9e143b1549b1daf8e /kernel/firmware_analyzer.c | |
| download | open-iov-2302fc09080bad51c73f293294e19fd4db464eb0.tar.gz open-iov-2302fc09080bad51c73f293294e19fd4db464eb0.zip | |
Open-IOV: initial public release
Open-IOV virtualizes NVIDIA GPUs so that unmodified guest drivers run with
real GPU functionality - CUDA first, 3D and video to follow - on unmodified
hosts, without a proprietary vGPU license and without patching any driver,
firmware or hypervisor.
Core principle: emulate the interface, pass through the work.
kernel/ open-iov-vfio.ko - host VFIO mdev driver: virtual GPU device
(PCI config space, BAR shadows, MSI-X), GSP boot emulation,
GSP-RM message-queue/RPC emulation, memory model (GPU-VA to
guest-PA discovery) and the semaphore-completion service that
advances guest UVM progress.
userspace/ libiov.so management API and examples.
tools/ guest diagnostics, firmware analysis wrappers, RM probes.
scripts/ stack manager, VM harness, build/load/test automation.
config/ hardware/version profiles (the portability surface).
docs/ documentation tree - start at docs/README.md.
wiki/ long-form firmware and protocol deep dives.
Status: initialization works end to end for a stock 570.144 Linux guest on
Turing (TU104): driver probe, nvidia-smi, the full GSP-RM control battery,
object creation and the early CUDA/UVM start-up path. GPU work forwarding -
the step that makes CUDA kernels execute on real hardware - is the active
work item; see docs/04-implementation/01-known-knowns-and-unknowns.md.
License: BSD-3-Clause for the project; GPL-2.0 for kernel modules that link
GPL-only kernel interfaces. See LICENSE and docs/09-reference/legal-and-licensing.md.
Diffstat (limited to 'kernel/firmware_analyzer.c')
| -rw-r--r-- | kernel/firmware_analyzer.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/kernel/firmware_analyzer.c b/kernel/firmware_analyzer.c new file mode 100644 index 0000000..2d8d84d --- /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 <crypto/hash.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 |
