--- /sys/lib/acid/jtag Sun Apr 7 17:18:58 2013 +++ /sys/lib/acid/jtag Sun Apr 7 00:00:00 2013 @@ -0,0 +1,245 @@ +include("/sys/lib/acid/arm"); + +// feroceon memory definitions +TmrWDenable=1<<4; +PHYSIO=0xf1000000; +TIMERREG=0xf1020300; +PsrMsvc=0x00000013; +timerctl=0; +KZERO=0x60000000; + +ureg = 0; +complex Ureg ureg; +uregtypeaddr=60; //kludge + +//generated from 8c -a +sizeofMMURegs = 32; +aggr MMURegs +{ + 'U' 0 cpid; + 'U' 4 control; + 'U' 8 ttb; + 'U' 12 dac; + 'U' 16 fsr; + 'U' 20 far; + 'U' 24 ct; + 'U' 28 pid; +}; + +defn +remapdata() +{ + mps = map(); + + while mps do { + m = head mps; + name = head m; + if name == "*data" then { + dend = head tail tail m; + map({"*data", KZERO, dend, KZERO}); + } + mps = tail mps; + } +} +//I want to be able to access MACH and other stuff +remapdata(); +memmap=map(); +//TO DO is there any arm with fpregs? +map({"regs", 0, sizeofUreg+sizeofMMURegs, 0}); + +defn +MMURegs(addr) { + complex MMURegs addr; + print(" cpid ", addr.cpid\X, "\n"); + print(" control ", addr.control\X, "\n"); + print(" ttb ", addr.ttb\X, "\n"); + print(" dac ", addr.dac\X, "\n"); + print(" fsr ", addr.fsr\X, "\n"); + print(" far ", addr.far\X, "\n"); + print(" ct ", addr.ct\X, "\n"); + print(" pid ", addr.pid\X, "\n"); +}; + +complex MMURegs mmuregs; +mmuregs=sizeofUreg; // plus size of floating point registers + +defn +mmuregs() +{ + MMURegs(mmuregs); +} + +defn +stopwdog() +{ + // change to svc mode to be able to access the address + stype = ureg.type; + *uregtypeaddr=PsrMsvc; + timerctl=*TIMERREG; + *TIMERREG = ~TmrWDenable&*TIMERREG; + *uregtypeaddr=stype; +} + +defn +startwdog() +{ + stype = ureg.type; + *uregtypeaddr=PsrMsvc; + *TIMERREG = TmrWDenable|timerctl; + *uregtypeaddr=stype; +} + +defn +hwbpset(addr, mask) +{ + printto("/proc/"+itoa(pid)+"/ctl", "breakpoint ", itoa(addr), " ", itoa(mask)); +} + +defn +veccatch(vecstr) +{ + printto("/proc/"+itoa(pid)+"/ctl", "veccatch ", vecstr); +} + +defn +reset() +{ + printto("/proc/"+itoa(pid)+"/ctl", "reset "); +} + +defn +debug(dbstr) +{ + printto("/proc/"+itoa(pid)+"/ctl", "debug ", dbstr); +} + +defn +cpuid() +{ + printto("/proc/"+itoa(pid)+"/ctl", "cpuid"); +} + +defn +jtaginfo() +{ + rc("cat /proc/"+itoa(pid)+"/ctl"); +} + +defn +sheevastop() +{ + stop(pid); + stopwdog(); +} + +defn +sheevastart() +{ + startwdog(); + start(pid); +} + +defn +sheevawaitstop() +{ + waitstop(pid); + stopwdog(); +} + +// FROM here down on, UNTRIED BUG BUG BUG!!!!! + +//CpCONTROL, h2acid +CpCmmu = 0x00000001; +CpCalign = 0x00000002; +CpCdcache = 0x00000004; +CpCwb = 0x00000008; +CpCi32 = 0x00000010; +CpCd32 = 0x00000020; +CpCbe = 0x00000080; +CpCsystem = 0x00000100; +CpCrom = 0x00000200; +CpCicache = 0x00001000; +CpChv = 0x00002000; + +//MMU definitions, h2acid +KB=1024; +MB=1024*1024; +Mbo = 0x10; +Coarse = (Mbo|1); +Section = (Mbo|2); +Fine = (Mbo|3); + +defn +ismmuon() +{ + mmu = mmuregs; + complex MMURegs mmu; + return mmu.control&CpCmmu; +} + +defn +l1x(vaddr) +{ + return ((vaddr>>20) & 0x0fff)<<2; +} + +defn +l2x(vaddr) +{ + return ((vaddr>>12) & 0xff)<<2; +} + +defn +pgsz(type) +{ + if type == Fine then { + return KB; + } else if type == Section then { + return MB; + } else if type == Coarse then { + return 4*KB; + } + return 4*KB; +} + +defn +kaddr(vaddr) +{ + return KZERO|vaddr; //very non portable +} + +defn +ttbpaddr(ttb, vaddr) +{ + if ! ismmuon() then { + print("paddr: mmu is off\n"); + return 0; + } + l1idx = l1x(vaddr); + l2idx = l2x(vaddr); + pte1 = *((kaddr(ttb)&~0x1fff) + l1idx); + if pte1 == 0 then { + return 0; + } + type = pte1 & (Fine|Section|Coarse); + sz = pgsz(type); + if type == Section then { + return (pte1 & ~(sz - 1)) + (vaddr & (sz - 1)); + } + + l2addr = pte1 & ~(sz - 1); + if l2addr == 0 then { + return 0; + } + pte2 = *(kaddr(l2addr) + l2idx); + return (pte2 & ~(sz - 1)) + (vaddr & (sz - 1)); +} + +defn +paddr(vaddr) +{ + mmu = mmuregs; + complex MMURegs mmu; + return ttbpaddr(mmu.ttb, vaddr); +} + --- /sys/man/4/jtagfs Sun Apr 7 17:19:00 2013 +++ /sys/man/4/jtagfs Sun Apr 7 00:00:00 2013 @@ -0,0 +1,202 @@ +.TH JTAGFS 4 +.SH NAME +jtagfs \- jtag kernel debugging file system +.SH SYNOPSIS +.B jtagfs +[ +.B -d +.I debugstr +] +[ +.B -b +.I motherbname +] +[ +.B -t +.I text +] +[ +.B -m +.I mountpoint +] +[ +.B -s +.I srvfile +] +.I jtagfile +.SH DESCRIPTION +.I Jjagfs +presents in +.BI /n/jtagfs/ctl +a set of process files for debugging +a kernel running on an arm over a jtag +.I device +in a manner similarly to +.IR rdbfs (4) +but without any need for the kernel collaborating. +In debug mode an arm stops and isolates itself from the +surroundings and can be probed and instructions injected +at will. +There are a number of options: +.TP +.B -d +Can be used to set the debug string, see below. +.TP +.B -m +and +.B -s +Set the mount point and srv name respectively. By default +the mount point is +.BI /n/jtagfs/ctl . +.TP +.B -b +Motherboard kind jtagfs is going to be run against. Valid parameters +are +.B sheeva, +which stands for the Feroceon Guruplug and the sheevaplug and is the default and +.B gurudisp +which stands for the Armada Guru Display. +.TP +.B -t +The +.B text +file presented is just a copy of +.I text +(default +.BR /arm/s9plug ). +It can usually be ignored, since +the debuggers open kernel +files directly rather than +using +.BI /proc/ n /text\fR. +.PP +Kernels can be remotely debugged only when they are +stopped and put in debug mode. This can be done +through instruction breakpoints, vector catching (on entry +to interrupts) or on demand using +.B stop(). +.PP +An acid library to use with the most common operations +called jtag is provided to make most common operations +simpler. In particular +.B start(), +.B stop() +and +.B waitstop() +have jtag specific variants +(for example +.B sheevastart() +) +which disable and reenable the watchdog. +Other than this functions and the symbol translations, this +program can be used to debug kernels from other operating systems. +.PP +The function +.B veccatch(str) +can be used to set a vectorcatch, which stops the processor right after +an interrupt. The string describes which interrupts to cacth. +Each caracter represents a type of interrupt: +.sp +.EX +.ta 4n +6n + 'R' Reset + 'S' SWI + 'P' PAbort + 'D' DAbort + 'I' Irq + 'F' Fiq +.fi +.EE +.PP +The function +.B debug(str) +can be used to set different levels of debug. Each character on +the string represent a different software layer: +.PP +.EX +.ta 6n +2n +4n +4n +4n +4n +4n +.sp +DFile = 'f', /* Reads, writes, flushes*/ +DPath = 'p', /* path for state transitions for tap debugging */ +DState = 's', /* state calculation and changes on tap interface */ +Dinst = 'i', /* mpsse instruction assembling debug */ +Dassln = 'a', /* print instructions before assembling */ +Dmach = 'm', /* print mpsse machine code and op results */ +Djtag = 'j', /* print jtag level operations */ +Dice = 'e', /* print icert level operations */ +Dchain = 'h', /* print icert chains */ +Dmmu = 'u', /* print MMU ops */ +Dctxt = 'c', /* dump context in and out */ +Darm = 'w', /* standard insts and so */ +Dmem = 'y', /* memory ops */ +Dfs = 'k', /* filesystem ops */ +DAll = 'A' +.fi +.EE +.SH EXAMPLES +.EX +jtagfs /dev/eiaU*/jtag +bind /n/jtagfs /proc/1 +term% acid -l jtag -k 1 /arm/s9plug +/arm/s9plug:ARM plan 9 boot image +/sys/lib/acid/port +/sys/lib/acid/arm +acid: reset() +acid: sheevastop() +ID: 0x20a023d3 +Must be 1: 1 +Manufacturer id: 0x1e9 +Part no: 0xa02 +Version: 0x2 +1: SVC/SWI Exception 0xc02e1094 no instruction +acid: dump(0xc02e1094, 4, "Xi") +0xc02e1094: 0x1204e0ff CMP.S $#0x100,R0 +0xc02e109c: 0xe0266003 B.NE etext+0x5fa536bc +0xc02e10a4: 0xe20c2040 AND $#0x8,R12,R0 +0xc02e10ac: 0xe20e1080 AND $#0x1,R14,R3 +0xc02e10b4: 0xe1811002 ORR (R0<<4),R3,R3 +acid: regs() +R0 0x5e20a2dc R1 0xf5518723 R2 0x001d1d00 +R3 0x369244e0 R4 0x2b9244fd R5 0xbbc54739 +R6 0x5e20a2dc R7 0x00000eb0 R8 0xdfd7ceb0 +R9 0x00000006 R10 0xc08c1f20 R11 0xc08c1f04 +R12 0x1d00001d R13 0xc08c1ea0 R14 0x00000000 +R15 0xc031fa8c +acid: sheevastart() +.EE +.SH SOURCE +.B /sys/src/cmd/jtag +.br +.B /sys/lib/acid/jtag +.SH "SEE ALSO" +.IR acid (1), +.IR db (1). +.br +``ARM9E-S Technical Reference Manual''. +.br +``ARM7TDMI-S Core Technical Reference Manual". +.br +``Application note 205 "Writing JTAG Sequences for Arm 9 Processors". +.br +``Design and Implementation of an On-Chip Debug for Embedded Target Systems", +Dominic Rath. +.br +``IEEE Standard 1149-1-2001 Test Access Port and Boundary Scan Architecture", +JTag IEEE standard. +.br +``AN2232C-01 Command Processor for MPSSE and MCU Host Bus Emulation Modes", +Future Technology Devices International Ltd. +.SH BUGS +After a while of the machine being on, the jtag will stop +working; maybe an autentication register needs to be set. +If this is the case +.B cpuid() +will return error. +Reset always works. +Reading and writing from memory is slow. +The filesystems needs a lot of cleaning. +Only the feroceon cpu and sheeva/guruplug boards are +supported, though more can be added. +Error report is sparse. +Jtagfs should be rewritten using the 9p library and it would shrink +to half. diff -Nru /sys/src/cmd/jtagfs/README /sys/src/cmd/jtagfs/README --- /sys/src/cmd/jtagfs/README Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/README Sun Apr 7 00:00:00 2013 @@ -0,0 +1,33 @@ +To install + +cp acidjtag /sys/lib/acid/jtag +cp acidarm /sys/lib/acid/arm +cp jtagfs.man /sys/man/4/jtagfs +mkdir /sys/src/cmd/jtagfs +cp * /sys/src/cmd/jtagfs +cd /sys/src/cmd/jtagfs +mk install + +change attachproc in libmach so that we can modify the kernel +registers. + +/sys/src/libmach/map.c:94 +< mode = ORDWR; +--- +> mode = OREAD; +recompile libmach and acid +It could also be done through acid using map(), but in this case +it is better this way. + +The armada implementation has never been run even once. + + +Byte endianness. As it is now, it depends on endianness of ARM being the +same as 386. Specifically, the /proc served is in the machine where it runs +order (i.e. in a little endian machine it would not work). This should be +changed on jtagfs, just before serving them. + +MMU state is served after the Uregs (there is no floating point, if not, it would +be after the floating point too). A map has been added to map it in read only. +h2acid for constants and macros + diff -Nru /sys/src/cmd/jtagfs/TODO /sys/src/cmd/jtagfs/TODO --- /sys/src/cmd/jtagfs/TODO Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/TODO Sun Apr 7 00:00:00 2013 @@ -0,0 +1,36 @@ + +Use openocd on a fake GURU to see reset +Unknown + +Fix start + +Fix the sheeva nsrst must be asserted while stopping/cpuiding +but this is just for the feroceon, an extra reset state before +cpuid/request. The order is strictly this: + +- UNTESTED +reset 0 1 +cpuid +set DBGRQ +reset 0 0 +wait halt + +It may work to break it into: +reset 0 1 +cpuid +reset 0 0 + +and + +reset 0 1 +set DBGRQ +reset 0 0 + + +Put currentch inside the tap description in an Armtap state. + +Add a ctl to identify the motherboard, not sure how to call initmpsse... +break it in two pieces maybe. +Identify which part is board dependant. + +Get more info about the GURU display. diff -Nru /sys/src/cmd/jtagfs/bebo.c /sys/src/cmd/jtagfs/bebo.c --- /sys/src/cmd/jtagfs/bebo.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/bebo.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,76 @@ +#include +#include + +void +hbeputv(void *p, uvlong v) +{ + uchar *a; + + a = p; + a[0] = v>>56; + a[1] = v>>48; + a[2] = v>>40; + a[3] = v>>32; + a[4] = v>>24; + a[5] = v>>16; + a[6] = v>>8; + a[7] = v; +} + +void +hbeputl(void *p, uint v) +{ + uchar *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +void +hbeputs(void *p, ushort v) +{ + uchar *a; + + a = p; + a[0] = v>>8; + a[1] = v; +} + +uvlong +behgetv(void *p) +{ + uchar *a; + uvlong v; + + a = p; + v = (uvlong)a[0]<<56; + v |= (uvlong)a[1]<<48; + v |= (uvlong)a[2]<<40; + v |= (uvlong)a[3]<<32; + v |= a[4]<<24; + v |= a[5]<<16; + v |= a[6]<<8; + v |= a[7]<<0; + return v; +} + +uint +behgetl(void *p) +{ + uchar *a; + + a = p; + return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); +} + +ushort +behgets(void *p) +{ + uchar *a; + + a = p; + return (a[0]<<8)|(a[1]<<0); +} diff -Nru /sys/src/cmd/jtagfs/bebo.h /sys/src/cmd/jtagfs/bebo.h --- /sys/src/cmd/jtagfs/bebo.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/bebo.h Sun Apr 7 00:00:00 2013 @@ -0,0 +1,6 @@ +extern uint behgetl(void *p); +extern ushort behgets(void *p); +extern uvlong behgetv(void *p); +extern void hbeputl(void *p, uint v); +extern void hbeputs(void *p, ushort v); +extern void hbeputv(void *p, uvlong v); diff -Nru /sys/src/cmd/jtagfs/chain.c /sys/src/cmd/jtagfs/chain.c --- /sys/src/cmd/jtagfs/chain.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/chain.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,219 @@ +#include +#include +#include +#include "lebo.h" +#include "bebo.h" +#include "debug.h" +#include "chain.h" + +/* functions to deal with bit chains, weird bit orderings an such */ + +#define Upper(byte, nbits) ((byte)>>(nbits)) +#define Lower(byte, nbits) ((bytes)&MSK(nbits)) +#define IsCut(bbits, ebits) (((ebits)/8 - (bbits)/8) > 0) + +/* 8: H L | 8: Next */ +/* Put at most 8 bits*/ +static void +put8bits(Chain *ch, void *p, int nbits) +{ + int e, nbye, nbie, nle; + uchar low, high, byte; + + byte = *(uchar *)p; + e = ch->e+nbits-1; /* offset of last bit to put in */ + nbie = ch->e%8; + nbye = ch->e/8; + nle = 8 - nbie; + + + if(IsCut(ch->e, e)) + ch->buf[nbye + 1] = byte >> nle; + + high = (byte & MSK(nle)) << nbie; + low = ch->buf[nbye]&MSK(nbie); + + + ch->buf[nbye] = low|high; + ch->e += nbits; +} + +/* Get at most 8 bits*/ +static uchar +get8bits(Chain *ch, int nbits) +{ + int b, nbyb, nbib, nlb; + uchar low, high, res; + + b = ch->b+nbits-1; + nbib = ch->b % 8; + nbyb = ch->b / 8; + nlb = 8 - nbib; + if(nlb > nbits) + nlb = nbits; + + low = MSK(nlb) & (ch->buf[nbyb] >> nbib); + if(IsCut(ch->b, b)) + high = (ch->buf[nbyb + 1] & MSK(nbib)) << nlb; + else + high = 0; + res = MSK(nbits)&(high | low); + ch->b += nbits; + return res; +} + +/* + * Putbits and getbits could be made more efficient + * simply by using memmove in the special case where + * a bunch of bytes is aligned, but I don't care. + * Packing in words would also help the so inclined. + */ +void +putbits(Chain *ch, void *p, int nbits) +{ + int nby, nbi, i; + uchar *vp; + + vp = p; + nby = nbits / 8; + nbi = nbits % 8; + + for(i = 0; i < nby; i++) + put8bits(ch, vp++, 8); + + if(nbi != 0) + put8bits(ch, vp, nbi); +} + +void +getbits(void *p, Chain *ch, int nbits) +{ + int nby, nbi, i; + uchar *vp; + + assert(ch->e >= ch->b); + nby = nbits / 8; + nbi = nbits % 8; + + vp = p; + for(i = 0; i < nby; i++) + *vp++ = get8bits(ch, 8); + + if(nbi != 0) + *vp = get8bits(ch, nbi); +} + +static void +revbych(Chain *ch) +{ + int i, nb; + + nb = (ch->e-ch->b+7)/8; + + for(i = 0; i < nb; i++) + ch->buf[i] = rtab[ch->buf[i]]; +} + +void +printchain(Chain *ch) +{ + int i, ng, nb; + uchar msk, c; + Chain *ch2; + + fprint(2, "chain buf:%#p b:%d e:%d\n", ch->buf, ch->b, ch->e); + ch2 = malloc(sizeof(Chain)); + if(ch2 == nil) + sysfatal("no memory"); + memmove(ch2, ch, sizeof(Chain)); + + + ng = 8; + nb = (ch2->e-ch2->b+7)/8; + + for(i = 0; i < nb; i++){ + if(ch2->e - ch2->b < ng) + ng = ch2->e - ch2->b; + getbits(&c, ch2, ng); + if(i != 0 && i%15 == 0) + fprint(2, "\n"); + if(ng < 8){ + msk = MSK(ng); + fprint(2, "%#2.2x ", c&msk); + } + else + fprint(2, "%#2.2x ", c); + + } + if(i%16 != 0) + fprint(2, "\n"); + + free(ch2); +} + +/* Good for debugging */ +static void +printchslice(Chain *ch, int b, int e) +{ + Chain *ch2; + + fprint(2, "Slice [%d, %d], b:%d e:%d\n", b, e, ch->b, ch->e); + ch2 = malloc(sizeof(Chain)); + if(ch2 == nil) + sysfatal("memory"); + memmove(ch2, ch, sizeof(Chain)); + if(b > e){ + fprint(2, "bad args in region\n"); + return; + } + if(b < ch2->b || b > ch2->e || e > ch2-> e || e < ch2->b){ + fprint(2, "bad region\n"); + return; + } + ch2->b = b; + ch2->e = e; + + fprint(2, "Slice - "); + printchain(ch2); + free(ch2); +} + + +static u32int +revbytes(u32int *v) +{ + u32int rv; + uchar *a, *b; + + a = (uchar *)v; + b = (uchar *)&rv; + + b[3] = rtab[a[3]]; + b[2] = rtab[a[2]]; + b[1] = rtab[a[1]]; + b[0] = rtab[a[0]]; + + return rv; +} + +u32int +hmsbputl(u32int *v) +{ + u32int rv, bev; + + hbeputl(&bev, *v); + rv = revbytes(&bev); + + return rv; +} + +u32int +msbhgetl(u32int *v) +{ + u32int rv, rev; + + rev = revbytes(v); + rv = lehgetl(&rev); + return rv; +} + diff -Nru /sys/src/cmd/jtagfs/chain.h /sys/src/cmd/jtagfs/chain.h --- /sys/src/cmd/jtagfs/chain.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/chain.h Sun Apr 7 00:00:00 2013 @@ -0,0 +1,23 @@ +typedef struct Chain Chain; + +extern uchar rtab[]; /* generated by genrtab.c */ + +#define MSK(nbits) ((1UL<<(nbits))-1) + +enum{ + MaxChLen = 128, /* bytes */ +}; + + +/* bit chain, can put at the beginning, get at the end */ +struct Chain { + int b; /* offset start in bits, (first full) */ + int e; /* offset end in bits (first empty) */ + uchar buf[MaxChLen]; +}; + +extern void getbits(void *p, Chain *ch, int nbits); +extern u32int hmsbputl(u32int *v); +extern u32int msbhgetl(u32int *v); +extern void printchain(Chain *ch); +extern void putbits(Chain *ch, void *p, int nbits); diff -Nru /sys/src/cmd/jtagfs/debug.c /sys/src/cmd/jtagfs/debug.c --- /sys/src/cmd/jtagfs/debug.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/debug.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,35 @@ +#include +#include + +uchar debug[255]; + +int +dprint(char d, char *fmt, ...) +{ + int n; + va_list args; + + if(!debug['A'] && !debug[d]) + return -1; + va_start(args, fmt); + n = vfprint(2, fmt, args); + va_end(args); + return n; +} + + +void +dumpbuf(char d, uchar *buf, int bufsz) +{ + int i; + + if(d != 0 && !debug[d]) + return; + for(i = 0; i < bufsz; i++){ + fprint(2, "%#2.2x ", buf[i]); + if(i != 0 && (i + 1) % 8 == 0) + fprint(2, "\n"); + } + if(i %16 != 0) + fprint(2, "\n"); +} diff -Nru /sys/src/cmd/jtagfs/debug.h /sys/src/cmd/jtagfs/debug.h --- /sys/src/cmd/jtagfs/debug.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/debug.h Sun Apr 7 00:00:00 2013 @@ -0,0 +1,24 @@ +extern uchar debug[]; + +enum { + DXFlush = 'x', /* special markers for debugging on the commands, disrupts behaviour*/ + + DFile = 'f', /* Reads, writes, flushes*/ + DPath = 'p', /* path for state transitions for tap debugging */ + DState = 's', /* state calculation and changes on tap interface */ + Dinst = 'i', /* mpsse instruction assembling debug */ + Dassln = 'a', /* print instructions before assembling */ + Dmach = 'm', /* print mpsse machine code and op results */ + Djtag = 'j', /* print jtag level operations */ + Dice = 'e', /* print icert level operations */ + Dchain = 'h', /* print icert chains */ + Dmmu = 'u', /* print MMU ops */ + Dctxt = 'c', /* dump context in and out */ + Darm = 'w', /* arm insts and so */ + Dmem = 'y', /* memory ops */ + Dfs = 'k', /* filesystem ops */ + DAll = 'A' +}; + +extern int dprint(char d, char *fmt, ...); +extern void dumpbuf(char d, uchar *buf, int bufsz); diff -Nru /sys/src/cmd/jtagfs/genrtab.c /sys/src/cmd/jtagfs/genrtab.c --- /sys/src/cmd/jtagfs/genrtab.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/genrtab.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include "debug.h" + +static uchar +reverse(uchar c) +{ + uchar rc; + int i, j, ns; + + rc = 0; + for(i = 0, j = 1; i < 8; i++, j <<= 1){ + ns = (7 - 2*i); + if( ns < 0){ + ns = -ns; + rc |= (c&j) >> ns; + } + else + rc |= (c&j) << ns; + } + return rc; +} + + +void +main(int , char *[]) +{ + int i; + + print("/* Generated automatically, *DO NOT EDIT* */\n\n"); + print("#include \n"); + print("#include \n\n"); + print("uchar rtab[256]=\n"); + print("{\n\t"); + for(i = 0; i < 256; i++){ + print("%#2.2ux, ", reverse(i)); + if(i != 0 && (i + 1) % 8 == 0) + print("\n\t"); + } + print("\n};"); + exits(nil); +} + diff -Nru /sys/src/cmd/jtagfs/guide /sys/src/cmd/jtagfs/guide --- /sys/src/cmd/jtagfs/guide Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/guide Sun Apr 7 00:00:00 2013 @@ -0,0 +1,85 @@ +hwbpset(0x60800c28, 0x3ff) +hwbpset(_vpabt, 0x0) +hwbpset(_vsvc, 0x0) +hwbpset(_vdabt, 0x0) +hwbpset(_vund, 0x0) +hwbpset(_virq, 0x0) +veccatch("SPDIF") + +dump(_vrst, 30, "Xi") + +usb/serial + +echo b115200 s15 l8 pn > /dev/eiaU3.1/eiaUctl +window -m +window -m +con /dev/eiaU3.1/eiaU + +echo debug . > /n/jtagfs/ctl + +window -scroll -r 0 5 700 369 plug + + +kill 8.tagfs|rc +/usr/paurea/src/jtag/8.jtagfs /dev/jtag*/jtag +echo reset > /n/jtagfs/ctl + +#start the jtag +echo cpuid > /n/jtagfs/ctl +echo breakpoint 0xc02dc244 0x0 > /n/jtagfs/ctl + +echo breakpoint 0x0062822c 0x00000000 > /n/jtagfs/ctl + echo breakpoint 0xc0208124 0xffff0000 > /n/jtagfs/ctl + +#see debug.h for the string +echo debugstr kc> /n/jtagfs/ctl + +BR 9 , pcadjust -5 works well +BR 6, 2 works well + +cat /n/jtagfs/ctl +echo stop > /n/jtagfs/ctl +echo start > /n/jtagfs/ctl + + echo debustr ew > /n/jtagfs/ctl +#/n/jtagfs/mem can be used to read raw memory at an arbitrary offset + + +# set entry on exceptions +# changed so that turn it is turn on in debug state +# + +#wait for entry in debug state (with timeout) + + +echo veccatch D > /n/jtagfs/ctl + +echo waitstop > /n/jtagfs/ctl +echo start > /n/jtagfs/ctl + + +window -scroll -r 0 5 700 369 plug + +bind /n/jtagfs /proc/1 +echo stop > /n/jtagfs/ctl +echo debug ky > /proc/1/ctl +acid -l /usr/paurea/src/jtag/acidjtag -k 1 /arm/s9plug + +_tiki: + //B _tiki +dump(*R15, 4, "X") +dump(*R15, 4, "Xi") +dump(0xc03207fc, 4, "X") +mem(0x00621120, "X") +regs() +troff -man jtagfs.man|page -w + +bootcmd=bootp ; bootp; tftp 0x1000 /srv/tftp/plugini ; bootp ; tftp 0x800000 /srv/tftp/9plug ; go 0x800000 + + + +boot linux: + +setenv bootargs $(bootargs) $(mtdpartitions) $(bootargs_root); +nand read.e 0x00800000 0x00100000 0x00400000; +bootm 0x00800000; diff -Nru /sys/src/cmd/jtagfs/h2acid /sys/src/cmd/jtagfs/h2acid --- /sys/src/cmd/jtagfs/h2acid Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/h2acid Sun Apr 7 00:00:00 2013 @@ -0,0 +1,30 @@ +#!/bin/awk -f + +# cat /sys/src/9/kw/mem.h /sys/src/9/kw/arm.h|h2acid|grep -v define + +BEGIN{ + isparen=0 +} + + +/#define ?[^ \(]+\(/{ + isparen=1 + fullname=$0 + sub(/\/\*.*\*\//, "", fullname) + sub(/\/\/.*/, "", fullname) + sub(/[ ]+$/, "", fullname); + name=$2 + sub(/\(.*/, "", name); + params=fullname + sub(/[^\(]+\(/, "", params) + sub(/\).*/, "", params); + val=fullname + sub(/[^\)]+\)/, "", val); + print("\ndefn", name"("params")", " {\n\t"name "res = " val ";\n}\n") +} + +/#define.*/ && !isparen{ + print($2 " = " $3 ";"); +} + +/.*/{ isparen = 0} diff -Nru /sys/src/cmd/jtagfs/icert.c /sys/src/cmd/jtagfs/icert.c --- /sys/src/cmd/jtagfs/icert.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/icert.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,970 @@ +#include +#include +#include +#include "chain.h" +#include "debug.h" +#include "tap.h" +#include "lebo.h" +#include "bebo.h" +#include "jtag.h" +#include "icert.h" +#include "mpsse.h" +#include "/sys/src/9/kw/arm.h" +#include "mmu.h" + +/* + * Read, paying special atention to anything called + * embedded-ice/debug of the arm core, normally + * there is a chapter called Debug or Debugging and some + * details sprinkled around. + * - ARM9E-S "Technical Reference Manual" + * - ARM7TDMI-S "Core Technical Reference Manual" + * - Open On-Chip Debugger thesis + * "Design and Implementation of an On-Chip Debug for Embedded + * Target Systems" + * - Application note 205 "Writing JTAG Sequences for Arm 9 Processors" + * The bit ordering on the last one is confusing/wrong, but the + * procedures for doing things clarify a lot. + */ + + +/* + * BUG: print pack and unpack could be generated automatically + * except for the wild endianness of the instr. Minilanguage? + */ + +static char * +printech1(EiceChain1 *ec1, char *s, int ssz) +{ + char *e, *te; + + te = s + ssz -1; + + e = seprint(s, te, "rwdata: %#8.8ux\n", ec1->rwdata); + e = seprint(e, te, "wptandbkpt: %1.1d\n", ec1->wptandbkpt); + e = seprint(e, te, "sysspeed: %1.1d\n", ec1->sysspeed); + e = seprint(e, te, "instr: %#8.8ux\n", ec1->instr); + return e; +} + +static int +packech1(Chain *ch, EiceChain1 *ec1) +{ + EiceChain1 lec; + uchar unused; + + unused = 0; + + lec.instr = hmsbputl(&ec1->instr); + hbeputl(&lec.rwdata, ec1->rwdata); + + putbits(ch, &lec.rwdata, RWDataSz); + putbits(ch, &unused, 1); + putbits(ch, &lec.wptandbkpt, WpTanDBKptSz); + + putbits(ch, &ec1->sysspeed, SysSpeedSz); + putbits(ch, &lec.instr, InstrSz); + + return 0; +} + +static int +unpackech1(EiceChain1 *ec1, Chain *ch) +{ + uchar unused; + + unused = 0; + + + getbits(&ec1->rwdata, ch, RWDataSz); + getbits(&unused, ch, 1); + getbits(&ec1->wptandbkpt, ch, WpTanDBKptSz); + getbits(&ec1->sysspeed, ch, SysSpeedSz); + getbits(&ec1->instr, ch, InstrSz); + + ec1->instr = msbhgetl(&ec1->instr); + ec1->rwdata = lehgetl(&ec1->rwdata); + + return 0; +} + + +static char * +printech2(EiceChain2 *ec2, char *s, int ssz) +{ + char *e, *te; + + te = s + ssz -1; + + e = seprint(s, te, "data: %#8.8ux\n", ec2->data); + e = seprint(e, te, "addr: %#2.2ux\n", ec2->addr); + e = seprint(e, te, "rw: %1.1d\n", ec2->rw); + return e; +} + +static int +valregaddr(uchar addr) +{ + if(addr <= CtlMskReg) + return 1; + if( (addr&Wp0) <= CtlMskReg || (addr&Wp1) <= CtlMskReg) + return 1; + return 0; +} + +static int +packech2(Chain *ch, EiceChain2 *ec2) +{ + EiceChain2 lec; + + if(!valregaddr(ec2->addr)) + return -1; + + hleputl(&lec.data, ec2->data); + + putbits(ch, &lec.data, DataSz); + putbits(ch, &ec2->addr, AddrSz); + putbits(ch, &ec2->rw, RWSz); + return 0; +} + +static int +unpackech2(EiceChain2 *ec2, Chain *ch) +{ + + getbits(&ec2->data, ch, DataSz); + getbits(&ec2->addr, ch, AddrSz); + getbits(&ec2->rw, ch, RWSz); + + if(!valregaddr(ec2->addr)) + return -1; + + ec2->data = lehgetl(&ec2->data); + + return 0; +} + + +static char * +printwp(EiceWp *wp, char *s, int ssz) +{ + char *e, *te; + + te = s + ssz -1; + + e = seprint(s, te, "addrval: %#8.8ux\n", wp->addrval); + e = seprint(e, te, "addmsk: %#8.8ux\n", wp->addrmsk); + e = seprint(e, te, "dataval: %#8.8ux\n", wp->dataval); + e = seprint(e, te, "datamsk: %#8.8ux\n", wp->datamsk); + + e = seprint(e, te, "ctlval: %#4.4ux\n", wp->ctlval); + e = seprint(e, te, "ctlmsk: %#2.2ux\n", wp->ctlmsk); + return e; +} + +static char * +printregs(EiceRegs *regs, char *s, int ssz) +{ + char *e, *te, i; + + te = s + ssz -1; + + e = seprint(s, te, "debug: %#2.2ux\n", regs->debug); + e = seprint(e, te, "debsts: %#2.2ux\n", regs->debsts); + e = seprint(e, te, "veccat: %#2.2ux\n", regs->veccat); + e = seprint(e, te, "debcomctl: %#2.2ux\n", regs->debcomctl); + e = seprint(e, te, "debcomdata: %#8.8ux\n", regs->debcomdata); + + for(i = 0; iwp); i++) + e = printwp(regs->wp + i, e, ssz+(s-e)); + return e; +} + +static u32int +unpackcpuid(uchar *v) +{ + u32int id; + + id = lehgetl(v); + return id; +} + +static void +prcpuid(u32int id) +{ + + fprint(2, "ID: %#8.8ux\n", id); + fprint(2, "Must be 1: %d\n", id&1); + fprint(2, "Manufacturer id: %#lux\n", MANID(id)); + fprint(2, "Part no: %#lux\n", PARTNO(id)); + fprint(2, "Version: %#lux\n", VERS(id)); + return; +} + + + +int +setinst(JMedium *jmed, uchar inst, int nocommit) +{ + int res, com; + ShiftTDesc req; + uchar buf[1+ShiftMarg]; + + memset(buf, 0, 5); + buf[0] = inst; + com = 0; + + dprint(Djtag, "Setting jtag instr: %#2.2ux, commit: %d\n", + inst, !nocommit); + if(nocommit) + com = ShiftNoCommit; + req.reg = TapIR; + req.buf = buf; + req.nbits = InLen; + req.op = ShiftOut|com; + + res = tapshift(jmed, &req, nil, jmed->tapcpu); + if(res < 0){ + werrstr("setinst: shifting instruction %#2.2ux for jtag", inst); + return -1; + } + return res; +} + +int +icesetreg(JMedium *jmed, int regaddr, u32int val) +{ + Chain ch; + ShiftTDesc req; + EiceChain2 ec2; + char debugstr[128]; + int res; + + dprint(Dice, "Set eice 2 reg[%#2.2ux] to %#8.8ux\n", regaddr, val); + ec2.data = val; + ec2.addr = regaddr; + ec2.rw = 1; + memset(&ch, 0, sizeof ch); + + + res = packech2(&ch, &ec2); + if(res < 0) + return -1; + if(debug[Dchain] || debug[DAll]){ + printech2(&ec2, debugstr, sizeof debugstr); + fprint(2, "%s", debugstr); + printchain(&ch); + } + req.reg = TapDR; + req.buf = ch.buf; + req.nbits = EiceCh2Len; + req.op = ShiftOut; + res = tapshift(jmed, &req, nil, jmed->tapcpu); + if(res < 0) + return -1; + + return 0; +} + + +u32int +icegetreg(JMedium *jmed, int regaddr) +{ + Chain ch; + ShiftTDesc req; + int res; + EiceChain2 ec2; + char debugstr[128]; + + ec2.data = 0; + ec2.addr = regaddr; + ec2.rw = 0; + memset(&ch, 0, sizeof ch); + + res = packech2(&ch, &ec2); + if(res < 0) + return -1; + if(debug[Dchain] || debug[DAll]){ + printech2(&ec2, debugstr, sizeof debugstr); + fprint(2, "%s", debugstr); + printchain(&ch); + } + req.reg = TapDR; + req.buf = ch.buf; + req.nbits = EiceCh2Len; + req.op = ShiftOut; + res = tapshift(jmed, &req, nil, jmed->tapcpu); + if(res < 0) + return -1; + req.reg = TapDR; + req.buf = ch.buf; + req.nbits = EiceCh2Len; + req.op = ShiftOut|ShiftIn; + res = tapshift(jmed, &req, nil, jmed->tapcpu); + if(res < 0) + return -1; + ch.b = 0; + ch.e = EiceCh2Len; + res = unpackech2(&ec2, &ch); + if(res < 0) + return -1; + if(debug[Dice] || debug[DAll]){ + printchain(&ch); + printech2(&ec2, debugstr, sizeof debugstr); + fprint(2, "Get eice 2 reg[%#2.2ux] to %#8.8ux\n", regaddr, ec2.data); + } + return ec2.data; +} + +int +setchain(JMedium *jmed, int commit, uchar chain) +{ + ShiftTDesc req; + uchar data[1+ShiftMarg]; + int res, com; + + dprint(Dice, "Set chain %d, chain before %d\n", chain, jmed->currentch); + if(jmed->currentch == chain) + return 0; + res = setinst(jmed, InScanN, 1); + if(res < 0) + return -1; + data[0] = chain; + com = 0; + if(!commit) + com = ShiftNoCommit; + req.reg = TapDR; + req.buf = data; + req.nbits = 5; + req.op = ShiftPauseIn|ShiftOut|com; + res = tapshift(jmed, &req, nil, jmed->tapcpu); + if(res < 0) + return -1; + res = setinst(jmed, InTest, 1); + if(res < 0) + return -1; + jmed->currentch = chain; + return 0; +} + + +int +armgofetch(JMedium *jmed, u32int instr, int sysspeed) +{ + char debugstr[128]; + ShiftTDesc req; + EiceChain1 ec1; + Chain ch; + int res; + + ec1.instr = instr; + ec1.sysspeed = sysspeed; + ec1.wptandbkpt = 0; + ec1.rwdata = 0; + memset(&ch, 0, sizeof (Chain)); + res = packech1(&ch, &ec1); + if(res < 0) + return -1; + if(debug[Dchain] || debug[DAll]){ + printech1(&ec1, debugstr, sizeof debugstr); + fprint(2, "%s", debugstr); + printchain(&ch); + } + req.reg = TapDR; + req.buf = ch.buf; + req.nbits = ch.e-ch.b; + req.op = ShiftOut|ShiftPauseOut|ShiftPauseIn; + res = tapshift(jmed, &req, nil, jmed->tapcpu); + if(res < 0) + return -1; + return 0; + +} + +int +armgetexec(JMedium *jmed, int nregs, u32int *regs, u32int inst) +{ + ShiftTDesc req; + int i, res, flags; + uchar *buf; + ShiftRDesc *replies; + + dprint(Darm, "Exec: %#8.8ux\n", inst); + replies = mallocz(nregs*sizeof(ShiftRDesc), 1); + if(replies == nil) + sysfatal("read, no mem %r"); + buf = mallocz(EiceCh1Len+ShiftMarg, 1); + if(buf == nil) + sysfatal("read, no mem %r"); + armgofetch(jmed, inst, 0); + /* INST in decode stage */ + armgofetch(jmed, ARMNOP, 0); + /* INST in execute stage */ + armgofetch(jmed, ARMNOP, 0); + /* INST in access stage */ + flags = ShiftAsync|ShiftIn|ShiftPauseIn|ShiftPauseOut; + for(i = 0; i < nregs; i++){ + req.reg = TapDR; + req.buf = buf; + req.nbits = EiceCh1Len; + req.op = flags; + res = tapshift(jmed, &req, &replies[i], jmed->tapcpu); + if(res < 0) + return -1; + } + for(i = 0; i < nregs; i++){ + res = jmed->rdshiftrep(jmed, buf, &replies[i]); + if(res < 0) + return -1; + regs[i] = *(u32int *)buf; + dprint(Darm, "Read [%d]: %#8.8ux\n", i, regs[i]); + } + armgofetch(jmed, ARMNOP, 0); + /* INST in writeback stage */ + armgofetch(jmed, ARMNOP, 0); + /* INST end, pipeline full of NOPs */ + + free(buf); + free(replies); + return 0; +} + +int +armsetexec(JMedium *jmed, int nregs, u32int *regs, u32int inst) +{ + ShiftTDesc req; + int i, res, flags; + uchar *buf; + + ShiftRDesc *replies; + + dprint(Darm, "Exec: %#8.8ux\n", inst); + replies = mallocz(nregs*sizeof(ShiftRDesc), 1); + if(replies == nil) + sysfatal("read, no mem %r"); + buf = mallocz(EiceCh1Len+ShiftMarg, 1); + if(buf == nil) + sysfatal("readregs, no mem %r"); + armgofetch(jmed, inst, 0); + /* STMIA in decode stage */ + armgofetch(jmed, ARMNOP, 0); + /* STMIA in execute stage */ + armgofetch(jmed, ARMNOP, 0); + /* STMIA in memory acess stage */ + flags = ShiftOut|ShiftPauseIn|ShiftPauseOut; + for(i = 0; i < nregs; i++){ + *(u32int *)buf = regs[i]; + dprint(Darm, "Set [%d]: %#8.8ux\n", i, regs[i]); + req.reg = TapDR; + req.buf = buf; + req.nbits = EiceCh1Len; + req.op = flags; + res = tapshift(jmed, &req, nil, jmed->tapcpu); + if(res < 0) + return -1; + } + armgofetch(jmed, ARMNOP, 0); + /* STMIA in writeback stage */ + armgofetch(jmed, ARMNOP, 0); + /* STMIA end, pipeline full of NOPs */ + + free(buf); + free(replies); + return 0; +} + +/* reads r0-r15 to regs if bits of mask are 1 */ +static int +armgetregs(JMedium *jmed, u32int mask, u32int *regs) +{ + int ireps, nregs, r, i; + u32int *regrd; + + nregs = 0; + ireps = 0; + for(i = 0; i < 16; i++){ + if( (1 << i) & mask) + nregs++; + } + regrd = mallocz(nregs*sizeof(u32int), 1); + if(regrd == nil) + sysfatal("read, no mem %r"); + + r = armgetexec(jmed, nregs, regrd, ARMSTMIA|mask); + if(r < 0) + return -1; + + for(i = 0; i < 16; i++){ + if( (1 << i) & mask){ + regs[i] = regrd[ireps++]; + dprint(Darm, "rd register r%d: %#8.8ux\n", i, regs[i]); + } + } + free(regrd); + return 0; +} +/* sets r0-r15 to regs if bits of mask are 1 */ +int +armsetregs(JMedium *jmed, int mask, u32int *regs) +{ + int ireps, nregs, r, i; + u32int *regrw; + + nregs = 0; + ireps = 0; + for(i = 0; i < 16; i++){ + if( (1 << i) & mask) + nregs++; + } + regrw = mallocz(nregs*sizeof(u32int), 1); + if(regrw == nil) + sysfatal("read, no mem %r"); + for(i = 0; i < 16; i++){ + if( (1 << i) & mask){ + regrw[ireps++] = regs[i]; + dprint(Darm, "wr register r%d: %#8.8ux\n", i, regs[i]); + } + } + r = armsetexec(jmed, nregs, regrw, ARMLDMIA|mask); + if(r < 0) + return -1; + + free(regrw); + return 0; +} + +static void +armprctxt(ArmCtxt *ctxt) +{ + int i; + + fprint(2, "Arm is in debug: %d\nregs\n", ctxt->debug); + for(i = 0; i < 16; i++) + fprint(2, "r%d: %#8.8ux\n", i, ctxt->r[i]); + fprint(2, "cpsr: %#8.8ux\n", ctxt->cpsr); + fprint(2, "spsr: %#8.8ux\n", ctxt->spsr); +} + +char * +armsprctxt(ArmCtxt *ctxt, char *s, int ssz) +{ + char *e, *te; + int i; + + te = s + ssz -1; + + e = seprint(s, te, "Arm is in debug: %d\nregs\n", ctxt->debug); + for(i = 0; i < 16; i++) + e = seprint(e, te, "r%d: %#8.8ux\n", i, ctxt->r[i]); + e = seprint(e, te, "cpsr: %#8.8ux\n", ctxt->cpsr); + e = seprint(e, te, "spsr: %#8.8ux\n", ctxt->spsr); + return e; +} + +static char dbgstr[4*1024]; + +int +armsavectxt(JMedium *jmed, ArmCtxt *ctxt) +{ + int res; + + res = setchain(jmed, 0, 1); + if(res < 0) + goto Error; + + res = armgetregs(jmed, 0xffff, ctxt->r); + if(res < 0) + goto Error; + res = armgofetch(jmed, ARMMRSr0CPSR, 0); + if(res < 0) + goto Error; + res = armgetexec(jmed, 1, &ctxt->cpsr, ARMSTMIA|0x0001); + if(res < 0) + goto Error; + res = armgofetch(jmed, ARMMRSr0SPSR, 0); + if(res < 0) + goto Error; + res = armgetexec(jmed, 1, &ctxt->spsr, ARMSTMIA|0x0001); + if(res < 0) + goto Error; + res = mmurdregs(jmed, ctxt); + if(res < 0){ + werrstr("mmurdregs %r"); + return -1; + } + if(debug[Dmmu] || debug[DAll]){ + printmmuregs(ctxt, dbgstr, sizeof dbgstr); + dprint(Dfs, "MMU state:\n%s\n", dbgstr); + } + if(debug[Dctxt] || debug[DAll]){ + fprint(2, "Arm save ctxt\n"); + armprctxt(ctxt); + } + return 0; +Error: + return -1; +} + +static int +armjmpctxt(JMedium *jmed, ArmCtxt *ctxt) +{ + int res; + + if(debug[Dctxt] || debug[DAll]){ + fprint(2, "Arm jmp ctxt\n"); + armprctxt(ctxt); + } + res = armsetexec(jmed, 1, &ctxt->cpsr, ARMLDMIA|0x0001); + if(res < 0) + return -1; + res = armgofetch(jmed, ARMMSRr0CPSR, 0); + if(res < 0) + return -1; + /* it is quite important this is the last instr for PC calculations */ + ctxt->r[15] += ctxt->pcadjust; + res = armsetregs(jmed, 0xffff, ctxt->r); + if(res < 0) + return -1; + + return 0; +} + + +int +armfastexec(JMedium *jmed, u32int inst) +{ + int res; + + dprint(Darm, "Exec: %#8.8ux\n", inst); + res = armgofetch(jmed, ARMNOP, 0); + if(res < 0) + return -1; + + res = armgofetch(jmed, ARMNOP, 0); + if(res < 0) + return -1; + res = armgofetch(jmed, ARMNOP, 0); + if(res < 0) + return -1; + res = armgofetch(jmed, inst, 0); + if(res < 0) + return -1; + res = armgofetch(jmed, ARMNOP, 1); + if(res < 0) + return -1; + return 0; +} + +int +icedebugstate(JMedium *jmed) +{ + int res; + + res = setchain(jmed, ChCommit, 2); + if(res < 0) + return 0; + return icegetreg(jmed, DebStsReg) & DBGACK; +} + +int +icewaitdebug(JMedium *jmed) +{ + int i, res; + + res = setchain(jmed, ChCommit, 2); + if(res < 0) + return -1; + + for(i = 0; i < 20; i ++){ + if(icedebugstate(jmed)) + break; + sleep(100); + } + if( i == 20) + return -1; + + icesetreg(jmed, DebugCtlReg, INTDIS|DBGACK); /* clear RQ */ + return 0; +} + +int +armrdmemwd(JMedium *jmed, u32int addr, u32int *data, int sz) +{ + int res; + u32int d, byte; + + res = setchain(jmed, ChCommit, 1); + if(res < 0) + sysfatal("setchain %r"); + byte = 0; + if(sz == 1) + byte = BYTEWDTH; + /* load address in r0 */ + res = armsetexec(jmed, 1, &addr, ARMLDMIA|0x0001); + if(res < 0) + return -1; + + /* LDR r1, [r0] place data in r1 */ + res = armfastexec(jmed, byte|ARMLDRindr1xr0); + if(res < 0) + return -1; + + setinst(jmed, InRestart, 0); + res = icewaitdebug(jmed); + if(res < 0) + return -1; + res = setchain(jmed, ChCommit, 1); + if(res < 0) + sysfatal("setchain %r"); + armgetexec(jmed, 1, &d, byte|ARMSTMIA|0x0002); + + *data = d; + dprint(Dmem, "Read data %#8.8ux addr %#8.8ux \n", + d, addr); + return sz; +} + +int +armwrmemwd(JMedium *jmed, u32int addr, u32int data, int sz) +{ + int res; + u32int byte; + + dprint(Dmem, "Write data %#8.8ux addr %#8.8ux \n", + data, addr); + res = setchain(jmed, ChCommit, 1); + if(res < 0) + sysfatal("setchain %r"); + byte = 0; + if(sz == 1) + byte = BYTEWDTH; + /* load address in r0 */ + res = armsetexec(jmed, 1, &addr, ARMLDMIA|0x0001); + if(res < 0) + return -1; + /* load data in r1 */ + res = armsetexec(jmed, 1, &data, byte|ARMLDMIA|0x0002); + if(res < 0) + return -1; + + /* STR [r0], r1 place data in [r0] */ + res = armfastexec(jmed, ARMSTRindxr0r1); + if(res < 0) + return -1; + + setinst(jmed, InRestart, 0); + res = icewaitdebug(jmed); + if(res < 0) + return -1; + return sz; +} + + +u32int +armidentify(JMedium *jmed) +{ + uchar *buf; + ShiftTDesc req; + u32int cpuid; + int res, i; + + //if(jmed->motherb == Sheeva) + // jmed->resets(jmed->mdata, 0, 1); + buf = malloc(sizeof(u32int)*0x100+ ShiftMarg); + if(buf == nil) + sysfatal("memory"); + + for(i = 0; i < sizeof(u32int)*0x100; i++) + buf[i] = i; /* just a pattern to see in debugging */ + + req.reg = TapDR; + req.buf = buf; + req.nbits = sizeof(u32int)*0x100; /* Bug by 8? */ + req.op = ShiftOut|ShiftIn; + res = tapshift(jmed, &req, nil, jmed->tapcpu); + if(res < 0){ + free(buf); + return ~0; + } + //if(jmed->motherb == Sheeva) + // jmed->resets(jmed->mdata, 0, 0); + cpuid = unpackcpuid(buf); + if(cpuid != jmed->taps[jmed->tapcpu].hwid){ + fprint(2, "cpuid: %#8.8ux != %#8.8ux\n", cpuid, jmed->taps[jmed->tapcpu].hwid); + free(buf); + return ~0; + } + prcpuid(cpuid); + free(buf); + return cpuid; +} + +/* see if the fixed bypass value is ok */ +int +armbpasstest(JMedium *jmed) +{ + ShiftTDesc req; + uchar buf[2+ShiftMarg]; + int res; + + memset(buf, 0xff, sizeof(buf)); + req.reg = TapIR; + req.buf = buf; + req.nbits = 2+InLen; + req.op = ShiftOut|ShiftIn; + res = tapshift(jmed, &req, nil, jmed->tapcpu); + if(res < 0) + sysfatal("jmed->regshift %r"); + if((buf[0] & 0xf) != 0x1){ + fprint(2, "bad bypass: %#2.2ux\n", buf[0]); + return -1; + } + + return 0; +} + +/* BUG: werrstr in the inner functions to indicate error, warnings... */ + + +int +icewaitentry(JMedium *jmed, ArmCtxt *ctxt) +{ + int res; + + if(ctxt->debugreas == NoReas){ + werrstr("No debug activated"); + return -1; + } + res = setchain(jmed, ChCommit, 2); + if(res < 0) + return -1; + res = icewaitdebug(jmed); + if(res < 0) + return -1; + + res = setchain(jmed, ChNoCommit, 1); + if(res < 0) + return -1; + + /* BUG: should change to Arm from Thumb or Jazelle, adjust, keep PC. + * If I ever come down to doing this, Remember to repeat Thumb inst + * to be endian independant... + */ + res = armsavectxt(jmed, ctxt); + if(res < 0) + return -1; + ctxt->debug = 1; + + ctxt->pcadjust = 0; /* BUG: Thumb not impl */ + ctxt->r[15] -= 6*ArmInstSz; + switch(ctxt->debugreas){ + case BreakReas: + case VeccatReas: + ctxt->pcadjust = -2*ArmInstSz; + break; + case DebugReas: + break; + } + return 0; +} + +int +iceenterdebug(JMedium *jmed, ArmCtxt *ctxt) +{ + int res; + + res = setchain(jmed, ChCommit, 2); + if(res < 0) + return -1; + + //if(jmed->motherb == Sheeva) + // jmed->resets(jmed->mdata, 0, 1); + if(!(icegetreg(jmed, DebStsReg) & DBGACK)){ + icesetreg(jmed, DebugCtlReg, DBGRQ); /* freeze */ + } + + //if(jmed->motherb == Sheeva) + // jmed->resets(jmed->mdata, 0, 0); + ctxt->debugreas = DebugReas; + res = icewaitentry(jmed, ctxt); + if(res < 0) + return -1; + ctxt->exitreas = NoReas; + return 0; +} + + +/* + * The branch is the way to exit debug permanently + * no branch and you fall back to debug + * The context needs to be restored (some register needs + * to be written), don't know why + * but you will fall back to debug if not, no matter what doc says. + * The bypass is not mentioned to be necessary anywhere, but + * the feroceon will not restart without it. + */ + +int +iceexitdebug(JMedium *jmed, ArmCtxt *ctxt) +{ + int res, i; + uchar wctl; + u32int sts; + + res = setchain(jmed, ChCommit, 1); + if(res < 0) + return -1; + + res = armjmpctxt(jmed, ctxt); + if(res < 0) + return -1; + + /* 4 entry to debug, Store(does not count) 3 NOPS, Branch = -8 */ + res = armfastexec(jmed, ARMBRIMM|0xfffffb); + if(res < 0) + return -1; + + res = setchain(jmed, ChCommit, 2); + if(res < 0) + return -1; + + wctl = icegetreg(jmed, Wp0|CtlValReg); + if(ctxt->exitreas != BreakReqReas) + icesetreg(jmed, Wp0|CtlValReg, wctl&~EnableWPCtl); + + if(ctxt->exitreas != VeccatReqReas) + icesetreg(jmed, VecCatReg, 0); + icesetreg(jmed, DebugCtlReg, 0); /* clear all,*/ + setinst(jmed, InBypass, 0); + setinst(jmed, InRestart, 0); + sleep(100); + res = setchain(jmed, ChCommit, 2); + if(res < 0) + return -1; + + for(i = 0; i < 10; i ++){ + sts = icegetreg(jmed, DebStsReg); + if((sts&DBGACK) == 0) + break; + sleep(100); + } + if( i == 10) + return -1; + + switch(ctxt->exitreas){ + case NoReas: + ctxt->debugreas = NoReas; + break; + case VeccatReqReas: + ctxt->debugreas = VeccatReas; + break; + case BreakReqReas: + ctxt->debugreas = BreakReas; + break; + default: + ctxt->debugreas = NoReas; + break; + } + ctxt->debug = 0; + return 0; +} diff -Nru /sys/src/cmd/jtagfs/icert.h /sys/src/cmd/jtagfs/icert.h --- /sys/src/cmd/jtagfs/icert.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/icert.h Sun Apr 7 00:00:00 2013 @@ -0,0 +1,273 @@ +typedef struct ArmCtxt ArmCtxt; +typedef struct EiceChain1 EiceChain1; +typedef struct EiceChain2 EiceChain2; +typedef struct EiceRegs EiceRegs; +typedef struct EiceWp EiceWp; +typedef struct MMURegs MMURegs; +typedef struct PackedSz PackedSz; + +enum{ + /* + * This is an arm 926, except no MOE, only 1 Wpoint + * I think this means "mcr access" + * for the MMU. It would be good knowing for real + * what the good doc for it is or if it is just + * random mix and match!!. + */ + FeroceonId = 0x20a023d3, + + /* Looks like a pxa/feroceon frankenstein */ + ArmadaId = 0x304113d3, + + ArmInstSz = 4, + ThumbInstSz = 2, +}; + + +/* instructions for the IR, 4 bits */ +enum { + InExtest = 0x0, + InScanN = 0x2, /* shift the scan chain id, 5 bits */ + InSampPre = 0x3, + InRestart = 0x4, + InHighZ = 0x7, + InClampZ = 0x9, + InTest = 0xc, + InIdCode = 0xe, + InBypass = 0xf, + + InGuruTapctl = 0x98, + InGuruLen = 9, + DrGuruTapctl = 0x00a, + DrGuruLen = 16, + + InLen = 4, /* bits */ + EiceCh1Len = 67, /* bits */ + EiceCh2Len = 38, /* bits */ +}; + +/* sizes are in bits */ +#define WpN(nreg, field) ((nreg<<(nreg))|(field)) + +enum { + + /* chain 1 */ + InstrSz = 32, + SysSpeedSz = 1, + WpTanDBKptSz = 1, + Ch1ResvdSz, + RWDataSz = 32, + + + /* chain 2, here registers are directed through addr */ + DataSz = 32, + AddrSz = 5, + RWSz = 1, + + DebugCtlReg = 0x00, + DebStsReg = 0x01, + VecCatReg = 0x02, + DebComCtlReg = 0x04, + DebComDataReg = 0x05, + + Wp0 = 1<<4, /* Wp0|AddrValReg and so on */ + Wp1 = 1<<5, /* Wp1|AddrValReg and so on */ + + AddrValReg = 0x00, + AddrMskReg = 0x01, + DataValReg = 0x02, + DataMskReg = 0x03, + CtlValReg = 0x04, + CtlMskReg = 0x05, + + /* sizes in bits */ + DebugCtlRegSz = 6, + DebStsRegSz = 10, /* in feroceon 5, no Moe */ + VecCatRegSz = 8, + DebComCtlRegSz = 6, + DebComDataRegSz = 32, + + AddrValRegSz = 32, + AddrMskRegSz = 32, + DataValRegSz = 32, + DataMskRegSz = 32, + CtlValRegSz = 9, + CtlMskRegSz = 8, + + /* DebugCtlReg (write) */ + DBGACK = 1, /* I am dealing with debug mode */ + DBGRQ = 1<<1, /* Enter debug mode */ + INTDIS = 1<<2, /* Disable interrupts */ + SBZ0 = 1<<3, /* Should be Zero */ + MONENAB = 1<<4, /* Monitor mode/halt mode */ + EICEDISAB = 1<<5, /* Power down */ + + /* DebugSTSReg (read) + * two first bits same as ctl + */ + IFEN = 1<<2, /* Are interrupts enabled */ + SYSCOMP = 1<<3, /* Memory access complete */ + ITBIT = 1<<4, /* Thumb/Arm mode */ + SBZ1 = 1<<5, + MOEMSK = 0xf<<6, /* Method of entry */ + + /* VecCatReg (write), one bit per exception to catch */ + ResetCat = 1, + UndefCat = 1<<1, + SWICat = 1<<2, + PAbortCat = 1<<3, + DAbortCat = 1<<4, + RsvdCat = 1<<5, + IrqCat = 1<<6, + FiqCat = 1<<7, + + /* + * Watchpoint control registers: + * CtlValReg CtlMskReg + * Bit 1 in Msk makes condition ignored. + */ + + /* Data version of CtlValReg bits 6, 7, 8 are shared */ + DnRWWPCtl = 1, /* 0 access is read 1 write */ + DmasWPCtlMsk = 3<<1, /* this two bits represent size of access */ + DataWPCtl = 1<<3, /* compare against data/0 is inst */ + DnTransWPCtl = 1<<4, /* 0 user 1 privileged */ + DbgExtWPCtl = 1<<5, /* External condition for watchpoint */ + ChainWPCtl = 1<<6, /* Chain together watchpoints */ + RangeWPCtl = 1<<7, /* Range connecting watchpoints together */ + EnableWPCtl = 1<<8, /* Cannot be masked */ + /* Inst version of CtlValReg */ + IgnWPCtl = 1, /* ignored */ + ITbitWPCtlMsk = 3<<1, /* 1 Thumb */ + IJbitWPCtl = 1<<2, /* 1 Jazelle */ + InTransWPCtl = 1<<4, /* 0 user 1 privileged */ + + + /* Id code */ + ManIdSz = 11, + PartNoSz = 16, + VerSz = 4, + +}; + +enum{ + ChNoCommit = 0, + ChCommit = 1, +}; + +struct PackedSz { + uchar unpkSz; + uchar pkSz; +}; + +/* + * The bit ordering on this one is peculiar, see packech1() + * in particular the instruction is upside down, + */ + +struct EiceChain1 { + u32int instr; /* 32 bits */ + uchar sysspeed; /* 1 bit */ + uchar wptandbkpt; /* 1 bit */ + /* 1 bit rsvd */ + u32int rwdata; /* 32 bits */ +}; + +struct EiceChain2 { + u32int data; /* 32 bits */ + uchar addr; /* 5 bits */ + uchar rw; /* 1 bit */ +}; + +struct EiceWp { + u32int addrval; /* 32 bits */ + u32int addrmsk; /* 32 bits */ + u32int dataval; /* 32 bits */ + u32int datamsk; /* 32 bits */ + u16int ctlval; /* 9 bits */ + uchar ctlmsk; /* 8 bits */ +}; + +struct EiceRegs { + uchar debug; /* 4 bits */ + uchar debsts; /* 5 bits */ + uchar veccat; /* 8 bits */ + uchar debcomctl; /* 6 bits */ + u32int debcomdata; /* 32 bits */ + EiceWp wp[2]; +}; + +enum{ + ARMSTMIA = 0xe8800000, + ARMLDMIA = 0xe8900000, + BYTEWDTH = 0x00400000, + ARMLDRindr1xr0 = 0xe5901000, /* LDR r1, [r0] || [r0]->r1*/ + ARMSTRindxr0r1 = 0xe5801000, /* STR [r0], r1 || r1->[r0] */ + ARMNOP = 0xe1a08008, + ARMMRSr0CPSR = 0xE10F0000, + ARMMSRr0CPSR = 0xE12FF000, + SPSR = 1<<22, + ARMMRSr0SPSR = ARMMRSr0CPSR|SPSR, + ARMMSRr0SPSR = ARMMSRr0CPSR|SPSR, + ARMBRIMM = 0xea000000, +}; + + +#define MOE(dbgreg) (((dbgreg)&MOEMSK)>>6); + +#define MANID(id) (((id)>>1)&MSK(ManIdSz)) +#define PARTNO(id) (((id)>>ManIdSz+1)&MSK(PartNoSz)) +#define VERS(id) (((id)>>ManIdSz+1+PartNoSz)&MSK(VerSz)) + +struct MMURegs { + u32int cpid; /* main ID */ + u32int control; /* control */ + u32int ttb; /* translation table base */ + u32int dac; /* domain access control */ + u32int fsr; /* fault status */ + u32int far; /* fault address */ + u32int ct; /* cache type */ + u32int pid; /* address translation pid */ +}; + +enum{ + NoReas, + BreakReqReas, + BreakReas, + VeccatReqReas, + VeccatReas, + DebugReas, +}; + +struct ArmCtxt { + int pcadjust; + int debug; + int debugreas; + int exitreas; + int cpuid; + u32int r[16]; + u32int cpsr; + u32int spsr; + MMURegs; +}; + +extern int armbpasstest(JMedium *jmed); +extern int armfastexec(JMedium *jmed, u32int inst); +extern int armgetexec(JMedium *jmed, int nregs, u32int *regs, u32int inst); +extern int armgofetch(JMedium *jmed, u32int instr, int sysspeed); +extern u32int armidentify(JMedium *jmed); +extern int armrdmemwd(JMedium *jmed, u32int addr, u32int *data, int sz); +extern int armsavectxt(JMedium *jmed, ArmCtxt *ctxt); +extern int armsetexec(JMedium *jmed, int nregs, u32int *regs, u32int inst); +extern int armsetregs(JMedium *jmed, int mask, u32int *regs); +extern int armwrmemwd(JMedium *jmed, u32int addr, u32int data, int sz); +extern int icedebugstate(JMedium *jmed); +extern int iceenterdebug(JMedium *jmed, ArmCtxt *ctxt); +extern int iceexitdebug(JMedium *jmed, ArmCtxt *ctxt); +extern u32int icegetreg(JMedium *jmed, int regaddr); +extern int icesetreg(JMedium *jmed, int regaddr, u32int val); +extern int icewaitdebug(JMedium *jmed); +extern int icewaitentry(JMedium *jmed, ArmCtxt *ctxt); +extern int setchain(JMedium *jmed, int commit, uchar chain); +extern int setinst(JMedium *jmed, uchar inst, int nocommit); +extern char * armsprctxt(ArmCtxt *ctxt, char *s, int ssz); diff -Nru /sys/src/cmd/jtagfs/jtag.c /sys/src/cmd/jtagfs/jtag.c --- /sys/src/cmd/jtagfs/jtag.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/jtag.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,77 @@ +#include +#include +#include +#include "debug.h" +#include "tap.h" +#include "chain.h" +#include "jtag.h" + +/* + We will only have one active tap at a time. + For now we will suppose they are concatenated. + IR will be 1s for the rest of the bits (Bypass the rest) + DR will be 1 bit per ignored Tap +*/ + +static uchar bufch[4*1024]; +int +tapshift(JMedium *jmed, ShiftTDesc *req, ShiftRDesc *rep, int tapidx) +{ + int i; + u32int ones; + ShiftTDesc lreq; + Chain *ch; + + lreq = *req; + ones = ~0; + memset(bufch, 0, sizeof bufch); + ch = (Chain *)bufch; + + for(i = 0; i < jmed->ntaps; i++){ + if(req->reg == TapDR){ + if(i == tapidx){ + dprint(DState, "tap %d, DR\n", tapidx); + putbits(ch, req->buf, req->nbits); + } + else { + dprint(DState, "tap %d, ignoring DR\n", i); + putbits(ch, &ones, 1); + } + } + else if(req->reg == TapIR){ + if(i == tapidx){ + dprint(DState, "tap %d, IR\n", tapidx); + putbits(ch, req->buf, req->nbits); + } + else { + dprint(DState, "tap %d, IR\n", tapidx); + putbits(ch, &ones, jmed->taps[tapidx].irlen); + } + } + } + + lreq.buf = ch->buf; + lreq.nbits = ch->e - ch->b; + jmed->TapSm = jmed->taps[tapidx].TapSm; + if(jmed->regshift(jmed, &lreq, rep) < 0) + return -1; + jmed->taps[tapidx].TapSm = jmed->TapSm; + + if(req->op & ShiftIn) + for(i = 0; i < jmed->ntaps; i++){ + if(req->reg == TapDR){ + if(i == tapidx) + getbits(req->buf, ch, ch->e - ch->b); + else + getbits(&ones, ch, 1); + } + else if(req->reg == TapIR){ + if(i == tapidx) + getbits(req->buf, ch, ch->e - ch->b); + else + getbits(&ones, ch, jmed->taps[tapidx].irlen); + } + } + + return 0; +} diff -Nru /sys/src/cmd/jtagfs/jtag.h /sys/src/cmd/jtagfs/jtag.h --- /sys/src/cmd/jtagfs/jtag.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/jtag.h Sun Apr 7 00:00:00 2013 @@ -0,0 +1,47 @@ +typedef struct JMedium JMedium; +typedef struct ShiftTDesc ShiftTDesc; +typedef struct ShiftRDesc ShiftRDesc; + + +/* descriptor for a shift if not there are too many parameters */ +struct ShiftTDesc { + int reg; + uchar *buf; + int nbits; + int op; +}; + +/* descriptor for a reply to do async replies, timing agh... */ +struct ShiftRDesc{ + int nbyread; /* total bytes */ + int nbiprelast; /* bits of next to last */ + int nbilast; /* bits of last */ +}; + +/* this is the interface for the medium */ +struct JMedium { + TapSm; /* of the tap I am currently addressing */ + int motherb; + int tapcpu; + Tap taps[16]; + int ntaps; + uchar currentch; /* BUG: in tap[tapcpu]->private */ + void *mdata; + int (*regshift)(JMedium *jmed, ShiftTDesc *req, ShiftRDesc *rep); + int (*flush)(void *mdata); + int (*term)(void *mdata); + int (*resets)(void *mdata, int trst, int srst); + int (*rdshiftrep)(JMedium *jmed, uchar *buf, ShiftRDesc *rep); +}; + +enum{ + ShiftMarg = 4, /* Max number of extra bytes in response */ + + ShiftIn = 1, + ShiftOut = 2, + ShiftNoCommit = 4, /* Normally you want to commit... */ + ShiftPauseOut = 8, /* go through pause on the way out */ + ShiftPauseIn = 16, /* go through pause on the way in */ + ShiftAsync = 32, /* do not read reply synch */ +}; +extern int tapshift(JMedium *jmed, ShiftTDesc *req, ShiftRDesc *rep, int tapidx); diff -Nru /sys/src/cmd/jtagfs/jtagfs.c /sys/src/cmd/jtagfs/jtagfs.c --- /sys/src/cmd/jtagfs/jtagfs.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/jtagfs.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,1590 @@ +#include +#include +#include +#include +#include +#include +#include "debug.h" +#include "tap.h" +#include "chain.h" +#include "jtag.h" +#include "icert.h" +#include "mmu.h" +#include "mpsse.h" +#include "/sys/src/9/kw/arm.h" +#include "/arm/include/ureg.h" +#include "lebo.h" + +/* + * Like rdbfs + jtag icert control for arm cores. + * Memory just goes through in arm order, registers get translated + * to host order and back after. + */ + +typedef struct Fid Fid; +typedef struct Fs Fs; +enum +{ + OPERM = 0x3, /* mask of all permission types in open mode */ + Nfidhash = 32, + + /* + * qids + */ + Qroot = 1, + Qctl, + Qnote, + Qmem, + /* copied from rdbfs */ + Qkregs, + Qfpregs, + Qproc, + Qregs, + Qtext, + Qstatus, +}; + +static int textfd; +static char* textfile = "/arm/s9plug"; + + +struct Fid +{ + Lock; + Fid *next; + Fid **last; + uint fid; + int ref; /* number of fcalls using the fid */ + int attached; /* fid has beed attached or cloned and not clunked */ + + int open; + Qid qid; +}; + +static int dostat(int, uchar*, int); +static void* emalloc(uint); +static void fatal(char*, ...); +static void usage(void); + +struct Fs +{ + Lock; /* for fids */ + + Fid *hash[Nfidhash]; + uchar statbuf[1024]; /* plenty big enough */ + JMedium *jmed; +}; + +static void fsrun(Fs*, int); +static Fid* getfid(Fs*, uint); +static Fid* mkfid(Fs*, uint); +static void putfid(Fs*, Fid*); +static char* fsversion(Fs*, Fcall*); +static char* fsauth(Fs*, Fcall*); +static char* fsattach(Fs*, Fcall*); +static char* fswalk(Fs*, Fcall*); +static char* fsopen(Fs*, Fcall*); +static char* fscreate(Fs*, Fcall*); +static char* fsread(Fs*, Fcall*); +static char* fswrite(Fs*, Fcall*); +static char* fsclunk(Fs*, Fcall*); +static char* fsremove(Fs*, Fcall*); +static char* fsstat(Fs*, Fcall*); +static char* fswstat(Fs*, Fcall*); + +static char *(*fcalls[])(Fs*, Fcall*) = +{ + [Tversion] fsversion, + [Tattach] fsattach, + [Tauth] fsauth, + [Twalk] fswalk, + [Topen] fsopen, + [Tcreate] fscreate, + [Tread] fsread, + [Twrite] fswrite, + [Tclunk] fsclunk, + [Tremove] fsremove, + [Tstat] fsstat, + [Twstat] fswstat +}; + +static char Eperm[] = "permission denied"; +static char Enotdir[] = "not a directory"; +static char Enotexist[] = "file does not exist"; +static char Eisopen[] = "file already open for I/O"; +static char Einuse[] = "fid is already in use"; +static char Enofid[] = "no such fid"; +static char Enotopen[] = "file is not open"; +static char Ebadcmd[] = "error in jtag operation"; + +static ArmCtxt ctxt; +static Fs fs; +static int messagesize = 8192+IOHDRSZ; + +static int +cmdsetdebug(uchar *deb) +{ + int i; + + memset(debug, 0, 255); + for(i = 0; i < strlen((char *)deb); i++){ + debug[deb[i]]++; + } + return 0; +} + + + +void +main(int argc, char **argv) +{ + char buf[12], *mnt, *srv, *mbname; + int fd, p[2], mb; + uchar *deb; + + deb = nil; + mb = Sheeva; + mbname = nil; + mnt = "/n/jtagfs"; + srv = nil; + ARGBEGIN{ + case 'b': + mbname = EARGF(usage()); + break; + case 's': + srv = ARGF(); + mnt = nil; + break; + case 'm': + mnt = ARGF(); + break; + case 'd': + deb = (uchar *)EARGF(usage()); + break; + case 't': + textfile = EARGF(usage()); + break; + }ARGEND + + fmtinstall('F', fcallfmt); + if(deb != nil) + cmdsetdebug(deb); + if(argc != 1) + usage(); + print("jtagfs: %s\n", argv[0]); + fd = open(argv[0], ORDWR); + if(fd < 0) + fatal("can't open jtag file %s: %r", argv[0]); + + if(mbname == nil) + mb = Sheeva; + else if(strncmp(mbname, "sheeva", 6) == 0) + mb = Sheeva; + else if(strncmp(mbname, "gurudisp", 6) == 0) + mb = GuruDisp; + fs.jmed = initmpsse(fd, mb); + if(fs.jmed == nil) + fatal("jtag initialization %r"); + + if(pipe(p) < 0) + fatal("pipe failed"); + + switch(rfork(RFPROC|RFMEM|RFNOTEG|RFNAMEG)){ + case 0: + fsrun(&fs, p[0]); + exits(nil); + case -1: + fatal("fork failed"); + } + + if(mnt == nil){ + if(srv == nil) + usage(); + fd = create(srv, OWRITE, 0666); + if(fd < 0){ + remove(srv); + fd = create(srv, OWRITE, 0666); + if(fd < 0){ + close(p[1]); + fatal("create of %s failed", srv); + } + } + sprint(buf, "%d", p[1]); + if(write(fd, buf, strlen(buf)) < 0){ + close(p[1]); + fatal("writing %s", srv); + } + close(p[1]); + exits(nil); + } + + if(mount(p[1], -1, mnt, MREPL, "") < 0){ + close(p[1]); + fatal("mount failed"); + } + close(p[1]); + exits(nil); +} + +static void +fsrun(Fs *fs, int fd) +{ + Fcall rpc; + char *err; + uchar *buf; + int n; + + buf = emalloc(messagesize); + for(;;){ + /* + * reading from a pipe or a network device + * will give an error after a few eof reads + * however, we cannot tell the difference + * between a zero-length read and an interrupt + * on the processes writing to us, + * so we wait for the error + */ + n = read9pmsg(fd, buf, messagesize); + if(n == 0) + continue; + if(n < 0) + fatal("mount read"); + + rpc.data = (char*)buf + IOHDRSZ; + if(convM2S(buf, n, &rpc) == 0) + continue; + // fprint(2, "recv: %F\n", &rpc); + + + /* + * TODO: flushes are broken + */ + if(rpc.type == Tflush) + continue; + else if(rpc.type >= Tmax || !fcalls[rpc.type]) + err = "bad fcall type"; + else + err = (*fcalls[rpc.type])(fs, &rpc); + if(err){ + rpc.type = Rerror; + rpc.ename = err; + } + else + rpc.type++; + n = convS2M(&rpc, buf, messagesize); + // fprint(2, "send: %F\n", &rpc); + if(write(fd, buf, n) != n) + fatal("mount write"); + } +} + +static Fid* +mkfid(Fs *fs, uint fid) +{ + Fid *f; + int h; + + h = fid % Nfidhash; + for(f = fs->hash[h]; f; f = f->next){ + if(f->fid == fid) + return nil; + } + + f = emalloc(sizeof *f); + f->next = fs->hash[h]; + if(f->next != nil) + f->next->last = &f->next; + f->last = &fs->hash[h]; + fs->hash[h] = f; + + f->fid = fid; + f->ref = 1; + f->attached = 1; + f->open = 0; + return f; +} + +static Fid* +getfid(Fs *fs, uint fid) +{ + Fid *f; + int h; + + h = fid % Nfidhash; + for(f = fs->hash[h]; f; f = f->next){ + if(f->fid == fid){ + if(f->attached == 0) + break; + f->ref++; + return f; + } + } + return nil; +} + +static void +putfid(Fs *, Fid *f) +{ + f->ref--; + if(f->ref == 0 && f->attached == 0){ + *f->last = f->next; + if(f->next != nil) + f->next->last = f->last; + free(f); + } +} + +static char* +fsversion(Fs *, Fcall *rpc) +{ + if(rpc->msize < 256) + return "version: message size too small"; + if(rpc->msize > messagesize) + rpc->msize = messagesize; + messagesize = rpc->msize; + if(strncmp(rpc->version, "9P2000", 6) != 0) + return "unrecognized 9P version"; + rpc->version = "9P2000"; + return nil; +} + +static char* +fsauth(Fs *, Fcall *) +{ + return "searchfs: authentication not required"; +} + +static char* +fsattach(Fs *fs, Fcall *rpc) +{ + Fid *f; + + f = mkfid(fs, rpc->fid); + if(f == nil) + return Einuse; + f->open = 0; + f->qid.type = QTDIR; + f->qid.path = Qroot; + f->qid.vers = 0; + rpc->qid = f->qid; + putfid(fs, f); + return nil; +} + +typedef struct DEntry DEntry; +struct DEntry { + uvlong path; + char *name; +}; + +DEntry entries[] = { + {Qctl, "ctl"}, + {Qmem, "mem"}, + {Qkregs, "kregs"}, + {Qfpregs, "fpregs"}, + {Qproc, "proc"}, + {Qregs, "regs"}, + {Qtext, "text"}, + {Qstatus, "status"}, + {Qnote, "note"}, + {~0, nil}, +}; + +static uvlong +nm2qpath(char *name) +{ + int i; + + for(i = 0; entries[i].name != nil; i++) + if(strcmp(name, entries[i].name) == 0) + return entries[i].path; + + return ~0; +} + +static char* +fswalk(Fs *fs, Fcall *rpc) +{ + Fid *f, *nf; + int nqid, nwname, type; + char *err, *name; + ulong path, fpath; + + f = getfid(fs, rpc->fid); + if(f == nil) + return Enofid; + nf = nil; + if(rpc->fid != rpc->newfid){ + nf = mkfid(fs, rpc->newfid); + if(nf == nil){ + putfid(fs, f); + return Einuse; + } + nf->qid = f->qid; + putfid(fs, f); + f = nf; /* walk f */ + } + + err = nil; + path = f->qid.path; + nwname = rpc->nwname; + for(nqid=0; nqidwname[nqid]; + /* BUG this is a kludge, rewrite */ + fpath = nm2qpath(name); + if(fpath != ~0){ + type = QTFILE; + path = fpath; + } + else if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + type = QTDIR; + else { + err = Enotexist; + break; + } + + rpc->wqid[nqid] = (Qid){path, 0, type}; + } + + if(nwname > 0){ + if(nf != nil && nqid < nwname) + nf->attached = 0; + if(nqid == nwname) + f->qid = rpc->wqid[nqid-1]; + } + + putfid(fs, f); + rpc->nwqid = nqid; + f->open = 0; + return err; +} + +static char * +fsopen(Fs *fs, Fcall *rpc) +{ + Fid *f; + int mode; + char buf[512]; + + f = getfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(f->open){ + putfid(fs, f); + return Eisopen; + } + mode = rpc->mode & OPERM; + if(mode == OEXEC + || f->qid.path == Qroot && (mode == OWRITE || mode == ORDWR)){ + putfid(fs, f); + return Eperm; + } + if(f->qid.path == Qtext){ + close(textfd); + textfd = open(textfile, OREAD); + if(textfd < 0) { + snprint(buf, sizeof buf, "text: %r"); + putfid(fs, f); + return "opening text file"; + } + } + f->open = 1; + rpc->qid = f->qid; + rpc->iounit = messagesize-IOHDRSZ; + putfid(fs, f); + return nil; +} + +static char * +fscreate(Fs *, Fcall *) +{ + return Eperm; +} + +static int +readbuf(Fcall *rpc, void *s, long n) +{ + int count; + count = rpc->count; + if(rpc->offset >= n){ + count = 0; + } + if(rpc->offset+count > n) + count = n - rpc->offset; + memmove(rpc->data, (char*)s+rpc->offset, count); + rpc->count = count; + return count; +} + +enum{ + Maxctl = 4*1024, +}; + +static char ctlread[Maxctl]; + +static int +readctl(Fs *fs, Fcall *rpc) +{ + char *e; + int ssz; + + ssz = Maxctl; + ctxt.debug = icedebugstate(fs->jmed); + + if(ctxt.debug == 0){ + e = seprint(ctlread, ctlread+Maxctl, "Arm is in debug: %d", ctxt.debug); + ssz = readbuf(rpc, ctlread, e - ctlread); + return ssz; + } + e = armsprctxt(&ctxt, ctlread, ssz); + if(e >= ctlread + Maxctl) + return e - ctlread; + ssz = Maxctl - (ctlread - e); + e = printmmuregs(&ctxt, e, ssz); + + ssz = readbuf(rpc, ctlread, e - ctlread); + return ssz; + +} + +static int +readbytes(JMedium *jmed, u32int startaddr, u32int nbytes, u32int dataoff, Fcall *rpc) +{ + u32int i, data; + int nb, res; + + nb = 0; + for(i = startaddr; i < startaddr+nbytes; i++){ + res = armrdmemwd(jmed, i, &data, 1); + if(res < 0){ + fprint(2, "Error reading [%#8.8ux]\n", + i); + werrstr("read error"); + return -1; + break; + } + nb += res; + rpc->data[dataoff] = (char)data; + dprint(Dfs, "[B%#8.8ux] = %#2.2ux \n", + i, data); + } + return nb; + +} + +/* + * BUG: This is horrifyingly slow, could be made much (10x) + * faster using load multiple/store multiple + * both on memory and while reading back the registers. + * but it doesn't matter for acid (normally it reads words + * or byte long chunks). There may also be another way + * where you can inject instructions without waiting for + * debug mode on the way back. I haven't been able to + * make it work. + */ +static int +readmem(Fs *fs, Fcall *rpc) +{ + u32int count, i, data, addr, prenb, nb, postnb, st; + int res; + + count = (u32int)rpc->count; + + addr = (u32int)rpc->offset; + dprint(Dfs, "[%#8.8ux, %ud] =?\n", + addr, count); + + prenb = 0; + nb = 0; + /* The start is not aligned */ + if(addr & 0x3U){ + prenb = sizeof(u32int) - (addr & 0x3U); + res = readbytes(fs->jmed, addr, prenb, 0, rpc); + if(res < 0){ + werrstr("read error"); + return -1; + } + nb += res; + dprint(Dfs, "readmem: aligned now\n"); + } + for(i = prenb; i/4 < (count-prenb)/sizeof(u32int); i += sizeof(u32int)){ + res = armrdmemwd(fs->jmed, addr+i, &data, 4); + if(res < 0){ + fprint(2, "Error reading %#8.8ux [%#8.8ux]\n", + addr+i, i); + werrstr("read error"); + return -1; + break; + } + nb += res; + + *(u32int *)(rpc->data+i) = data; + dprint(Dfs, "%d[%#8.8ux] = %#8.8ux \n", + i, addr+i, data); + } + + dprint(Dfs, "readmem: end of aligned\n"); + /* The end is not aligned */ + if((count-prenb) & 0x3U){ + postnb = (count-prenb)%sizeof(u32int); + st = addr + 1 + ((count-prenb)& 0x3U); + res = readbytes(fs->jmed, st, postnb, nb, rpc); + if(res < 0){ + werrstr("read error"); + return -1; + } + nb += res; + dprint(Dfs, "readmem: end of non aligned\n"); + } + return nb; +} + +static int +writebytes(JMedium *jmed, u32int startaddr, u32int nbytes, u32int dataoff, Fcall *rpc) +{ + u32int i, data; + int nb, res; + + nb = 0; + for(i = startaddr; i < startaddr+nbytes; i++){ + data = (char)rpc->data[dataoff]; + res = armwrmemwd(jmed, i, data, 1); + if(res < 0){ + fprint(2, "Error writing [%#8.8ux]\n", + i); + werrstr("read error"); + return -1; + break; + } + nb += res; + dprint(Dfs, "[B%#8.8ux] = %#2.2ux \n", + i, data); + } + return nb; + +} + +static int +writemem(Fs *fs, Fcall *rpc) +{ + u32int count, i, addr, *p, prenb, nb, postnb, st; + int res; + + count = rpc->count; + addr = rpc->offset; + + prenb = 0; + nb = 0; + /* not aligned offset */ + if(addr & 0x3U){ + prenb = sizeof(u32int) - (addr & 0x3U); + res = writebytes(fs->jmed, addr, prenb, 0, rpc); + if(res < 0){ + werrstr("write error"); + return -1; + } + nb += res; + dprint(Dfs, "writemem: aligned now\n"); + } + for(i = prenb; i/4 < (count-prenb)/sizeof(u32int); i += sizeof(u32int)){ + p = (u32int *)(rpc->data + i); + dprint(Dfs, "%d[%#8.8ux] = %#8.8ux \n", + i, addr+i, *p); + res = armwrmemwd(fs->jmed, addr + i, *p, 4); + if(res < 0){ + fprint(2, "Error writing %#8.8ux [%#8.8ux]\n", + addr+i, i); + werrstr("write error"); + return -1; + break; + } + nb += res; + } + dprint(Dfs, "writemem: end of aligned\n"); + /* not aligned end */ + if((count-prenb) & 0x3U){ + postnb = (count-prenb)%sizeof(u32int); + st = addr + 1 + ((count-prenb)& 0x3U); + res = writebytes(fs->jmed, st, postnb, nb, rpc); + if(res < 0){ + werrstr("write error"); + return -1; + } + nb += res; + dprint(Dfs, "writemem: end of non aligned\n"); + } + return nb; +} + +/* host to Arm (le) */ +static void +setkernur(Ureg *kur, ArmCtxt *context) +{ + int i; + u32int *p; + + kur->type = context->cpsr&PsrMask; + hleputl(&kur->type, kur->type); + + kur->psr = context->spsr; + hleputl(&kur->psr, kur->psr); + + p = (u32int *)kur; + for(i = 0; i < 14; i++) + hleputl(p + i, context->r[i]); + kur->pc = context->r[15]; + hleputl(&kur->pc, kur->pc); +} + +typedef struct Regs Regs; +struct Regs { + Ureg kur; + MMURegs mmust; +}; + +static Regs procregs; /* kludge: just for reading */ + +/* host to Arm (le) */ +static void +setmmust(MMURegs *mmust, ArmCtxt *context) +{ + int i; + u32int *o, *d; + d = (u32int *)mmust; + o = (u32int *) &context->MMURegs; + for(i = 0; i < sizeof(MMURegs)/sizeof(u32int); i++) + hleputl(d + i, o[i]); +} + +static char* +fsread(Fs *fs, Fcall *rpc) +{ + Fid *f; + int off, count, len, i; + uchar *rptr; + char buf[512]; + + f = getfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(!f->open){ + putfid(fs, f); + return Enotopen; + } + count = rpc->count; + off = rpc->offset; + if(f->qid.path == Qroot){ + if(off > 0) + rpc->count = 0; + else { + rpc->count = 0; + for(i = 0; entries[i].name != nil; i++) + rpc->count += dostat(entries[i].path, + rpc->count+(uchar*)rpc->data, count-rpc->count); + } + + putfid(fs, f); + if(off == 0 && rpc->count <= BIT16SZ) + return "directory read count too small"; + return nil; + } + len = 0; + if(f->qid.path == Qctl){ + dprint(Dfs, "ctlread\n"); + len = readctl(fs, rpc); + dprint(Dfs, "ctlread read %d %s\n", len, ctlread); + if(len < 0){ + putfid(fs, f); + return "ctl read"; + } + } + else if(f->qid.path == Qmem){ + dprint(Dfs, "readmem\n"); + len = readmem(fs, rpc); + dprint(Dfs, "readmem read %d %s\n", len, ctlread); + if(len < 0){ + putfid(fs, f); + return "readmem read"; + } + } + else if(f->qid.path == Qkregs){ + dprint(Dfs, "kregs read n %d off %d\n", rpc->count, rpc->offset); + memset(&procregs, 0, sizeof(Regs)); + setkernur(&procregs.kur, &ctxt); + setmmust(&procregs.mmust, &ctxt); + rptr = (uchar*)&procregs; + len = readbuf(rpc, rptr, sizeof(Regs)); + } + else if(f->qid.path == Qtext){ + dprint(Dfs, "text\n"); + len = pread(textfd, rpc->data, rpc->count, rpc->offset); + if(len < 0) { + rerrstr(buf, sizeof buf); + fprint(2, "error reading text: %r"); + putfid(fs, f); + return "text read"; + } + } + else if(f->qid.path == Qstatus){ + dprint(Dfs, "status\n"); + len = snprint(buf, sizeof buf, "%-28s%-28s%-28s", "remote", "system", "New"); + for(i = 0; i < 9; i++) + len += snprint(buf + len, sizeof buf - len, "%-12d", 0); + len = readbuf(rpc, buf, len); + + } + putfid(fs, f); + rpc->count = len; + return nil; +} + +typedef struct Cmdbuf Cmdbuf; +typedef struct Cmdtab Cmdtab; +struct Cmdbuf +{ + char *buf; + char **f; + int nf; +}; + +struct Cmdtab +{ + int index; /* used by client to switch on result */ + char *cmd; /* command name */ + int narg; /* expected #args; 0 ==> variadic */ +}; +/* + * Generous estimate of number of fields, including terminal nil pointer + */ +static int +ncmdfield(char *p, int n) +{ + int white, nwhite; + char *ep; + int nf; + + if(p == nil) + return 1; + + nf = 0; + ep = p+n; + white = 1; /* first text will start field */ + while(p < ep){ + nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */ + if(white && !nwhite) /* beginning of field */ + nf++; + white = nwhite; + } + return nf+1; /* +1 for nil */ +} + +/* + * parse a command written to a device + */ +static Cmdbuf* +parsecmd(char *p, int n) +{ + Cmdbuf *cb; + int nf; + char *sp; + + nf = ncmdfield(p, n); + + /* allocate Cmdbuf plus string pointers plus copy of string including \0 */ + sp = malloc(sizeof(*cb) + nf * sizeof(char*) + n + 1); + if(sp == nil) + sysfatal("memory"); + cb = (Cmdbuf*)sp; + cb->f = (char**)(&cb[1]); + cb->buf = (char*)(&cb->f[nf]); + + memmove(cb->buf, p, n); + + /* dump new line and null terminate */ + if(n > 0 && cb->buf[n-1] == '\n') + n--; + cb->buf[n] = '\0'; + + cb->nf = tokenize(cb->buf, cb->f, nf-1); + cb->f[cb->nf] = nil; + + return cb; +} + +/* + * Look up entry in table + */ + +static Cmdtab* +lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab) +{ + int i; + Cmdtab *ct; + + if(cb->nf == 0){ + werrstr("empty control message"); + return nil; + } + + for(ct = ctab, i=0; icmd, "*") !=0) /* wildcard always matches */ + if(strcmp(ct->cmd, cb->f[0]) != 0) + continue; + if(ct->narg != 0 && ct->narg != cb->nf){ + werrstr("bad # args to command"); + return nil; + } + return ct; + } + + werrstr("unknown control message"); + return nil; +} + +enum { + CMcpuid, + CMdebug, + CMreset, + CMstop, + CMstartstop, + CMwaitstop, + CMstart, + CMdump, + CMveccatch, + CMbreakpoint, +}; + +static Cmdtab ctab[] = { + CMdebug, "debug", 2, + CMreset, "reset", 1, + CMcpuid, "cpuid", 1, + CMstop, "stop", 1, + CMstart, "start", 1, + CMdump, "dump", 3, + CMveccatch, "veccatch", 2, + CMwaitstop, "waitstop", 1, + CMstartstop, "startstop", 1, + CMbreakpoint, "breakpoint", 0, /* 2 or 3 args, optional mask */ +}; + +static int +cmdcpuid(JMedium *jmed) +{ + u32int cpuid; + + cpuid = armidentify(jmed); + dprint(Dfs, "---- Cpuid --- %8.8ux\n", cpuid); + if(cpuid == ~0){ + werrstr("not feroceon or nstrs bug..."); + return -1; + } + + dprint(Dfs, "---- Bypass probe --- \n"); + if(armbpasstest(jmed) < 0){ + fprint(2, "error in bypass\n"); + werrstr("bypass test"); + return -1; + } + ctxt.cpuid = cpuid; + return 0; +} + +static int +checkcpuid(JMedium *jmed) +{ + if(ctxt.cpuid != 0) + return 0; + + return cmdcpuid(jmed); +} + + +typedef struct VecMode VecMode; +struct VecMode{ + char c; + u32int mode; +}; + +static VecMode vmode[] = { + {'R', ResetCat}, + {'S', SWICat}, + {'P', PAbortCat}, + {'D', DAbortCat}, + {'I', IrqCat}, + {'F', FiqCat}, +}; + +static u32int +vecval(char *conds) +{ + int i, j; + u32int vcregval; + + vcregval = 0; + for(i = 0; i < strlen(conds); i++){ + for(j = 0; j < nelem(vmode); j++){ + if(vmode[j].c == conds[i]) + vcregval |= vmode[j].mode; + } + } + + return vcregval; +} +/* if running, stop, set catch, start, else, set catch, not start*/ +static int +cmdveccatch(JMedium *jmed, char *conds) +{ + u32int vcregval; + int res, wasdebug; + + wasdebug = ctxt.debug; + if(!wasdebug){ + res = iceenterdebug(jmed, &ctxt); + if(res< 0){ + werrstr("entering debug to set veccat"); + return -1; + } + } + + vcregval = vecval(conds); + res = setchain(jmed, ChCommit, 2); + if(res < 0) + return -1; + fprint(Dice, "veccatch: %#8.8ux\n", vcregval); + res = icesetreg(jmed, VecCatReg, vcregval); + if(res < 0) + return -1; + + ctxt.exitreas = VeccatReqReas; + if(!wasdebug){ + res = iceexitdebug(jmed, &ctxt); + if(res < 0){ + werrstr("exiting debug to set veccat"); + return -1; + } + } + + return 0; +} + +/* if running, stop, set breakpoint, start, else, set breakpoint, not start*/ +static int +cmdbreakpoint(JMedium *jmed, u32int addr, u32int mask) +{ + int res, wasdebug; + + wasdebug = ctxt.debug; + if(!wasdebug){ + if(ctxt.debugreas != BreakReas && ctxt.debugreas != NoReas){ + werrstr("already waiting debug"); + return -1; + } + res = iceenterdebug(jmed, &ctxt); + if(res< 0){ + werrstr("entering debug to set breakpoint"); + return -1; + } + } + + res = setchain(jmed, ChCommit, 2); + if(res < 0) + return -1; + res = icesetreg(jmed, Wp0|AddrValReg, addr); + if(res < 0) + return -1; + + res = icesetreg(jmed, Wp0|AddrMskReg, mask); + if(res < 0) + return -1; + + res = icesetreg(jmed, Wp0|DataMskReg, 0xffffffff); + if(res < 0) + return -1; + + res = icesetreg(jmed, Wp0|CtlMskReg, 0xff&~DataWPCtl); + if(res < 0) + return -1; + res = icesetreg(jmed, Wp0|CtlValReg, EnableWPCtl); + if(res < 0) + return -1; + fprint(Dice, "breakpoint: addr %#8.8ux msk %#8.8ux \n", addr, mask); + + ctxt.exitreas = BreakReqReas; + if(!wasdebug){ + res = iceexitdebug(jmed, &ctxt); + if(res < 0){ + werrstr("exiting debug to set breakpoint"); + return -1; + } + } + return 0; +} + +static char dbgstr[4*1024]; + +static int +cmdstop(JMedium *jmed) +{ + int res; + + res = iceenterdebug(jmed, &ctxt); + if(res < 0){ + return -1; + } + + ctxt.debug = icedebugstate(fs.jmed); + return 0; +} + +static int +cmdwaitstop(JMedium *jmed) +{ + int res; + + res = icewaitentry(jmed, &ctxt); + if(res < 0){ + werrstr("timeout waiting for entry to debug state"); + return -1; + } + return 0; +} + +static int +cmdstart(JMedium *jmed) +{ + int res; + res = iceexitdebug(jmed, &ctxt); + if(res < 0){ + werrstr("could not exit debug"); + return -1; + } + ctxt.debug = icedebugstate(fs.jmed); + return 0; +} + +static int +cmddump(JMedium *jmed, u32int start, u32int end) +{ + int res, i; + u32int data; + debug[Dctxt] = 1; + + + for(i = 0; i < end-start; i+=4){ + res = armrdmemwd(jmed, start+i, &data, 4); + if(res < 0){ + fprint(2, "Error reading %#8.8ux [%#8.8ux]\n", start+i, i); + werrstr("read error"); + return -1; + break; + } + dprint(Dfs, "%d[%#8.8ux] = %#8.8ux \n", + i, start+i, data); + } + return 0; +} + +static int +cmdhwreset(Fs *fs) +{ + JMedium *jmed; + + jmed = fs->jmed; + if(jmed->flush(jmed->mdata)){ + werrstr("flush"); + return -1; + } + + fs->jmed = resetmpsse(fs->jmed); + if(fs->jmed == nil) + return -1; + ctxt.debug = 0; + + jmed = fs->jmed; + /* BUG make this medium independant..., add a send 5 TMS to interface */ + if(pushcmd(jmed->mdata, "TmsCsOut EdgeDown LSB B0x7 0x7f") < 0) { + werrstr("going to reset"); + return -1; + } + if(jmed->flush(jmed->mdata)){ + werrstr("flush"); + return -1; + } + sleep(1000); + jmed->resets(jmed->mdata, 1, 0); + sleep(200); + jmed->resets(jmed->mdata, 1, 1); + sleep(200); + jmed->resets(jmed->mdata, 0, 1); + sleep(200); + jmed->resets(jmed->mdata, 0, 0); + sleep(200); + if(jmed->flush(jmed->mdata)){ + werrstr("flush"); + return -1; + } + memset(&ctxt, 0, sizeof(ArmCtxt)); + return 0; +} + +static int +cmdstartstop(JMedium *jmed) +{ + if(cmdstart(jmed) < 0){ + werrstr("starting"); + return -1; + } + if(cmdstop(jmed) < 0){ + werrstr("stopping"); + return -1; + } + return 0; +} + + +static int +runcmd(Fs *fs, char *data, int count) +{ + Cmdtab *t; + Cmdbuf *cb; + int res; + u32int st, end, addr, msk; + + cb = parsecmd(data, count); + if(cb->nf < 1){ + werrstr("empty control message"); + free(cb); + return -1; + } + + t = lookupcmd(cb, ctab, nelem(ctab)); + if(t == nil){ + free(cb); + return -1; + } + + if(t->index != CMreset && t->index != CMcpuid && + t->index != CMdebug && + checkcpuid(fs->jmed) < 0) + return -1; + + switch(t->index){ + case CMdebug: + res = cmdsetdebug((uchar *)cb->f[1]); + break; + case CMreset: + dprint(Dfs, "reset\n"); + res = cmdhwreset(fs); + break; + case CMcpuid: + dprint(Dfs, "cpuid\n"); + res = checkcpuid(fs->jmed); + break; + case CMstop: + dprint(Dfs, "stop\n"); + res = cmdstop(fs->jmed); + break; + case CMwaitstop: + dprint(Dfs, "waitstop\n"); + res = cmdwaitstop(fs->jmed); + break; + case CMstart: + dprint(Dfs, "start\n"); + res = cmdstart(fs->jmed); + break; + case CMdump: + st = atol(cb->f[1]); + end = atol(cb->f[2]); + dprint(Dfs, "dump %#8.8ux %#8.8ux\n", st, end); + res = cmddump(fs->jmed, st, end); + break; + case CMveccatch: + dprint(Dfs, "veccatch %s\n", cb->f[1]); + res = cmdveccatch(fs->jmed, cb->f[1]); + break; + case CMbreakpoint: + addr = atol(cb->f[1]); + if(cb->nf > 0 && cb->nf == 3) + msk = atol(cb->f[2]); + else if(cb->nf > 0 && cb->nf == 2) + msk = 0; + else { + res = -1; + goto Exit; + } + + dprint(Dfs, "breakpoint addr %#8.8ux mask %#8.8ux\n", addr, msk); + res = cmdbreakpoint(fs->jmed, addr, msk); + break; + default: + res = -1; + } +Exit: + free(cb); + return res; +} + +/* Arm (le) order to host */ +static void +getkernur(ArmCtxt *ct, Ureg *kur) +{ + int i; + + ct->cpsr = (ct->cpsr&~PsrMask) | kur->type; + ct->cpsr = lehgetl(&ct->cpsr); + + ct->spsr = kur->psr; + ct->spsr = lehgetl(&ct->spsr); + + memmove(ct->r, kur, 14*sizeof(u32int)); + ct->r[15] = kur->pc; + for(i = 0; i < 15; i++) + ct->r[i] = lehgetl(ct->r + i); +} + +static int +setkregs(Fs *fs, Fcall *rpc) +{ + Ureg kur; + ArmCtxt lc; + u32int mask, *lp, *gp; + int res, nb, i; + char *p; + JMedium *jmed; + + jmed = fs->jmed; + + nb = 0; + lc = ctxt; + setkernur(&kur, &ctxt); + p = (char *)&kur; + if(rpc->count + rpc->offset > sizeof(Ureg)) /* cannot write mmuregs*/ + return -1; + else + memmove(p + rpc->offset, rpc->data, rpc->count); + + getkernur(&ctxt, &kur); + + /* BUG? hmm, order matters here, if I get both I am not sure of what to do */ + if(ctxt.cpsr != lc.cpsr){ + res = armsetexec(jmed, 1, &ctxt.cpsr, ARMLDMIA|0x0001); + if(res < 0) + return -1; + res = armgofetch(jmed, ARMMSRr0CPSR, 0); + if(res < 0) + return -1; + nb += sizeof(u32int); + } + + if(ctxt.spsr != lc.spsr){ + res = armsetexec(jmed, 1, &ctxt.spsr, ARMLDMIA|0x0001); + if(res < 0) + return -1; + res = armgofetch(jmed, ARMMSRr0SPSR, 0); + if(res < 0) + return -1; + nb += sizeof(u32int); + } + + + + /* last I update the registers */ + lp = (u32int *)&lc; + gp = (u32int *)&ctxt; + mask = 0; + for(i = 0; i < 16; i++){ + if(lp[i] != gp[i]){ /* see which ones changed */ + mask |= (1 << i); + nb += sizeof(u32int); + } + } + mask |= 0x0001; /* this one I contaminated myself */ + res = armsetregs(jmed, mask, ctxt.r); + if(res < 0) + return -1; + return nb; +} + +static char* +fswrite(Fs *fs, Fcall *rpc) +{ + Fid *f; + int res, len; + + f = getfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(!f->open){ + putfid(fs, f); + return Enotopen; + } + + if(f->qid.path == Qctl){ + res = runcmd(fs, rpc->data, rpc->count); + if(res < 0){ + fprint(2, "error %r\n"); + putfid(fs, f); + return Ebadcmd; + } + } + else if(f->qid.path == Qmem){ + len = writemem(fs, rpc); + if(len < 0){ + putfid(fs, f); + return "reading memory"; + } + } + else if(f->qid.path == Qkregs){ + dprint(Dfs, "kregs write n %d off %d\n", rpc->count, rpc->offset); + len = setkregs(fs, rpc); + if(len < 0){ + putfid(fs, f); + return "writing kregs"; + } + } + else { + putfid(fs, f); + return Eperm; + } + putfid(fs, f); + return nil; +} + +static char * +fsclunk(Fs *fs, Fcall *rpc) +{ + Fid *f; + + f = getfid(fs, rpc->fid); + if(f != nil){ + f->attached = 0; + putfid(fs, f); + } + return nil; +} + +static char * +fsremove(Fs *, Fcall *) +{ + return Eperm; +} + +static char * +fsstat(Fs *fs, Fcall *rpc) +{ + Fid *f; + + f = getfid(fs, rpc->fid); + if(f == nil) + return Enofid; + rpc->stat = fs->statbuf; + rpc->nstat = dostat(f->qid.path, rpc->stat, sizeof fs->statbuf); + putfid(fs, f); + if(rpc->nstat <= BIT16SZ) + return "stat count too small"; + return nil; +} + +static char * +fswstat(Fs *, Fcall *) +{ + return Eperm; +} + +static int +dostat(int path, uchar *buf, int nbuf) +{ + Dir d; + + switch(path){ + case Qroot: + d.name = "."; + d.mode = DMDIR|0555; + d.qid.type = QTDIR; + break; + case Qctl: + d.name = "ctl"; + d.mode = 0666; + d.qid.type = QTFILE; + break; + case Qmem: + d.name = "mem"; + d.mode = 0666; + d.qid.type = QTFILE; + break; + case Qkregs: + d.name = "kregs"; + d.mode = 0666; + d.qid.type = QTFILE; + break; + case Qfpregs: + d.name = "fpregs"; + d.mode = 0666; + d.qid.type = QTFILE; + break; + case Qproc: + d.name = "proc"; + d.mode = 0444; + d.qid.type = QTFILE; + break; + case Qregs: + d.name = "regs"; + d.mode = 0666; + d.qid.type = QTFILE; + break; + case Qtext: + d.name = "text"; + d.mode = 0444; + d.qid.type = QTFILE; + break; + case Qstatus: + d.name = "status"; + d.mode = 0444; + d.qid.type = QTFILE; + break; + } + d.qid.path = path; + d.qid.vers = 0; + d.length = 0; + d.uid = d.gid = d.muid = "none"; + d.atime = d.mtime = time(nil); + return convD2M(&d, buf, nbuf); +} + +static void +fatal(char *fmt, ...) +{ + va_list arg; + char buf[1024]; + + write(2, "jtagfs: ", 8); + va_start(arg, fmt); + vseprint(buf, buf+1024, fmt, arg); + va_end(arg); + write(2, buf, strlen(buf)); + write(2, "\n", 1); + exits(fmt); +} + +static void * +emalloc(uint n) +{ + void *p; + + p = malloc(n); + if(p == nil) + fatal("out of memory"); + memset(p, 0, n); + return p; +} + +static void +usage(void) +{ + fprint(2, "usage: jtagfs [-t text] [-d debug] [-m mountpoint] [-s srvfile] jtag\n"); + exits("usage"); +} diff -Nru /sys/src/cmd/jtagfs/lebo.c /sys/src/cmd/jtagfs/lebo.c --- /sys/src/cmd/jtagfs/lebo.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/lebo.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,77 @@ +#include +#include +#include + +void +hleputv(void *p, uvlong v) +{ + uchar *a; + + a = p; + a[7] = v>>56; + a[6] = v>>48; + a[5] = v>>40; + a[4] = v>>32; + a[3] = v>>24; + a[2] = v>>16; + a[1] = v>>8; + a[0] = v; +} + +void +hleputl(void *p, uint v) +{ + uchar *a; + + a = p; + a[3] = v>>24; + a[2] = v>>16; + a[1] = v>>8; + a[0] = v; +} + +void +hleputs(void *p, ushort v) +{ + uchar *a; + + a = p; + a[1] = v>>8; + a[0] = v; +} + +uvlong +lehgetv(void *p) +{ + uchar *a; + uvlong v; + + a = p; + v = (uvlong)a[7]<<56; + v |= (uvlong)a[6]<<48; + v |= (uvlong)a[5]<<40; + v |= (uvlong)a[4]<<32; + v |= a[3]<<24; + v |= a[2]<<16; + v |= a[1]<<8; + v |= a[0]<<0; + return v; +} + +uint +lehgetl(void *p) +{ + uchar *a; + + a = p; + return (a[3]<<24)|(a[2]<<16)|(a[1]<<8)|(a[0]<<0); +} + +ushort +lehgets(void *p) +{ + uchar *a; + + a = p; + return (a[1]<<8)|(a[0]<<0); +} diff -Nru /sys/src/cmd/jtagfs/lebo.h /sys/src/cmd/jtagfs/lebo.h --- /sys/src/cmd/jtagfs/lebo.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/lebo.h Sun Apr 7 00:00:00 2013 @@ -0,0 +1,6 @@ +uint lehgetl(void *p); +ushort lehgets(void *p); +uvlong lehgetv(void *p); +void hleputl(void *p, uint v); +void hleputs(void *p, ushort v); +void hleputv(void *p, uvlong v); diff -Nru /sys/src/cmd/jtagfs/ma.c /sys/src/cmd/jtagfs/ma.c --- /sys/src/cmd/jtagfs/ma.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/ma.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,820 @@ +#include +#include +#include /* BUG: yes I know... ascii*/ +#include +#include "debug.h" +#include "tap.h" +#include "jtag.h" +#include "icert.h" /* BUG: separate medium and ice */ +#include "mpsse.h" + +/* + * Mpsse assembler + * Based on AN2232C-01 Command Processor for MPSSE and MCU Host Bus + * and various magical commands from source code all around. + */ + + +enum{ + /* MPSSE */ + + /* + * This are used only to make opcodes + * for Clocking in/out data to TD0 to TDI + */ + OpModOut = 0x10, + OpTmsCs = 0x4a, /* clock data to TmsCs */ + + /* Modifiers for both */ + OpModShort = 0x02, /* len only one byte */ + OpModOutFalling = 0x01, + OpModInFalling = 0x04, + OpModLSB = 0x08, + OpModIn = 0x20, + + OpModBitsH = 0x02, + OpModBitsIn = 0x01, + OpSetBits = 0x80, /* value(?)/direction on pins*/ + + OpLoop = 0x84, /* loop connect TDI/DO with TDO/DI */ + OpBreakLoop = 0x85, /* break loop */ + OpTckSkDiv = 0x86, /* valL valH, ?? description of reset */ + + OpDiv5ClkEnab = 0x8b, + OpDiv5ClkDisab = 0x8a, + + + /* MCU host emulation */ + OpModLong = 0x01, /* addrH addrL */ + + OpMCURd = 0x90, /* addr */ + OpMCUWr = 0x92, /* addr Data */ + + OpBad = 0, /* made this up */ + + /* Both modes */ + OpSendImm = 0x87, /* flush buffer back to PC ??? */ + OpWaitIOHigh = 0x88, /* IO high, GPIOH in MPSSE, I/O1 on MCU */ + OpWaitIOLow = 0x89, /* IO low, GPIOH in MPSSE, I/O1 on MCU */ + + OpAdaptClkEnab = 0x96, + OpAdaptClkDisab = 0x97, + RBad = 0xFA, /* followed by the bad command */ + + MaxDataSz = 0xffff+1, +}; + +typedef struct Inst Inst; +struct Inst{ + int hasresp; + int isshort; + int islsb; /* bits come/go LSB */ + int nedge; + uchar edge[2]; + uchar opcode; + int lensz; + uchar len[2]; + int immsz; + uchar imm[3]; + int datasz; + uchar data[1]; +}; + +enum{ + DataIn, + DataOut, + DataOutIn, + TmsCsOut, + TmsCsOutIn, + SetBitsL, + SetBitsH, + TckSkDiv, + MCURd, /* from this down, no param */ + MCUWr, + SendImm, + WaitIOHigh, + WaitIOLow, + AdaptClkEnab, + AdaptClkDisab, + Div5ClkEnab, + Div5ClkDisab, + GetBitsL, + GetBitsH, + Loop, + BreakLoop, + ILast, +}; + +static char *inst[] = { + [DataIn] "DataIn", + [DataOut] "DataOut", + [DataOutIn] "DataOutIn", + [TmsCsOut] "TmsCsOut", + [TmsCsOutIn] "TmsCsOutIn", + [SetBitsL] "SetBitsL", + [SetBitsH] "SetBitsH", + [TckSkDiv] "TckSkDiv", + [MCURd] "MCURd", + [MCUWr] "MCUWr", + [SendImm] "SendImm", + [WaitIOHigh] "WaitIOHigh", + [WaitIOLow] "WaitIOLow", + [AdaptClkEnab] "AdaptClkEnab", + [AdaptClkDisab] "AdaptClkDisab", + [Div5ClkEnab] "Div5ClkEnab", + [Div5ClkDisab] "Div5ClkDisab", + [GetBitsL] "GetBitsL", + [GetBitsH] "GetBitsH", + [Loop] "Loop", + [BreakLoop] "BreakLoop", + [ILast] nil, + +}; + + +enum{ + EdgeNone, + EdgeUp, + EdgeDown, + EdgeBad, +}; + +static uchar +dataopcode(Inst *inst, int instid, int issh, int islsb) +{ + uchar opcode = 0; + + switch(instid){ + case DataOutIn: + if(inst->edge[1] == EdgeDown) + opcode = OpModInFalling; + /* fall through */ + case DataOut: + opcode |= OpModOut; + if (inst->edge[0] == EdgeDown) + opcode |= OpModOutFalling; + break; + case DataIn: + if (inst->edge[0] == EdgeDown) + opcode |= OpModInFalling; + break; + } + + opcode |= inst->hasresp? OpModIn : 0; + opcode |= issh? OpModShort : 0; + opcode |= islsb? OpModLSB : 0; + + return opcode; +} + + +static uchar +opcode(Inst *inst, int instid) +{ + int issh, islsb; + uchar opcode; + + opcode = 0; + issh = inst->isshort; + islsb = inst->islsb; + + if(instid != TmsCsOutIn && instid != TmsCsOut&& instid != DataOutIn) + if(inst->edge[1] != EdgeNone){ + werrstr("should not have edge"); + return OpBad; + } + + switch(instid){ + case DataOutIn: + case DataOut: + case DataIn: + opcode = dataopcode(inst, instid, issh, islsb); + break; + + case TmsCsOutIn: + opcode = OpModIn; + if (inst->edge[1] == EdgeDown) + opcode |= OpModInFalling; + /* fall through */ + case TmsCsOut: + opcode |= OpTmsCs; + if(!issh || (inst->len[0] + 1) > 7){ + werrstr("len too big: %d, issh:%d", + inst->len[0]+1, issh); + return OpBad; + } + if (inst->edge[0] == EdgeDown) + opcode |= OpModOutFalling; + opcode |= OpModShort; + opcode |= OpModLSB; + break; + case MCURd: + opcode = OpMCURd; + opcode |= !issh? OpModLong : 0; + break; + case MCUWr: + opcode = OpMCUWr; + opcode |= !issh? OpModLong : 0; + break; + case SendImm: + opcode = OpSendImm; + break; + case WaitIOHigh: + opcode = OpWaitIOHigh; + break; + case TckSkDiv: + opcode = OpTckSkDiv; + break; + case WaitIOLow: + opcode = OpWaitIOLow; + break; + case AdaptClkEnab: + opcode = OpAdaptClkEnab; + break; + case AdaptClkDisab: + opcode = OpAdaptClkDisab; + break; + case Div5ClkEnab: + opcode = OpDiv5ClkEnab; + break; + case Div5ClkDisab: + opcode = OpDiv5ClkDisab; + break; + case GetBitsH: + case SetBitsH: + opcode = OpModBitsH; + case GetBitsL: + /* fall through */ + if(instid == GetBitsL || instid == GetBitsH) + opcode |= OpModBitsIn; + case SetBitsL: + opcode |= OpSetBits; + break; + case Loop: + opcode = OpLoop; + break; + case BreakLoop: + opcode = OpBreakLoop; + break; + default: + werrstr("unknown instruction"); + return OpBad; + } + return opcode; +} + +enum { + ParLen = 'L', /* Data len 2 bytes or 1 byte (bit len) */ + ParShLen = 'l', /* bit len Bnumber */ + ParData = 'D', /* data or @ to place data later */ + ParShData = 'd', /* short data, 1 byte at most */ + ParImm = 'I', /* immediate operator, 2/1 bytes */ + ParLImm = 'i', /* immediate operator, 2 bytes */ + ParShImm = 'c', /* immediate operator, 1 byte */ + ParEdge = 'E', /* EdgeUp or EdgeDown */ + ParEndian = 'N', /* LSB or MSB */ + ParLsb = 'n', /* LSB */ +}; + +typedef struct InstDesc InstDesc; +struct InstDesc +{ + char *name; + char *desc; + int hasresp; +}; + +static InstDesc idesc[] = { + [DataIn] {"DataIn", "ENL", 1}, + [DataOut] {"DataOut", "ENLD", 0}, + [DataOutIn] {"DataOutIn", "EENLD", 1}, + [TmsCsOut] {"TmsCsOut", "ENld", 0}, + [TmsCsOutIn] {"TmsCsOutIn", "EENld", 1}, + [MCURd] {"MCURd", "I", 1}, + [MCUWr] {"MCUWr", "Id", 0}, + [SendImm] {"SendImm", "", 0}, + [WaitIOHigh] {"WaitIOHigh", "", 0}, + [WaitIOLow] {"WaitIOLow", "", 0}, + [AdaptClkEnab] {"AdaptClkEnab", "", 0}, + [AdaptClkDisab] {"AdaptClkDisab", "", 0}, + [Div5ClkEnab] {"Div5ClkEnab", "", 0}, + [Div5ClkDisab] {"Div5ClkDisab", "", 0}, + [SetBitsL] {"SetBitsL", "cc", 0}, + [GetBitsL] {"GetBitsL", "c", 0}, + [SetBitsH] {"SetBitsH", "cc", 0}, + [GetBitsH] {"GetBitsH", "c", 0}, + [TckSkDiv] {"TckSkDiv", "i", 0}, + [Loop] {"Loop", "", 0}, + [BreakLoop] {"BreakLoop", "", 0}, + [ILast] {nil, nil, 0}, +}; + + +static int +nametoid(char *name) +{ + int i; + for (i = 0; i < ILast; i++){ + if(strcmp(inst[i], name) == 0) + break; + } + if(i == ILast) + return -1; + return i; +} + +static int +edgetoid(char *edgename) +{ + if(strcmp(edgename, "EdgeUp") == 0) + return EdgeUp; + else if(strcmp(edgename, "EdgeDown") == 0) + return EdgeDown; + + return EdgeBad; +} + +static int +isendian(char *endianname) +{ + if(strcmp(endianname, "LSB") == 0) + return 1; + else if(strcmp(endianname, "MSB") == 0) + return 1; + + return 0; +} + + +static int +datalen(Inst *inst, char *p) +{ + int len; + if(p[0] == 'B'){ + inst->isshort++; + p++; + } + else + inst->isshort = 0; + len = atoi(p); //strtol and check + if(inst->isshort && len > 8) + return -1; + if(len > MaxDataSz) + return -1; + return len; +} + +static void +setlen(Inst *inst, int dlen) +{ + if(dlen != 0){ + inst->lensz++; + inst->len[0] = (dlen - 1 ) & 0xff; + if(!inst->isshort){ + inst->len[1] = 0xff & ((dlen - 1) >> 8); + inst->lensz++; + } + } +} + + +static int +setdata(Inst *inst, char *nm, char *p, char *te, int bdlen) +{ + int i; + char *e; + + if(p == nil){ + werrstr("%s: should have data", nm); + return -1; + } + if(*p == '@'){ + inst->datasz = bdlen; + return 0; + } + + if(te != p) + p[strlen(p)] = ' '; /* untokenize */ + + for (i = 0; i < bdlen; i++){ + inst->data[i] = strtol(p, &e, 0); + if(!isspace(*e) && i != bdlen-1) + return -1; + p = e; + } + inst->datasz = i; + return 0; + +} + +static int +hasresp(int instid) +{ + return idesc[instid].hasresp; +} + + +static int +hasnoparam(int instid) +{ + return strlen(idesc[instid].desc) == 0; +} + +static char * +endfield(char *p) +{ + char *s; + + s = p; + if(*p =='\0') + return nil; + while(*p != '\0'){ + if(isspace(*p)){ + if(s == p) + return nil; + return p; + } + p++; + } + return p; +} + +enum{ + LongSz, + ShortSz, + AnySz, +}; + +static int +setimm(Inst *inst, int imm, int kindsz) +{ + int isl, immh, imml; + + isl = imm&~0xff; + + if(isl && kindsz == ShortSz) + return -1; + + + if(isl || kindsz == LongSz){ + immh = (imm >> 8)&0xff; + inst->imm[inst->immsz++] = immh; /* High, then low */ + } + imml = imm&0xff; + inst->imm[inst->immsz++] = imml; + return 0; +} + +static Inst* +parseinst(int instid, char *pars, char *idname) +{ + char *tok[15], *err, *e; + char tf; + int i, ntok, tnf, dlen, bdlen, imm, ndata; + Inst *inst; + + tf = '\0'; + dlen = bdlen = 0; + err = ""; + inst = mallocz(sizeof(Inst) + 1, 1); + if(inst == nil) + return nil; + tnf = strlen(idesc[instid].desc); + e = pars + strlen(pars) + 1; + ntok = tokenize(pars, tok, tnf); + if(tnf != ntok){ + werrstr("%s: bad nr param %d!=%d", idname, tnf, ntok); + return nil; + } + + for(i = 0; i < tnf; i++){ + tf = idesc[instid].desc[i]; + switch(tf){ + case ParLen: + dlen = datalen(inst, tok[i]); + if(dlen < 0){ + err = "bad len"; + goto Err; + } + if(inst->isshort) + bdlen = 1; + else { + bdlen = dlen; + inst = realloc(inst, sizeof(Inst) + bdlen); + if(inst == nil) + return nil; + } + break; + case ParShLen: + dlen = datalen(inst, tok[i]); + if(dlen < 0){ + err = "bad len"; + goto Err; + } + bdlen = 1; + if(!inst->isshort) { + err = "should be short"; + goto Err; + } + break; + case ParData: + if(bdlen != 0){ + ndata = setdata(inst, idname, tok[i], e, bdlen); + if(ndata < 0){ + err = "short data"; + goto Err; + } + } + else { + err = "no data"; + goto Err; + } + break; + case ParShData: + if(bdlen != 0){ + ndata = setdata(inst, idname, tok[i], e, bdlen); + if(ndata < 0){ + err = "short data"; + goto Err; + } + } + else { + err = "no data"; + goto Err; + } + if(ndata > 1){ + err = "more than 1 byte data"; + goto Err; + } + break; + case ParImm: + imm = atoi(tok[i]); + if(setimm(inst, imm, AnySz) < 0) + goto Err; + break; + case ParLImm: + imm = atoi(tok[i]); + if(setimm(inst, imm, LongSz) < 0) + goto Err; + break; + case ParShImm: + imm = atoi(tok[i]); + if(setimm(inst, imm, ShortSz) < 0) + goto Err; + break; + case ParEdge: + inst->edge[inst->nedge] = edgetoid(tok[i]); + if(inst->edge[inst->nedge] == EdgeBad) + goto Err; + inst->nedge++; + break; + case ParEndian: + inst->islsb = strcmp(tok[i], "LSB") == 0; + if(!isendian(tok[i])){ + err = "bad endianness"; + goto Err; + } + break; + case ParLsb: + inst->islsb = strcmp(tok[i], "LSB") == 0; + if(inst->islsb){ + err = "bad endianness: not LSB"; + goto Err; + } + break; + default: + err = "bad param"; + goto Err; + } + + if(bdlen > MaxDataSz || (inst->isshort && dlen > 8)){ + err = "len too big"; + goto Err; + } + } + + setlen(inst, dlen); + inst->hasresp = hasresp(instid); + inst->opcode = opcode(inst, instid); + if(inst->opcode == OpBad){ + err = "bad opcode"; + goto Err; + } + return inst; +Err: + werrstr("inst:%s, nf:%d, ftype:%c, fval:%s:%s\n", idname, i, tf, tok[i], err); + free(inst); + return nil; +} + +static Inst * +parseassln(char *assline) +{ + int instid, lnsz; + Inst *inst; + char *ef, inm[32]; + + dprint(Dassln, "%s\n", assline); + + lnsz = strlen(assline); + if(lnsz == 0){ + werrstr("%s: line does not match instruction", assline); + return nil; + } + + ef = endfield(assline); + if(ef == nil){ + werrstr("%s: empty inst", assline); + return nil; + } + strncpy(inm, assline, ef-assline); + inm[ef-assline] = '\0'; + instid = nametoid(inm); + if(instid < 0){ + werrstr("unrecognized instruction %s", inm); + return nil; + } + inst = parseinst(instid, ef, inm); + return inst; +} + +static int +unpacklen(Inst *inst) +{ + int len; + + if (inst->lensz == 0) + return 0; + len = ((inst->len[1]<<8) | inst->len[0]) + 1; + + return len; +} + +static int +instpacksz(Inst *inst) +{ + return 1 + inst->immsz + inst->lensz + inst->datasz; +} + +static void +sdumpinst(char *s, int ssz, Inst *inst) +{ + int i; + char *e, *te; + + e = s + ssz - 1; + te = e; + + e = seprint(s, te, "op: %#2.2ux\n", inst->opcode); + if(e >= te) + return; + if(inst->hasresp){ + e = seprint(e, te, "hasresp: %d\n", inst->hasresp); + if(e >= te) + return; + } + if(inst->isshort){ + e = seprint(e, te, "isshort: %d\n", inst->isshort); + if(e >= te) + return; + } + e = seprint(e, te, "islsb: %d\n", inst->islsb); + if(e >= te) + return; + + for(i = 0; i < inst->immsz; i++){ + e = seprint(e, te, "imm%d: %#2.2ux\n", i, inst->imm[i]); + if(e >= te) + return; + } + + if(inst->lensz != 0){ + e = seprint(e, te, "lensz: %d\n", inst->lensz); + if(e >= te) + return; + e = seprint(e, te, "len: %d\n", unpacklen(inst)); + if(e >= te) + return; + e = seprint(e, te, "datasz: %d\n", inst->datasz); + if(e >= te) + return; + for(i = 0; i < inst->datasz; i++){ + e = seprint(e, te, "%#2.2ux ", inst->data[i]); + if(e >= te) + return; + } + } +} + +static void +debpack(Inst *inst) +{ + int i; + if(!debug[Dmach] && !debug[DAll]) + return; + fprint(2, "%#2.2ux ", inst->opcode); + + for(i = 0; i < inst->immsz; i++) + fprint(2, "%#2.2ux ", inst->imm[i]); + for(i = 0; i < inst->lensz; i++) + fprint(2, "%#2.2ux ", inst->len[i]); + fprint(2, "\n"); + dumpbuf(Dmach, inst->data, inst->datasz); + fprint(2, "\n"); +} + + +static int +instpack(Mpsse *mpsse, Inst *inst) +{ + int n; + int sz; + Biobufhdr *bp; + + bp = &mpsse->bout; + + sz = instpacksz(inst); + if(sz + Bbuffered(bp) > MpsseBufSz){ + n = mpsseflush(mpsse); + if(n < 0) + return -1; + } + debpack(inst); + n = Bwrite(bp, &inst->opcode, 1); + if(n < 0) + return -1; + n = Bwrite(bp, inst->imm, inst->immsz); + if(n < 0) + return -1; + n = Bwrite(bp, inst->len, inst->lensz); + if(n < 0) + return -1; + n = Bwrite(bp, inst->data, inst->datasz); + if(n < 0) + return -1; + return 0; +} + +int +pushcmd(Mpsse *mpsse, char *ln) +{ + Inst *inst; + int res; + char *dmp, *lln; + + res = 0; + lln = strdup(ln); + inst = parseassln(lln); + free(lln); + + if(inst == nil) + return -1; + if(debug[Dinst]){ + dmp = malloc(255); + if(dmp == nil) + return -1; + sdumpinst(dmp, 255, inst); + fprint(2, "%s\n", dmp); + free(dmp); + } + + if(instpack(mpsse, inst) < 0) + res = -1; + free(inst); + return res; +} + +int +pushcmdwdata(Mpsse *mpsse, char *ln, uchar *buf, int buflen) +{ + Inst *inst; + int res; + char *dmp, *lln; + + res = 0; + lln = strdup(ln); + inst = parseassln(lln); + free(lln); + + if(inst == nil) + return -1; + if(inst->datasz != buflen){ + werrstr("wrong data in cmd %d != %d", inst->datasz, buflen); + return -1; + } + memmove(inst->data, buf, inst->datasz); + if(debug[Dinst]){ + dmp = malloc(255); + if(dmp == nil) + return -1; + sdumpinst(dmp, 255, inst); + fprint(2, "%s\n", dmp); + free(dmp); + } + + if(instpack(mpsse, inst) < 0) + res = -1; + free(inst); + return res; +} + diff -Nru /sys/src/cmd/jtagfs/maexamples /sys/src/cmd/jtagfs/maexamples --- /sys/src/cmd/jtagfs/maexamples Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/maexamples Sun Apr 7 00:00:00 2013 @@ -0,0 +1,16 @@ +DataIn EdgeDown LSB 3 +DataIn EdgeDown LSB B3 +DataOutIn EdgeDown EdgeUp LSB 3 0x42 0x34 0x56 +DataOutIn EdgeDown EdgeDown LSB 3 @ +DataOutIn EdgeDown EdgeUp LSB B3 0x42 +DataOutIn EdgeDown EdgeDown LSB B3 @ +TmsCsOut EdgeDown MSB B0x7 0x7 +TmsCsOut EdgeDown LSB B7 0x7 +TmsCsOutIn EdgeDown EdgeUp LSB B0x7 0x7 +MCURd 0x34 +SendImm +WaitIOHigh +AdaptClkDisab +Div5ClkEnab +Loop +SetBitsL 0x32 0x34 diff -Nru /sys/src/cmd/jtagfs/maexamplesbad /sys/src/cmd/jtagfs/maexamplesbad --- /sys/src/cmd/jtagfs/maexamplesbad Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/maexamplesbad Sun Apr 7 00:00:00 2013 @@ -0,0 +1,11 @@ +DataOutIn EdgeDown LSB 3 +DataOutIn EdgeDown LSB 3 0x42 x34 0x56 +DataOutIn EdgeDown EdgeUp LSB 3 0x42 x34 0x56 +DataOutIn EdgeDown LSB B3 0x42 0x34 0x56 +TmsCsOut EdgeDown B0x7 0x7 +TmsCsOut EdgeDown B8 0x7 +TmsCsOut EdgeDown LSB 0x7 0x7 +TmsCsOutIn EdgeDown EdgeDown LSB 0x7 0x7 +TmsCsOutIn EdgeUp MSB 0x7 0x7 +TmsCsOutIn 0x2 0x7 +tiki diff -Nru /sys/src/cmd/jtagfs/matest /sys/src/cmd/jtagfs/matest --- /sys/src/cmd/jtagfs/matest Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/matest Sun Apr 7 00:00:00 2013 @@ -0,0 +1,10 @@ +#!/bin/rc + +./8.mpssetest -t [2] errors +xd -c -bx xdg + +#this should all error: +./8.mpssetest -t < examplesbad xdb >[2] errors +cat errors +xd -c -bx xdb + diff -Nru /sys/src/cmd/jtagfs/mkfile /sys/src/cmd/jtagfs/mkfile --- /sys/src/cmd/jtagfs/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/mkfile Sun Apr 7 00:00:00 2013 @@ -0,0 +1,62 @@ + rtab.c + +rtest:V: all + ./test + +pubfiles=`{ls} + + +/usr/paurea/src/jtag/jtag.tgz: $pubfiles + cd /usr/paurea/src/jtag/ && tar cv jtag/^$pubfiles|gzip > jtag.tgz + +dist:V: /usr/paurea/src/jtag/jtag.tgz + mk all && mk clean && 9fs sources && cp ../jtag.tgz /n/sources/contrib/paurea + diff -Nru /sys/src/cmd/jtagfs/mmu.c /sys/src/cmd/jtagfs/mmu.c --- /sys/src/cmd/jtagfs/mmu.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/mmu.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,125 @@ +#include +#include +#include +#include "chain.h" +#include "debug.h" +#include "tap.h" +#include "lebo.h" +#include "bebo.h" +#include "jtag.h" +#include "icert.h" +#include "mmu.h" +#include "mpsse.h" +#include "/sys/src/9/kw/arm.h" + +/* + * Feroceon's scan chain 15 does not work, I suspect it has the multi-ice + * 40 bits limit (even if it is only one core). Just pushing MCR/MRC and + * reading back seems to be more portable + * The only bad thing is that using chain 15 one can + * change things secondary effects (I cannot think of any, but there may be) + */ + +char * +printmmuregs(MMURegs *mmuregs, char *s, int ssz) +{ + char *e, *te; + + te = s + ssz - 1; + + e = seprint(s, te, "cpid: %#8.8ux\n", mmuregs->cpid); + e = seprint(e, te, "ct: %#8.8ux\n", mmuregs->ct); + e = seprint(e, te, "control: %#8.8ux\n", mmuregs->control); + e = seprint(e, te, "ttb: %#8.8ux\n", mmuregs->ttb); + e = seprint(e, te, "dac: %#8.8ux\n", mmuregs->dac); + e = seprint(e, te, "fsr: %#8.8ux\n", mmuregs->fsr); + e = seprint(e, te, "far: %#8.8ux\n", mmuregs->far); + e = seprint(e, te, "pid: %#8.8ux\n", mmuregs->pid); + return e; +} + +/* Chain 15 does not work properly and is not portable, use MCR/MRC */ + +static int +jtagmrc(JMedium *jmed, u32int *data, uchar op1, uchar op2, uchar crn, uchar crm) +{ + int res; + u32int d; + + res = armfastexec(jmed, ARMMRC(CpSC, op1, 0, crn, crm, op2)); + if(res < 0) + return -1; + + setinst(jmed, InRestart, 0); + res = icewaitdebug(jmed); + if(res < 0) + return -1; + res = setchain(jmed, ChCommit, 1); + if(res < 0) + sysfatal("setchain %r"); + armgetexec(jmed, 1, &d, ARMSTMIA|0x0001); + + *data = d; + dprint(Dmmu, "MCR data %#8.8ux \n", d); + return 0; +} + +static int +jtagmcr(JMedium *jmed, u32int data, uchar op1, uchar op2, uchar crn, uchar crm) +{ + int res; + dprint(Dmem, "MCR data %#8.8ux\n", + data); + /* load data in r0 */ + res = armsetexec(jmed, 1, &data, ARMLDMIA|0x0001); + if(res < 0) + return -1; + + res = armfastexec(jmed, ARMMCR(CpSC, op1, 0, crn, crm, op2)); + if(res < 0) + return -1; + + setinst(jmed, InRestart, 0); + res = icewaitdebug(jmed); + if(res < 0) + return -1; + return 0; +} + + +int +mmurdregs(JMedium *jmed, MMURegs *regs) +{ + int res; + res = setchain(jmed, ChCommit, 1); + if(res < 0) + sysfatal("setchain %r"); + res = jtagmrc(jmed, ®s->cpid, 0, CpIDid, C(CpID), C(0)); + if(res < 0) + return -1; + res = jtagmrc(jmed, ®s->control, 0, 0, C(CpCONTROL), C(0)); + if(res < 0) + return -1; + res = jtagmrc(jmed, ®s->ttb, 0, 0, C(CpTTB), C(0)); + if(res < 0) + return -1; + res = jtagmrc(jmed, ®s->dac, 0, 0, C(CpDAC), C(0)); + if(res < 0) + return -1; + res = jtagmrc(jmed, ®s->fsr, 0, 0, C(CpFSR), C(0)); + if(res < 0) + return -1; + res = jtagmrc(jmed, ®s->far, 0, 0, C(CpFAR), C(0)); + if(res < 0) + return -1; + res = jtagmrc(jmed, ®s->ct, 0, CpIDct, C(CpID), C(0)); + if(res < 0) + return -1; + res = jtagmrc(jmed, ®s->pid, 0, 0, C(CpPID), C(0)); + if(res < 0) + return -1; + + return 0; +} + + diff -Nru /sys/src/cmd/jtagfs/mmu.h /sys/src/cmd/jtagfs/mmu.h --- /sys/src/cmd/jtagfs/mmu.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/mmu.h Sun Apr 7 00:00:00 2013 @@ -0,0 +1,36 @@ +typedef struct MMUMcrChain15 MMUMcrChain15; + +/* Version of the 15 Chain for mcr access */ +struct MMUMcrChain15 { + uchar rw; /* 1bit */ + uchar op1; /* 3 bits */ + uchar op2; /* 3 bits */ + uchar crn; /* 4 bits */ + uchar crm; /* 4 bits */ + uchar access; /* 1 bit */ + u32int data; /* 32 bits */ +}; + +enum { + /* other sizes taken from icert.h */ + AccessSz = 1, + OpSz = 3, + CrxSz = 4, +}; + +enum{ + ARMMCROP = 0xee000010, + ARMMRCLFLAG = 0x00100000, +}; + +#define C(cr) ((cr)&0x7) +#define ARMMCR(cp, op1, rd, crn, crm, op2) (ARMMCROP | \ + crm | (op2 << 5) | (cp<<8) | \ + (rd<<12) | (crn<<16) | (op1<<21)) + + +#define ARMMRC(cp, op1, rd, crn, crm, op2) (ARMMRCLFLAG | \ + ARMMCR(cp, op1, rd, crn, crm, op2)) + +extern int mmurdregs(JMedium *jmed, MMURegs *regs); +extern char * printmmuregs(MMURegs *mmuregs, char *s, int ssz); diff -Nru /sys/src/cmd/jtagfs/mpsse.c /sys/src/cmd/jtagfs/mpsse.c --- /sys/src/cmd/jtagfs/mpsse.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/mpsse.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,648 @@ +#include +#include +#include +#include "debug.h" +#include "lebo.h" +#include "tap.h" +#include "chain.h" +#include "jtag.h" +#include "icert.h" +#include "mpsse.h" + +/* + See schematics, openocd code. + Guessing for the GuruDisp... + */ + +static uchar cablingH[][4] = { + [Sheeva] {[TRST] 0x2, [SRST] 0x0, [TRSTnOE]0x0, [SRSTnOE] 0x04,}, + [GuruDisp] {[TRST] 0x2, [SRST] 0x0, [TRSTnOE]0x0, [SRSTnOE] 0x04,}, +}; + +static uchar cablingL[][5] = { + [Sheeva] {[TCK] 0x01, [TDI] 0x02, [TDO]0x04, [TMS] 0x08, [nOE] 0x10,}, + [GuruDisp] {[TCK] 0x01, [TDI] 0x02, [TDO]0x04, [TMS] 0x08, [nOE] 0x10,}, +}; + +static uchar valcabling[][2] = { + [Sheeva] {isPushPull, isOpenDrain}, + [GuruDisp] {isPushPull, isPushPull}, +}; + +static u32int cpuids[] = { + [Sheeva] FeroceonId, + [GuruDisp] ArmadaId, +}; + +static uchar +hinitvalH(int motherb, int trst, int srst) +{ + uchar hout; + + hout = 0; + + if(trst){ + hout |= cablingH[motherb][TRSTnOE]; + hout &= ~cablingH[motherb][TRST]; + } + else{ + hout &= ~cablingH[motherb][TRSTnOE]; + hout |= cablingH[motherb][TRST]; + } + if(srst){ + hout &= ~cablingH[motherb][SRSTnOE]; + hout |= cablingH[motherb][SRST]; + } + else{ + hout |= cablingH[motherb][SRSTnOE]; + hout &= ~cablingH[motherb][SRST]; + } + + return hout; +} + +static uchar +hinitdirH(int motherb) +{ + USED(motherb); + //int i; + //uchar dir; + //dir = 0; + //for(i = 0; i < 4; i++) + // dir |= cablingH[motherb][i]; /* BUG: TODO */ + return 0xf; +} + +int +mpsseflush(void *mdata) +{ + int r, nb; + ushort s; + Mpsse *mpsse; + + mpsse = mdata; + s = 0xbebe; + + r = 0; + + nb = Bbuffered(&mpsse->bout); + if(debug[DFile] || debug[DAll]) + fprint(2, "Flush %d\n", nb); + + if(debug[DXFlush]) + Bwrite(&mpsse->bout, &s, sizeof s); + if(nb && Bflush(&mpsse->bout) == Beof) + r = -1; + + return r; +} + +static int +mpsseresets(void *mdata, int trst, int srst) +{ + char cmd[128]; + uchar hout, hdir; + int motherb; + Mpsse *mpsse; + + mpsse = mdata; + motherb = mpsse->motherb; + + hdir = hinitdirH(motherb); + hout = hinitvalH(motherb, trst, srst); + + snprint(cmd, sizeof(cmd), "SetBitsH %#2.2ux %#2.2ux", hout, hdir); + if(pushcmd(mpsse, cmd) < 0) + return -1; + if(mpsseflush(mpsse) < 0) + return -1; + return 0; +} + + +static int +initpins(Mpsse *mpsse) +{ + char cmd[128]; + uchar hout, hdir; + int motherb; + Biobufhdr *bout; + + motherb = mpsse->motherb; + bout = &mpsse->bout; + + /* value of this also depends on opendrain etc?, seems it does not */ + hout = cablingL[motherb][TMS]; + hdir = cablingL[motherb][TCK]|cablingL[motherb][TDI]; + hdir |= cablingL[motherb][TMS]|cablingL[motherb][nOE]; + snprint(cmd, sizeof(cmd), "SetBitsL %#2.2ux %#2.2ux", hout, hdir); + if(pushcmd(mpsse, cmd) < 0){ + Bterm(bout); + return -1; + } + if(mpsseflush(mpsse) < 0) + return -1; + + /* is this Board dependant? */ + if(mpsseresets(mpsse, 0, 0) < 0) + return -1; + + return 0; +} + +static int +mpsseterm(void *mdata) +{ + Mpsse *mpsse; + int r; + + mpsse = mdata; + r = Bterm(&mpsse->bout); + free(mpsse); + + return r; +} + +/* I always work with bits on the lsb side, nbits count the + * n less significant bits, no matter msb or lsb order + * I know that mpsse works with lsb on the lsb side and + * msb I am not sure how it is codified msb on lsb side would be + * 00001234, instead of lsb on msb side 12340000 + * In any case I never use msb on the mpsse, + * just on the paths for convenience and they are lsb side + * like 000001234 + * in other words, nbits always counts from lsb bit + */ +static ulong +msb2lsb(ulong msbl, int nbits) +{ + int i; + ulong lsbl, bit; + + lsbl = 0; + for(i = 0; i < nbits; i++){ + bit = (msbl >> (nbits - i - 1))&1; + lsbl |= bit << i; + } + return lsbl; +} + +/* how many clk bits takes to clock out a data bit */ +static int +tmsdata2clk(int nbits) +{ + return ((nbits + 6)/7) * 3; +} + +#define takebits(byte, nbits, offset) (((byte) >> (offset)) & ((1U << (nbits))-1)) + +static void +dropbits(Mpsse *mpsse, int nbits) +{ + int nby, nbi; + + nby = nbits / 8; + nbi = nbits % 8; + + assert(mpsse->nbits >= nbits); + + mpsse->nbits -= 8*nby; + mpsse->rb += nby; + + mpsse->rbits = (mpsse->rbits + nbi) %8; + + mpsse->nbits -= nbi; +} + +static int +runpath(JMedium *jmed, SmPath *pth, int isrd, int issend) +{ + SmPath pref; + uchar tmslsb, lastbit; + int nclkbits; + char cmd[128]; + Mpsse *mpsse; + + mpsse = jmed->mdata; + lastbit = 0; + nclkbits = 0; + if(issend && mpsse->nbits != 0){ + lastbit = mpsse->lastbit; + mpsse->nbits--; + nclkbits = 1; + } + + while(pth->ptmslen != 0){ + pref = takepathpref(pth, MaxNbitsT); + tmslsb = msb2lsb(pref.ptms, pref.ptmslen); + + if(issend && nclkbits--){ + tmslsb |= lastbit<<7; + } + if(isrd) + snprint(cmd, sizeof(cmd), "TmsCsOutIn EdgeDown EdgeUp LSB B%#2.2ux %#2.2ux", + (uchar)pref.ptmslen, tmslsb); + else + snprint(cmd, sizeof(cmd), "TmsCsOut EdgeDown LSB B%#2.2ux %#2.2ux", + (uchar)pref.ptmslen, tmslsb); + + if(pushcmd(mpsse, cmd) < 0) + return -1; + } + + moveto(jmed, pth->st); + return 0; +} + +static int +sendbytes(Mpsse *mpsse, int nbytes, int op) +{ + char cmd[128]; + int totbytes, nwbytes; + uchar *buf; + + buf = mpsse->rb; + totbytes = mpsse->nbits/8; + nwbytes = nbytes; + if(totbytes < nbytes){ + werrstr("sendbytes: not enough %d<%d", totbytes, nbytes); + return -1; + } + if( (op&ShiftIn ) && !(op&ShiftOut) ){ + snprint(cmd, sizeof(cmd), "DataIn EdgeUp LSB %#2.2ux", + nbytes); + nwbytes = 0; + } + else if( !(op&ShiftIn) && (op&ShiftOut) ) + snprint(cmd, sizeof(cmd), "DataOut EdgeDown LSB %#2.2ux @", + nbytes); + else if( (op&ShiftIn) && (op&ShiftOut) ) + + snprint(cmd, sizeof(cmd), "DataOutIn EdgeDown EdgeUp LSB %#2.2ux @", + nbytes); + else + return -1; + + + if(pushcmdwdata(mpsse, cmd, buf, nwbytes) < 0) + return -1; + + dropbits(mpsse, 8*nbytes); + return 0; +} + + +static int +sendbits(Mpsse *mpsse, int nbits, int op) +{ + char cmd[128]; + uchar lastbyte, obyte; + + lastbyte = *mpsse->rb; + if(nbits > 8){ + werrstr("too many bits %d>%d", nbits, MaxNbitsS); + return -1; + } + + + obyte = takebits(lastbyte, nbits, mpsse->rbits); + + if( (op&ShiftIn ) && !(op&ShiftOut) ) + snprint(cmd, sizeof(cmd), "DataIn EdgeUp LSB B%#2.2ux", + nbits); + else if( !(op&ShiftIn) && (op&ShiftOut) ) + snprint(cmd, sizeof(cmd), "DataOut EdgeDown LSB B%#2.2ux %#2.2ux", + nbits, obyte); + else if( (op&ShiftIn) && (op&ShiftOut) ) + + snprint(cmd, sizeof(cmd), "DataOutIn EdgeDown EdgeUp LSB B%#2.2ux %#2.2ux", + nbits, obyte); + else + return -1; + + + if(pushcmd(mpsse, cmd) < 0) + return -1; + + dropbits(mpsse, nbits); + return 0; +} + +static int +movetost(JMedium *jmed, int dst, int isrd, int issend) +{ + SmPath pth; + int len; + + pth = pathto(jmed, dst); + len = pth.ptmslen; + if(runpath(jmed, &pth, isrd, issend) < 0) + return -1; + return len; +} + +static int +stayinst(JMedium *jmed, int nclock) +{ + SmPath pth; + + pth.ptms = 0; + pth.ptmslen = nclock; + if(runpath(jmed, &pth, 0, 0) < 0) + return -1; + return nclock; +} + +static int +mpsserdshiftrep(JMedium *jmed, uchar *buf, ShiftRDesc *rep) +{ + int nr, npartial, nby; + uchar msk; + + Mpsse *mpsse; + + mpsse = jmed->mdata; + dprint(DFile, "Reading %d\n", rep->nbyread); + + nr = readn(mpsse->jtagfd, buf, rep->nbyread); + + npartial = 0; + + dprint(DFile, "Read %d\n", nr); + dumpbuf(DFile, buf, nr); + if(nr <= 0) + return -1; + if(rep->nbiprelast != 0){ + npartial++; + } + if(rep->nbilast != 0){ + npartial++; + } + nby = rep->nbyread - npartial; + if(rep->nbiprelast != 0){ + buf[nby] = buf[nby] >> (8 - rep->nbiprelast); + } + if(rep->nbilast != 0){ + msk = MSK(rep->nbilast); + buf[nby] |= (buf[nby+1] & msk) << (8 - rep->nbilast); + } + return (7 + nby + npartial) / 8; /* readjust after compacting */ +} + +/* + * May need a couple of two or three bytes of margin for reading, + * do not call it with just enough bytes +*/ + +static int +mpsseregshift(JMedium *jmed, ShiftTDesc *req, ShiftRDesc *rep) +{ + int npsend, nr, nl, isrd, nback, nby, ntrail, reg, nbits, op; + ShiftRDesc rr; + uchar *buf; + Mpsse *mpsse; + + reg = req->reg; + buf = req->buf; + nbits = req->nbits; + op = req->op; + + mpsse = jmed->mdata; + if(rep == nil) + rep = &rr; + + nr = 0; + nby = 0; + nback = 0; + npsend = 0; + + + if(reg != TapDR && reg != TapIR) + sysfatal("unknown register"); + + isrd = op&ShiftIn; + + /* + * May need to go to Pause to cross capture before starting + * May still have a trailing bit from last shifting + */ + + if(op&ShiftPauseIn){ + nl = movetost(jmed, TapPauseDR+reg, 0, mpsse->nbits != 0); + if(nl < 0){ + werrstr("regshift: going to pause in"); + return -1; + } + } + + if(movetost(jmed, TapShiftDR+reg, 0, mpsse->nbits != 0) < 0){ + werrstr("regshift: going to shift"); + return -1; + } + + mpsse->nbits = nbits; + mpsse->rb = buf; + mpsse->rbits = 0; + + if((mpsse->nbits / 8) > 1){ + nby = mpsse->nbits / 8; + if(mpsse->nbits % 8 == 0) + nby--; + if(sendbytes(mpsse, nby, op) < 0){ + werrstr("regshift: shifting bytes"); + return -1; + } + nback = nby; + } + + + if(mpsse->nbits > 1){ + nback++; /* each op gives a partial byte */ + npsend = mpsse->nbits; + if(mpsse->nbits > 7) /* apparently hw does not like 8 bytes */ + npsend = MaxNbitsS; + npsend -= 1; /* one is for the way */ + if(sendbits(mpsse, npsend, op) < 0){ + werrstr("regshift: shifting bits"); + return -1; + } + mpsse->lastbit = takebits(*mpsse->rb, 1, mpsse->rbits); + } + + + /* I only go through pause if I need extra time to shift + * for example, I need a command to read or if I am told + */ + + if(isrd || (op&ShiftPauseOut)){ + nback++; + nl = movetost(jmed, TapPauseDR+reg, isrd, mpsse->nbits != 0); + if(nl < 0){ + werrstr("regshift: going to pause out"); + return -1; + } + } + + if(! (op&ShiftNoCommit)){ + nl = movetost(jmed, TapIdle, 0, mpsse->nbits != 0); + if(nl < 0){ + werrstr("regshift: going to idle out"); + return -1; + } + } + if(isrd){ + if(mpsseflush(mpsse) < 0){ + werrstr("regshift: flushing"); + return -1; + } + + ntrail = nbits - npsend - 8*nby; + rep->nbyread = nback; + rep->nbiprelast = npsend; + rep->nbilast = ntrail; + if( !(op&ShiftAsync) ) + nr = mpsserdshiftrep(jmed, buf, rep); + } + return nr; +} + +JMedium * +newmpsse(int fd, int motherb) +{ + Mpsse *mpsse; + Biobufhdr *bout; + JMedium *jmed; + int i; + + jmed = mallocz(sizeof(JMedium), 1); + if(jmed == nil) + return nil; + + mpsse = malloc(sizeof(Mpsse)); + if(mpsse == nil) + return nil; + + jmed->motherb = motherb; + jmed->mdata = mpsse; + jmed->regshift = mpsseregshift; + jmed->rdshiftrep = mpsserdshiftrep; + jmed->flush = mpsseflush; + jmed->term = mpsseterm; + jmed->resets = mpsseresets; + + /* BUG: configuration file? */ + if(motherb == Sheeva){ + jmed->ntaps = 1; + jmed->tapcpu = 0; + jmed->taps[0].hwid = FeroceonId; + jmed->taps[0].irlen = InLen; + } + else if(motherb == GuruDisp){ + /* BUG: set concatenating mode */ + jmed->ntaps = 3; + jmed->taps[0].hwid = 0; + jmed->taps[0].irlen = 1; + jmed->tapcpu = 1; + jmed->taps[1].hwid = ArmadaId; + jmed->taps[1].irlen = InLen; + jmed->taps[2].hwid = 0; + jmed->taps[2].irlen = 9; + } + else + sysfatal("Unkwown motherboard"); + + for(i = 0; i < jmed->ntaps; i++) + jmed->taps[i].state = TapUnknown; + jmed->state = TapUnknown; + + mpsse->jtagfd = fd; + mpsse->motherb = motherb; + + + bout = &mpsse->bout; + + if(Binits(bout, fd, OWRITE, mpsse->bp, MpsseBufSz) == Beof){ + free(mpsse); + return nil; + } + return jmed; +} + +JMedium * +initmpsse(int fd, int motherb) +{ + Mpsse *mpsse; + JMedium *jmed; + uchar buf[32]; + ShiftTDesc req; + + + jmed = newmpsse(fd, motherb); + if(jmed == nil){ + free(jmed); + return nil; + } + mpsse = jmed->mdata; + + if(initpins(mpsse) < 0) + goto Err; + + if(pushcmd(mpsse, "TckSkDiv 0x0200") < 0) + goto Err; + if(mpsseflush(mpsse) < 0) + goto Err; + + if(pushcmd(mpsse, "BreakLoop") < 0) + goto Err; + if(mpsseflush(mpsse) < 0) + goto Err; + + /* Board dependant ? */ + if(mpsseresets(mpsse, 0, 0) < 0) + goto Err; + + if(motherb == GuruDisp){ /* set concat mode */ + req.reg = TapIR; + hleputs(buf, InGuruTapctl); + req.buf = buf; + req.nbits = InGuruLen; + req.op = ShiftOut; + + jmed->regshift(jmed, &req, nil); + req.reg = TapDR; + hleputs(buf, DrGuruTapctl); + req.buf = buf; + req.nbits = 16; + req.op = ShiftOut; + jmed->regshift(jmed, &req, nil); + jmed->taps[2].TapSm = jmed->TapSm; + } + return jmed; +Err: + mpsseterm(mpsse); + free(jmed); + return nil; +} + +static void +termmpsse(JMedium *jmed) +{ + mpsseterm(jmed->mdata); + free(jmed); +} + + +JMedium * +resetmpsse(JMedium *jmed) +{ + Mpsse mpsse; + JMedium *jmnew; + + mpsse = *(Mpsse *)jmed->mdata; + mpsseflush((Mpsse *)jmed->mdata); + free(jmed->mdata); + + jmnew = initmpsse(mpsse.jtagfd, mpsse.motherb); + return jmnew; +} + diff -Nru /sys/src/cmd/jtagfs/mpsse.h /sys/src/cmd/jtagfs/mpsse.h --- /sys/src/cmd/jtagfs/mpsse.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/mpsse.h Sun Apr 7 00:00:00 2013 @@ -0,0 +1,59 @@ +typedef struct Mpsse Mpsse; + +enum { + KHz = 1000, + FtdiHSpeedClk = 30*KHz, /* 2232H 4232H */ + FtdiMaxClk = 60*KHz, /* 2232C */ + FtdiReqTck = -1, +}; + +enum{ + isPushPull, + notPushPull, + isOpenDrain, + notOpenDrain, +}; + +enum{ + MpsseBufSz = 131072, /* from openocd, noone knows why */ + MaxNbitsT = 7, /* maximum bits per state transition */ + MaxNbitsS = 7, /* maximum bits of data clocked */ + + nOE = 0, + TDI, + TDO, + TMS, + TCK, + + TRSTnOE = 0, + TRST, + SRSTnOE, + SRST, + + Sheeva = 0, + GuruDisp = 1, +}; + + +struct Mpsse{ + int nread; /* bytes left to read */ + int nbits; /* length of bits left to process */ + uchar *rb; /* current point of buf processing */ + int rbits; /* offset in the last bit to process counting from LSB */ + int lastbit; + int motherb; + int jtagfd; + Biobufhdr bout; + uchar bp[Bungetsize+MpsseBufSz]; +}; + +int mpsseflush(void *mdata); /* internal, for ma.c */ + +/* from ma.c */ +extern int pushcmd(Mpsse *mpsse, char *ln); +extern int pushcmdwdata(Mpsse *mpsse, char *ln, uchar *buf, int buflen); + +/* from mpsse.c */ +extern JMedium * initmpsse(int fd, int motherb); +extern JMedium * newmpsse(int fd, int motherb); +extern JMedium * resetmpsse(JMedium *jmed); diff -Nru /sys/src/cmd/jtagfs/mpssetest.c /sys/src/cmd/jtagfs/mpssetest.c --- /sys/src/cmd/jtagfs/mpssetest.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/mpssetest.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include "debug.h" +#include "tap.h" +#include "chain.h" +#include "jtag.h" +#include "icert.h" +#include "mmu.h" +#include "mpsse.h" + +static Biobuf bin; + +static void +testsh(JMedium *jmed) +{ + char *ln; + while(ln = Brdstr(&bin, '\n', 1)){ + if(ln == nil) + break; + fprint(2, "[%s]\n", ln); + if(strcmp(ln, "") == 0 || ln[0] == '#'){ + free(ln); + continue; + } + if(pushcmd(jmed->mdata, ln) < 0) + fprint(2, "error: %r\n"); + free(ln); + } +} + +static void +hwreset(JMedium *jmed) +{ + /* BUG make this medium independant... */ + if(pushcmd(jmed->mdata, "TmsCsOut EdgeDown LSB B0x7 0x7f") < 0) + sysfatal("resetting %r"); + if(jmed->flush(jmed->mdata)) + sysfatal("resetting %r"); + sleep(1000); + jmed->resets(jmed->mdata, 1, 0); + sleep(200); + jmed->resets(jmed->mdata, 1, 1); + sleep(200); + jmed->resets(jmed->mdata, 0, 1); + sleep(200); + jmed->resets(jmed->mdata, 0, 0); + sleep(200); +} + +static void +usage(void) +{ + fprint(2, "Usage: %s [-t] [-r] [-d 'a'...] jtagfile\n", argv0); + exits("usage"); +} + +static Chain ch; +static EiceChain1 ec1; +static EiceChain2 ec2; +static char dbgstr[256]; + +static ArmCtxt ctxt; +static u32int regs[16]; + +static void +testice(JMedium *jmed) +{ + int res, i; + u32int cpuid, data; + debug[Dctxt] = 1; + + cpuid = armidentify(jmed); + fprint(2, "---- Cpuid --- %8.8ux\n", cpuid); + if(cpuid == ~0) + sysfatal("not feroceon or WFI bug..."); + + fprint(2, "---- Bypass probe --- \n"); + if(armbpasstest(jmed) < 0) + sysfatal("bypass test"); + + fprint(2, "---- Check state --- \n"); + res = setchain(jmed, ChCommit, 2); + if(res < 0) + sysfatal("setchain %r"); + + icegetreg(jmed, DebStsReg); + + fprint(2, "---- Freeze --- \n"); + res = iceenterdebug(jmed, &ctxt); + if(res < 0) + sysfatal("could not enter debug"); + fprint(2, "\n\n\tIn debug state for 1 sec\n\n"); + sleep(1000); + fprint(2, "---- MMU test --- \n"); + res = mmurdregs(jmed, &ctxt); + if(res < 0) + sysfatal("mmurdregs %r"); + + printmmuregs(&ctxt, dbgstr, sizeof dbgstr); + fprint(2, "MMU state:\n%s\n", dbgstr); + + fprint(2, "---- Read mem --- \n"); + for(i = 0; i < 10; i++){ + res = armrdmemwd(jmed, ctxt.r[15]+4*i, &data, 4); + if(res < 0){ + fprint(2, "Error reading %#8.8ux pc[%d]\n", ctxt.r[15]+4*i, i); + break; + } + fprint(2, "Read data %#8.8ux addr %#8.8ux pc[%d]\n", + data, ctxt.r[15]+4*i, i); + } + + fprint(2, "---- Write mem (dangerous test) --- \n"); + + if(0) + for(i = 0; i < 10; i++){ + data = 0; + fprint(2, "Write data %#8.8ux addr %#8.8ux pc[%d]\n", data, ctxt.r[15]+4*i, i); + res = armwrmemwd(jmed, ctxt.r[15]+4*i, data, 4); + if(res < 0){ + fprint(2, "Error reading %#8.8ux pc[%d]\n", ctxt.r[15]+4*i, i); + break; + } + } + + fprint(2, "---- Unfreeze --- \n"); + res = iceexitdebug(jmed, &ctxt); + if(res < 0) + sysfatal("could not exit debug"); +} + + +void +main(int argc, char *argv[]) +{ + JMedium *jmed; + int tsh, rsh, i, jtagfd; + char *deb; + + deb = nil; + argv0 = "mpssetest"; + tsh = rsh = 0; + + ARGBEGIN{ + case 't': + tsh = 1; + break; + case 'r': + rsh = 1; + break; + case 'd': + deb = EARGF(usage()); + break; + default: + usage(); + } ARGEND + + if(argc != 1) + usage(); + + jtagfd = open(argv[0], ORDWR); + if(jtagfd < 0) + sysfatal("cannot open jtag file"); + + if(deb != nil) + for(i = 0; i < strlen(deb); i++) + debug[deb[i]]++; + Binit(&bin, 0, OREAD); + + if(tsh){ + jmed = newmpsse(jtagfd, Sheeva); + if(jmed == nil) + sysfatal("newmpsse %r"); + testsh(jmed); + } + else if(rsh){ + jmed = initmpsse(jtagfd, Sheeva); + if(jmed == nil) + sysfatal("initialization %r"); + hwreset(jmed); + } + else { + jmed = initmpsse(jtagfd, Sheeva); + if(jmed == nil) + sysfatal("initialization %r"); + + testice(jmed); + } + Bterm(&bin); + jmed->term(jmed); +} diff -Nru /sys/src/cmd/jtagfs/tap.c /sys/src/cmd/jtagfs/tap.c --- /sys/src/cmd/jtagfs/tap.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/tap.c Sun Apr 7 00:00:00 2013 @@ -0,0 +1,269 @@ +#include +#include +#include "debug.h" +#include "tap.h" + + +static stdebug = 1; + +/* + !8c -FVw tap.c + !8l -o tap tap.8 + !tap > /tmp/l +*/ + +static char *stnames[] = { + [TapReset] "TapReset", + [TapIdle] "TapIdle", + [TapSelDR] "TapSelDR", + [TapCaptureDR] "TapCaptureDR" , + [TapShiftDR] "TapShiftDR", + [TapExit1DR] "TapExit1DR", + [TapPauseDR] "TapPauseDR", + [TapExit2DR] "TapExit2DR", + [TapUpdateDR] "TapUpdateDR", + [TapSelIR] "TapSelIR", + [TapCaptureIR] "TapCaptureIR", + [TapShiftIR] "TapShiftIR", + [TapExit1IR] "TapExit1IR", + [TapPauseIR] "TapPauseIR", + [TapExit2IR] "TapExit2IR", + [TapUpdateIR] "TapUpdateIR", + [NStates] "", + [TapUnknown] "TapUnknown", +}; + +typedef struct TapState TapState; +struct TapState { + int next[2]; +}; + + +static TapState sm[] = { + [TapReset] {TapIdle, TapReset}, + [TapIdle] {TapIdle, TapSelDR}, + [TapSelDR] {TapCaptureDR, TapSelIR}, + [TapCaptureDR] {TapShiftDR, TapExit1DR}, + [TapShiftDR] {TapShiftDR, TapExit1DR,}, + [TapExit1DR] {TapPauseDR, TapUpdateDR}, + [TapPauseDR] {TapPauseDR, TapExit2DR}, + [TapExit2DR] {TapShiftDR, TapUpdateDR}, + [TapUpdateDR] {TapIdle, TapSelDR}, + [TapSelIR] {TapCaptureIR, TapReset}, + [TapCaptureIR] {TapShiftIR, TapExit1IR}, + [TapShiftIR] {TapShiftIR, TapExit1IR}, + [TapExit1IR] {TapPauseIR, TapUpdateIR}, + [TapPauseIR] {TapPauseIR, TapExit2IR}, + [TapExit2IR] {TapShiftIR, TapUpdateIR}, + [TapUpdateIR] {TapIdle, TapSelDR}, +}; + +static int state=TapReset; + +static int +isvalidst(int state) +{ + if(state < 0 || state >= NStates && state != TapUnknown){ + fprint(2, "invalid state: %d\n", state); + return 0; + } + return 1; +} + +static int +tapsmmove(int state, int tms) +{ + assert(tms == 0 || tms == 1); + + state = sm[state].next[tms]; + + return state; +} + +/* + * To find the shortest path from here to a state + * go in parallel through all neighbour states till I find it + * with memoization of advancing passed states + * (Breadth First Search) + * (all paths weight the same, simple topology) + * could probably use this to generate a lookup table + * but all time is spent waiting for usb anyway + */ + +static void +movepath(SmPath *path, int tms, int newst) +{ + path->st = newst; + path->ptmslen++; + path->ptms <<= 1; + path->ptms |= tms; +} + + +static SmPath +findpath(int origin, int dest) +{ + int np, memost, tms, nc, i, j, ip, st; + SmPath paths[NStates], nextp, newp; + + memset(paths, 0, sizeof(SmPath)); + paths[0].st = origin; + np = 1; + memost = 0; + if(origin == dest) + return *paths; + + for(i = 0; i < NStates; i++){ + for(j = 0; j < np; j++){ + nc = 0; + nextp = paths[j]; + for(tms = 0; tms < 2; tms++){ + newp = nextp; + st = tapsmmove(newp.st, tms); + if((1 << st) & memost){ + if(++nc == 2) /*trapped state kill*/ + paths[j] = paths[--np]; + continue; + } + movepath(&newp, tms, st); + memost |= 1 << newp.st; + + if(newp.st == dest) + return newp; + if(tms == 0) + ip = j; + else + ip = np++; + paths[ip] = newp; + + } + } + } + fprint(2, "should not come through here\n"); + newp.st = -1; + return newp; +} + + +static void +concatpath(SmPath *p1, SmPath *p2) +{ + ulong msk; + assert(p1->ptmslen < 8*sizeof(p1->ptms)); + + msk = (1 << p2->ptmslen)-1; + p1->ptms = (p1->ptms << p2->ptmslen)|(p2->ptms & msk); + p1->ptmslen += p2->ptmslen; + p1->st = p2->st; +} + +static void +dumppath(SmPath *pth) +{ + uchar frstbit; + int i; + + fprint(2, "\n("); + for(i = 0; i < pth->ptmslen; i++){ + frstbit = (pth->ptms >> (pth->ptmslen-i-1))&1; + fprint(2, "tms[%d] = %ud ", i, frstbit); + } + fprint(2, ")\n"); +} + +SmPath +pathto(TapSm *sm, int dest) +{ + SmPath pth1, pth2; + + if(!isvalidst(sm->state) || !isvalidst(dest)) + sysfatal("invalid state"); + dprint(DState, "pathto: %s -> %s\n", + stnames[sm->state], stnames[dest]); + if(sm->state == TapUnknown){ + pth1.ptmslen = 5; /* resynch */ + pth1.ptms = 0x1f; + pth2 = findpath(TapReset, dest); + + concatpath(&pth1, &pth2); + if(debug[DPath]) + dumppath(&pth1); + return pth1; + } + pth1 = findpath(sm->state, dest); + if(debug[DPath]) + dumppath(&pth1); + return pth1; +} + +void +moveto(TapSm *sm, int dest) +{ + dprint(DState, "moveto: %s -> %s\n", + stnames[sm->state], stnames[dest]); + sm->state = dest; +} + +static ulong +pathpref(SmPath *pth, int nbits) +{ + ulong msk; + assert(pth->ptmslen >= nbits); + + msk = (1 << nbits)-1; + return msk & (pth->ptms >> (pth->ptmslen - nbits)); +} + +SmPath +takepathpref(SmPath *pth, int nbits) +{ + SmPath pref; + + pref.ptmslen = 0; + if(pth->ptmslen == 0) + return pref; + if(nbits > pth->ptmslen) + nbits = pth->ptmslen; + pref.ptmslen = nbits; + pref.ptms = pathpref(pth, nbits); + pth->ptmslen -= nbits; + return pref; +} + +/* + * Testing + +void +main(int, char *[]) +{ + SmPath pth; + TapSm sm; + int orig, dest; + + orig = TapReset; + dest = TapExit2DR; + + print("origin %s dest %s\n", stnames[orig], stnames[dest]); + pth = findpath(orig, dest); + print("\n"); + dumppath(&pth); + orig = TapShiftIR; + dest = TapExit2DR; + + print("origin %s dest %s\n", stnames[orig], stnames[dest]); + pth = findpath(orig, dest); + print("\n"); + dumppath(&pth); + orig = TapIdle; + dest = TapExit2IR; + + print("origin %s dest %s\n", stnames[orig], stnames[dest]); + pth = findpath(orig, dest); + print("\n"); + dumppath(&pth); + sm.state = TapUnknown; + pth = pathto(&sm, TapExit2DR); + dumppath(&pth); + +} +*/ diff -Nru /sys/src/cmd/jtagfs/tap.h /sys/src/cmd/jtagfs/tap.h --- /sys/src/cmd/jtagfs/tap.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jtagfs/tap.h Sun Apr 7 00:00:00 2013 @@ -0,0 +1,53 @@ +typedef struct SmPath SmPath; +typedef struct TapSm TapSm; +typedef struct Tap Tap; + +enum{ + TapReset, + TapIdle, + TapSelDR, + TapCaptureDR, + TapShiftDR, + TapExit1DR, + TapPauseDR, + TapExit2DR, + TapUpdateDR, + TapSelIR, + TapCaptureIR, + TapShiftIR, + TapExit1IR, + TapPauseIR, + TapExit2IR, + TapUpdateIR, + NStates, + TapUnknown, +}; + + +enum { + TapDR, + TapIR = TapSelIR-TapSelDR, +}; + +struct TapSm{ + int state; +}; + +struct SmPath{ + ulong ptms; /* msb order on the lsb side, see comment on msb2lsb */ + ulong ptmslen; + int st; +}; + +struct Tap { + TapSm; + u32int hwid; + int irlen; + int drlen; + char name[32]; + void *private; +}; + +extern void moveto(TapSm *sm, int dest); +extern SmPath pathto(TapSm *sm, int dest); +extern SmPath takepathpref(SmPath *pth, int nbits);