usbmon

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit dcbf4dd06a9aea4635266958f637d5853713dc63
parent 1b5db92dbd54dc1e4ccdfde3848e31457196a6c8
Author: Brian Swetland <swetland@frotz.net>
Date:   Tue, 23 Sep 2014 20:07:33 -0700

dismpsse: disassemble ftdi mpsse traffic

- converts mpsse command stream to vector of TDI/TMS bits
- plays vector through JTAG simulation to generate DR/IR writes
- parses DR/IR writes for DAP operations
- displays DAP operations

Diffstat:
Adismpsse.c | 404+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 404 insertions(+), 0 deletions(-)

diff --git a/dismpsse.c b/dismpsse.c @@ -0,0 +1,404 @@ +// Copyright 2014 Brian Swetland <swetland@frotz.net> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long u64; + +#define TRACE_MPSSE 0 +#define TRACE_JTAG 0 + +// ----- simulate arm dap ----- + +void dap_abort(u64 v) { + u32 data = v; + printf("abort wr %08x\n", data); // 36? +} + +u32 dap_apnum = 0; +u32 dap_bank = 0; + +const char *dp_addr2name(unsigned addr) { + switch (addr) { + case 0x00: return "ABORT"; + case 0x04: return "CSW"; + case 0x08: return "SELECT"; + case 0x0C: return "RDBUFF"; + default: return "XXX"; + } +} + +void dap_dpacc(u64 v) { + u32 data = v >> 3; + u32 cmd = v & 7; + u32 addr = (cmd & 6) << 1; + + printf("dpacc %s %08x -> DP %02x %s\n", + (cmd & 1) ? "rd" : "wr", data, addr, dp_addr2name(addr)); + + if (addr == 0x08) { // DPSEL + dap_apnum = data >> 24; + dap_bank = data & 0xF0; + } +} + +const char *ap_addr2name(unsigned addr) { + switch (addr) { + case 0x00: return "CSW"; + case 0x04: return "TAR"; + case 0x0C: return "DRW"; + case 0x10: return "BD0"; + case 0x14: return "BD1"; + case 0x18: return "BD2"; + case 0x1C: return "BD3"; + case 0xF4: return "CFG"; + case 0xF8: return "BASE"; + case 0xFC: return "IDR"; + default: return "XXX"; + } +} + +void dap_apacc(u64 v) { + u32 data = v >> 3; + u32 cmd = v & 7; + u32 addr = dap_bank | ((cmd & 6) << 1); + printf("apacc %s %08x -> AP%d %02x %s\n", + (cmd & 1) ? "rd" : "wr", data, dap_apnum, addr, + ap_addr2name(addr)); +} + + +// ----- simulate zynq jtag chain -------- + +// TDI -> ARM(4) -> FPGA(6) -> TDO +u32 ir_fpga = 0xffffffff; +u32 ir_arm = 0xffffffff; + +void sim_ir(u64 data) { + ir_fpga = data & 0x3f; + ir_arm = (data >> 6) & 0xf; +// printf("ARM(%02x) FPGA(%02x)\n", ir_arm, ir_fpga); +} + +void sim_dr(u64 data) { + // if fpga's not in bypass, no idea what we're doing + if (ir_fpga != 0x3f) return; + // discard the bit being fed into fpga's bypass register + data >>= 1; + + switch (ir_arm) { + case 0x08: dap_abort(data); break; + case 0x0a: dap_dpacc(data); break; + case 0x0b: dap_apacc(data); break; + } +} + +// ----- simulate jtag core ----- + +#define JTAG_RESET 0 +#define JTAG_IDLE 1 +#define JTAG_DRSELECT 2 +#define JTAG_DRCAPTURE 3 +#define JTAG_DRSHIFT 4 +#define JTAG_DREXIT1 5 +#define JTAG_DRPAUSE 6 +#define JTAG_DREXIT2 7 +#define JTAG_DRUPDATE 8 +#define JTAG_IRSELECT 9 +#define JTAG_IRCAPTURE 10 +#define JTAG_IRSHIFT 11 +#define JTAG_IREXIT1 12 +#define JTAG_IRPAUSE 13 +#define JTAG_IREXIT2 14 +#define JTAG_IRUPDATE 15 + +static const char *JSTATE[16] = { + "RESET", "IDLE", "DRSELECT", "DRCAPTURE", + "DRSHIFT", "DREXIT1", "DRPAUSE", "DREXIT2", + "DRUPDATE", "IRSELECT", "IRCAPTURE", "IRSHIFT", + "IREXIT1", "IRPAUSE", "IREXIT1", "IRUPDATE" +}; + +struct { + u8 next0; + u8 next1; +} GRAPH[16] = { + [JTAG_RESET] = { JTAG_IDLE, JTAG_RESET }, + [JTAG_IDLE] = { JTAG_IDLE, JTAG_DRSELECT }, + [JTAG_DRSELECT] = { JTAG_DRCAPTURE, JTAG_IRSELECT }, + [JTAG_DRCAPTURE] = { JTAG_DRSHIFT, JTAG_DREXIT1 }, + [JTAG_DRSHIFT] = { JTAG_DRSHIFT, JTAG_DREXIT1 }, + [JTAG_DREXIT1] = { JTAG_DRPAUSE, JTAG_DRUPDATE }, + [JTAG_DRPAUSE] = { JTAG_DRPAUSE, JTAG_DREXIT2 }, + [JTAG_DREXIT2] = { JTAG_DRSHIFT, JTAG_DRUPDATE }, + [JTAG_DRUPDATE] = { JTAG_IDLE, JTAG_DRSELECT }, + [JTAG_IRSELECT] = { JTAG_IRCAPTURE, JTAG_RESET }, + [JTAG_IRCAPTURE] = { JTAG_IRSHIFT, JTAG_IREXIT1 }, + [JTAG_IRSHIFT] = { JTAG_IRSHIFT, JTAG_IREXIT1 }, + [JTAG_IREXIT1] = { JTAG_IRPAUSE, JTAG_IRUPDATE }, + [JTAG_IRPAUSE] = { JTAG_IRPAUSE, JTAG_IREXIT2 }, + [JTAG_IREXIT2] = { JTAG_IRSHIFT, JTAG_IRUPDATE }, + [JTAG_IRUPDATE] = { JTAG_IDLE, JTAG_DRSELECT }, +}; + +unsigned state = JTAG_RESET; +unsigned shiftcount = 0; +unsigned idlecount = 0; +u64 shiftdata = 0; + +void _sim_jtag(unsigned tdi, unsigned tms) { + switch (state) { + case JTAG_IDLE: + idlecount++; + break; + case JTAG_DRSHIFT: + case JTAG_IRSHIFT: + if (shiftcount < 64) { + if (tdi) shiftdata |= (1ULL << shiftcount); + } else { + shiftdata >>= 1; + if (tdi) shiftdata |= 0x8000000000000000ULL; + } + shiftcount++; + break; + case JTAG_DRCAPTURE: + case JTAG_IRCAPTURE: + shiftcount = 0; + shiftdata = 0; + break; + case JTAG_DRUPDATE: +#if TRACE_JTAG + printf("jtag: %4d -> DR %016lx\n", shiftcount, shiftdata); +#endif + sim_dr(shiftdata); + break; + case JTAG_IRUPDATE: +#if TRACE_JTAG + printf("jtag: %4d -> IR %016lx\n", shiftcount, shiftdata); +#endif + sim_ir(shiftdata); + break; + }; +#if TRACE_JTAG + printf("jtag: state = %s\n", JSTATE[state]); +#endif + state = tms ? GRAPH[state].next1 : GRAPH[state].next0; +} + +u8 stream[1024*1024]; +unsigned scount = 0; +unsigned last_tms; + +void sim_jtag(void) { + u8 *x = stream; + while (scount-- > 0) { + _sim_jtag(*x & 1, *x >> 1); + x++; + } +} + +void wr_jtag(unsigned tdi, unsigned tms) { + if (scount == sizeof(stream)) { + fprintf(stderr,"OVERFLOW\n"); + exit(-1); + } + stream[scount++] = tdi | (tms << 1); + last_tms = tms; +} + +void wr_tdi(unsigned count, unsigned bits) { + while (count > 0) { + wr_jtag(bits & 1, last_tms); + bits >>= 1; + count--; + } +} + +void wr_tms(unsigned count, unsigned bits, unsigned tdi) { + while (count > 0) { + wr_jtag(tdi, bits & 1); + bits >>= 1; + count--; + } +} + +// ----- disassemble mpsse stream into tdi/tms jtag vectors ----- + +#if TRACE_MPSSE +#define dprintf(x...) printf(x) +#else +#define dprintf(x...) do {} while(0) +#endif + +static void pbin(u32 val, u32 bits) { + u32 n; + for (n = 0; n < bits; n++) { + dprintf( "%c", (val & 1) ? '1' : '0'); + val >>= 1; + } +} + +// display mpsse command stream in a (sortof) human readable form +static void dismpsse(u8 *data, u32 n) { + u32 x, i; + while (n > 0) { + dprintf("%02x: ", data[0]); + switch(data[0]) { + case 0x6B: // tms rw + case 0x6F: // tms rw -veWr -veRd + dprintf( "x1 <- TDO, "); + // fall through + case 0x4B: // tms wo + dprintf( "TMS <- "); + pbin(data[2],data[1]+1); + dprintf( ", TDI <- "); + pbin((data[2] & 0x80) ? 0xFF : 0, data[1] + 1); + dprintf( "\n"); + wr_tms(data[1] + 1, data[2], (data[2] & 0x80) ? 1 : 0); + data += 3; + n -= 3; + break; + case 0x2A: // ro bits + dprintf( "x%d <- TDO\n", data[1] + 1); + data += 2; + n -= 2; + break; + case 0x28: // ro bytes + case 0x2C: // ro bytes -veWr -veRd + x = ((data[2] << 8) | data[1]) + 1; + dprintf( "x%d <- TDO\n", (int) x * 8); + data += 3; + n -= 3; + break; + case 0x1B: // wo bits + case 0x3B: // rw bits + case 0x3F: // rw bits -veWR -veRD + dprintf( "TDI <- "); + pbin(data[2], data[1] + 1); + if (data[0] == 0x3B) { + dprintf( ", x%d <- TDO\n", data[1] + 1); + } else { + dprintf( "\n"); + } + wr_tdi(data[1] + 1, data[2]); + data += 3; + n -= 3; + break; + case 0x19: // wo bytes + case 0x39: // rw bytes + case 0x3D: // rw bytes -veWR -veRD + x = ((data[2] << 8) | data[1]) + 1; + dprintf( "TDI <- "); + for (i = 0; i < x; i++) pbin(data[3+i], 8); + if (data[0] == 0x1B) { + dprintf( ", x%d <- TDO\n", (int) x); + } else { + dprintf("\n"); + } + for (i = 0; i < x; i++) wr_tdi(8, data[3+i]); + data += (3 + x); + n -= (3 + x); + break; + case 0x87: + dprintf("FLUSH\n"); + data += 1; + n -= 1; + break; + case 0xAA: + case 0xAB: + dprintf("BADCMD\n"); + data += 1; + n -=1; + break; + case 0x80: + case 0x82: + dprintf("SET%s %02x %02x\n", (data[0] & 2) ? "HI" : "LO", data[1], data[2]); + data += 3; + n -= 3; + break; + case 0x81: + case 0x83: + dprintf("READ%s\n", (data[0] & 2) ? "HI" : "LO"); + data += 1; + n -= 1; + break; + case 0x84: + case 0x85: + dprintf("LOOPBACK %s\n", (data[0] & 1) ? "OFF" : "ON"); + data += 1; + n -= 1; + break; + case 0x86: + dprintf("CLOCKDIV %d\n", (data[1] | (data[2] << 8))); + data += 3; + n -= 3; + break; + case 0x8A: + case 0x8B: + dprintf("DIVBY5 %s\n", (data[0] & 1) ? "ENABLE" : "DISABLE"); + data += 1; + n -= 1; + break; + default: + fprintf(stderr, "INVALID OPCODE %02x\n",data[0]); + n = 0; + } + } +} + +u8 buffer[1024*1024]; + +int main(int argc, char **argv) { + char hex[32]; + int n, c, count; + + n = 0; + count = 0; + for (;;) { + if ((c = getchar()) < 0) break; + if (isspace(c)) { + if (n != 2) { + printf("bad input\n"); + return -1; + } + hex[n] = 0; + buffer[count++] = strtoul(hex, 0, 16); + n = 0; + continue; + } + if (n == 2) { + printf( "bad input\n"); + return -1; + } + hex[n++] = c; + } + printf("count %d\n", count); + dismpsse(buffer, count); + printf("scount %d\n", scount); + sim_jtag(); + return 0; + +} + +// notes +// +// extract bulk out data from usbmon dump: +// grep " Bo " LOG | grep -v OK | sed 's/.* Bo .... //' +