/* * Firmware Analyzer - Runtime GSP firmware analysis * Auto-discovers RPC handler addresses, struct layouts, and firmware signatures */ #include #include #include #include #include #include #include #include #include #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");