Files
2026-09-30 22:48:06 +02:00

141 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> Historical snapshot (2026-09-26 working notes). Two claims are superseded:
> update entry is hold-PAD-while-powering-on (see ../firmware-update.md),
> and the cmtd checksum is solved (see ../../tools/README.md).
# Lofi-12 XT.bin — Reverse Engineering Notes
- File: `Lofi-12 XT.bin` (firmware v1.5.205)
- Size: 1,707,049 bytes (`0x1A0C29`)
- SHA256: `a8bffa65919c0a2d4ff2ce77d16301551af4d2964497b8804deed49ce4198026`
- Source: `Lofi-12XT_v1.5.205` update ZIP (also contains `Dibiase/`, `Ski Beatz/` sample folders)
- Update method: copy `.bin` to SD root, boot with button held → `SYSTEM UPDATE`
## Hardware context
Based on community teardown (SmplTrek / Lofi-12 XT / Cydrums share platform):
- TI TMS320C6748 (ARM9 + C674x floating-point DSP) — main SoC
- ARTERY AT32F421K8T (ARM Cortex-M, USB/aux)
- Etron EM68C16CWQG 1 Gbit DDR2, base `0xC0000000`
- Macronix MX25L12833F SPI flash (bootloader lives here, parses SD `.bin`)
All `sect` load addresses are `0xC2xxxxxx`, i.e. DDR2. The `.bin` is a DDR snapshot
loaded by the SPI-flash bootloader, not a flash image itself.
## Container format (Sonicware custom, not AIS/ELF)
```
[cmtd 48B][mmtd 36B][sect x7 -> EOF, exact fit]
```
### cmtd (file header, 0x00–0x2F, 48 bytes)
| Off | Bytes | Meaning |
|-----|-------|---------|
| 0x00 | `63 6d 74 64` (`cmtd`) | magic |
| 0x04 | `29 0c 1a 00` | u32 LE filesize = 1707049 (matches) |
| 0x08 | `57 08 7c b1` | u32 checksum (?) `0xB17C0857`, algorithm TBD (not plain CRC32) |
| 0x0C | `00 00 00 00` | reserved |
| 0x10 | `0c 00 00 00` | 12 (?) |
| 0x14 | `01 00 00 00` | container ver major? (1) |
| 0x18 | `03 00 00 00` | container ver minor? (3) |
| 0x1C | `01 00 00 00` | firmware major (1) |
| 0x20 | `05 00 00 00` | firmware minor (5) |
| 0x24 | `cd 00 00 00` | firmware patch (205) → **v1.5.205** |
| 0x28 | `30 00 00 00` | 48 = cmtd header len |
| 0x2C | `f9 0b 1a 00` | remaining size = filesize − 48 = 1707001 |
### mmtd (image header, 0x30–0x53, 36 bytes)
| Off | Bytes | Meaning |
|-----|-------|---------|
| 0x30 | `6d 6d 74 64` (`mmtd`) | magic |
| 0x34 | `f9 0b 1a 00` | remaining size |
| 0x38 | `e8 bc f8 4f` | timestamp/flags? (`0x4FF8BCE8`) |
| 0x3C | `ff ff ff ff` | −1 |
| 0x40 | `01 00 00 00` | fw major (1) |
| 0x44 | `05 00 00 00` | fw minor (5) |
| 0x48 | `cd 00 00 00` | fw patch (205) |
| 0x4C | `a0 1a cd c2` | **entry point `0xC2CD1AA0`** (inside big code sect) |
| 0x50 | `07 00 00 00` | sect count = 7 |
### sect (0x54 → EOF)
Each: `73 65 63 74` (`sect`) | u32 LE load_addr | u32 LE len | payload.
Chain verified: `next_off = off + 12 + len`, ends exactly at EOF (`0x1A0C29`).
| # | File off | Load addr | Len | End addr | Role |
|---|----------|-----------|-----|----------|------|
| 0 | 0x000054 | C2DA7600 | 86168 (`0x15098`) | C2DBC698 | asset/blob (`7e xx` patterned) |
| 1 | 0x0150F8 | C2DA6DD4 | 688 (`0x2B0`) | C2DA7084 | float table (~−97…−102) |
| 2 | 0x0153B4 | C2DA7400 | 512 (`0x200`) | C2DA7600 | record table, links #1→#0 |
| 3 | 0x0155C0 | C2B80C00 | 1490112 (`0x16BCC0`) | C2CEC8C0 | **main code (.text), C6000 DSP** |
| 4 | 0x18128C | C2B809E8 | 8 | C2B809F0 | zeros (gap/patch slot) |
| 5 | 0x1812A0 | C2D84DE0 | 125081 (`0x1E899`) | C2DA32D9 | **.rodata/.data** (all readable strings) |
| 6 | 0x019FB45 | C2DA5680 | 4312 (`0x10D8`) | C2DA6758 | float ramp (~−99.34 step) |
Sorted by address they tile `C2B809E8…C2DBC698` with small gaps (descriptors/BSS).
Unpacked with:
```python
import struct
off = 0x54
while d[off:off+4] == b'sect':
a, b = struct.unpack('<II', d[off+4:off+12])
open(f'sect_addr{a:08X}_off{off:06X}.bin','wb').write(d[off+12:off+12+b])
off += 12 + b
```
Split files in `/tmp/opencode/lofi/sect*_addr*.bin` (7 files).
## Code identification
- Entry `0xC2CD1AA0` → file off `0x16646C`. Disassembled as **TMS320C64x LE**
(capstone `CS_ARCH_TMS320C64X`) gives a textbook C6000 reset prelude:
`ZERO b0; mvc b0,ier; mvc b0,csr; mvk/mvklh b15/b14; and -8,…`
(disable interrupts, align stack). ARM/Thumb disassembly of the big sect
yields ~nothing (2× `E92D` push in 1.5 MB; `bx lr` hits are coincidental
Thumb-dense false positives).
- Data sect has 6,362 words pointing into the big code range (`C2BDxxxx`,
e.g. C++ vtables) + 3,051 self-pointers. Big sect itself has ~0 pointers
into data (PC-relative `addkpc` style) + 102 self-words — consistent with
C6000 `.text` vs `.rodata` split.
- Big sect entropy ~6.9–7.0 with ~12% zeros (code+data mix, not packed/encrypted);
tail (`+0x140000…`) drops to ~3.0 with repeating 64 B zero-padded structs
(data/BSS area).
## Data sect contents (file 0x1812A0–0x19FB45)
- C++ RTTI/mangled names (libc++, `NSt3__`, so TI clang-based CGT):
`FileUtil::removeResourceFork/copyFolderRecursive/convFsRecursive/…`,
`14AppSongBrowser/TestControl/TestEncoder`, `13AppFileRename/FileSelect/
SongRename/SystemInfo/SystemMenu`, `12AppSceneMenu/TempoMenu/TrackMenu`,
`11AppSDReader/SongEdit`, `10AppTapeRec/TestADC/TestLED`, `N3HAL9I2CDriverE/
SPIDriver/IODriver/SDDriver`, `N8SoundOSC4StepE`, `N5Asset5PLockE`, …
- C674x crash dumper: `Legacy NMI Exception`, `IERR=`, `Fetch packet`,
`Execute packet`, `Opcode/Privilege/Loop buffer`, `A0=…A31=`, `B0=…B31=`,
`NTSR/ITSR/IRP/SSR/AMR/RILC/ILC, Exception at, EFR/NRP`, cache/security faults.
- UI strings: `Lofi-12XT`, `PATTERN MIXDOWN/MENU`, `PROJECT SAVE AS/SELECT`,
`SONG MIXDOWN`, `SCENE/TRACK MANAGER`, `CARD/MIDI SETTING`, `ARE YOU SURE?`,
`PROCESSING/COPYING…`, `Threshold`, `Tempo: 120.0`, `*.wav`, …
- Container/chunk tags: `RIFF/WAVE/fmt /smpl/labl/adtl/data/cue /MThd/MTrk`,
project tags `PJST/SMPS/PTD2/PTDT/PJCF/SONG/TRAU/TMAM/…`, `TRACK0/TRK0`.
## Small sects
- #0 (86 kB): byte pattern `00 7e 7e 00 … 7e xx 00 0f` — bitmap/font/waveform asset.
- #1/#6: LE float32 tables (filter/window coeffs?).
- #2 (512 B): fixed records, e.g. `f6 54 3c 00 5a a3 xx 00 … 6a 61 00`
(`ja\0`/`jma\0` fragments) — preset/pattern table.
## Open questions / next steps
1. Checksum at `cmtd+0x08` (`0xB17C0857`) — not CRC32 of obvious spans; brute-force
variants (BE, seeded, Fletcher, TI AIS style) before attempting repack.
2. Full C64x disassembly of sect #3 (TI CGT / Ghidra C6000, or capstone script);
recover vtables using pointers in sect #5.
3. Diff vs older `.bin` (e.g. v1.2.x) to isolate v1.5 feature code.
4. Confirm ARM9 vs DSP split (ARM9 may be bootloader-only in SPI flash;
everything in `.bin` looks like DSP).