Chip: TMS370C16 (F16E88PJA11)
Firmware: "2004 BCM OS.bin" — 57,344 bytes loaded in Ghidra at 0x10000-0x1DFFF
Architecture: 16-bit CPU, 17-bit address space (128KB), big endian, variable-length instructions (2-6 bytes, always even)
- TMS370C16 Architecture Notes
- Operating System Structure
- Mode $27 Security Access — Complete Architecture
- The Seed-Key Algorithm
- Mode $23 Read Memory By Address
- Practical Attack Vectors
- RAM Variable Map
- ROM Constants
- Function Table
- Subsystem Handler Table
- SLEIGH Spec Bugs
- Open Questions
- Registers: 0x0000-0x000F (R0-R15, memory-mapped). R15 = ZR (zero register, always reads 0).
- Peripheral I/O: 0x0010-0x00FF
- RAM: 0x0100+ (chip-variant dependent)
- Internal ROM: 0x8000-0xFFFF range in data address space (chip-specific routines, NOT in firmware dump)
- External ROM (firmware): 0x10000-0x1DFFF in program address space
- Word vs byte addressing: The PC holds word addresses. Byte address = word address x 2.
- CALL/JMP encoding:
EC 00 XX YY(4 bytes). Target byte address = 0xXXYY x 2. - Branch formula (ALL branch types):
target = inst_start + 4 + sdisp8 * 2. The "+4" accounts for the TMS370C16 pipeline (PC points 2 words ahead during execution). - BRBIT encoding: 4 bytes total:
opcode(1) + disp8(1) + addr16(2). Tests a bit in memory and branches conditionally. - 6-byte MOV:
28 FF src16 dst16=MOV *src16, *dst16(absolute memory-to-memory copy). - Code overlap pattern: Branches into the middle of multi-byte instructions to execute the trailing bytes as a different instruction. Intentional ROM size optimization / anti-RE technique.
MOV family: 0x02-0x2B (even=word, odd=byte)
0x02/03 = MOV/MOVB Rs,Rd
0x04/05 = MOV/MOVB Rs,*Rd (indirect store; when rs=15: CLR *Rd)
0x06/07 = MOV/MOVB *Rs,Rd (indirect load)
0x08/09 = MOV/MOVB Rs,*sdisp16[Rd] (4-byte indexed store)
0x1A/1B = MOV/MOVB #imm16/#imm8,Rd (4-byte load immediate)
0x20/21 = MOV/MOVB #imm16/#imm8,*sdisp16[Rd] (6-byte store immediate)
0x22/23 = MOV/MOVB *sdisp16[Rs],Rd (4-byte indexed load)
0x28/29 = MOV/MOVB *sdisp16[Rs],*sdisp16[Rd] (6-byte mem-to-mem)
ALU: 0x30-0x3F (ADD/ADDB/SUB/SUBB and carry variants)
Logic: 0x40-0x57 (AND/OR/XOR and variants)
CMP: 0x60-0x67 (CMP/CMPB variants)
Quick: 0x80=MOVQ, 0x82/83=ADQ/ADQB, 0x86/87=SUBQ/SUBQB
Carry: 0x8A=ADC, 0x8C=SBB, 0x8E/8F=CMPC
Bit: 0x90-0x97 (SBIT1/SBIT0/LDBIT/STBIT), 0x98=TBIT (missing from SLEIGH spec)
Shifts: 0xB0-0xBF (SHL/SHLL/ASR/ASRL/LSR/LSRL)
Branch: 0xC0=BR, C1=BNC, C2=BC, C3=BEQ, C4=BNE, C5=BHI, C6=BLS,
C7=BGT, C8=BLE, C9=BGE, CA=BLT, CB=BV, CC=BNV, CD=BP, CE=BPZ, CF=BN
BRBIT: 0xD0-D7=BRBIT0 #0-7, 0xD8-DF=BRBIT1 #0-7 (4 bytes each)
Ctrl: 0xE8=JMP Rd, 0xE9=JMP addr, 0xEA=JMP *sdisp16[Rd], 0xEC=CALL addr
0xF8=RTDU, 0xF9=PUSH, 0xFA=POP, 0xFB 00=RTS, 0xFC=RTI
Other: 0x7A=SHL4, 0x7B=SHL8, 0x7C=SHR8, 0xA6=MPYSB, 0xA7=MPYS, 0xA8=DBNZ,
0xFD=SWAPB, 0xFE=IDLE, 0xFF=TRAP
Extended bit: 0xE0-E7 (SBIT1/SBIT0/LDBIT/STBIT with register/memory variants)
The BCM's main loop is at 0x16A08. It sequentially CALLs every subsystem handler, then dispatches via a computed jump table. This runs continuously while the BCM is powered.
0x16A08: CALL turn_signal_handler (0x1263E)
0x16A0C: CALL theft_deterrent_handler (0x164C2)
0x16A10: CALL nop_placeholder (0x145BC)
0x16A14: CALL retained_accessory_power (0x13372)
0x16A18: CALL battery_saver (0x14496)
0x16A1C: CALL headlight_output_driver (0x1635C)
0x16A20: CALL wiper_washer_handler (0x13922)
0x16A24: CALL horn_handler (0x12558)
... test flags, conditional dispatch ...
0x16A2C: CALL chime_buzzer_handler (0x15DBE)
... jump table dispatch via *0xB436[R12] ...
0x16A5C: CALL internal_rom_D9B4 (0x0D9B4) ** INTERNAL ROM **
0x16A60: CALL firmware_11BCE (0x11BCE)
0x16A64: CALL internal_rom_D1DE (0x0D1DE) ** INTERNAL ROM **
0x16A68: CALL firmware_14366 (0x14366)
0x16A6C: CALL internal_rom_F508 (0x0F508) ** INTERNAL ROM **
... more handlers ...
0x16A82: CALL convergence_step (0x161E6) (entry into verification function)
0x16A86: CALL internal_rom_F00A (0x0F00A) ** INTERNAL ROM **
0x16A8A: CALL internal_rom_B800 (0x0B800) ** INTERNAL ROM **
0x16A8E: CALL internal_rom_BF66 (0x0BF66) ** INTERNAL ROM **
... more handlers, then loop ...
Five handlers run in internal chip ROM (addresses below 0x10000) and are invisible to us. These handle chip-level security, timers, and other silicon-specific functions.
Mode $27 implements UDS Security Access for the BCM. The architecture is more complex than a simple seed-key comparison — it uses an ISR-driven convergence algorithm operating on three 32-bit values.
FIRMWARE (visible) INTERNAL ROM (hidden)
┌──────────────────────────────┐ ┌──────────────────────────┐
Mode $27 ────>│ Handler 0x1A5BC │ │ │
Sub $01/$02 │ Routes via bits in 0x288C │ │ │
│ | │ │ │
│ v │ │ │
│ State Machine 0x16566 │ │ │
│ (timers, counters, │ │ │
│ status codes at 0x2D9C) │ │ │
│ | │ │ │
│ v │ │ │
│ Security Dispatch 0x15458 │ │ │
│ Checks sub-function vs │ │ │
│ config table at R0+n: │ │ │
│ R0+1 → convergence check │ │ │
│ R0+2 → Send Key path │ │ │
│ R0+3 → ROM algorithm call ──┼─CALL─>│ ROM Routine 0xF782 │
│ │ │ - Reads seed (0x275C) │
│ │<─RET──│ - Reads received key │
│ │ │ - Computes A, B, C │
│ Source Table 0x20E0-0x20EC │ │ - Writes to 0x20E0-20EC │
│ A (32-bit), B, C, flags │ │ │
│ | │ └──────────────────────────┘
│ ISR 0x1608C (periodic) │
│ Copies source table to │
│ verify area 0x267A-2684 │
│ Loads threshold from │
│ ROM 0x9816-0x9818 │
│ | │
│ Convergence 0x161AE │
│ Euclidean subtraction │
│ loop on A, B, C │
│ | │
│ ┌─────┴──────┐ │
│ A==B==C? A!=B!=C? │
│ | | │
│ 0x1549C 0x15562 │
│ UNLOCK DENY │
│ │
│ Write-back 0x1B562 │
│ (converged result -> source) │
│ │
│ Init/Reset 0x1825E │
│ (clears buffers, calls │
│ ROM 0xF782, sets flags) │
└──────────────────────────────┘
The Mode $27 handler at 0x1A5BC routes based on bits in RAM 0x288C:
- Bit 7: Skip to end (already processed)
- Bit 6: Send Key path (sub-function $02)
- Bit 2: Fast exit
For Request Seed (sub $01): generates a 16-bit seed at 0x275C-0x275D using code at 0x1A160 (calls bit manipulation utility at 0x13160 and helpers at 0x16904, 0x16718).
For Send Key (sub $02): routes through the state machine at 0x16566 to the security dispatch at 0x15458, which conditionally calls internal ROM at 0xF782.
When the seed equals 0x0000, the handler increments a counter at 0x288D. After 26 consecutive calls with seed == 0, the BCM sends a positive response ($67) and grants security access without any key verification.
The state machine manages a dual-timer lockout system:
Status 1 ("Ready"):
- New seed generated
- Seed validity timer loaded from ROM 0x9AAB -> RAM 0x2800
Status 2-3 ("Challenge active"):
- Seed is valid, accepting key attempts
- Attempt counter at 0x2807 decrements per failed attempt
Status 4 ("Attempts exhausted"):
- Counter reached 0
Status 5 ("Lockout"):
- Lockout timer = ROM(0x9AAD) * 2 -> RAM 0x2804
- All attempts rejected with $7F $27 $37
- Processing flags cleared
When lockout timer expires:
-> Back to Status 1 (NEW seed generated)
Critical for brute force: the seed changes after every lockout cycle.
This interrupt service routine runs periodically and:
-
Copies the source table (0x20E0-0x20EC) to the verification area (0x267A-0x2684):
- 0x20E0:0x20E2 -> 0x267A:0x267C (A, 32-bit hi:lo)
- 0x20E4:0x20E6 -> 0x267E:0x2680 (B, 32-bit hi:lo)
- 0x20E8:0x20EA -> 0x2682:0x2684 (C, 32-bit hi:lo)
- 0x20EC -> 0x266E (flags)
-
Loads convergence threshold from internal ROM 0x9816:0x9818 (32-bit, values unknown)
-
Runs multi-step comparison:
- Quick check: if A==B==C trivially, jump to quick exit
- If B != C: enter convergence subtraction loop at 0x161BC
- Otherwise: threshold-based comparisons
-
Falls through to FUN_016174 (convergence pre-check) then to the verification function at 0x161AE.
-
Ends with RTI (Return from Interrupt) — confirming it's an ISR.
Operates on three 32-bit values in the verification area:
- A at 0x267A:0x267C
- B at 0x267E:0x2680
- C at 0x2682:0x2684
- Temp at 0x2672:0x2674
- Output at 0x2686:0x2688
- Convergence flag: bit 7 of 0x266C
Contains a Euclidean-style subtraction loop at 0x161BC that iteratively converges the three values. If A==B==C after convergence:
- Calls 0x1549C (security UNLOCK)
If mismatch:
- Calls 0x15562 (denial handler)
- Calls 0x16726 (send negative response)
- Sets bit 7 of 0x2786
When convergence is NOT yet complete (bit 7 of 0x266C clear):
- Copies temp value to ALL source table entries: A = B = C = temp
- Copies temp to output (0x2686:0x2688)
- Sets bit 7 of 0x2784
This feeds the converged intermediate result back to the source table for the next ISR cycle.
After exhaustive searching of the firmware binary for any code that writes to the source table at 0x20E0-0x20EC, we found:
- 3 firmware-level references: ISR reads (0x1608C), init passthrough (0x1825E), convergence write-back (0x1B562)
- 0 firmware-level WRITE origins: No code in the firmware computes initial A, B, C values
The seed-key derivation is performed by internal ROM routine at byte address 0xF782 (word address 0x7BC1). This routine is called from:
- 0x154B8 — conditionally, when the diagnostic sub-function matches config[R0+3]
- 0x18342 — during init/reset (0x1825E function)
- 0x1A66A — near the Mode $27 handler (cleanup/reset path)
The internal ROM is baked into the TMS370C16 silicon and cannot be read from the external EPROM dump. This is a deliberate GM/TI security measure.
Per the pcmhacking.net community, this BCM likely uses GM algorithm $12 (0x12). GM uses ~256 numbered algorithms across their modules. The algorithm number determines the mathematical transformation from seed to key.
Documented GM algorithms (like $0B) use simple operations:
temp = (seed << 2) | (seed >> 14) // rotate left 2
key = byte_swap(temp + 0xB6B6) - 0xFDDA
Algorithm $12 specifically is not publicly documented. The convergence architecture (Euclidean subtraction on three 32-bit values) suggests a more sophisticated scheme than typical GM algorithms.
Mode $23 IS implemented in the firmware (contrary to the "error 0x00" observed over OBD — that error is because security access is required).
The handler's entry sequence:
- SID/mode check against configuration byte at R0+9
- CALL 0x14226 — security/configuration gate
- If gate returns 0 -> reject with negative response
- If gate passes -> proceed with memory read
- Tests bit 5 of ROM config byte at 0x9AC2
- If not set: sends negative response and returns (service disabled)
- If set: continues
- Additional checks against 0x27EC and ROM values
- Returns non-zero if access is granted
Conclusion: Mode $23 requires Mode $27 security access. You must authenticate first.
If the seed is ever 0x0000 (possible after cold start or specific conditions):
- Send 26 consecutive
$27 $01requests - BCM grants security access without key verification
- Then use Mode $23 freely
Setup: ELM327 + Python script over J1850 VPW
Procedure:
- Request seed (
$27 $01) -> get 2-byte seed - Send candidate key (
$27 $02 KK KK) - Repeat until success or lockout
Complications:
- Attempt counter (0x2807) limits tries per seed — count from ROM 0x9AAE
- Lockout timer (0x2804) blocks attempts — duration from ROM 0x9AAD * 2
- Seed changes after each lockout cycle (Status 5 -> Status 1)
- Each cycle is probabilistic: N/65536 chance of hitting the right key
Estimated times (seed changes each cycle, so it's probabilistic):
| Attempts/seed (N) | Lockout (L) | Expected time |
|---|---|---|
| 3 | 10 sec | ~61 hours |
| 5 | 10 sec | ~36 hours |
| 3 | 30 sec | ~7.6 days |
| 10 | 10 sec | ~18 hours |
Step 1: Probe lockout parameters empirically — send wrong keys until $7F $27 $36, count N, time L.
Step 2: If 4-byte key instead of 2-byte, brute force is infeasible (4 billion combinations).
The TMS370C16 has an MPSD (Multi-Pin Serial Debug) port. The FT2232H adapter supports JTAG.
Capabilities:
- Direct RAM read/write (bypasses all software security)
- Read internal ROM (including the algorithm at 0xF782!)
- Read ROM configuration constants (0x9AAA-0x9AAE, 0x9816-0x9818, 0x9AC2)
- Full chip debug
Drawback: Requires physical access to the BCM board (JTAG pins), and the BCM is difficult to reach in the truck.
Search automotive security communities for GM algorithm $12 implementations. Key resources:
| Address | Label | Purpose |
|---|---|---|
| 0x0060-0x0063 | OUTPUT_PORTS_1_4 | Output/control ports |
| 0x0064-0x0067 | INPUT_PORTS_1_4 | Input/status ports |
| 0x0068-0x006F | IO_PORTS_A_D | Bidirectional I/O |
| 0x20A0-0x20B1 | KEY_INPUT_BUFFER | Input buffer for security algorithm (cleared after use) |
| 0x20D0-0x20D2 | SECURITY_ROM_PARAMS | ROM constants copied during init |
| 0x20E0:0x20E2 | SOURCE_A | Source table: value A (32-bit, hi:lo) |
| 0x20E4:0x20E6 | SOURCE_B | Source table: value B (32-bit, hi:lo) |
| 0x20E8:0x20EA | SOURCE_C | Source table: value C (32-bit, hi:lo) |
| 0x20EC | SOURCE_FLAGS | Source table: flags |
| 0x21BA | INIT_STATE_FLAG | Set during init, cleared during convergence write-back |
| 0x23B4 | DIAG_SUCCESS_FLAGS | Diagnostic handler success flags |
| 0x23DB | SID_GROUP_TRACKER | SID group tracker |
| 0x23DC | SID_LOW_NIBBLE | SID low nibble storage |
| 0x23E0 | SID_COPY | SID copy storage |
| 0x2484 | DIAG_MSG_BUFFER | Diagnostic message buffer (85 bytes) |
| 0x2508-0x2511 | SEED_WORK_BUF | Working buffer for seed bit manipulation |
| 0x25F8 | DIAG_CURRENT_SID | Current SID being processed |
| 0x25FA | DIAG_SUBFUNC | Sub-function byte from diagnostic message |
| 0x2613 | DIAG_SESSION_STATE | Session state |
| 0x266C | CONVERGENCE_FLAG | Bit 7 = convergence complete |
| 0x266E | VERIFY_FLAGS | Verification flags (from 0x20EC) |
| 0x2672:0x2674 | VERIFY_TEMP | Temp 32-bit value for convergence |
| 0x267A:0x267C | VERIFY_A | Verification copy of A (32-bit) |
| 0x267E:0x2680 | VERIFY_B | Verification copy of B (32-bit) |
| 0x2682:0x2684 | VERIFY_C | Verification copy of C (32-bit) |
| 0x2686:0x2688 | VERIFY_OUTPUT | Verification output (32-bit) |
| 0x26AC-0x26CA | SECURITY_PARAM_BLOCK | Security algorithm parameter block |
| 0x275C-0x275D | SEED_VALUE | 16-bit seed for Mode $27 |
| 0x2781-0x2788 | SECURITY_COMPLETION_FLAGS | Bit 7 of each = completion/status flags |
| 0x2800 | SEED_TIMER | Seed validity countdown timer |
| 0x2804 | LOCKOUT_TIMER | Security lockout countdown timer |
| 0x2807 | ATTEMPT_COUNTER | Remaining key attempts before lockout |
| 0x2808 | SECURITY_STATUS_FLAGS | Security status flags (BRBIT tested) |
| 0x288C | SECURITY_SUBFUNC_FLAGS | Sub-function routing bits (bit 6=SendKey, bit 7=skip) |
| 0x288D | ZERO_KEY_BYPASS_CTR | Counter for 0-key bypass (triggers at 26) |
| 0x2CE0-0x2CE3 | SECURITY_STATE | Security access state |
| 0x2D9C-0x2D9E | SECURITY_STATUS_CODES | Status codes (1=ready, 4=exhausted, 5=lockout) |
| 0x27FD-0x27FE | SECURITY_PROC_FLAGS | Processing flags |
| Address | Purpose |
|---|---|
| 0x9816-0x9818 | Convergence threshold (32-bit) |
| 0x9867-0x9868 | Bit test constants for seed generation (0x13160) |
| 0x9AAA | Security config (referenced by state machine) |
| 0x9AAB | Seed validity timer initial value -> 0x2800 |
| 0x9AAD | Lockout timer base value (doubled) -> 0x2804 |
| 0x9AAE | Max attempt count (shifted) -> 0x2807 |
| 0x9AC2 | Mode $23 service enable flag (bit 5) |
| 0x9BED-0x9BEE | ROM constants copied to 0x20D0-0x20D1 during init |
| 0x9AD9 | ROM constant copied to 0x20D2 during init |
| 0x9D14 | Constant used by denial handler (0x15562) |
| Address | Name | Size | Purpose |
|---|---|---|---|
| 0x1608C | (ISR) | ~488B | Key verification ISR driver. Copies source table, runs convergence, ends with RTI |
| 0x13160 | (unnamed) | 80B | Bit manipulation utility for seed generation |
| 0x14226 | FUN_014226 | 42B | Security/config gate for diagnostic services |
| 0x15458 | FUN_015458 | ~150B+ | Security dispatch — routes sub-functions, calls ROM 0xF782 |
| 0x1549C | (unnamed) | ? | Security UNLOCK function (called when A==B==C) |
| 0x15562 | (unnamed) | ? | Security denial / iteration handler (XOR/AND ops) |
| 0x161AE | FUN_0161ae | 200B | Seed-key verification: convergence loop + A==B==C check |
| 0x16174 | FUN_016174 | 58B | Convergence pre-check, falls through to 0x161AE |
| 0x164C2 | FUN_0164c2 | 208B | Theft deterrent handler |
| 0x16566 | (state machine) | 210B | Mode $27 state machine (timers, counters, status) |
| 0x16718 | util_called_32 | ? | Utility called from seed generation (Ghidra body=4B, actually much larger) |
| 0x16726 | (unnamed) | ? | Send negative diagnostic response |
| 0x16734 | (unnamed) | ? | Send diagnostic response |
| 0x16904 | (unnamed) | ? | Read/process field from diagnostic message |
| 0x1692C | (unnamed) | ? | Complex security data processing, operates on 0x26AC-0x26CA |
| 0x177AA | (unnamed) | ? | Mode $23 handler (security-gated) |
| 0x1825E | (unnamed) | 268B | Security init/reset. Clears buffers, calls ROM 0xF782, sets flags |
| 0x1A160 | (unnamed) | ? | Seed generation code |
| 0x1A5BC | mode27_security_handler | ? | Mode $27 handler entry point |
| 0x1A620 | FUN_01A620 | ? | Diagnostic state cleanup |
| 0x1B562 | (unnamed) | ? | Convergence write-back (temp -> source table when not yet converged) |
| 0x16A08 | FUN_016a08 | large | Main OS scheduler loop — calls all subsystem handlers |
Called sequentially by the main loop at 0x16A08:
| # | Address | Name | Confidence |
|---|---|---|---|
| 1 | 0x154DE | timer_scheduler | Medium |
| 2 | 0x10CF2 | io_port_init | High |
| 3 | 0x10276 | calibration_table_loader | Medium |
| 4 | 0x13D92 | class2_serial_data_handler | High |
| 5 | 0x11002 | pwm_timer_setup | Medium |
| 6 | 0x169B6 | task_dispatcher_body | High |
| 7 | 0x11598 | output_driver_update | High |
| 8 | 0x11A9E | state_machine_dispatch | Low |
| 9 | 0x107F8 | sys_config_init | Low |
| 10 | 0x128E0 | door_lock_handler | High |
| 11 | 0x12FF2 | exterior_lighting_control | High |
| 12 | 0x1263E | turn_signal_handler | Medium |
| 13 | 0x164C2 | theft_deterrent_handler | High |
| 14 | 0x145BC | nop_placeholder | High |
| 15 | 0x13372 | retained_accessory_power | High |
| 16 | 0x14496 | battery_saver | High |
| 17 | 0x1635C | headlight_output_driver | High |
| 18 | 0x13922 | wiper_washer_handler | High |
| 19 | 0x12558 | horn_handler | Medium |
| 20 | 0x15DBE | chime_buzzer_handler | High |
The Ghidra SLEIGH specification at ghidra-tms370c16/data/languages/TMS370C16/TMS370C16.slaspec has several bugs that cause garbled disassembly:
-
Branch offset formula (line 313):
inst_next + (sdisp8 * 2)should beinst_start + 4 + (sdisp8 * 2). -
BRBIT size (lines 2563-2644): Defined as 6 bytes (3 tokens). Should be 4 bytes (2 tokens: opcode+disp8, addr16).
-
Missing opcode 0x98: TBIT #imm4, Rd (Test Bit) is not defined.
-
brword token definition (line 106): Incorrect.
-
0x28/0x29 opcode (MOV/MOVB mem-to-mem): Defined as 4-byte but should be 6-byte (3 x 16-bit tokens). This causes cascading misalignment in Ghidra's disassembly.
-
Overlapping token fields:
AND #imm16, *sdisp16[Rd]has conflicting field definitions.
These bugs cause Ghidra to produce incorrect disassembly for much of the firmware. Manual decoding from raw bytes (using the opcode map above) is necessary for accurate analysis.
-
Algorithm $12 implementation: The actual seed-to-key transformation is in internal ROM at 0xF782. Can it be extracted via JTAG, or found in community documentation?
-
ROM constant values: All security-critical constants (0x9AAA-0x9AAE, 0x9816-0x9818, 0x9AC2) are in internal ROM. Exact values unknown.
-
Lockout parameters: Attempt count (N) and lockout duration (L) are from internal ROM. Must be measured empirically.
-
Key length: Almost certainly 2 bytes (seed is 16-bit at 0x275C-0x275D), but should be confirmed by observing the Mode $27 exchange.
-
0-key bypass conditions: Under what conditions does the seed equal 0x0000? Cold start? Timer expiry? Always-zero initial state?
-
Mode $23 capability: Once security is bypassed, what address ranges can Mode $23 read? Can it access internal ROM?
-
Source table population: Confirmed that internal ROM routine writes to 0x20E0-0x20EC, but the exact algorithm remains unknown.
-
Function at 0x15562: The denial/iteration handler contains XOR/AND operations on RAM 0x27C6-0x27CF. Role in the verification pipeline unclear.
Report generated from multi-session Ghidra analysis of "2004 BCM OS.bin" firmware dump. Last updated: 2026-02-08