Enables both USBJTAG HS-CAN kernel execute and HP Tuners unlock recognition on T87A TCM modules (SPC564A80).
tools/t87a_patch.py| # | Flash Offset | Stock Bytes | Patched Bytes | PowerPC Instruction | Purpose | Tool |
|---|---|---|---|---|---|---|
| 1 | 0x02B39C |
4B FF FD 11 |
39 60 00 01 |
li r11, 1 |
Set "valid" flag value (r11 = 1) | USBJTAG |
| 2 | 0x02B3A0 |
48 00 01 C4 |
99 6C DB 23 |
stb r11, -0x24DD(r12) |
Write "valid" flag — bypasses signature check | USBJTAG |
| 3 | 0x034648 |
40 82 00 60 |
48 00 00 60 |
b +0x60 (was bne +0x60) |
HPT credential validation bypass | HPT |
| 4 | 0x0346AC |
40 82 00 18 |
60 00 00 00 |
nop (was bne +0x18) |
HPT lock check bypass | HPT |
| 5 | 0x034A70 |
40 82 00 08 |
48 00 00 08 |
b +0x08 (was bne +0x08) |
HPT $34 signature bypass | HPT |
r11 = 1, Patch 2 writes that value as the "kernel valid" flag. Without Patch 1, the stb writes garbage and the signature check still fails (NRC 0x85).
0x000000 - 0x007FFF Boot Sector (32KB) DO NOT ERASE - MCU reset vector 0x008000 - 0x01FFFF NVM / Adaptation DO NOT ERASE - VIN, seed keys, PNs 0x020000 - 0x03FFFF Boot Block (128KB) USBJTAG patches 1+2 live here HS-CAN kernel auto-protects this region 0x040000 - 0x07FFFF OS Code (256KB) HPT patches 3+4+5 live here 0x080000 - 0x17FFFF Calibration (1MB) Safe to read/write 0x180000 - 0x3FFFFF OS Code (2.5MB) Safe to read/write
The stock bootloader at 0x020000 calls a signature validation function before executing any uploaded kernel. The function returns a pass/fail result. USBJTAG's patch replaces the function CALL with a direct write of "1" (valid) to the result flag location, skipping the validation entirely.
Stock code: 0x02B39C: 4B FF FD 11 bl sig_validate ; call signature checker 0x02B3A0: 48 00 01 C4 b next_step ; continue (result in flag) USBJTAG patch: 0x02B39C: 39 60 00 01 li r11, 1 ; r11 = "valid" 0x02B3A0: 99 6C DB 23 stb r11, -0x24DD(r12) ; write flag = valid
HP Tuners checks three branch instructions in the OS code to determine if the module is "unlocked". Stock code uses conditional branches (bne) that enforce lock checks. HPT patches convert these to unconditional branches (b) or NOPs, bypassing the lock validation.
Status at a glance — patches confirmed applied; bench verification varies by OS.
| OS PN | Transmission | Status |
|---|---|---|
| 24288836 | 10L80/10L90 | Verified |
| 24293216 | 10L80/10L90 | Verified (BAM) |
| 24272182 | Mixed | Untested |
| 24281243 | TBD | Untested |
| 24283721 | 8L45 (CT6) | Untested |
| 24285377 | TBD | Untested |
| 24286913 | TBD | Untested |
| 24286985 | TBD | Untested |
| 24288259 | 10L | Untested |
| 24288574 | TBD | Untested |
| 24288835 | 8L90 | Untested |
| 24291283 | 8L90 (CTSV) | Untested |
Rebuilt 2026-04-22 from vendor baselines using tools/t87a_patch.py. One file per OS version, full 4 MB flash, all 5 patches applied, Boot Block CS1/CS2 recalculated and verified. Prior BAM-only sidecars were retired — BAM write now handles the 0x020000 offset in firmware, so a single full-flash image covers BAM, HS-CAN, and JTAG flows.
Download: T87A-Patched-Library-v1.zip — attached to the latest Flashy release.
| OS PN | Cal ID | Transmission | Patched File | CS1 / CS2 | File CRC32 (full 4 MB) | Bench Status |
|---|---|---|---|---|---|---|
| 24281243 | 24279974 | TBD | T87A_OS-24281243_Unlock_Patched.bin | 0x5685 / 0x1898 | 0x177A4354 | Untested |
| 24283721 | 24284015 | 8L45 (CT6) | T87A_OS-24283721_Unlock_Patched.bin | 0x5685 / 0x1898 | 0x03C54CF1 | Untested |
| 24285377 | 24289138 | TBD | T87A_OS-24285377_Unlock_Patched.bin | 0x5685 / 0x1898 | 0xB4CF9AEA | Untested |
| 24286913 | 24287045 | TBD | T87A_OS-24286913_Unlock_Patched.bin | 0x5685 / 0x1898 | 0x0882A817 | Untested |
| 24286985 | 24288833 | TBD | T87A_OS-24286985_Unlock_Patched.bin | 0x7EFA / 0x84AE | 0x68F56712 | Untested |
| 24288259 | 24292839 | 10L | T87A_OS-24288259_Unlock_Patched.bin | 0x5685 / 0x1898 | 0xFE6D5CF7 | Untested |
| 24288574 | 24288577 | TBD | T87A_OS-24288574_Unlock_Patched.bin | 0x5685 / 0x1898 | 0x00022D14 | Untested |
| 24288835 | 24288866 | 8L90 | T87A_OS-24288835_Unlock_Patched.bin | 0x5685 / 0x1898 | 0x34F44018 | Untested |
| 24288836 | 24289638 | 10L80/10L90 | T87A_OS-24288836_Unlock_Patched.bin | 0x5685 / 0x1898 | 0x6550B8B6 | Verified |
| 24291283 | 24289552 | 8L90 (CTSV) | T87A_OS-24291283_Unlock_Patched.bin | 0x5685 / 0x1898 | 0xB6500DD8 | Untested |
| 24293216 | 24295245 | 10L80/10L90 | T87A_OS-24293216_Unlock_Patched.bin | 0x5685 / 0x1898 | 0x826D64D4 | Verified (BAM) |
*-BAM.bin sidecars that pre-trimmed the first 128 KB for BAM's 0x020000 start. That was a workaround for a firmware bug — current Flashy BAMWRITE reads the full 4 MB image and skips the 0x000000-0x01FFFF range itself. Feed any of the files above to BAMWRITE, HSWRITE, or JTAG — same image, same checksums.
All five per-file fields read from each patched bin as 4-byte big-endian integers. The 0x014638 block holds four related GM part numbers that vary per OS; 0x080010 is a calibration-region marker (ASCII prefix "2288") that stays near-constant across files.
| File (OS PN) | 0x014638 OS PN | 0x01463C Cal ID | 0x014640 SW Mod 1 | 0x014644 SW Mod 2 | 0x080010 Cal marker |
|---|---|---|---|---|---|
| 24281243 | 24281243 | 24279974 | 24275695 | 24275700 | 842281529 |
| 24283721 | 24283721 | 24284015 | 24284019 | 24284020 | 842281528 |
| 24285377 | 24285377 | 24289138 | 24281661 | 24281662 | 842281528 |
| 24286913 | 24286913 | 24287045 | 24285039 | 24285040 | 842281528 |
| 24286985 | 24286985 | 24288833 | 24281378 | 24281379 | 842281528 |
| 24288259 | 24288259 | 24292839 | 24288197 | 24288198 | 842281529 |
| 24288574 | 24288574 | 24288577 | 24288579 | 24288581 | 842281528 |
| 24288835 | 24288835 | 24288866 | 24288868 | 24288869 | 842281528 |
| 24288836 | 24288836 | 24289638 | 24289640 | 24289641 | 842281528 |
| 24291283 | 24291283 | 24289552 | 24289553 | 24289554 | 842281528 |
| 24293216 | 24293216 | 24295245 | 24290804 | 24290805 | 842281529 |
Generated 2026-04-22 by tools/t87a_patch.py --verify (Cal ID + CRC32 reported inline).
Not every write path supports swapping to a different OS version. Boot block has OS-specific anchors (signature hashes, OS PN, region CRCs) — if you write a new OS without also matching the boot block, the TCM refuses to boot. This was verified on the bench 2026-04-22.
| Method | Same-OS (cal edits, reflash) | Cross-OS (different OS PN) | Why |
|---|---|---|---|
| BAM Write (bench) | Safe | Safe — load any T87A bin | Rewrites through factory BAM protocol; no boot/OS anchor mismatch possible |
| JTAG Full Flash (USBJTAG/NT-Link BDM) | Safe | Safe | Writes full 4 MB including matching boot + OS together |
HS Full Write (Flashy HSWRITE) | Safe (verified) | Bricks — blocked by firmware | Preserves existing boot; new OS at 0x040000+ fails anchor validation on reboot |
HS Cal Write (Flashy CALWRITE) | Safe (verified) | N/A | Doesn't touch OS region; purely 0x080000-0x17FFFF cal |
cmd_t87a_hswrite reads the source
bin's OS PN at offset 0x014638 and compares against the live TCM OSID (Mode 9 PID 4) before
any kernel load or flash touch. Mismatch → aborts with a banner, no erase, no write, TCM unchanged.
Same-OS writes proceed normally.
| MCU | SPC564A80 (Freescale/NXP), 120MHz PowerPC e200z4 |
| MCU ID | 5640, Version 2.0 |
| IDCODE | 2AE02041 |
| OnCE JTAG ID | 0x07C2601D |
| Flash Module 0 MCR | C3F88000 = 0x05408600 |
| Flash Module 1 MCR | C3F8C000 = 0x05010600 |
| Flash Base | 0x00000000 |
| Flash Size | 4MB (4,194,304 bytes) |
| Seed-Key Algo | 135 (0x87) — 5-byte AES-128 |
| CAN IDs | Tester: 0x7E2, ECU: 0x7EA |
| CAN Baud | 500 kbps |
| Size | 1,980 bytes |
| Load Address | 0x40010000 |
| $34 Format | Size-based: 34 00 00 07 BC |
| $36 Format | Single-shot: 36 80 40 01 00 00 + kernel data |
| Heartbeat | 55 53 42 4A 54 41 47 ("USBJTAG") on 0x7EA |
| Read Command | A0 [addr:4] [size:3] — streams data on 0x7EA |
| Read Speed | 32.1 KB/s (4MB in ~128s) |
After applying patches, Boot Block checksums MUST be recalculated. Without valid checksums, the bootloader silently rejects the patched code.
| Checksum | Algorithm | Address | Storage |
|---|---|---|---|
| CS1 | CRC16-IBM (poly 0x8005, reflected) | 0x028720 | 2 bytes, byte-swapped |
| CS2 | Wordsum (big-endian 16-bit words) | 0x028700 | 2 bytes, two's complement |
Order matters: CS1 must be written first, then CS2 recalculated (CS2 range includes CS1 location).
# Apply patches + recalculate checksums: python tools/t87a_patch.py input.bin [output.bin] # Verify existing file: python tools/t87a_patch.py --verify file.bin # Batch patch all bins in a directory: python tools/t87a_patch.py --batch directory/
Flashy Project — Updated 2026-04-22