myriad of arm/adi additions. working on moving past arm7 into adiv5
[goodfet] / client / GoodFETADIv5.py
diff --git a/client/GoodFETADIv5.py b/client/GoodFETADIv5.py
new file mode 100644 (file)
index 0000000..c2006b5
--- /dev/null
@@ -0,0 +1,425 @@
+#!/usr/bin/env python
+# GoodFET ARM Debugging Interface v5 (ADIv5) Client Library.
+# the ideal interface to pwning ARM Cortex
+# 
+# Contributions and bug reports welcome.
+#
+# 
+
+import sys, binascii, struct, time
+import atlasutils.smartprint as asp
+from GoodFET import GoodFET
+from intelhex import IntelHex
+
+
+#Global Commands
+READ  = 0x00
+WRITE = 0x01
+PEEK  = 0x02
+POKE  = 0x03
+SETUP = 0x10
+START = 0x20
+STOP  = 0x21
+CALL  = 0x30
+EXEC  = 0x31
+NOK   = 0x7E
+OK    = 0x7F
+
+# ARM7TDMI JTAG commands
+IR_SHIFT =              0x80
+DR_SHIFT =              0x81
+RESETTAP =              0x82
+RESETTARGET =           0x83
+GET_REGISTER =          0x87
+SET_REGISTER =          0x88
+DEBUG_INSTR =           0x89
+# Really ARM specific stuff
+WAIT_DBG =              0x91
+CHAIN0 =                0x93
+SCANCHAIN1 =            0x94
+EICE_READ =             0x95
+EICE_WRITE =            0x96
+
+#4-bit ARM JTAG INSTRUCTIONS - STANDARD
+IR_ABORT =              0x8
+IR_RESERVED1 =          0x9
+IR_DPACC =              0xA
+IR_APACC =              0xB
+IR_RESERVED2 =          0xC
+IR_RESERVED3 =          0xD
+IR_IDCODE =             0xE
+IR_BYPASS =             0xF
+#4-bit ARM JTAG INSTRUCTIONS - IMPLEMENTATION-DEFINED
+IR_EXTEST =             0x0
+IR_SAMPLE =             0x1
+IR_PRELOAD =            0x2
+IR_RESERVED =           0x3
+IR_INTEST =             0x4
+IR_CLAMP =              0x5
+IR_HIGHZ =              0x6
+IR_CLAMPZ =             0x7
+
+DP_CTRLSTAT_OFF =       0x4
+DP_SELECT_OFF =         0x8
+DP_RDBUFF_OFF =         0xC
+
+PM_usr = 0b10000
+PM_fiq = 0b10001
+PM_irq = 0b10010
+PM_svc = 0b10011
+PM_abt = 0b10111
+PM_und = 0b11011
+PM_sys = 0b11111
+proc_modes = {
+    0:      ("UNKNOWN, MESSED UP PROCESSOR MODE","fsck", "This should Never happen.  MCU is in funky state!"),
+    PM_usr: ("User Processor Mode", "usr", "Normal program execution mode"),
+    PM_fiq: ("FIQ Processor Mode", "fiq", "Supports a high-speed data transfer or channel process"),
+    PM_irq: ("IRQ Processor Mode", "irq", "Used for general-purpose interrupt handling"),
+    PM_svc: ("Supervisor Processor Mode", "svc", "A protected mode for the operating system"),
+    PM_abt: ("Abort Processor Mode", "abt", "Implements virtual memory and/or memory protection"),
+    PM_und: ("Undefined Processor Mode", "und", "Supports software emulation of hardware coprocessor"),
+    PM_sys: ("System Processor Mode", "sys", "Runs privileged operating system tasks (ARMv4 and above)"),
+}
+
+PSR_bits = [ 
+    None, None, None, None, None, "Thumb", "nFIQ_int", "nIRQ_int", 
+    "nImprDataAbort_int", "BIGendian", None, None, None, None, None, None, 
+    "GE_0", "GE_1", "GE_2", "GE_3", None, None, None, None, 
+    "Jazelle", None, None, "Q (DSP-overflow)", "oVerflow", "Carry", "Zero", "Neg",
+    ]
+
+ARM_INSTR_NOP =             0xe1a00000L
+ARM_INSTR_BX_R0 =           0xe12fff10L
+ARM_INSTR_STR_Rx_r14 =      0xe58f0000L # from atmel docs
+ARM_READ_REG =              ARM_INSTR_STR_Rx_r14
+ARM_INSTR_LDR_Rx_r14 =      0xe59f0000L # from atmel docs
+ARM_WRITE_REG =             ARM_INSTR_LDR_Rx_r14
+ARM_INSTR_LDR_R1_r0_4 =     0xe4901004L
+ARM_READ_MEM =              ARM_INSTR_LDR_R1_r0_4
+ARM_INSTR_STR_R1_r0_4 =     0xe4801004L
+ARM_WRITE_MEM =             ARM_INSTR_STR_R1_r0_4
+ARM_INSTR_MRS_R0_CPSR =     0xe10f0000L
+ARM_INSTR_MSR_cpsr_cxsf_R0 =0xe12ff000L
+ARM_INSTR_STMIA_R14_r0_rx = 0xE88e0000L      # add up to 65k to indicate which registers...
+ARM_INSTR_LDMIA_R14_r0_rx = 0xE89e0000L      # add up to 65k to indicate which registers...
+ARM_STORE_MULTIPLE =        ARM_INSTR_STMIA_R14_r0_rx
+ARM_INSTR_SKANKREGS =       0xE88F7fffL
+ARM_INSTR_CLOBBEREGS =      0xE89F7fffL
+
+ARM_INSTR_B_IMM =           0xea000000L
+ARM_INSTR_B_PC =            0xea000000L
+ARM_INSTR_BX_PC =           0xe1200010L      # need to set r0 to the desired address
+THUMB_INSTR_LDR_R0_r0 =     0x68006800L
+THUMB_WRITE_REG =           THUMB_INSTR_LDR_R0_r0
+THUMB_INSTR_STR_R0_r0 =     0x60006000L
+THUMB_READ_REG =            THUMB_INSTR_STR_R0_r0
+THUMB_INSTR_MOV_R0_PC =     0x46b846b8L
+THUMB_INSTR_MOV_PC_R0 =     0x46474647L
+THUMB_INSTR_BX_PC =         0x47784778L
+THUMB_INSTR_NOP =           0x1c001c00L
+THUMB_INSTR_B_IMM =         0xe000e000L
+ARM_REG_PC =                15
+
+
+LDM_BITMASKS = [(1<<x)-1 for x in xrange(16)]
+
+CSYSPWRUPACK =              1<<31
+CSYSPWRUPACK_BIT =          31
+CSYSPWRUPREQ =              1<<30
+CSYSPWRUPREQ_BIT =          30
+CDBGPWRUPACK =              1<<29
+CDBGPWRUPACK_BIT =          29
+CDBGPWRUPREQ =              1<<28
+CDBGPWRUPREQ_BIT =          28
+CSYSRSTACK =                1<<27
+CSYSRSTACK_BIT =            27
+CSYSRSTREQ =                1<<26
+CSYSRSTREQ_BIT =            26
+TRNCNT =                    0x3ff<<12
+TRNCNT_BIT =                12
+MASKLANE =                  0xf<<8
+MASKLANE_BIT =              8
+WDATAERR =                  1<<7
+WDATAERR =                  1<<7
+READOK =                    1<<6
+READOK =                    1<<6
+STICKYERR =                 1<<5
+STICKYERR =                 1<<5
+STICKYCMP =                 1<<4
+STICKYCMP =                 1<<4
+TRNMODE =                   3<<2
+TRNMODE =                   3<<2
+STICKYORUN =                1<<1
+STICKYORUN =                1<<1
+ORUNDETECT =                1<<0
+ORUNDETECT =                1<<0
+
+
+
+def debugstr(strng):
+    print >>sys.stderr,(strng)
+def PSRdecode(psrval):
+    output = [ "(%s mode)"%proc_modes[psrval&0x1f][1] ]
+    for x in xrange(5,32):
+        if psrval & (1<<x):
+            output.append(PSR_bits[x])
+    return " ".join(output)
+   
+fmt = ["B", "B", "<H", "<L", "<L", "<Q", "<Q", "<Q", "<Q"]
+def chop(val,byts):
+    s = struct.pack(fmt[byts], val)
+    return [ord(b) for b in s ][:byts]
+        
+class GoodFETADIv5(GoodFET):
+    """A GoodFET variant for use with ARM7TDMI microprocessor."""
+    def __init__(self):
+        GoodFET.__init__(self)
+        self.storedPC =         0xffffffff
+        self.current_dbgstate = 0xffffffff
+        self.flags =            0xffffffff
+        self.nothing =          0xffffffff
+        self.ir_status =        None
+        self.ap_selected =      None
+        self.ap_bank =          0
+        self.aps =              []
+
+    def __del__(self):
+        pass
+
+    def setup(self):
+        """Move the FET into the JTAG ARM application."""
+        #print "Initializing ARM."
+        self.writecmd(0x14,SETUP,0,self.data)
+
+    def getpc(self):
+        return self.ADIgetPC()
+
+    def flash(self,file):
+        """Flash an intel hex file to code memory."""
+        print "Flash not implemented.";
+
+    def dump(self,file,start=0,stop=0xffff):
+        """Dump an intel hex file from code memory."""
+        print "Dump not implemented.";
+
+    def ADIident(self):
+        """Get an ARM's ID."""
+        raise Exception("Not Implemented.  Abstract base class method called.")
+    def ADIidentstr(self):
+        ident=self.ADIident()
+        ver     = (ident >> 28)
+        partno  = (ident >> 12) & 0xffff
+        mfgid   = (ident >> 1)  & 0x7ff
+        return "Chip IDCODE: 0x%x\n\tver: %x\n\tpartno: %x\n\tmfgid: %x\n" % (ident, ver, partno, mfgid); 
+
+    def ADIget_register(self, reg):
+        """Get an ARM's Register"""
+        self.writecmd(0x14,GET_REGISTER,1,[reg&0xf])
+        retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
+        return retval
+    def ADIset_register(self, reg, val):
+        """Get an ARM's Register"""
+        self.writecmd(0x14,SET_REGISTER,8,[val&0xff, (val>>8)&0xff, (val>>16)&0xff, val>>24, reg,0,0,0])
+        retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
+        return retval
+    def ADIget_registers(self):
+        """Get ARM Registers"""
+        regs = [ self.ADIget_register(x) for x in range(15) ]
+        regs.append(self.ADIgetPC())            # make sure we snag the "static" version of PC
+        return regs
+    def ADIset_registers(self, regs, mask):
+        """Set ARM Registers"""
+        for x in xrange(15):
+          if (1<<x) & mask:
+            self.ADIset_register(x,regs.pop(0))
+        if (1<<15) & mask:                      # make sure we set the "static" version of PC or changes will be lost
+          self.ADIsetPC(regs.pop(0))
+
+    def ADIrestart(self):
+        raise Exception("Not Implemented.  Abstract base class method called.")
+
+    ####### Common DP features #######
+    def ADIgetAPACC(self, addr, flags):         # candidate for firmware.  not sure the intelligence wants to be there, but performance may want it.
+        raise Exception("Not Implemented.  Abstract base class method called.")
+    def ADIsetAPACC(self, addr, val):
+        raise Exception("Not Implemented.  Abstract base class method called.")
+    def ADIgetDPACC(self, addr, flags):         # candidate for firmware.  not sure the intelligence wants to be there, but performance may want it.
+        raise Exception("Not Implemented.  Abstract base class method called.")
+    def ADIsetDPACC(self, addr, val):
+        raise Exception("Not Implemented.  Abstract base class method called.")
+    def ADIsetABORT(self):
+        """ ONLY use if the debugger has received WAIT responses over an extended period"""
+        raise Exception("Not Implemented.  Abstract base class method called.")
+
+    def ADIsetSELECT(self, num):
+        return self.ADIsetDPACC(0x8, num)
+    def ADIgetSELECT(self):
+        return self.ADIgetDPACC(0x8)
+    def ADIgetSELECTrepr(self):
+        raw = self.ADIgetSELECT()
+        swdp_ctrlsel = raw&1
+        apbanksel = (raw>>4) & 0xf
+        apsel = raw>>24
+        return "SWDP_CTRLSEL = %d\nAPBANKSEL = %d\nAPSEL = %d\n"
+
+    def ADIgetWCR(self):        # SWDP only
+        raise Exception("IMPLEMENT ME: ADIgetWCR")
+
+    def ADIgetRESEND(self):     # SWDP only
+        raise Exception("IMPLEMENT ME: ADIgetRESEND")
+
+class GoodFETADIjtag(GoodFETADIv5):
+    """A GoodFET variant for use with ARM7TDMI microprocessor."""
+    def ADIshift_IR(self, IR, noretidle=0):
+        if (self.ir_status != IR):
+            self.writecmd(0x14,IR_SHIFT,2, [IR, LSB|noretidle])
+            self.ir_status = IR
+        return self.data
+    def ADIshift_DR(self, bignum, bits, flags=0):
+        data = [bits&0xff, flags&0xff,0,0]
+        data.extend(chop(data, bits/4))
+        self.writecmd(0x14,DR_SHIFT, len(data), data)
+        return self.data
+    def ADIident(self):
+        """Get an ARM's ID."""
+        self.ADIshift_IR(IR_IDCODE,0)
+        self.ADIshift_DR(0,32,LSB)
+        retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
+        return retval
+
+    def ADIdebuginstr(self,instr,bkpt):
+        """if type (instr) == int or type(instr) == long:
+            instr = struct.pack("<L", instr)
+        instr = [int("0x%x"%ord(x),16) for x in instr]
+        instr.extend([bkpt])
+        self.writecmd(0x14,DEBUG_INSTR,len(instr),instr)
+        return (self.data)"""
+        pass
+    def ADI_nop(self, bkpt=0):
+        """if self.status() & DBG_TBIT:
+            return self.ADIdebuginstr(THUMB_INSTR_NOP, bkpt)
+        return self.ADIdebuginstr(ARM_INSTR_NOP, bkpt)"""
+        pass
+
+    def ADIrestart(self):
+        self.ADIshift_IR(IR_RESTART)
+
+    ####### Common DP features #######
+    def ADIgetAPACC(self, addr):                # candidate for firmware.  not sure the intelligence wants to be there, but performance may want it.
+        self.ADIshift_IR(IR_APACC)
+        data = 1 | (addr>>1)                    # addr[3:2] goes in bits [2:1].  this *must* be a multiple of 4.
+        return self.ADIshift_DR(data, 35, LSB)
+    def ADIsetAPACC(self, addr, val):
+        """
+        0 < val < 0xffffffff
+        addr == 4*n   (ie.  multiple of four, returns the word at that register)
+        """
+        self.ADIshift_IR(IR_APACC)
+        data = (val<<3) | (addr>>1)             # addr[3:2] goes in bits [2:1].  this *must* be a multiple of 4.
+        self.ADIshift_DR(data, 35, LSB)
+    def ADIgetDPACC(self, addr, flags):         # candidate for firmware.  not sure the intelligence wants to be there, but performance may want it.
+        self.ADIshift_IR(IR_DPACC)
+        data = 1 | (addr>>1)                    # addr[3:2] goes in bits [2:1].  this *must* be a multiple of 4.
+        return self.ADIshift_DR(data, 35, LSB)
+    def ADIsetDPACC(self, addr, val):
+        """
+        0 < val < 0xffffffff
+        addr == 4*n   (ie.  multiple of four, returns the word at that register)
+        """
+        self.ADIshift_IR(IR_DPACC)
+        data = (val<<3) | (addr>>1)             # addr[3:2] goes in bits [2:1].  this *must* be a multiple of 4.
+        self.ADIshift_DR(data, 35, LSB)
+
+    def ADIsetABORT(self):
+        """ ONLY use if the debugger has received WAIT responses over an extended period"""
+        self.ADIshift_IR(self, IR_ABORT)
+        self.ADIshift_DR(self, 1, 35)
+
+    def ADIsetSELECT(self, apnum, apbank, flags="ignored for jtag"):
+        if (apnum != self.ap_selected or apbank != self.ap_bank):
+            select = (apnum<<24) | (apbank<<4)
+            self.ADIsetDPACC(0x8, select)
+            self.ap_selected = apnum
+            self.ap_bank = apbank
+    def ADIgetSELECT(self, noncached=False):
+        if (noncached):
+            return self.ADIgetDPACC(0x8)
+        return self.ap_selected
+
+
+
+class ADI_AccessPort:           # define common AP calls
+    def __init__(self, DP, apnum):
+        self.dp = DP            # link to parent.  all calls should use this to access the underlying debug port.
+        self.apnum = apnum      # which AP am i to this DP?
+
+    def getRegister(self, bank, off):
+        self.dp.ADIsetSELECT(self.apnum, bank)
+        return self.dp.ADIgetAPACC(off)
+
+    def getRegisterByAddr(self, addr):
+        bank = addr/16
+        self.dp.ADIsetSELECT(self.apnum, bank)
+        return self.dp.ADIgetAPACC(addr&0xf)
+
+    def getIdentRegister(self):
+        ident = self.getRegisterByAddr(0xfc)
+        return ident
+
+    def getIdentRegisterrepr(self):
+        raw = self.getIdentRegister()
+        ap_rev = raw>>28
+        jep_cont = (raw>>24) & 0xf
+        jep_ident = (raw>>17) & 0x7f
+        ap_class = (raw>>16) & 1
+        ap_ident = raw & 0xff
+        return "AP Revision: 0x%x\nJEP-106 Continuation Code: 0x%x\nJEP-106 Identity Code: 0x%x\nAP Class: 0x%x\nAP Identification: %x (%s)\n"%(ap_rev,jep_cont,jep_ident,ap_class,ap_ident,AP_IDENT_TYPES[ap_ident&0xf])
+    #def 
+
+class ADI_MEM_AP(ADI_AccessPort):
+    def __init__(self, DP, apnum):
+        ADI_AccessPort.__init__(self, DP, apnum)
+        self.cfg = self.getCFG()                    # necessary to cache endianness information
+        # FIXME: how do i determine if this adi supports multibyte access or just word?  or packed transfers?
+        self.setMemAccessSize(4)
+
+    def setMemAccessSize(self, bytecount):
+        csw = self.getCSW()
+        csw &= 0xfffffff8
+        csw |= (bytecount>>1)
+        self.setCSW(csw)
+
+    def getCSW(self):
+        pass
+    def setCSW(self, csw):
+        pass
+
+    def getTAR(self):
+        pass
+    def setTAR(self, csw):
+        pass
+
+    def getDRW(self):
+        pass
+    def setDRW(self, csw):
+        pass
+
+    def getBD0(self):
+        pass
+    def setBD0(self, csw):
+        pass
+
+    def getCFG(self):
+        pass
+    def setCFG(self, csw):
+        pass
+
+    def getBASE(self):
+        pass
+    def setBASE(self, csw):
+        pass
+
+    def getIDR(self):
+        pass
+    def setIDR(self, csw):
+        pass