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