Clarify coverage wording and add ROM analysis guide
This commit is contained in:
10
README.md
10
README.md
@@ -24,8 +24,8 @@ engineering ADSP-2191 firmware.
|
|||||||
|
|
||||||
## Plugin Status
|
## Plugin Status
|
||||||
|
|
||||||
The decoder now covers most ADSP-219x instruction types and has
|
The decoder implements all documented ADSP-219x instruction types and
|
||||||
assembler verification for 34 of 37 documented opcode families.
|
has assembler verification for 34 of 37 documented opcode families.
|
||||||
|
|
||||||
Assembler-verified coverage includes:
|
Assembler-verified coverage includes:
|
||||||
|
|
||||||
@@ -49,6 +49,12 @@ an open-source assembler/linker for ADSP-218x/219x. Build
|
|||||||
instructions are in the open21xx README. The resulting ELF is
|
instructions are in the open21xx README. The resulting ELF is
|
||||||
converted to raw binary with `dd` (extract the `int_pm` section).
|
converted to raw binary with `dd` (extract the `int_pm` section).
|
||||||
|
|
||||||
|
## Analysis Guides
|
||||||
|
|
||||||
|
- `docs/GETTING_STARTED.md` - setup, loading ROMs, and basic radare2 use
|
||||||
|
- `docs/LARGE_ROM_ANALYSIS_WORKFLOW.md` - step-by-step workflow for
|
||||||
|
analyzing large raw ADSP-219x ROM dumps
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Plugin code: LGPL-3.0-only. Example code from Analog Devices
|
Plugin code: LGPL-3.0-only. Example code from Analog Devices
|
||||||
|
|||||||
@@ -57,3 +57,11 @@ instruction set reference chapters (PDF and text extracts):
|
|||||||
- `9x_flowops.*` - Flow control (jumps, loops, returns)
|
- `9x_flowops.*` - Flow control (jumps, loops, returns)
|
||||||
- `9x_moveops.*` - Data move operations
|
- `9x_moveops.*` - Data move operations
|
||||||
- `9x_multiops.*` - Multifunction operations
|
- `9x_multiops.*` - Multifunction operations
|
||||||
|
|
||||||
|
## Large ROM Workflow
|
||||||
|
|
||||||
|
For a practical step-by-step workflow for analyzing a large raw
|
||||||
|
ADSP-219x ROM image, including how to separate likely code from likely
|
||||||
|
data and when to use graph analysis, see:
|
||||||
|
|
||||||
|
- `LARGE_ROM_ANALYSIS_WORKFLOW.md`
|
||||||
|
|||||||
524
docs/LARGE_ROM_ANALYSIS_WORKFLOW.md
Normal file
524
docs/LARGE_ROM_ANALYSIS_WORKFLOW.md
Normal file
@@ -0,0 +1,524 @@
|
|||||||
|
# Large ADSP-219x ROM Analysis Workflow
|
||||||
|
|
||||||
|
This guide describes a practical workflow for reverse engineering a
|
||||||
|
large raw ADSP-219x ROM image in radare2 using the `adsp219x`
|
||||||
|
architecture plugin from this repository.
|
||||||
|
|
||||||
|
It is written for the common case where you have a large blob, for
|
||||||
|
example a 1 MB ROM dump, and you do not yet know:
|
||||||
|
|
||||||
|
- where code starts and where data starts
|
||||||
|
- how many independent code regions exist
|
||||||
|
- where the main loops and dispatchers are
|
||||||
|
- which PM words are instructions and which are tables
|
||||||
|
|
||||||
|
The workflow below is intentionally conservative. For raw DSP ROMs,
|
||||||
|
manual validation beats aggressive auto-analysis.
|
||||||
|
|
||||||
|
## Assumptions
|
||||||
|
|
||||||
|
- The ROM is for an ADSP-219x family DSP.
|
||||||
|
- Instructions are 24-bit words.
|
||||||
|
- The dump is raw program memory, not a richly annotated executable.
|
||||||
|
- You already have a working radare2 installation and the
|
||||||
|
`adsp219x` plugin is installed or loadable with `-L`.
|
||||||
|
|
||||||
|
## Load The ROM
|
||||||
|
|
||||||
|
For an installed plugin:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
r2 -a adsp219x -b 24 firmware.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
For a local plugin build that is not installed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
r2 -a adsp219x -L ./r2plugin/asm_adsp219x.so -b 24 firmware.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
Why the flags matter:
|
||||||
|
|
||||||
|
- `-a adsp219x`: forces the custom architecture plugin
|
||||||
|
- `-b 24`: tells radare2 to treat the code as 24-bit ADSP-219x words
|
||||||
|
|
||||||
|
## ROM Format Sanity Check
|
||||||
|
|
||||||
|
Before analyzing flow, check whether the byte layout looks correct.
|
||||||
|
|
||||||
|
ADSP-219x code is usually stored either as:
|
||||||
|
|
||||||
|
- packed 24-bit words: `aa bb cc dd ee ff ...`
|
||||||
|
- padded 32-bit words with a leading zero byte
|
||||||
|
|
||||||
|
If your disassembly looks completely implausible, confirm the ROM
|
||||||
|
format first.
|
||||||
|
|
||||||
|
Helpful first commands:
|
||||||
|
|
||||||
|
```text
|
||||||
|
px 64
|
||||||
|
s 0
|
||||||
|
pd 16
|
||||||
|
```
|
||||||
|
|
||||||
|
If the first few decoded instructions look impossible but the hex dump
|
||||||
|
shows a repeating `00 xx xx xx` structure, you may be looking at a
|
||||||
|
32-bit padded dump and need to strip padding before analysis.
|
||||||
|
|
||||||
|
## High-Level Strategy
|
||||||
|
|
||||||
|
For a large ROM, do not start with `aaa`.
|
||||||
|
|
||||||
|
A better sequence is:
|
||||||
|
|
||||||
|
1. validate the entry region manually
|
||||||
|
2. mark only plausible functions
|
||||||
|
3. follow explicit control flow
|
||||||
|
4. distinguish code from tables
|
||||||
|
5. expand analysis gradually
|
||||||
|
6. use graph views only after local validation
|
||||||
|
|
||||||
|
`aaa` on a large raw DSP ROM often creates a false sense of structure
|
||||||
|
by treating valid-looking data words as code.
|
||||||
|
|
||||||
|
## Workflow Diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Load ROM in r2] --> B[Check raw bytes and first instructions]
|
||||||
|
B --> C{Plausible code at reset area?}
|
||||||
|
C -- no --> D[Verify ROM packing, offset, endianness, dump source]
|
||||||
|
D --> B
|
||||||
|
C -- yes --> E[Create first function manually]
|
||||||
|
E --> F[Inspect linear disassembly and graph]
|
||||||
|
F --> G{Looks like real control flow?}
|
||||||
|
G -- no --> H[Do not mark as function, treat as possible data]
|
||||||
|
G -- yes --> I[Follow jumps, calls, DO UNTIL loops]
|
||||||
|
I --> J[Name regions and comment findings]
|
||||||
|
J --> K[Search for more entry points and dispatchers]
|
||||||
|
K --> L[Only then expand with broader analysis]
|
||||||
|
L --> M[Separate code islands from PM data tables]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 1: Inspect The Reset Region Manually
|
||||||
|
|
||||||
|
Start at address zero unless you have evidence of a different boot
|
||||||
|
mapping.
|
||||||
|
|
||||||
|
```text
|
||||||
|
s 0
|
||||||
|
pd 20
|
||||||
|
pd 64
|
||||||
|
```
|
||||||
|
|
||||||
|
Look for signs of real code:
|
||||||
|
|
||||||
|
- immediate register loads
|
||||||
|
- `JUMP` or `CALL`
|
||||||
|
- `DO ... UNTIL`
|
||||||
|
- initialization of `I`, `M`, `L`, `CNTR`, status registers
|
||||||
|
- branches to other regions
|
||||||
|
|
||||||
|
Red flags that suggest you are not in code:
|
||||||
|
|
||||||
|
- long stretches of decodeable but nonsensical instructions
|
||||||
|
- no branches, no calls, no returns
|
||||||
|
- bizarre register moves with no purpose
|
||||||
|
- disassembly that looks random but hex bytes look regular
|
||||||
|
|
||||||
|
## Step 2: Mark The First Function Manually
|
||||||
|
|
||||||
|
Once the entry region looks plausible:
|
||||||
|
|
||||||
|
```text
|
||||||
|
s 0
|
||||||
|
af
|
||||||
|
pdf
|
||||||
|
agf
|
||||||
|
```
|
||||||
|
|
||||||
|
Meaning:
|
||||||
|
|
||||||
|
- `af`: define a function at the current address
|
||||||
|
- `pdf`: print the current function linearly
|
||||||
|
- `agf`: show the control-flow graph of the current function
|
||||||
|
|
||||||
|
This is a better first move than `aaa`, because it forces you to
|
||||||
|
validate one region before scaling out.
|
||||||
|
|
||||||
|
## Step 3: Use Graphs Locally, Not Globally
|
||||||
|
|
||||||
|
For a large ROM, graphs are most useful once you already believe the
|
||||||
|
current region is code.
|
||||||
|
|
||||||
|
Useful commands:
|
||||||
|
|
||||||
|
```text
|
||||||
|
agf
|
||||||
|
VV
|
||||||
|
afb
|
||||||
|
```
|
||||||
|
|
||||||
|
- `agf`: static graph of current function
|
||||||
|
- `VV`: visual graph mode
|
||||||
|
- `afb`: list/show basic blocks
|
||||||
|
|
||||||
|
If `agf` produces a clean graph with obvious branches and loop edges,
|
||||||
|
the region is probably code.
|
||||||
|
|
||||||
|
If `agf` is trivial, chaotic, or makes no semantic sense, you may be
|
||||||
|
looking at data or a bad function boundary.
|
||||||
|
|
||||||
|
## Step 4: Expand By Following Explicit Flow
|
||||||
|
|
||||||
|
After validating the first function, expand manually through explicit
|
||||||
|
targets:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pdf
|
||||||
|
axt
|
||||||
|
afl
|
||||||
|
```
|
||||||
|
|
||||||
|
Then jump to interesting destinations:
|
||||||
|
|
||||||
|
```text
|
||||||
|
s <target>
|
||||||
|
af
|
||||||
|
pdf
|
||||||
|
agf
|
||||||
|
```
|
||||||
|
|
||||||
|
Prioritize:
|
||||||
|
|
||||||
|
- call targets
|
||||||
|
- jump targets
|
||||||
|
- loop bodies
|
||||||
|
- indirect branch setup code
|
||||||
|
|
||||||
|
For ADSP-219x specifically, also watch for:
|
||||||
|
|
||||||
|
- `DO ... UNTIL`
|
||||||
|
- tight MAC loops
|
||||||
|
- register setup for DAGs
|
||||||
|
- memory access kernels
|
||||||
|
|
||||||
|
## Step 5: Search For ADSP-219x-Specific Patterns
|
||||||
|
|
||||||
|
Search directly for common control-flow patterns:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/a JUMP
|
||||||
|
/a CALL
|
||||||
|
/a DO
|
||||||
|
/a RTS
|
||||||
|
/a RTI
|
||||||
|
```
|
||||||
|
|
||||||
|
Also inspect likely DSP kernel patterns:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/a MR
|
||||||
|
/a MX0
|
||||||
|
/a MY0
|
||||||
|
/a IO(
|
||||||
|
```
|
||||||
|
|
||||||
|
These searches are not perfect, but they help locate:
|
||||||
|
|
||||||
|
- processing loops
|
||||||
|
- dispatch logic
|
||||||
|
- hardware initialization
|
||||||
|
- coefficient loads
|
||||||
|
|
||||||
|
## Step 6: Distinguish Code From Data
|
||||||
|
|
||||||
|
In a large ADSP-219x ROM, many PM words are data, not instructions.
|
||||||
|
You must actively separate them.
|
||||||
|
|
||||||
|
### Heuristic: likely code
|
||||||
|
|
||||||
|
A region is likely code if it has:
|
||||||
|
|
||||||
|
- incoming xrefs from jumps or calls
|
||||||
|
- meaningful block structure
|
||||||
|
- function-like boundaries
|
||||||
|
- setup followed by control flow
|
||||||
|
- loops, branches, and exits
|
||||||
|
|
||||||
|
Check with:
|
||||||
|
|
||||||
|
```text
|
||||||
|
axt @ <addr>
|
||||||
|
pdf
|
||||||
|
agf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Heuristic: likely data
|
||||||
|
|
||||||
|
A region is likely data if it has:
|
||||||
|
|
||||||
|
- regular numeric patterns in hex
|
||||||
|
- no meaningful control flow
|
||||||
|
- no incoming code xrefs
|
||||||
|
- no returns or loop structure
|
||||||
|
- many decodeable instructions that make no programmatic sense
|
||||||
|
|
||||||
|
Check with both disassembly and raw bytes:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pd 32
|
||||||
|
px 64
|
||||||
|
```
|
||||||
|
|
||||||
|
If the hex dump looks more plausible than the disassembly, it is often
|
||||||
|
a table.
|
||||||
|
|
||||||
|
### ADSP-219x-specific data patterns
|
||||||
|
|
||||||
|
Common PM data in DSP firmware:
|
||||||
|
|
||||||
|
- FIR coefficients
|
||||||
|
- IIR coefficients
|
||||||
|
- FFT sine/cosine tables
|
||||||
|
- lookup tables
|
||||||
|
- packed constants
|
||||||
|
- boot configuration words
|
||||||
|
|
||||||
|
These often decode into valid-looking instructions by accident.
|
||||||
|
|
||||||
|
## Step 7: Delay Global Auto-Analysis
|
||||||
|
|
||||||
|
Only after you have mapped a few real code islands should you broaden
|
||||||
|
analysis:
|
||||||
|
|
||||||
|
```text
|
||||||
|
aa
|
||||||
|
afl
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefer `aa` first.
|
||||||
|
|
||||||
|
Use `aaa` only when:
|
||||||
|
|
||||||
|
- the ROM format is confirmed
|
||||||
|
- the entry region is valid
|
||||||
|
- you already understand where major code regions are
|
||||||
|
- the plugin behaves consistently on this image
|
||||||
|
|
||||||
|
Why this matters:
|
||||||
|
|
||||||
|
- `aa` is less aggressive
|
||||||
|
- `aaa` can create junk functions in large raw ROMs
|
||||||
|
- false positives are expensive to clean up mentally
|
||||||
|
|
||||||
|
## Step 8: Name What You Understand
|
||||||
|
|
||||||
|
As soon as a region is understood, name and annotate it.
|
||||||
|
|
||||||
|
Useful commands:
|
||||||
|
|
||||||
|
```text
|
||||||
|
afn entry_init
|
||||||
|
afn main_loop
|
||||||
|
afn io_dispatch
|
||||||
|
CCu probable coefficient table
|
||||||
|
CCu hardware init and watchdog setup
|
||||||
|
```
|
||||||
|
|
||||||
|
Naming reduces rework and makes graph navigation much easier.
|
||||||
|
|
||||||
|
## Step 9: Build A Region Map
|
||||||
|
|
||||||
|
For a 1 MB ROM, keep a rough map as you go:
|
||||||
|
|
||||||
|
- boot/reset code
|
||||||
|
- hardware init
|
||||||
|
- interrupt vector area
|
||||||
|
- main loop
|
||||||
|
- DSP kernels
|
||||||
|
- dispatch tables
|
||||||
|
- PM data tables
|
||||||
|
- obvious unused or padding regions
|
||||||
|
|
||||||
|
This can live in:
|
||||||
|
|
||||||
|
- r2 comments
|
||||||
|
- a notebook
|
||||||
|
- a separate markdown file
|
||||||
|
|
||||||
|
The important thing is to stop treating the ROM as one continuous
|
||||||
|
thing. Large firmware becomes manageable once you divide it into
|
||||||
|
regions.
|
||||||
|
|
||||||
|
## Step 10: Revisit Ambiguous Areas Later
|
||||||
|
|
||||||
|
Do not force a conclusion too early.
|
||||||
|
|
||||||
|
When a region is ambiguous:
|
||||||
|
|
||||||
|
- leave it unnamed or mark it as tentative
|
||||||
|
- inspect surrounding xrefs first
|
||||||
|
- compare with neighboring validated code
|
||||||
|
- revisit it after understanding more of the firmware
|
||||||
|
|
||||||
|
Good reverse engineering on large DSP ROMs is iterative.
|
||||||
|
|
||||||
|
## Recommended First 15 Minutes
|
||||||
|
|
||||||
|
If you want a concrete first-pass routine for a 1 MB ROM:
|
||||||
|
|
||||||
|
### 1. Open the ROM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
r2 -a adsp219x -b 24 firmware.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Check the first bytes and first instructions
|
||||||
|
|
||||||
|
```text
|
||||||
|
s 0
|
||||||
|
px 64
|
||||||
|
pd 32
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. If plausible, define the first function
|
||||||
|
|
||||||
|
```text
|
||||||
|
af
|
||||||
|
pdf
|
||||||
|
agf
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Enter visual graph mode
|
||||||
|
|
||||||
|
```text
|
||||||
|
VV
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Follow obvious branch targets manually
|
||||||
|
|
||||||
|
```text
|
||||||
|
s <target>
|
||||||
|
af
|
||||||
|
pdf
|
||||||
|
agf
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Search for loops and calls
|
||||||
|
|
||||||
|
```text
|
||||||
|
/a DO
|
||||||
|
/a CALL
|
||||||
|
/a JUMP
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Compare suspicious regions with hex
|
||||||
|
|
||||||
|
```text
|
||||||
|
pd 32
|
||||||
|
px 64
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. Only then widen analysis
|
||||||
|
|
||||||
|
```text
|
||||||
|
aa
|
||||||
|
afl
|
||||||
|
```
|
||||||
|
|
||||||
|
## When To Suspect A Bad Decode
|
||||||
|
|
||||||
|
Pause and reassess if you see:
|
||||||
|
|
||||||
|
- no meaningful flow anywhere near the entry region
|
||||||
|
- every region looks equally nonsensical
|
||||||
|
- branch targets never lead to reasonable code
|
||||||
|
- graph mode shows nonsense everywhere
|
||||||
|
- the ROM appears to decode but nothing behaves like firmware
|
||||||
|
|
||||||
|
Then check:
|
||||||
|
|
||||||
|
- ROM packing
|
||||||
|
- dump alignment
|
||||||
|
- whether the image is compressed or encrypted
|
||||||
|
- whether the base offset is wrong
|
||||||
|
- whether the file contains headers before the actual code
|
||||||
|
|
||||||
|
## Practical Command Cheat Sheet
|
||||||
|
|
||||||
|
Open:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
r2 -a adsp219x -b 24 firmware.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
Start region:
|
||||||
|
|
||||||
|
```text
|
||||||
|
s 0
|
||||||
|
pd 20
|
||||||
|
pd 64
|
||||||
|
px 64
|
||||||
|
```
|
||||||
|
|
||||||
|
Define and inspect function:
|
||||||
|
|
||||||
|
```text
|
||||||
|
af
|
||||||
|
pdf
|
||||||
|
agf
|
||||||
|
```
|
||||||
|
|
||||||
|
Visual graph:
|
||||||
|
|
||||||
|
```text
|
||||||
|
VV
|
||||||
|
```
|
||||||
|
|
||||||
|
Search:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/a JUMP
|
||||||
|
/a CALL
|
||||||
|
/a DO
|
||||||
|
/a RTS
|
||||||
|
```
|
||||||
|
|
||||||
|
Cross-references:
|
||||||
|
|
||||||
|
```text
|
||||||
|
axt
|
||||||
|
axt @ <addr>
|
||||||
|
```
|
||||||
|
|
||||||
|
Broader analysis:
|
||||||
|
|
||||||
|
```text
|
||||||
|
aa
|
||||||
|
afl
|
||||||
|
```
|
||||||
|
|
||||||
|
Naming:
|
||||||
|
|
||||||
|
```text
|
||||||
|
afn main_loop
|
||||||
|
CCu probable PM coefficient table
|
||||||
|
```
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
For a large ADSP-219x ROM:
|
||||||
|
|
||||||
|
- do not trust auto-analysis first
|
||||||
|
- validate the entry region manually
|
||||||
|
- graph only locally at first
|
||||||
|
- follow explicit flow edges
|
||||||
|
- use both disassembly and hex dumps
|
||||||
|
- treat PM as mixed code and data
|
||||||
|
- expand analysis gradually
|
||||||
|
|
||||||
|
The core rule is simple:
|
||||||
|
|
||||||
|
**Local confidence first, global analysis later.**
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
/* asm_adsp219x.c -- Radare2 arch plugin for Analog Devices ADSP-219x
|
/* asm_adsp219x.c -- Radare2 arch plugin for Analog Devices ADSP-219x
|
||||||
Copyright (C) 2026 Dr. Christian Giessen
|
Author: Dr. Christian Giessen
|
||||||
|
Date: 2026
|
||||||
|
|
||||||
Decodes most of the ADSP-219x instruction set (Types 1-37).
|
Decodes all documented ADSP-219x instruction types (Types 1-37).
|
||||||
Verified against open21xx assembler output for Types 1, 3, 4,
|
Most types are verified against open21xx assembler output; see
|
||||||
6, 7, 8, 9, 9a, 10, 10a, 11, 15, 17, 18, 20, 25, 33.
|
TESTING.md for the current verification matrix and any remaining
|
||||||
Structurally implemented but not yet assembler-verified:
|
structural-only cases. */
|
||||||
Types 12, 14, 16, 19, 21, 21a, 22, 22a, 23, 24, 26, 29,
|
|
||||||
30, 31, 32, 32a, 34, 35, 36, 37. */
|
|
||||||
|
|
||||||
#include <r_arch.h>
|
#include <r_arch.h>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user