- LICENSE (MIT, own work) + README license section - FINDINGS.md per-version notes point into docs/firmware/ - checksum brute-forcers marked superseded (code + tool docs)
122 lines
6.4 KiB
Markdown
122 lines
6.4 KiB
Markdown
# Lofi-12 XT — Findings Library
|
||
|
||
Living knowledge base for this device. Process/tips live in `RE-PROCESS.md`.
|
||
Per-version structural notes: `docs/firmware/notes-v{1.1.156,1.2.179,1.5.205}.md`; version diff: `rev-diff.md`;
|
||
mod feasibility: `tweakability-report.md`; tool docs: `tools/README.md`.
|
||
Deep dives: `docs/hardware.md`, `docs/bootloader.md`, `docs/firmware-update.md`,
|
||
`docs/firmware/v1.5.205.md`.
|
||
|
||
## 1. Hardware
|
||
|
||
- Device: Sonicware Lofi-12 XT (shares platform with SmplTrek/Cydrums).
|
||
- Main SoC (core module): **TI TMS320C6748** — ARM9 + C674x DSP. Marking on unit:
|
||
`TMS320 C6748EZW T / 13CP99W / GI 375 / 527 ZWT`.
|
||
- DRAM (core module): **EtronTech EM68C16CWQG-25H** — 1 Gbit DDR2, base `0xC0000000`.
|
||
- SPI flash (core module, 2×): **Macronix MX25L12833F** (`MXIC MX 25L12833F M2I-10G`),
|
||
16 MByte each, SOIC-8, silkscreen `IC300` / `IC301`. Shipping units hide the
|
||
marking under small stickers (`C047`/`C949`-style labels) — peel + photo to confirm.
|
||
- USB/aux MCU (main board): **ARTERY AT32F421K8T** (own firmware, out of scope).
|
||
- Storage: full-size SD slot (firmware update + samples/projects).
|
||
- PCBs seen: `405-VTOR-3852` (I/O), `405-VTOR-3849B` (main), `405-VTOR-3853` (jack),
|
||
core module with `CN201A/CN200A/CN203A/CN203B` board-to-board connectors.
|
||
- Silkscreen pin tables on main board expose `UART1_RX1/TX1`, `SPI1_SCK/MOSI`,
|
||
`SD_*`, `USB_DP/DN`, key/LED matrix — worth mapping before any invasive work.
|
||
|
||
## 2. Memory map (DDR2)
|
||
|
||
| Region | Content |
|
||
|---|---|
|
||
| `0xC0000000…` | DDR2 base (128 MB) |
|
||
| `0xC2xxxxxx` | SD `.bin` application image (DSP). v1.5 code `C2B80C00`, strings `C2D84DE0`, assets `C2DA7600` |
|
||
| `0xC7074630–C70B0598` | SPI AIS bootloader image (DSP), entry `C70A28A0` |
|
||
| `0xC706F280` | Runtime constant seen in updater (also == checksum seed, §5) |
|
||
| `0xC27C6282` | Scratch DDR used by updater (§5; value doubles as checksum seed) |
|
||
|
||
## 3. SPI flash layout (`ic300.bin` / `ic301.bin`, 16 MiB each)
|
||
|
||
Dumps: `ic300.bin` (`c2b86808…`), `ic301.bin` (`81f7b1f5…`). Always keep these
|
||
as recovery backups; re-verify with a second read (`sha256sum` + `cmp`).
|
||
|
||
**IC300 (boot + app):** TI AIS image, magic `0x41504954` (`TIPA`) at file `0x0`.
|
||
PLL/DDR config (`0x5853590D` ×2), then 10× Section Load (`0x58535901`):
|
||
|
||
| File off | DDR | Size |
|
||
|---|---|---|
|
||
| `0x000050` | `C7074630` | `0x1D0` |
|
||
| `0x00022C` | `C7074800` | `0x33B00` (main code) |
|
||
| `0x033D38` | `C70A8300` | `0x28CC` (rodata) |
|
||
| … | `C70AABD0`/`C70AABE0`/`C70AABF0` | `0xC`/`0xC`/`0xD44` |
|
||
| … | `C70AF000`/`C70AF4E8`/`C70AF5EC`/`C70AF660` | `0x200`/`0x104`/`0x70`/`0xF38` |
|
||
|
||
`JumpClose (0x58535906)` at file `0x38660` → entry `C70A28A0` (DSP reset prelude
|
||
`ZERO b0; mvc b0,ier; mvc b0,csr`). After it: `FF` gap to `~0x100000`, data to
|
||
`~0x1EFFFF`, `FF` gap `0x1F–0x4Fxxxx`, data `0x500000–0xFC7C17`. No AIS CRC opcodes.
|
||
|
||
**IC301 (data):** `AILS` header + drum-pattern names (`KICK/HIHAT/BALLAD/BLUES/…`),
|
||
24× `RIFF/WAVE` starting `0x8007E0`, nearly full to `0xFFFD7B`. Factory-sample flash.
|
||
|
||
**Update path (from updater strings + code):** `SystemUpdater::{start,
|
||
updateBootSection, updateMainSection, updatePresetSection, updateMCUSection,
|
||
updateMCUBootSection}(CatMetaData::ROMSection)` + `SystemVerify::verifyPresetSection`.
|
||
Public ZIPs ship only the SYSTEM section (the SD `.bin`); BOOT/PRESET/MCU use
|
||
separate (unreleased) files. Updater lives in AIS flash, so a bad SYSTEM image
|
||
cannot kill recovery — hold PAD while powering on → `SYSTEM UPDATE` still works.
|
||
|
||
## 4. SD `.bin` container (`cmtd/mmtd/sect → EOF`)
|
||
|
||
| Off | Field |
|
||
|---|---|
|
||
| `0x00` | `cmtd` magic |
|
||
| `0x04` | u32 LE filesize (must match actual) |
|
||
| `0x08` | u32 checksum — **solved, §5** |
|
||
| `0x0C` | reserved 0 |
|
||
| `0x10–0x2F` | `12, 1, 3, fw_maj, fw_min, fw_patch, 48, remaining` |
|
||
| `0x30` | `mmtd` magic; `+0x04` remaining; `+0x08` flags (per-build, part of hash) |
|
||
| `0x40` | fw triple; `0x4C` entry point; `0x50` sect count |
|
||
| `0x54…` | `sect` ×N: `73 65 63 74` + u32 load_addr + u32 len + payload; must tile EOF |
|
||
|
||
Known builds: v1.1.156 (1,595,605 B, entry `C26C4820`, 7 sects), v1.2.179
|
||
(1,606,197 B, entry `C26CA4C0`, 6 sects), v1.5.205 (1,707,049 B, entry
|
||
`C2CD1AA0`, 7 sects). Code is C674x LE, entropy ~6.89, unencrypted. Parser/packer:
|
||
`tools/lofi_image.py` (`parse_image` asserts filesize + chain fit).
|
||
|
||
## 5. Checksum (SOLVED)
|
||
|
||
**Rule:** `cmtd+0x08` = CRC32-IEEE (init 0) over the entire file with bytes
|
||
`[8:12]` replaced by `0xC27C6282`.
|
||
|
||
- `tools/lofi_image.py`: `CKSEED = 0xC27C6282`, `compute_checksum()`; `build_image()`
|
||
auto-computes by default (`--checksum keep` in `lofi_pack.py` preserves).
|
||
- Proof: GF(2) linear solve per image for the 32 unknown bits at `[8:12]` gives
|
||
`K = 0xC27C6282` on **all three** images independently (2⁻⁶⁴ fluke); forward
|
||
recompute reproduces `F58AF539 / DFF0D8C2 / B17C0857`; `unpack→pack` rebuilds
|
||
stock v1.5.205 byte-identical; a `Threshold→ThresholX` mod forges to a
|
||
self-consistent `462F735B`.
|
||
- Code anchors (bootloader DSP): CRC routine `C70A0A60`
|
||
(`NOT A6→state`, poly `EDB88320` via `MVK/MVKH`, byte loop, final `NOT` in delay
|
||
slot ≡ zlib chaining `F(buf,len,init)`); 4K-chunk caller `C708E4E4`
|
||
(`MVK 0x1000`, `CMPGT`, `CALLP C70A0A60`); check fn `C709E7C0`
|
||
(16-byte header CRC init 0 → chained 0x40000 chunks → `CMPEQ A13,A12` compare);
|
||
orchestrator `C708678C CALLP C709E7C0`, reject path builds
|
||
`ERROR : This file is invalid.` (`C70A8E82`).
|
||
- Forging: build image normally, then set `[8:12] = compute_checksum(image)`.
|
||
|
||
## 6. OLED / updater UI strings (orientation)
|
||
|
||
`Checking... 0%`, `Check the firmware file.`, `ERROR : This file is invalid.`,
|
||
`Erasing.../Writing.../Verifying... 0%`, `100%, done./OK./NG.`,
|
||
`Update completed./Update failed.`, `Please restart.`, `Are you sure?`,
|
||
`[CLR] : No / [OK] : Yes`, `>> BOOT/MCU/MCU Boot/PRESET/SYSTEM`,
|
||
`BOOT:/MCU:/MCU Boot:/PRESET:/SYSTEM:`, `$ systemupdate`, `[Lofi-12 XT] ~`,
|
||
`SYSTEM INFO/VERSION/BOOT/SYSTEM/MCU`, crash dumper (`Legacy NMI Exception`,
|
||
`A0–A31/B0–B31`, `NTSR/ITSR/…`). v1.5-only markers: `AppAudioExport*`,
|
||
`MIsolator/MRackComp/SlipRoll/HoldDelay`, `MIDINoteMap`, `REVERSE`,
|
||
`removeResourceFork` (all present in IC300 → board runs v1.5).
|
||
|
||
## 7. Open questions
|
||
|
||
- `mmtd+0x08` flags semantics (timestamp? covered by checksum as-is, no need to crack).
|
||
- Exact flash section table for BOOT/PRESET/MCU offsets (only SYSTEM reverse-engineered).
|
||
- AT32F421 firmware extraction/update protocol.
|
||
- UART/JTAG test-point mapping (silkscreen names exist, continuity not traced).
|