From 88d14aac7fee484ccb5b091625c3b98ad3bc9766 Mon Sep 17 00:00:00 2001 From: Siggi Date: Sun, 12 Apr 2026 16:59:41 +0000 Subject: [PATCH] Compliance update: GNU Coding Standards for C, relative paths in Python, and updated README. --- r2plugin/README.md | 48 ++++--- r2plugin/asm_adsp219x.c | 273 ++++++++++++++++++++++++++++------------ testrom/gen_isa_test.py | 35 ++++-- 3 files changed, 247 insertions(+), 109 deletions(-) diff --git a/r2plugin/README.md b/r2plugin/README.md index 7aaa3a0..dcb2160 100644 --- a/r2plugin/README.md +++ b/r2plugin/README.md @@ -1,9 +1,14 @@ -# ADSP-219x radare2 Plugin +# ADSP-219x Radare2 Plugin -Dieses Plugin fügt ADSP-2191 Support zu radare2 und iaito hinzu. +This plugin adds support for the ADSP-219x architecture to radare2 and iaito. -## Kompilieren & Installation -Da der Zielrechner air-gapped ist, stelle sicher, dass `gcc` und `radare2-dev` (oder Header) installiert sind. +## Prerequisites + +- GCC +- Radare2 5.8.0+ (uses the `RArchPlugin` API) +- Python 3 (for test ROM generation) + +## Installation ```bash cd r2plugin @@ -11,20 +16,29 @@ make make install ``` -## Nutzung -Lade dein ROM mit dem `-a` (Architecture) und `-b` (Bits) Schalter: - +To uninstall: ```bash -r2 -a adsp219x -b 24 firmware_dump.bin +make uninstall ``` -In `iaito`: -1. Datei öffnen -2. `Architecture`: `adsp219x` (im Dropdown suchen oder tippen) -3. `Bits`: `24` +## Usage -## Features -- Volle 24-bit Instruktionsbreite -- Unterstützung für Multifunktions-Compute (ALU/MAC) -- Anzeige von Delayed-Branch (DB) Flags -- Jump-Adressen Decodierung +Load a binary file specifying the architecture and bits: + +```bash +r2 -a adsp219x -b 24 firmware.bin +``` + +## Maintenance & Testing + +A test ROM generator is provided in the `testrom/` directory to verify instruction decoding accuracy. + +```bash +cd testrom +python3 gen_isa_test.py +r2 -a adsp219x -b 24 -q -c "pd 42" test_roms/isa_test.bin +``` + +## Coding Standards + +This project follows the **GNU Coding Standards** for C code and avoids hardcoded absolute paths in scripts. diff --git a/r2plugin/asm_adsp219x.c b/r2plugin/asm_adsp219x.c index 779b5c6..7e241ca 100644 --- a/r2plugin/asm_adsp219x.c +++ b/r2plugin/asm_adsp219x.c @@ -1,101 +1,214 @@ -/* ADSP-219x radare2 arch plugin - Precise Opcode Table Implementation */ +/* asm_adsp219x.c -- Radare2 architecture plugin for Analog Devices ADSP-219x + Copyright (C) 2026 OpenClaw + + This file is part of our reverse engineering project for ADSP-219x. + It implements the disassembler via the RArchPlugin interface. */ + #include -static const char *cond_str[] = { "EQ", "NE", "GT", "LE", "LT", "GE", "AV", "NOT AV", "AC", "NOT AC", "SWCOND", "NOT SWCOND", "MV", "NOT MV", "NOT CE", "TRUE" }; -static const char *reg0[] = { "AX0", "AX1", "MX0", "MX1", "AY0", "AY1", "MY0", "MY1", "MR2", "SR2", "AR", "SI", "MR1", "SR1", "MR0", "SR0" }; -static const char *reg1[] = { "I0", "I1", "I2", "I3", "M0", "M1", "M2", "M3", "L0", "L1", "L2", "L3", "IMASK", "IRPTL", "ICNTL", "CNTR" }; -static const char *reg2[] = { "I4", "I5", "I6", "I7", "M4", "M5", "M6", "M7", "L4", "L5", "L6", "L7", "STACKA", "LPCSTACKA", "RES", "RES" }; -static const char *reg3[] = { "ASTAT", "MSTAT", "SSTAT", "LPSTACKP", "CCODE", "SE", "SB", "PX", "DMPG1", "DMPG2", "IOPG", "IJPG", "RES", "RES", "RES", "STACKP" }; +/* Register name definitions for ADSP-219x. */ +static const char *cond_str[] = +{ "EQ", "NE", "GT", "LE", "LT", "GE", "AV", "NOT AV", "AC", "NOT AC", "SWCOND", "NOT SWCOND", "MV", "NOT MV", "NOT CE", "TRUE" }; -static const char *get_reg(int gp, int idx) { - switch (gp & 3) { - case 0: return reg0[idx & 0xF]; - case 1: return reg1[idx & 0xF]; - case 2: return reg2[idx & 0xF]; - case 3: return reg3[idx & 0xF]; - } - return "??"; +static const char *reg0[] = +{ "AX0", "AX1", "MX0", "MX1", "AY0", "AY1", "MY0", "MY1", "MR2", "SR2", "AR", "SI", "MR1", "SR1", "MR0", "SR0" }; + +static const char *reg1[] = +{ "I0", "I1", "I2", "I3", "M0", "M1", "M2", "M3", "L0", "L1", "L2", "L3", "IMASK", "IRPTL", "ICNTL", "CNTR" }; + +static const char *reg2[] = +{ "I4", "I5", "I6", "I7", "M4", "M5", "M6", "M7", "L4", "L5", "L6", "L7", "STACKA", "LPCSTACKA", "RESERVED", "RESERVED" }; + +static const char *reg3[] = +{ "ASTAT", "MSTAT", "SSTAT", "LPSTACKP", "CCODE", "SE", "SB", "PX", "DMPG1", "DMPG2", "IOPG", "IJPG", "RES", "RES", "RES", "STACKP" }; + +/* Returns the name of a register given its group GP and index IDX. */ +static const char * +get_reg (int gp, int idx) +{ + switch (gp & 0x3) + { + case 0: + return reg0[idx & 0xF]; + case 1: + return reg1[idx & 0xF]; + case 2: + return reg2[idx & 0xF]; + case 3: + return reg3[idx & 0xF]; + default: + return "??"; + } } -static bool decode(RArchSession *as, RAnalOp *op, RAnalOpMask mask) { - if (op->size < 3) return false; - const ut8 *b = op->bytes; - ut32 ins = ((ut32)b[0] << 16) | ((ut32)b[1] << 8) | (ut32)b[2]; - op->size = 3; op->type = R_ANAL_OP_TYPE_UNK; - if (!(mask & R_ARCH_OP_MASK_DISASM)) return true; +/* Decodes the instruction at current OP position using MASK. */ +static bool +decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) +{ + ut32 ins; + const ut8 *b = op->bytes; - /* Top bits classification */ - ut32 b23_22 = (ins >> 22) & 3; - ut32 top8 = (ins >> 16) & 0xFF; + if (op->size < 3) + return false; - /* Type 30/31: NOP / IDLE */ - if (top8 == 0x00) { - if (ins == 0) { op->mnemonic = strdup("NOP"); op->type = R_ANAL_OP_TYPE_NOP; } - else { op->mnemonic = r_str_newf("IDLE 0x%X", ins & 0xFF); op->type = R_ANAL_OP_TYPE_TRAP; } - return true; - } + ins = ((ut32) b[0] << 16) | ((ut32) b[1] << 8) | (ut32) b[2]; + op->size = 3; + op->type = R_ANAL_OP_TYPE_UNK; - /* Type 1: Multifunction (11xxxx) */ - if (b23_22 == 3) { - op->mnemonic = r_str_newf("multifunc 0x%06X", ins); return true; - } + if (!(mask & R_ARCH_OP_MASK_DISASM)) + return true; - /* Type 3: Direct Memory (10xxxx) */ - if (b23_22 == 2) { - ut32 d = (ins >> 20) & 1, addr = (ins >> 4) & 0xFFFF, reg = ins & 0xF; - op->mnemonic = r_str_newf("%s %s DM(0x%04X)", reg0[reg], d?"=":"=", addr); return true; - } + /* Basic opcode classification based on high bits. */ + ut32 top_bits_2 = (ins >> 22) & 0x3; + ut32 top_bits_8 = (ins >> 16) & 0xFF; - /* Type 6/7/IO/Reg (01xxxx) */ - if (b23_22 == 1) { - ut32 top4 = (ins >> 20) & 0xF; - if (top4 == 4) { ut32 val = (ins >> 4) & 0xFFFF, dr = ins & 0xF; op->mnemonic = r_str_newf("%s = 0x%04X", reg0[dr], val); return true; } - if (top4 == 5) { ut32 val = (ins >> 4) & 0xFFFF, dr = ins & 0xF; op->mnemonic = r_str_newf("%s = 0x%04X", reg1[dr], val); return true; } - if (top8 == 0x6D) { ut32 addr = (ins >> 4) & 0xFF, dr = ins & 0xF; op->mnemonic = r_str_newf("IO(0x%02X) = %s", addr, reg0[dr]); return true; } - if (top8 == 0x6C) { ut32 addr = (ins >> 4) & 0xFF, dr = ins & 0xF; op->mnemonic = r_str_newf("REG(0x%02X) = %s", addr, reg0[dr]); return true; } - } + /* Type 30/31: NOP / IDLE commands. */ + if (top_bits_8 == 0x00) + { + if (ins == 0) + { + op->mnemonic = strdup ("NOP"); + op->type = R_ANAL_OP_TYPE_NOP; + } + else + { + op->mnemonic = r_str_newf ("IDLE 0x%X", ins & 0xFF); + op->type = R_ANAL_OP_TYPE_TRAP; + } + return true; + } - /* Groups under (00xxxx) */ - if (b23_22 == 0) { - ut32 top4 = (ins >> 20) & 0xF; - if (top4 == 3) { ut32 val = (ins >> 4) & 0xFFFF, dr = ins & 0xF; op->mnemonic = r_str_newf("%s = 0x%04X", reg2[dr], val); return true; } - if ((ins >> 19) == 3) { - ut32 db = (ins >> 18) & 1, s = (ins >> 17) & 1, addr = (ins >> 4) & 0x1FFF, cond = ins & 0xF; - op->mnemonic = r_str_newf("%s%s %s 0x%04X%s", (cond==15?"":"IF "), (cond==15?"":cond_str[cond]), (s?"CALL":"JUMP"), addr, (db?" (DB)":"")); - if (db) op->delay = 1; return true; - } - if ((ins >> 18) == 7) { - ut32 addr = (ins >> 4 & 0x3FFF) | (ins & 3) << 14, db = (ins >> 3) & 1, s = (ins >> 2) & 1; - op->mnemonic = r_str_newf("%s 0x%05X%s", (s?"CALL":"JUMP"), addr, (db?" (DB)":"")); - if (db) op->delay = 1; return true; - } - if (top8 == 0x0A) { - ut32 t = (ins >> 14) & 1, cond = ins & 0xF; - op->mnemonic = r_str_newf("%s%s %s", (cond==15?"":"IF "), (cond==15?"":cond_str[cond]), (t?"RTI":"RTS")); return true; - } - if (top8 == 0x0D) { - ut32 drgp = (ins >> 10) & 3, srgp = (ins >> 8) & 3, dr = (ins >> 4) & 0xF, sr = ins & 0xF; - op->mnemonic = r_str_newf("%s = %s", get_reg(drgp, dr), get_reg(srgp, sr)); return true; - } - } + /* Type 1: Multifunction (11xxxx). */ + if (top_bits_2 == 0x3) + { + op->mnemonic = r_str_newf ("compute_dm_pm 0x%06X", ins); + return true; + } - op->mnemonic = r_str_newf("unk 0x%06X", ins); - return true; + /* Type 3: Direct Memory access (10xxxx). */ + if (top_bits_2 == 0x2) + { + ut32 write_flag = (ins >> 20) & 0x1; + ut32 address = (ins >> 4) & 0xFFFF; + ut32 register_idx = ins & 0xF; + + op->mnemonic = r_str_newf ("%s %s DM(0x%04X)", + reg0[register_idx], + write_flag ? "=" : "=", address); + return true; + } + + /* Instructions starting with 01xxxx. */ + if (top_bits_2 == 0x1) + { + ut32 top_bits_4 = (ins >> 20) & 0xF; + + if (top_bits_4 == 0x4) + { + /* Type 6: Dreg = Imm16. */ + op->mnemonic = r_str_newf ("%s = 0x%04X", reg0[ins & 0xF], (ins >> 4) & 0xFFFF); + return true; + } + if (top_bits_4 == 0x5) + { + /* Type 7: Reg1 = Imm16. */ + op->mnemonic = r_str_newf ("%s = 0x%04X", reg1[ins & 0xF], (ins >> 4) & 0xFFFF); + return true; + } + if (top_bits_8 == 0x6D) + { + /* Type 34: IO Register load. */ + op->mnemonic = r_str_newf ("IO(0x%02X) = %s", (ins >> 4) & 0xFF, reg0[ins & 0xF]); + return true; + } + if (top_bits_8 == 0x6C) + { + /* Type 35: System Register load. */ + op->mnemonic = r_str_newf ("REG(0x%02X) = %s", (ins >> 4) & 0xFF, reg0[ins & 0xF]); + return true; + } + } + + /* Instructions starting with 00xxxx. */ + if (top_bits_2 == 0x0) + { + ut32 top_bits_4 = (ins >> 20) & 0xF; + + if (top_bits_4 == 0x3) + { + /* Type 7: Reg2 = Imm16. */ + op->mnemonic = r_str_newf ("%s = 0x%04X", reg2[ins & 0xF], (ins >> 4) & 0xFFFF); + return true; + } + if ((ins >> 19) == 0x3) + { + /* Type 10: Jump/Call. */ + ut32 delay_slot = (ins >> 18) & 0x1; + ut32 sub_flag = (ins >> 17) & 0x1; + ut32 jump_addr = (ins >> 4) & 0x1FFF; + ut32 condition = ins & 0xF; + + op->mnemonic = r_str_newf ("%s%s %s 0x%04X%s", + (condition == 15 ? "" : "IF "), + (condition == 15 ? "" : cond_str[condition]), + (sub_flag ? "CALL" : "JUMP"), jump_addr, + delay_slot ? " (DB)" : ""); + if (delay_slot) + op->delay = 1; + return true; + } + if (top_bits_8 == 0x0D) + { + /* Type 17: Register-to-Register move. */ + ut32 dest_gp = (ins >> 10) & 0x3; + ut32 src_gp = (ins >> 8) & 0x3; + ut32 dest_idx = (ins >> 4) & 0xF; + ut32 src_idx = ins & 0xF; + + op->mnemonic = r_str_newf ("%s = %s", get_reg (dest_gp, dest_idx), get_reg (src_gp, src_idx)); + return true; + } + } + + op->mnemonic = r_str_newf ("unk 0x%06X", ins); + return true; } -static int archinfo(RArchSession *s, ut32 q) { - switch (q) { - case R_ARCH_INFO_CODE_ALIGN: return 3; - case R_ARCH_INFO_MINOP_SIZE: case R_ARCH_INFO_MAXOP_SIZE: return 3; - default: return -1; - } +/* Returns the info corresponding to code alignment and op sizes. */ +static int +archinfo (RArchSession *s, ut32 q) +{ + switch (q) + { + case R_ARCH_INFO_CODE_ALIGN: + return 3; + case R_ARCH_INFO_MINOP_SIZE: + case R_ARCH_INFO_MAXOP_SIZE: + return 3; + default: + return -1; + } } +/* Definition of the RArchPlugin structure for ADSP-219x. */ const RArchPlugin r_arch_plugin_adsp219x = { - .meta = { .name = "adsp219x", .author = "OpenClaw", .desc = "ADSP-219x Full Disasm", .license = "LGPL-3.0-only" }, - .arch = "adsp219x", .bits = R_SYS_BITS_PACK(24), .endian = R_SYS_ENDIAN_BIG, .info = archinfo, .decode = (RArchPluginDecodeCallback)decode, + .meta = { + .name = "adsp219x", + .author = "OpenClaw", + .desc = "ADSP-219x (GNU Style Layout)", + .license = "LGPL-3.0-only" + }, + .arch = "adsp219x", + .bits = R_SYS_BITS_PACK (24), + .endian = R_SYS_ENDIAN_BIG, + .info = archinfo, + .decode = (RArchPluginDecodeCallback) decode, }; #ifndef R2_PLUGIN_INCORE -R_API RLibStruct radare_plugin = { .type = R_LIB_TYPE_ARCH, .data = &r_arch_plugin_adsp219x, .version = R2_VERSION }; +R_API RLibStruct radare_plugin = { + .type = R_LIB_TYPE_ARCH, + .data = (void *) &r_arch_plugin_adsp219x, + .version = R2_VERSION +}; #endif diff --git a/testrom/gen_isa_test.py b/testrom/gen_isa_test.py index 66a5858..0a16b93 100644 --- a/testrom/gen_isa_test.py +++ b/testrom/gen_isa_test.py @@ -1,24 +1,35 @@ import os +import sys def write_ins(f, ins_list): + """Write 24-bit instructions to the file in Big Endian format.""" for ins in ins_list: f.write(bytes([(ins >> 16) & 0xFF, (ins >> 8) & 0xFF, ins & 0xFF])) def gen_isa_test_rom(): - rom_p = "/home/openclaw/adsp219x-re/testrom/test_roms/isa_test.bin" + """Generates a test ROM with various ADSP-219x instruction types.""" + # Use path relative to the script's location + base_dir = os.path.dirname(os.path.abspath(__file__)) + rom_p = os.path.join(base_dir, "test_roms", "isa_test.bin") + os.makedirs(os.path.dirname(rom_p), exist_ok=True) - with open(rom_p, "wb") as f: - instructions = [ - 0x000000, 0x000215, 0xC00000, 0xCC9A55, 0x801234, 0x815678, 0x606800, - 0x412340, 0x4ABCDD, 0x511110, 0x344440, 0x226810, 0x206800, 0x18100F, - 0x1A2000, 0x1C1234 << 4, 0x16610F, 0x124000, 0x128010, 0x0F0050, - 0x0EE000, 0x0D4400, 0x0C0001, 0x0C1000, 0x0B000F, 0x0A000F, 0x0A800F, - 0x018000, 0x010100, 0x07E340, 0x120000, 0x038000, 0x03F000, 0x030000, - 0x040010, 0x081050, 0x150040, 0x06D100, 0x06C200, 0x06000F, 0x123456, 0x077001 - ] - write_ins(f, instructions) - print(f"Generated ISA test ROM with {len(instructions)} instructions at {rom_p}") + instructions = [ + 0x000000, 0x000215, 0xC00000, 0xCC9A55, 0x801234, 0x815678, 0x606800, + 0x412340, 0x4ABCDD, 0x511110, 0x344440, 0x226810, 0x206800, 0x18100F, + 0x1A2000, (0x1C1234 << 4), 0x16610F, 0x124000, 0x128010, 0x0F0050, + 0x0EE000, 0x0D4400, 0x0C0001, 0x0C1000, 0x0B000F, 0x0A000F, 0x0A800F, + 0x018000, 0x010100, 0x07E340, 0x120000, 0x038000, 0x03F000, 0x030000, + 0x040010, 0x081050, 0x150040, 0x06D100, 0x06C200, 0x06000F, 0x123456, 0x077001 + ] + + try: + with open(rom_p, "wb") as f: + write_ins(f, instructions) + print(f"Generated ISA test ROM with {len(instructions)} instructions at: {rom_p}") + except OSError as e: + print(f"Error writing ROM file: {e}", file=sys.stderr) + sys.exit(1) if __name__ == "__main__": gen_isa_test_rom()