From 459bae4ea180cd43edf3f4f6c499142e6e443947 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Sat, 16 Apr 2016 16:21:46 -0700 Subject: [PATCH 01/11] ADIv5 structural changes. - Probe ROM table - Mem-AP no longer pretends to be a target - AP no longer provides priv pointer --- src/adiv5.c | 162 +++++++++++++++++++++++++++--------------- src/cortexm.c | 88 ++++++++++++++--------- src/efm32.c | 2 +- src/include/adiv5.h | 11 +-- src/include/cortexm.h | 4 ++ src/include/target.h | 1 - src/jtag_scan.c | 1 + 7 files changed, 170 insertions(+), 99 deletions(-) diff --git a/src/adiv5.c b/src/adiv5.c index 4bc64bb..fdc26e2 100644 --- a/src/adiv5.c +++ b/src/adiv5.c @@ -20,27 +20,24 @@ /* This file implements the transport generic functions of the * ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A. - * - * Issues: - * Currently doesn't use ROM table for introspection, just assumes - * the device is Cortex-M3. */ #include "general.h" #include "jtag_scan.h" #include "gdb_packet.h" #include "adiv5.h" -#include "target.h" +#include "cortexm.h" #ifndef DO_RESET_SEQ #define DO_RESET_SEQ 0 #endif -static const char adiv5_driver_str[] = "ARM ADIv5 MEM-AP"; +/* ROM table CIDR values */ +#define CIDR_ROM_TABLE 0xb105100d +#define CIDR_GENERIC_IP 0xb105e00d -static bool ap_check_error(target *t); - -static void ap_mem_read(target *t, void *dest, uint32_t src, size_t len); -static void ap_mem_write(target *t, uint32_t dest, const void *src, size_t len); +#define PIDR_REV_MASK 0x0FFF00000ULL +#define PIDR_ARMv7M 0x4000BB000ULL +#define PIDR_ARMv7MF 0x4000BB00CULL void adiv5_dp_ref(ADIv5_DP_t *dp) { @@ -62,8 +59,6 @@ void adiv5_ap_unref(ADIv5_AP_t *ap) { if (--(ap->refcnt) == 0) { adiv5_dp_unref(ap->dp); - if (ap->priv) - ap->priv_free(ap->priv); free(ap); } } @@ -73,6 +68,91 @@ void adiv5_dp_write(ADIv5_DP_t *dp, uint16_t addr, uint32_t value) dp->low_access(dp, ADIV5_LOW_WRITE, addr, value); } +static uint32_t adiv5_mem_read32(ADIv5_AP_t *ap, uint32_t addr) +{ + uint32_t ret; + adiv5_mem_read(ap, &ret, addr, sizeof(ret)); + return ret; +} + +static void adiv5_component_probe(ADIv5_AP_t *ap, uint32_t addr) +{ + addr &= ~3; + uint64_t pidr = 0; + uint32_t cidr = 0; + for (int i = 0; i < 4; i++) { + uint32_t x = adiv5_mem_read32(ap, addr + 0xfe0 + 4*i); + pidr |= (x & 0xff) << (i * 8); + } + pidr |= (uint64_t)adiv5_mem_read32(ap, addr + 0xfd0) << 32; + for (int i = 0; i < 4; i++) { + uint32_t x = adiv5_mem_read32(ap, addr + 0xff0 + 4*i); + cidr |= ((uint64_t)(x & 0xff)) << (i * 8); + } + + switch (cidr) { + case CIDR_ROM_TABLE: /* This is a ROM table, probe recursively */ + for (int i = 0; i < 256; i++) { + uint32_t entry = adiv5_mem_read32(ap, addr + i*4); + if (entry == 0) + break; + + if ((entry & 1) == 0) + continue; + + adiv5_component_probe(ap, addr + (entry & ~0xfff)); + } + break; + case CIDR_GENERIC_IP: + switch (pidr & ~PIDR_REV_MASK) { + case PIDR_ARMv7MF: + case PIDR_ARMv7M: + cortexm_probe(ap); + break; + } + break; + } +} + +ADIv5_AP_t *adiv5_new_ap(ADIv5_DP_t *dp, uint8_t apsel) +{ + ADIv5_AP_t *ap, tmpap; + + /* Assume valid and try to read IDR */ + memset(&tmpap, 0, sizeof(tmpap)); + tmpap.dp = dp; + tmpap.apsel = apsel; + tmpap.idr = adiv5_ap_read(&tmpap, ADIV5_AP_IDR); + + if(!tmpap.idr) /* IDR Invalid - Should we not continue here? */ + return NULL; + + /* Check for ARM Mem-AP */ + uint16_t mfg = (tmpap.idr >> 17) & 0x3ff; + uint8_t cls = (tmpap.idr >> 13) & 0xf; + uint8_t type = tmpap.idr & 0xf; + if (mfg != 0x23B) /* Ditch if not ARM */ + return NULL; + if ((cls != 8) || (type == 0)) /* Ditch if not Mem-AP */ + return NULL; + + /* It's valid to so create a heap copy */ + ap = malloc(sizeof(*ap)); + memcpy(ap, &tmpap, sizeof(*ap)); + adiv5_dp_ref(dp); + + ap->cfg = adiv5_ap_read(ap, ADIV5_AP_CFG); + ap->base = adiv5_ap_read(ap, ADIV5_AP_BASE); + ap->csw = adiv5_ap_read(ap, ADIV5_AP_CSW) & + ~(ADIV5_AP_CSW_SIZE_MASK | ADIV5_AP_CSW_ADDRINC_MASK); + + DEBUG("%3d: IDR=%08X CFG=%08X BASE=%08X CSW=%08X\n", + apsel, ap->idr, ap->cfg, ap->base, ap->csw); + + return ap; +} + + void adiv5_dp_init(ADIv5_DP_t *dp) { uint32_t ctrlstat; @@ -113,56 +193,26 @@ void adiv5_dp_init(ADIv5_DP_t *dp) /* Probe for APs on this DP */ for(int i = 0; i < 256; i++) { - ADIv5_AP_t *ap, tmpap; - target *t; + ADIv5_AP_t *ap = adiv5_new_ap(dp, i); + if (ap == NULL) + continue; - /* Assume valid and try to read IDR */ - memset(&tmpap, 0, sizeof(tmpap)); - tmpap.dp = dp; - tmpap.apsel = i; - tmpap.idr = adiv5_ap_read(&tmpap, ADIV5_AP_IDR); - - if(!tmpap.idr) /* IDR Invalid - Should we not continue here? */ - break; - - /* It's valid to so create a heap copy */ - ap = malloc(sizeof(*ap)); - memcpy(ap, &tmpap, sizeof(*ap)); - adiv5_dp_ref(dp); - - ap->cfg = adiv5_ap_read(ap, ADIV5_AP_CFG); - ap->base = adiv5_ap_read(ap, ADIV5_AP_BASE); - ap->csw = adiv5_ap_read(ap, ADIV5_AP_CSW) & - ~(ADIV5_AP_CSW_SIZE_MASK | ADIV5_AP_CSW_ADDRINC_MASK); + if (ap->base == 0xffffffff) { + /* No debug entries... useless AP */ + adiv5_ap_unref(ap); + continue; + } /* Should probe further here to make sure it's a valid target. * AP should be unref'd if not valid. */ - /* Prepend to target list... */ - t = target_new(sizeof(*t)); - adiv5_ap_ref(ap); - t->priv = ap; - t->priv_free = (void (*)(void *))adiv5_ap_unref; - - t->driver = adiv5_driver_str; - t->check_error = ap_check_error; - - t->mem_read = ap_mem_read; - t->mem_write = ap_mem_write; - /* The rest sould only be added after checking ROM table */ - cortexm_probe(t); + adiv5_component_probe(ap, ap->base); } adiv5_dp_unref(dp); } -static bool ap_check_error(target *t) -{ - ADIv5_AP_t *ap = adiv5_target_ap(t); - return adiv5_dp_error(ap->dp) != 0; -} - enum align { ALIGN_BYTE = 0, ALIGN_HALFWORD = 1, @@ -208,10 +258,9 @@ static void * extract(void *dest, uint32_t src, uint32_t val, enum align align) return (uint8_t *)dest + (1 << align); } -static void -ap_mem_read(target *t, void *dest, uint32_t src, size_t len) +void +adiv5_mem_read(ADIv5_AP_t *ap, void *dest, uint32_t src, size_t len) { - ADIv5_AP_t *ap = adiv5_target_ap(t); uint32_t tmp; uint32_t osrc = src; enum align align = MIN(ALIGNOF(src), ALIGNOF(len)); @@ -237,10 +286,9 @@ ap_mem_read(target *t, void *dest, uint32_t src, size_t len) extract(dest, src, tmp, align); } -static void -ap_mem_write(target *t, uint32_t dest, const void *src, size_t len) +void +adiv5_mem_write(ADIv5_AP_t *ap, uint32_t dest, const void *src, size_t len) { - ADIv5_AP_t *ap = adiv5_target_ap(t); uint32_t odest = dest; enum align align = MIN(ALIGNOF(dest), ALIGNOF(len)); diff --git a/src/cortexm.c b/src/cortexm.c index 1d0fdfb..5f9c9c1 100644 --- a/src/cortexm.c +++ b/src/cortexm.c @@ -82,6 +82,7 @@ static int cortexm_hostio_request(target *t); static void cortexm_hostio_reply(target *t, int32_t retcode, uint32_t errcode); struct cortexm_priv { + ADIv5_AP_t *ap; bool stepping; bool on_bkpt; /* Watchpoint unit status */ @@ -197,8 +198,48 @@ static const char tdesc_cortex_mf[] = " " ""; -bool cortexm_probe(target *t) +ADIv5_AP_t *cortexm_ap(target *t) { + return ((struct cortexm_priv *)t->priv)->ap; +} + +static void cortexm_mem_read(target *t, void *dest, uint32_t src, size_t len) +{ + adiv5_mem_read(cortexm_ap(t), dest, src, len); +} + +static void cortexm_mem_write(target *t, uint32_t dest, const void *src, size_t len) +{ + adiv5_mem_write(cortexm_ap(t), dest, src, len); +} + +static bool cortexm_check_error(target *t) +{ + ADIv5_AP_t *ap = cortexm_ap(t); + return adiv5_dp_error(ap->dp) != 0; +} + +static void cortexm_priv_free(void *priv) +{ + adiv5_ap_unref(((struct cortexm_priv *)priv)->ap); + free(priv); +} + +bool cortexm_probe(ADIv5_AP_t *ap) +{ + target *t; + + t = target_new(sizeof(*t)); + adiv5_ap_ref(ap); + struct cortexm_priv *priv = calloc(1, sizeof(*priv)); + t->priv = priv; + t->priv_free = cortexm_priv_free; + priv->ap = ap; + + t->check_error = cortexm_check_error; + t->mem_read = cortexm_mem_read; + t->mem_write = cortexm_mem_write; + t->driver = cortexm_driver_str; t->attach = cortexm_attach; @@ -229,11 +270,6 @@ bool cortexm_probe(target *t) t->tdesc = tdesc_cortex_mf; } - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = calloc(1, sizeof(*priv)); - ap->priv = priv; - ap->priv_free = free; - /* Default vectors to catch */ priv->demcr = CORTEXM_DEMCR_TRCENA | CORTEXM_DEMCR_VC_HARDERR | CORTEXM_DEMCR_VC_CORERESET; @@ -260,8 +296,7 @@ bool cortexm_probe(target *t) bool cortexm_attach(target *t) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; unsigned i; uint32_t r; int tries; @@ -323,8 +358,7 @@ bool cortexm_attach(target *t) void cortexm_detach(target *t) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; unsigned i; /* Clear any stale breakpoints */ @@ -343,7 +377,7 @@ enum { DB_DHCSR, DB_DCRSR, DB_DCRDR, DB_DEMCR }; static void cortexm_regs_read(target *t, void *data) { - ADIv5_AP_t *ap = adiv5_target_ap(t); + ADIv5_AP_t *ap = cortexm_ap(t); uint32_t *regs = data; unsigned i; @@ -374,7 +408,7 @@ static void cortexm_regs_read(target *t, void *data) static void cortexm_regs_write(target *t, const void *data) { - ADIv5_AP_t *ap = adiv5_target_ap(t); + ADIv5_AP_t *ap = cortexm_ap(t); const uint32_t *regs = data; unsigned i; @@ -459,8 +493,7 @@ static void cortexm_halt_request(target *t) static int cortexm_halt_wait(target *t) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; volatile uint32_t dhcsr = 0; volatile struct exception e; @@ -521,8 +554,7 @@ static int cortexm_halt_wait(target *t) void cortexm_halt_resume(target *t, bool step) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; uint32_t dhcsr = CORTEXM_DHCSR_DBGKEY | CORTEXM_DHCSR_C_DEBUGEN; if (step) @@ -639,8 +671,7 @@ int cortexm_run_stub(target *t, uint32_t loadaddr, static int cortexm_set_hw_bp(target *t, uint32_t addr) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; uint32_t val = addr; unsigned i; @@ -664,8 +695,7 @@ static int cortexm_set_hw_bp(target *t, uint32_t addr) static int cortexm_clear_hw_bp(target *t, uint32_t addr) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; unsigned i; for(i = 0; i < priv->hw_breakpoint_max; i++) @@ -686,8 +716,7 @@ static int cortexm_clear_hw_bp(target *t, uint32_t addr) static int cortexm_set_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; unsigned i; switch(len) { /* Convert bytes size to mask size */ @@ -728,8 +757,7 @@ cortexm_set_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len) static int cortexm_clear_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; unsigned i; switch(len) { @@ -764,8 +792,7 @@ cortexm_clear_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len) static int cortexm_check_hw_wp(target *t, uint32_t *addr) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; unsigned i; for(i = 0; i < priv->hw_watchpoint_max; i++) @@ -783,8 +810,7 @@ static int cortexm_check_hw_wp(target *t, uint32_t *addr) static bool cortexm_vector_catch(target *t, int argc, char *argv[]) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; const char *vectors[] = {"reset", NULL, NULL, NULL, "mm", "nocp", "chk", "stat", "bus", "int", "hard"}; uint32_t tmp = 0; @@ -862,8 +888,7 @@ static bool cortexm_vector_catch(target *t, int argc, char *argv[]) static int cortexm_hostio_request(target *t) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; uint32_t arm_regs[t->regs_size]; uint32_t params[4]; @@ -964,8 +989,7 @@ static int cortexm_hostio_request(target *t) static void cortexm_hostio_reply(target *t, int32_t retcode, uint32_t errcode) { - ADIv5_AP_t *ap = adiv5_target_ap(t); - struct cortexm_priv *priv = ap->priv; + struct cortexm_priv *priv = t->priv; uint32_t arm_regs[t->regs_size]; DEBUG("syscall return ret=%d errno=%d\n", retcode, errcode); diff --git a/src/efm32.c b/src/efm32.c index 25bb3f2..52b2ea0 100644 --- a/src/efm32.c +++ b/src/efm32.c @@ -252,7 +252,7 @@ char variant_string[40]; bool efm32_probe(target *t) { /* Read the IDCODE register from the SW-DP */ - ADIv5_AP_t *ap = adiv5_target_ap(t); + ADIv5_AP_t *ap = cortexm_ap(t); uint32_t ap_idcode = ap->dp->idcode; /* Check the idcode is silabs. See AN0062 Section 2.2 */ diff --git a/src/include/adiv5.h b/src/include/adiv5.h index 12d3bf4..b6fe30f 100644 --- a/src/include/adiv5.h +++ b/src/include/adiv5.h @@ -22,7 +22,6 @@ #define __ADIV5_H #include "jtag_scan.h" -#include "target.h" #define ADIV5_APnDP 0x100 #define ADIV5_DP_REG(x) (x) @@ -144,14 +143,12 @@ typedef struct ADIv5_AP_s { uint32_t cfg; uint32_t base; uint32_t csw; - - void *priv; - void (*priv_free)(void *); } ADIv5_AP_t; void adiv5_dp_init(ADIv5_DP_t *dp); void adiv5_dp_write(ADIv5_DP_t *dp, uint16_t addr, uint32_t value); +ADIv5_AP_t *adiv5_new_ap(ADIv5_DP_t *dp, uint8_t apsel); void adiv5_dp_ref(ADIv5_DP_t *dp); void adiv5_ap_ref(ADIv5_AP_t *ap); void adiv5_dp_unref(ADIv5_DP_t *dp); @@ -163,10 +160,8 @@ uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint16_t addr); void adiv5_jtag_dp_handler(jtag_dev_t *dev); int adiv5_swdp_scan(void); -static inline ADIv5_AP_t *adiv5_target_ap(target *target) -{ - return target->priv; -} +void adiv5_mem_read(ADIv5_AP_t *ap, void *dest, uint32_t src, size_t len); +void adiv5_mem_write(ADIv5_AP_t *ap, uint32_t dest, const void *src, size_t len); #endif diff --git a/src/include/cortexm.h b/src/include/cortexm.h index de57112..bf1d821 100644 --- a/src/include/cortexm.h +++ b/src/include/cortexm.h @@ -20,6 +20,7 @@ #define __CORTEXM_H #include "target.h" +#include "adiv5.h" /* Private peripheral bus base address */ #define CORTEXM_PPB_BASE 0xE0000000 @@ -155,6 +156,9 @@ #define CORTEXM_TOPT_INHIBIT_SRST (1 << 2) +bool cortexm_probe(ADIv5_AP_t *ap); +ADIv5_AP_t *cortexm_ap(target *t); + bool cortexm_attach(target *t); void cortexm_detach(target *t); void cortexm_halt_resume(target *t, bool step); diff --git a/src/include/target.h b/src/include/target.h index 1a4138f..9e73279 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -257,7 +257,6 @@ static inline void target_mem_write8(target *t, uint32_t addr, uint8_t value) /* Probe for various targets. * Actual functions implemented in their respective drivers. */ -bool cortexm_probe(target *t); bool stm32f1_probe(target *t); bool stm32f4_probe(target *t); bool stm32l0_probe(target *t); diff --git a/src/jtag_scan.c b/src/jtag_scan.c index 2d2194c..3bd3be1 100644 --- a/src/jtag_scan.c +++ b/src/jtag_scan.c @@ -28,6 +28,7 @@ #include "morse.h" #include "jtag_scan.h" #include "gdb_packet.h" +#include "target.h" #include "adiv5.h" #include "arm7tdmi.h" From 60f3be501e30f75c1f7f8de05537fa4370913a1d Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Sat, 16 Apr 2016 16:22:35 -0700 Subject: [PATCH 02/11] Pass breakpoint length to target. --- src/cortexm.c | 10 ++++++---- src/gdb_main.c | 4 ++-- src/include/target.h | 12 ++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/cortexm.c b/src/cortexm.c index 5f9c9c1..91da4f5 100644 --- a/src/cortexm.c +++ b/src/cortexm.c @@ -67,8 +67,8 @@ static int cortexm_halt_wait(target *t); static void cortexm_halt_request(target *t); static int cortexm_fault_unwind(target *t); -static int cortexm_set_hw_bp(target *t, uint32_t addr); -static int cortexm_clear_hw_bp(target *t, uint32_t addr); +static int cortexm_set_hw_bp(target *t, uint32_t addr, uint8_t len); +static int cortexm_clear_hw_bp(target *t, uint32_t addr, uint8_t len); static int cortexm_set_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len); static int cortexm_clear_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len); @@ -669,8 +669,9 @@ int cortexm_run_stub(target *t, uint32_t loadaddr, /* The following routines implement hardware breakpoints. * The Flash Patch and Breakpoint (FPB) system is used. */ -static int cortexm_set_hw_bp(target *t, uint32_t addr) +static int cortexm_set_hw_bp(target *t, uint32_t addr, uint8_t len) { + (void)len; struct cortexm_priv *priv = t->priv; uint32_t val = addr; unsigned i; @@ -693,8 +694,9 @@ static int cortexm_set_hw_bp(target *t, uint32_t addr) return 0; } -static int cortexm_clear_hw_bp(target *t, uint32_t addr) +static int cortexm_clear_hw_bp(target *t, uint32_t addr, uint8_t len) { + (void)len; struct cortexm_priv *priv = t->priv; unsigned i; diff --git a/src/gdb_main.c b/src/gdb_main.c index cec5822..c2f2337 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -452,9 +452,9 @@ handle_z_packet(char *packet, int plen) switch(type) { case 1: /* Hardware breakpoint */ if(set) - ret = target_set_hw_bp(cur_target, addr); + ret = target_set_hw_bp(cur_target, addr, len); else - ret = target_clear_hw_bp(cur_target, addr); + ret = target_clear_hw_bp(cur_target, addr, len); break; case 2: diff --git a/src/include/target.h b/src/include/target.h index 9e73279..2fcb4f0 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -76,11 +76,11 @@ target *target_attach(target *t, target_destroy_callback destroy_cb); (target)->halt_resume((target), (step)) /* Break-/watchpoint functions */ -#define target_set_hw_bp(target, addr) \ - (target)->set_hw_bp((target), (addr)) +#define target_set_hw_bp(target, addr, len) \ + (target)->set_hw_bp((target), (addr), (len)) -#define target_clear_hw_bp(target, addr) \ - (target)->clear_hw_bp((target), (addr)) +#define target_clear_hw_bp(target, addr, len) \ + (target)->clear_hw_bp((target), (addr), (len)) #define target_set_hw_wp(target, type, addr, len) \ @@ -169,8 +169,8 @@ struct target_s { void (*halt_resume)(target *t, bool step); /* Break-/watchpoint functions */ - int (*set_hw_bp)(target *t, uint32_t addr); - int (*clear_hw_bp)(target *t, uint32_t addr); + int (*set_hw_bp)(target *t, uint32_t addr, uint8_t len); + int (*clear_hw_bp)(target *t, uint32_t addr, uint8_t len); int (*set_hw_wp)(target *t, uint8_t type, uint32_t addr, uint8_t len); int (*clear_hw_wp)(target *t, uint8_t type, uint32_t addr, uint8_t len); From f6b574e0b0e495840d43ac09146f8a8f4586fb4e Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Mon, 18 Apr 2016 11:30:25 -0700 Subject: [PATCH 03/11] Cortex-A target support. --- src/Makefile | 1 + src/adiv5.c | 11 + src/cortexa.c | 574 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 586 insertions(+) create mode 100644 src/cortexa.c diff --git a/src/Makefile b/src/Makefile index e090e46..7661837 100644 --- a/src/Makefile +++ b/src/Makefile @@ -18,6 +18,7 @@ SRC = \ adiv5_jtagdp.c \ adiv5_swdp.c \ command.c \ + cortexa.c \ cortexm.c \ crc32.c \ efm32.c \ diff --git a/src/adiv5.c b/src/adiv5.c index fdc26e2..39cd3db 100644 --- a/src/adiv5.c +++ b/src/adiv5.c @@ -34,10 +34,14 @@ /* ROM table CIDR values */ #define CIDR_ROM_TABLE 0xb105100d #define CIDR_GENERIC_IP 0xb105e00d +#define CIDR_DEBUG 0xb105900d #define PIDR_REV_MASK 0x0FFF00000ULL #define PIDR_ARMv7M 0x4000BB000ULL #define PIDR_ARMv7MF 0x4000BB00CULL +#define PIDR_ARMv7A 0x4000BBC09ULL + +extern bool cortexa_probe(ADIv5_AP_t *apb, uint32_t debug_base); void adiv5_dp_ref(ADIv5_DP_t *dp) { @@ -111,6 +115,13 @@ static void adiv5_component_probe(ADIv5_AP_t *ap, uint32_t addr) break; } break; + case CIDR_DEBUG: + switch (pidr & ~PIDR_REV_MASK) { + case PIDR_ARMv7A: + cortexa_probe(ap, addr); + break; + } + break; } } diff --git a/src/cortexa.c b/src/cortexa.c new file mode 100644 index 0000000..1e8bf4b --- /dev/null +++ b/src/cortexa.c @@ -0,0 +1,574 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2016 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements debugging functionality specific to ARM + * the Cortex-A9 core. This should be generic to ARMv7-A as it is + * implemented according to the "ARMv7-A Architecture Reference Manual", + * ARM doc DDI0406C. + * + * Cache line length is from Cortex-A9 TRM, may differ for others. + * Janky reset code is for Zynq-7000 which disconnects the DP from the JTAG + * scan chain during reset. + */ +#include "general.h" +#include "exception.h" +#include "jtagtap.h" +#include "jtag_scan.h" +#include "adiv5.h" +#include "target.h" +#include "command.h" +#include "gdb_packet.h" +#include "cortexm.h" +#include "morse.h" + +#include + +static char cortexa_driver_str[] = "ARM Cortex-A"; + +/* Signals returned by cortexa_halt_wait() */ +#define SIGINT 2 +#define SIGTRAP 5 +#define SIGSEGV 11 +#define SIGLOST 29 + +static bool cortexa_attach(target *t); +static void cortexa_detach(target *t); +static void cortexa_halt_resume(target *t, bool step); + +static void cortexa_regs_read(target *t, void *data); +static void cortexa_regs_write(target *t, const void *data); + +static void cortexa_reset(target *t); +static int cortexa_halt_wait(target *t); +static void cortexa_halt_request(target *t); + +static int cortexa_set_hw_bp(target *t, uint32_t addr, uint8_t len); +static int cortexa_clear_hw_bp(target *t, uint32_t addr, uint8_t len); +static uint32_t bp_bas(uint32_t addr, uint8_t len); + +static void apb_write(target *t, uint16_t reg, uint32_t val); +static uint32_t apb_read(target *t, uint16_t reg); +static void write_gpreg(target *t, uint8_t regno, uint32_t val); + +struct cortexa_priv { + uint32_t base; + ADIv5_AP_t *apb; + ADIv5_AP_t *ahb; + struct { + uint32_t r[16]; + uint32_t cpsr; + uint32_t fpscr; + uint64_t d[16]; + } reg_cache; + unsigned hw_breakpoint_max; + unsigned hw_breakpoint[16]; + uint32_t bpc0; +}; + +/* This may be specific to Cortex-A9 */ +#define CACHE_LINE_LENGTH (8*4) + +/* Debug APB registers */ +#define DBGDIDR 0 + +#define DBGDTRRX 32 /* DCC: Host to target */ +#define DBGITR 33 + +#define DBGDSCR 34 +#define DBGDSCR_TXFULL (1 << 29) +#define DBGDSCR_INSTRCOMPL (1 << 24) +#define DBGDSCR_EXTDCCMODE_STALL (1 << 20) +#define DBGDSCR_EXTDCCMODE_MASK (3 << 20) +#define DBGDSCR_HDBGEN (1 << 14) +#define DBGDSCR_ITREN (1 << 13) +#define DBGDSCR_UND_I (1 << 8) +#define DBGDSCR_MOE_MASK (0xf << 2) +#define DBGDSCR_MOE_HALT_REQ (0x0 << 2) +#define DBGDSCR_RESTARTED (1 << 1) +#define DBGDSCR_HALTED (1 << 0) + +#define DBGDTRTX 35 /* DCC: Target to host */ + +#define DBGDRCR 36 +#define DBGDRCR_CSE (1 << 2) +#define DBGDRCR_RRQ (1 << 1) +#define DBGDRCR_HRQ (1 << 0) + +#define DBGBVR(i) (64+(i)) +#define DBGBCR(i) (80+(i)) +#define DBGBCR_INST_MISMATCH (4 << 20) +#define DBGBCR_BAS_ANY (0xf << 5) +#define DBGBCR_BAS_LOW_HW (0x3 << 5) +#define DBGBCR_BAS_HIGH_HW (0xc << 5) +#define DBGBCR_EN (1 << 0) + +/* Instruction encodings for accessing the coprocessor interface */ +#define MCR 0xee000010 +#define MRC 0xee100010 +#define CPREG(coproc, opc1, rt, crn, crm, opc2) \ + (((opc1) << 21) | ((crn) << 16) | ((rt) << 12) | \ + ((coproc) << 8) | ((opc2) << 5) | (crm)) + +/* Debug registers CP14 */ +#define DBGDTRRXint CPREG(14, 0, 0, 0, 5, 0) +#define DBGDTRTXint CPREG(14, 0, 0, 0, 5, 0) + +/* Cache management registers CP15 */ +#define ICIALLU CPREG(15, 0, 0, 7, 5, 0) +#define DCCIMVAC CPREG(15, 0, 0, 7, 14, 1) +#define DCCMVAC CPREG(15, 0, 0, 7, 10, 1) + +/* Thumb mode bit in CPSR */ +#define CPSR_THUMB (1 << 5) + +/* GDB register map / target description */ +static const char tdesc_cortex_a[] = + "" + "" + "" + " arm" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +static void apb_write(target *t, uint16_t reg, uint32_t val) +{ + struct cortexa_priv *priv = t->priv; + ADIv5_AP_t *ap = priv->apb; + uint32_t addr = priv->base + 4*reg; + uint32_t csw = ap->csw | ADIV5_AP_CSW_ADDRINC_SINGLE | ADIV5_AP_CSW_SIZE_WORD; + adiv5_ap_write(ap, ADIV5_AP_CSW, csw); + adiv5_ap_write(ap, ADIV5_AP_TAR, addr); + adiv5_ap_write(ap, ADIV5_AP_DRW, val); +} + +static uint32_t apb_read(target *t, uint16_t reg) +{ + struct cortexa_priv *priv = t->priv; + ADIv5_AP_t *ap = priv->apb; + uint32_t addr = priv->base + 4*reg; + uint32_t csw = ap->csw | ADIV5_AP_CSW_ADDRINC_SINGLE | ADIV5_AP_CSW_SIZE_WORD; + adiv5_ap_write(ap, ADIV5_AP_CSW, csw); + adiv5_ap_write(ap, ADIV5_AP_TAR, addr); + return adiv5_ap_read(ap, ADIV5_AP_DRW); +} + +static void cortexa_mem_read(target *t, void *dest, uint32_t src, size_t len) +{ + /* Clean cache before reading */ + for (uint32_t cl = src & ~(CACHE_LINE_LENGTH-1); + cl < src + len; cl += CACHE_LINE_LENGTH) { + write_gpreg(t, 0, cl); + apb_write(t, DBGITR, MCR | DCCMVAC); + } + + ADIv5_AP_t *ahb = ((struct cortexa_priv*)t->priv)->ahb; + adiv5_mem_read(ahb, dest, src, len); +} + +static void cortexa_mem_write(target *t, uint32_t dest, const void *src, size_t len) +{ + /* Clean and invalidate cache before writing */ +#if 0 + /* I've taken this out for now because it makes loading painfully + * slow. + * FIXME This can cause data integrity problems if modifying the target + * state from the debugger! + */ + for (uint32_t cl = dest & ~(CACHE_LINE_LENGTH-1); + cl < dest + len; cl += CACHE_LINE_LENGTH) { + write_gpreg(t, 0, cl); + apb_write(t, DBGITR, MCR | DCCIMVAC); + } +#endif + ADIv5_AP_t *ahb = ((struct cortexa_priv*)t->priv)->ahb; + adiv5_mem_write(ahb, dest, src, len); +} + +static bool cortexa_check_error(target *t) +{ + ADIv5_AP_t *ahb = ((struct cortexa_priv*)t->priv)->ahb; + return adiv5_dp_error(ahb->dp) != 0; +} + + +bool cortexa_probe(ADIv5_AP_t *apb, uint32_t debug_base) +{ + target *t; + + DEBUG("%s base=0x%08"PRIx32"\n", __func__, debug_base); + + /* Prepend to target list... */ + t = target_new(sizeof(*t)); + adiv5_ap_ref(apb); + struct cortexa_priv *priv = calloc(1, sizeof(*priv)); + t->priv = priv; + t->priv_free = free; + priv->apb = apb; + /* FIXME Find a better way to find the AHB. This is likely to be + * device specific. */ + priv->ahb = adiv5_new_ap(apb->dp, 0); + adiv5_ap_ref(priv->ahb); + priv->base = debug_base; + uint32_t dbgdidr = apb_read(t, DBGDIDR); + priv->hw_breakpoint_max = ((dbgdidr >> 24) & 15)+1; + DEBUG("Target has %d breakpoints\n", priv->hw_breakpoint_max); + + t->check_error = cortexa_check_error; + + t->mem_read = cortexa_mem_read; + t->mem_write = cortexa_mem_write; + + t->driver = cortexa_driver_str; + + t->attach = cortexa_attach; + t->detach = cortexa_detach; + + t->tdesc = tdesc_cortex_a; + t->regs_read = cortexa_regs_read; + t->regs_write = cortexa_regs_write; + + t->reset = cortexa_reset; + t->halt_request = cortexa_halt_request; + t->halt_wait = cortexa_halt_wait; + t->halt_resume = cortexa_halt_resume; + t->regs_size = sizeof(priv->reg_cache); + + t->set_hw_bp = cortexa_set_hw_bp; + t->clear_hw_bp = cortexa_clear_hw_bp; + + return true; +} + +bool cortexa_attach(target *t) +{ + struct cortexa_priv *priv = t->priv; + int tries; + + /* Clear any pending fault condition */ + target_check_error(t); + + /* Enable halting debug mode */ + uint32_t dbgdscr = apb_read(t, DBGDSCR); + dbgdscr |= DBGDSCR_HDBGEN | DBGDSCR_ITREN; + dbgdscr = (dbgdscr & ~DBGDSCR_EXTDCCMODE_MASK) | DBGDSCR_EXTDCCMODE_STALL; + apb_write(t, DBGDSCR, dbgdscr); + DEBUG("DBGDSCR = 0x%08x\n", dbgdscr); + + target_halt_request(t); + tries = 10; + while(!platform_srst_get_val() && !target_halt_wait(t) && --tries) + platform_delay(2); + if(!tries) + return false; + + /* Clear any stale breakpoints */ + for(unsigned i = 0; i < priv->hw_breakpoint_max; i++) { + apb_write(t, DBGBCR(i), 0); + priv->hw_breakpoint[i] = 0; + } + + platform_srst_set_val(false); + + return true; +} + +void cortexa_detach(target *t) +{ + struct cortexa_priv *priv = t->priv; + + /* Clear any stale breakpoints */ + for(unsigned i = 0; i < priv->hw_breakpoint_max; i++) + apb_write(t, DBGBCR(i), 0); + + /* Disable halting debug mode */ + uint32_t dbgdscr = apb_read(t, DBGDSCR); + apb_write(t, DBGDSCR, dbgdscr & ~DBGDSCR_HDBGEN); +} + + +static uint32_t read_gpreg(target *t, uint8_t regno) +{ + /* To read a register we use DBGITR to load an MCR instruction + * that sends the value via DCC DBGDTRTX using the CP14 interface. + */ + uint32_t instr = MCR | DBGDTRTXint | ((regno & 0xf) << 12); + apb_write(t, DBGITR, instr); + /* Return value read from DCC channel */ + return apb_read(t, DBGDTRTX); +} + +static void write_gpreg(target *t, uint8_t regno, uint32_t val) +{ + /* Write value to DCC channel */ + apb_write(t, DBGDTRRX, val); + /* Run instruction to load register */ + uint32_t instr = MRC | DBGDTRRXint | ((regno & 0xf) << 12); + apb_write(t, DBGITR, instr); +} + +static void cortexa_regs_read(target *t, void *data) +{ + struct cortexa_priv *priv = (struct cortexa_priv *)t->priv; + memcpy(data, &priv->reg_cache, t->regs_size); +} + +static void cortexa_regs_write(target *t, const void *data) +{ + struct cortexa_priv *priv = (struct cortexa_priv *)t->priv; + /* Save in our register cache, in case we get asked again */ + memcpy(&priv->reg_cache, data, t->regs_size); +} + +static void cortexa_reset(target *t) +{ + /* This mess is Xilinx Zynq specific + * See Zynq-7000 TRM, Xilinx doc UG585 + */ +#define ZYNQ_SLCR_UNLOCK 0xf8000008 +#define ZYNQ_SLCR_UNLOCK_KEY 0xdf0d +#define ZYNQ_SLCR_PSS_RST_CTRL 0xf8000200 + target_mem_write32(t, ZYNQ_SLCR_UNLOCK, ZYNQ_SLCR_UNLOCK_KEY); + target_mem_write32(t, ZYNQ_SLCR_PSS_RST_CTRL, 1); + + /* Spin until Xilinx reconnects us */ + volatile struct exception e; + do { + TRY_CATCH (e, EXCEPTION_ALL) { + apb_read(t, DBGDIDR); + } + } while (e.type == EXCEPTION_ERROR); + + cortexa_attach(t); +} + +static void cortexa_halt_request(target *t) +{ + volatile struct exception e; + TRY_CATCH (e, EXCEPTION_TIMEOUT) { + apb_write(t, DBGDRCR, DBGDRCR_HRQ); + } + if (e.type) { + gdb_out("Timeout sending interrupt, is target in WFI?\n"); + } +} + +static int cortexa_halt_wait(target *t) +{ + struct cortexa_priv *priv = (struct cortexa_priv *)t->priv; + volatile uint32_t dbgdscr = 0; + volatile struct exception e; + TRY_CATCH (e, EXCEPTION_ALL) { + /* If this times out because the target is in WFI then + * the target is still running. */ + dbgdscr = apb_read(t, DBGDSCR); + } + switch (e.type) { + case EXCEPTION_ERROR: + /* Oh crap, there's no recovery from this... */ + target_list_free(); + morse("TARGET LOST.", 1); + return SIGLOST; + case EXCEPTION_TIMEOUT: + /* Timeout isn't a problem, target could be in WFI */ + return 0; + } + + if (!(dbgdscr & DBGDSCR_HALTED)) /* Not halted */ + return 0; + + DEBUG("%s: DBGDSCR = 0x%08x\n", __func__, dbgdscr); + /* Reenable DBGITR */ + dbgdscr |= DBGDSCR_ITREN; + apb_write(t, DBGDSCR, dbgdscr); + + /* Find out why we halted */ + int sig; + switch (dbgdscr & DBGDSCR_MOE_MASK) { + case DBGDSCR_MOE_HALT_REQ: + sig = SIGINT; + break; + default: + sig = SIGTRAP; + } + + /* Read registers to internal cache */ + memset(&priv->reg_cache, 0, t->regs_size); + for (int i = 0; i < 16; i++) { + priv->reg_cache.r[i] = read_gpreg(t, i); + } + /* Read CPSR */ + apb_write(t, DBGITR, 0xE10F0000); /* mrs r0, CPSR */ + priv->reg_cache.cpsr = read_gpreg(t, 0); + /* Read FPSCR */ + apb_write(t, DBGITR, 0xeef10a10); /* vmrs r0, fpscr */ + priv->reg_cache.fpscr = read_gpreg(t, 0); + /* Read out VFP registers */ + for (int i = 0; i < 16; i++) { + /* Read D[i] to R0/R1 */ + apb_write(t, DBGITR, 0xEC510B10 | i); /* vmov r0, r1, d0 */ + priv->reg_cache.d[i] = ((uint64_t)read_gpreg(t, 1) << 32) | read_gpreg(t, 0); + } + priv->reg_cache.r[15] -= (priv->reg_cache.cpsr & CPSR_THUMB) ? 4 : 8; + + return sig; +} + +void cortexa_halt_resume(target *t, bool step) +{ + struct cortexa_priv *priv = t->priv; + /* Set breakpoint comarator for single stepping if needed */ + if (step) { + uint32_t addr = priv->reg_cache.r[15]; + uint32_t bas = bp_bas(addr, (priv->reg_cache.cpsr & CPSR_THUMB) ? 2 : 4); + DEBUG("step 0x%08x %x\n", addr, bas); + /* Set match any breakpoint */ + apb_write(t, DBGBVR(0), priv->reg_cache.r[15] & ~3); + apb_write(t, DBGBCR(0), DBGBCR_INST_MISMATCH | bas | + DBGBCR_EN); + } else { + apb_write(t, DBGBVR(0), priv->hw_breakpoint[0] & ~3); + apb_write(t, DBGBCR(0), priv->bpc0); + } + + /* Write back register cache */ + /* First write back floats */ + for (int i = 0; i < 16; i++) { + write_gpreg(t, 1, priv->reg_cache.d[i] >> 32); + write_gpreg(t, 0, priv->reg_cache.d[i]); + apb_write(t, DBGITR, 0xec410b10 | i); /* vmov d[i], r0, r1 */ + } + /* Write back FPSCR */ + write_gpreg(t, 0, priv->reg_cache.fpscr); + apb_write(t, DBGITR, 0xeee10a10); /* vmsr fpscr, r0 */ + /* Write back the CPSR */ + write_gpreg(t, 0, priv->reg_cache.cpsr); + apb_write(t, DBGITR, 0xe12ff000); /* msr CPSR_fsxc, r0 */ + /* Finally the GP registers now that we're done using them */ + for (int i = 0; i < 15; i++) { + write_gpreg(t, i, priv->reg_cache.r[i]); + } + /* Write back PC with offset */ + write_gpreg(t, 15, priv->reg_cache.r[15] + + (priv->reg_cache.cpsr & CPSR_THUMB) ? 4 : 8); + + apb_write(t, DBGITR, MCR | ICIALLU); /* invalidate cache */ + + /* Disable DBGITR. Not sure why, but RRQ is ignored otherwise. */ + uint32_t dbgdscr = apb_read(t, DBGDSCR); + dbgdscr &= ~DBGDSCR_ITREN; + apb_write(t, DBGDSCR, dbgdscr); + + do { + apb_write(t, DBGDRCR, DBGDRCR_CSE | DBGDRCR_RRQ); + dbgdscr = apb_read(t, DBGDSCR); + DEBUG("%s: DBGDSCR = 0x%08x\n", __func__, dbgdscr); + } while (!(dbgdscr & DBGDSCR_RESTARTED)); +} + +/* Breakpoints */ +static uint32_t bp_bas(uint32_t addr, uint8_t len) +{ + if (len == 4) + return DBGBCR_BAS_ANY; + else if (addr & 2) + return DBGBCR_BAS_HIGH_HW; + else + return DBGBCR_BAS_LOW_HW; +} + +static int cortexa_set_hw_bp(target *t, uint32_t addr, uint8_t len) +{ + struct cortexa_priv *priv = t->priv; + unsigned i; + + for(i = 0; i < priv->hw_breakpoint_max; i++) + if((priv->hw_breakpoint[i] & 1) == 0) break; + + if(i == priv->hw_breakpoint_max) return -1; + + priv->hw_breakpoint[i] = addr | 1; + + apb_write(t, DBGBVR(i), addr & ~3); + uint32_t bpc = bp_bas(addr, len) | DBGBCR_EN; + apb_write(t, DBGBCR(i), bpc); + if (i == 0) + priv->bpc0 = bpc; + + return 0; +} + +static int cortexa_clear_hw_bp(target *t, uint32_t addr, uint8_t len) +{ + struct cortexa_priv *priv = t->priv; + unsigned i; + + (void)len; + + for (i = 0; i < priv->hw_breakpoint_max; i++) + if ((priv->hw_breakpoint[i] & ~1) == addr) + break; + if (i == priv->hw_breakpoint_max) + return -1; + + priv->hw_breakpoint[i] = 0; + + apb_write(t, DBGBCR(i), 0); + if (i == 0) + priv->bpc0 = 0; + + return 0; +} From 49f89cfc95758ed6bf15e1b017d0b378159c75ae Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Tue, 19 Apr 2016 10:19:19 -0700 Subject: [PATCH 04/11] cortexa: Fix detach. Also pulls out internal register cache functions from halt/resume. --- src/cortexa.c | 103 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 42 deletions(-) diff --git a/src/cortexa.c b/src/cortexa.c index 1e8bf4b..9c7165b 100644 --- a/src/cortexa.c +++ b/src/cortexa.c @@ -54,6 +54,8 @@ static void cortexa_halt_resume(target *t, bool step); static void cortexa_regs_read(target *t, void *data); static void cortexa_regs_write(target *t, const void *data); +static void cortexa_regs_read_internal(target *t); +static void cortexa_regs_write_internal(target *t); static void cortexa_reset(target *t); static int cortexa_halt_wait(target *t); @@ -331,12 +333,20 @@ void cortexa_detach(target *t) struct cortexa_priv *priv = t->priv; /* Clear any stale breakpoints */ - for(unsigned i = 0; i < priv->hw_breakpoint_max; i++) + for(unsigned i = 0; i < priv->hw_breakpoint_max; i++) { + priv->hw_breakpoint[i] = 0; apb_write(t, DBGBCR(i), 0); + } + + /* Restore any clobbered registers */ + cortexa_regs_write_internal(t); - /* Disable halting debug mode */ uint32_t dbgdscr = apb_read(t, DBGDSCR); - apb_write(t, DBGDSCR, dbgdscr & ~DBGDSCR_HDBGEN); + /* Disable halting debug mode */ + dbgdscr &= ~(DBGDSCR_HDBGEN | DBGDSCR_ITREN); + apb_write(t, DBGDSCR, dbgdscr); + /* Clear sticky error and resume */ + apb_write(t, DBGDRCR, DBGDRCR_CSE | DBGDRCR_RRQ); } @@ -369,10 +379,55 @@ static void cortexa_regs_read(target *t, void *data) static void cortexa_regs_write(target *t, const void *data) { struct cortexa_priv *priv = (struct cortexa_priv *)t->priv; - /* Save in our register cache, in case we get asked again */ memcpy(&priv->reg_cache, data, t->regs_size); } +static void cortexa_regs_read_internal(target *t) +{ + struct cortexa_priv *priv = (struct cortexa_priv *)t->priv; + /* Read general purpose registers */ + for (int i = 0; i < 16; i++) { + priv->reg_cache.r[i] = read_gpreg(t, i); + } + /* Read CPSR */ + apb_write(t, DBGITR, 0xE10F0000); /* mrs r0, CPSR */ + priv->reg_cache.cpsr = read_gpreg(t, 0); + /* Read FPSCR */ + apb_write(t, DBGITR, 0xeef10a10); /* vmrs r0, fpscr */ + priv->reg_cache.fpscr = read_gpreg(t, 0); + /* Read out VFP registers */ + for (int i = 0; i < 16; i++) { + /* Read D[i] to R0/R1 */ + apb_write(t, DBGITR, 0xEC510B10 | i); /* vmov r0, r1, d0 */ + priv->reg_cache.d[i] = ((uint64_t)read_gpreg(t, 1) << 32) | read_gpreg(t, 0); + } + priv->reg_cache.r[15] -= (priv->reg_cache.cpsr & CPSR_THUMB) ? 4 : 8; +} + +static void cortexa_regs_write_internal(target *t) +{ + struct cortexa_priv *priv = (struct cortexa_priv *)t->priv; + /* First write back floats */ + for (int i = 0; i < 16; i++) { + write_gpreg(t, 1, priv->reg_cache.d[i] >> 32); + write_gpreg(t, 0, priv->reg_cache.d[i]); + apb_write(t, DBGITR, 0xec410b10 | i); /* vmov d[i], r0, r1 */ + } + /* Write back FPSCR */ + write_gpreg(t, 0, priv->reg_cache.fpscr); + apb_write(t, DBGITR, 0xeee10a10); /* vmsr fpscr, r0 */ + /* Write back the CPSR */ + write_gpreg(t, 0, priv->reg_cache.cpsr); + apb_write(t, DBGITR, 0xe12ff000); /* msr CPSR_fsxc, r0 */ + /* Finally the GP registers now that we're done using them */ + for (int i = 0; i < 15; i++) { + write_gpreg(t, i, priv->reg_cache.r[i]); + } + /* Write back PC with offset */ + write_gpreg(t, 15, priv->reg_cache.r[15] + + (priv->reg_cache.cpsr & CPSR_THUMB) ? 4 : 8); +} + static void cortexa_reset(target *t) { /* This mess is Xilinx Zynq specific @@ -408,7 +463,6 @@ static void cortexa_halt_request(target *t) static int cortexa_halt_wait(target *t) { - struct cortexa_priv *priv = (struct cortexa_priv *)t->priv; volatile uint32_t dbgdscr = 0; volatile struct exception e; TRY_CATCH (e, EXCEPTION_ALL) { @@ -445,24 +499,7 @@ static int cortexa_halt_wait(target *t) sig = SIGTRAP; } - /* Read registers to internal cache */ - memset(&priv->reg_cache, 0, t->regs_size); - for (int i = 0; i < 16; i++) { - priv->reg_cache.r[i] = read_gpreg(t, i); - } - /* Read CPSR */ - apb_write(t, DBGITR, 0xE10F0000); /* mrs r0, CPSR */ - priv->reg_cache.cpsr = read_gpreg(t, 0); - /* Read FPSCR */ - apb_write(t, DBGITR, 0xeef10a10); /* vmrs r0, fpscr */ - priv->reg_cache.fpscr = read_gpreg(t, 0); - /* Read out VFP registers */ - for (int i = 0; i < 16; i++) { - /* Read D[i] to R0/R1 */ - apb_write(t, DBGITR, 0xEC510B10 | i); /* vmov r0, r1, d0 */ - priv->reg_cache.d[i] = ((uint64_t)read_gpreg(t, 1) << 32) | read_gpreg(t, 0); - } - priv->reg_cache.r[15] -= (priv->reg_cache.cpsr & CPSR_THUMB) ? 4 : 8; + cortexa_regs_read_internal(t); return sig; } @@ -485,25 +522,7 @@ void cortexa_halt_resume(target *t, bool step) } /* Write back register cache */ - /* First write back floats */ - for (int i = 0; i < 16; i++) { - write_gpreg(t, 1, priv->reg_cache.d[i] >> 32); - write_gpreg(t, 0, priv->reg_cache.d[i]); - apb_write(t, DBGITR, 0xec410b10 | i); /* vmov d[i], r0, r1 */ - } - /* Write back FPSCR */ - write_gpreg(t, 0, priv->reg_cache.fpscr); - apb_write(t, DBGITR, 0xeee10a10); /* vmsr fpscr, r0 */ - /* Write back the CPSR */ - write_gpreg(t, 0, priv->reg_cache.cpsr); - apb_write(t, DBGITR, 0xe12ff000); /* msr CPSR_fsxc, r0 */ - /* Finally the GP registers now that we're done using them */ - for (int i = 0; i < 15; i++) { - write_gpreg(t, i, priv->reg_cache.r[i]); - } - /* Write back PC with offset */ - write_gpreg(t, 15, priv->reg_cache.r[15] + - (priv->reg_cache.cpsr & CPSR_THUMB) ? 4 : 8); + cortexa_regs_write_internal(t); apb_write(t, DBGITR, MCR | ICIALLU); /* invalidate cache */ From a2ec877b7322ff1f051d22706a304a2afc639cbc Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Tue, 19 Apr 2016 10:29:55 -0700 Subject: [PATCH 05/11] cortexa: Restore cache clean and invalidate on memory writes. Include a small optimisation of APB access to speed up the process. --- src/cortexa.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/cortexa.c b/src/cortexa.c index 9c7165b..10d4abe 100644 --- a/src/cortexa.c +++ b/src/cortexa.c @@ -191,10 +191,8 @@ static void apb_write(target *t, uint16_t reg, uint32_t val) struct cortexa_priv *priv = t->priv; ADIv5_AP_t *ap = priv->apb; uint32_t addr = priv->base + 4*reg; - uint32_t csw = ap->csw | ADIV5_AP_CSW_ADDRINC_SINGLE | ADIV5_AP_CSW_SIZE_WORD; - adiv5_ap_write(ap, ADIV5_AP_CSW, csw); adiv5_ap_write(ap, ADIV5_AP_TAR, addr); - adiv5_ap_write(ap, ADIV5_AP_DRW, val); + adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_DRW, val); } static uint32_t apb_read(target *t, uint16_t reg) @@ -202,10 +200,9 @@ static uint32_t apb_read(target *t, uint16_t reg) struct cortexa_priv *priv = t->priv; ADIv5_AP_t *ap = priv->apb; uint32_t addr = priv->base + 4*reg; - uint32_t csw = ap->csw | ADIV5_AP_CSW_ADDRINC_SINGLE | ADIV5_AP_CSW_SIZE_WORD; - adiv5_ap_write(ap, ADIV5_AP_CSW, csw); adiv5_ap_write(ap, ADIV5_AP_TAR, addr); - return adiv5_ap_read(ap, ADIV5_AP_DRW); + adiv5_dp_low_access(ap->dp, ADIV5_LOW_READ, ADIV5_AP_DRW, 0); + return adiv5_dp_low_access(ap->dp, ADIV5_LOW_READ, ADIV5_DP_RDBUFF, 0); } static void cortexa_mem_read(target *t, void *dest, uint32_t src, size_t len) @@ -224,18 +221,11 @@ static void cortexa_mem_read(target *t, void *dest, uint32_t src, size_t len) static void cortexa_mem_write(target *t, uint32_t dest, const void *src, size_t len) { /* Clean and invalidate cache before writing */ -#if 0 - /* I've taken this out for now because it makes loading painfully - * slow. - * FIXME This can cause data integrity problems if modifying the target - * state from the debugger! - */ for (uint32_t cl = dest & ~(CACHE_LINE_LENGTH-1); cl < dest + len; cl += CACHE_LINE_LENGTH) { write_gpreg(t, 0, cl); apb_write(t, DBGITR, MCR | DCCIMVAC); } -#endif ADIv5_AP_t *ahb = ((struct cortexa_priv*)t->priv)->ahb; adiv5_mem_write(ahb, dest, src, len); } @@ -265,6 +255,9 @@ bool cortexa_probe(ADIv5_AP_t *apb, uint32_t debug_base) priv->ahb = adiv5_new_ap(apb->dp, 0); adiv5_ap_ref(priv->ahb); priv->base = debug_base; + /* Set up APB CSW, we won't touch this again */ + uint32_t csw = apb->csw | ADIV5_AP_CSW_SIZE_WORD; + adiv5_ap_write(apb, ADIV5_AP_CSW, csw); uint32_t dbgdidr = apb_read(t, DBGDIDR); priv->hw_breakpoint_max = ((dbgdidr >> 24) & 15)+1; DEBUG("Target has %d breakpoints\n", priv->hw_breakpoint_max); From 0ab878dcd26cd877cb0ca8188b64f11160140c1e Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Tue, 19 Apr 2016 13:29:22 -0700 Subject: [PATCH 06/11] cortexa: Add short delay after reset, before reattaching. Allows the early bootloader to configure the DDR ram. --- src/cortexa.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cortexa.c b/src/cortexa.c index 10d4abe..fc0fc61 100644 --- a/src/cortexa.c +++ b/src/cortexa.c @@ -440,6 +440,8 @@ static void cortexa_reset(target *t) } } while (e.type == EXCEPTION_ERROR); + platform_delay(100); + cortexa_attach(t); } From 88bf92ac36a27bb93c9bd0e1204d1fb0ae296721 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Wed, 20 Apr 2016 11:35:25 -0700 Subject: [PATCH 07/11] cortexa: Fix write back of PC and CPSR. --- src/cortexa.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cortexa.c b/src/cortexa.c index fc0fc61..7234134 100644 --- a/src/cortexa.c +++ b/src/cortexa.c @@ -412,13 +412,13 @@ static void cortexa_regs_write_internal(target *t) /* Write back the CPSR */ write_gpreg(t, 0, priv->reg_cache.cpsr); apb_write(t, DBGITR, 0xe12ff000); /* msr CPSR_fsxc, r0 */ + /* Write back PC, via r0. MRC clobbers CPSR instead */ + write_gpreg(t, 0, priv->reg_cache.r[15]); + apb_write(t, DBGITR, 0xe1a0f000); /* mov pc, r0 */ /* Finally the GP registers now that we're done using them */ for (int i = 0; i < 15; i++) { write_gpreg(t, i, priv->reg_cache.r[i]); } - /* Write back PC with offset */ - write_gpreg(t, 15, priv->reg_cache.r[15] + - (priv->reg_cache.cpsr & CPSR_THUMB) ? 4 : 8); } static void cortexa_reset(target *t) From 68bf825042185ebaf2e36d36e381716859ace120 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Wed, 20 Apr 2016 11:35:58 -0700 Subject: [PATCH 08/11] cortexa: Disable interrupts while single stepping. --- src/cortexa.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cortexa.c b/src/cortexa.c index 7234134..bf06176 100644 --- a/src/cortexa.c +++ b/src/cortexa.c @@ -100,6 +100,7 @@ struct cortexa_priv { #define DBGDSCR_EXTDCCMODE_MASK (3 << 20) #define DBGDSCR_HDBGEN (1 << 14) #define DBGDSCR_ITREN (1 << 13) +#define DBGDSCR_INTDIS (1 << 11) #define DBGDSCR_UND_I (1 << 8) #define DBGDSCR_MOE_MASK (0xf << 2) #define DBGDSCR_MOE_HALT_REQ (0x0 << 2) @@ -523,6 +524,10 @@ void cortexa_halt_resume(target *t, bool step) /* Disable DBGITR. Not sure why, but RRQ is ignored otherwise. */ uint32_t dbgdscr = apb_read(t, DBGDSCR); + if (step) + dbgdscr |= DBGDSCR_INTDIS; + else + dbgdscr &= ~DBGDSCR_INTDIS; dbgdscr &= ~DBGDSCR_ITREN; apb_write(t, DBGDSCR, dbgdscr); From bed662757929143a47303f4203feadefb6416ce7 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Tue, 19 Apr 2016 22:02:26 -0700 Subject: [PATCH 09/11] adiv5: Implement access to DP ABORT register. --- src/adiv5_jtagdp.c | 9 +++++++++ src/adiv5_swdp.c | 7 +++++++ src/include/adiv5.h | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/src/adiv5_jtagdp.c b/src/adiv5_jtagdp.c index d97bc99..b9e805a 100644 --- a/src/adiv5_jtagdp.c +++ b/src/adiv5_jtagdp.c @@ -44,6 +44,7 @@ static uint32_t adiv5_jtagdp_error(ADIv5_DP_t *dp); static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t RnW, uint16_t addr, uint32_t value); +static void adiv5_jtagdp_abort(ADIv5_DP_t *dp, uint32_t abort); void adiv5_jtag_dp_handler(jtag_dev_t *dev) { @@ -55,6 +56,7 @@ void adiv5_jtag_dp_handler(jtag_dev_t *dev) dp->dp_read = adiv5_jtagdp_read; dp->error = adiv5_jtagdp_error; dp->low_access = adiv5_jtagdp_low_access; + dp->abort = adiv5_jtagdp_abort; adiv5_dp_init(dp); } @@ -100,3 +102,10 @@ static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t RnW, return (uint32_t)(response >> 3); } +static void adiv5_jtagdp_abort(ADIv5_DP_t *dp, uint32_t abort) +{ + uint64_t request = (uint64_t)abort << 3; + jtag_dev_write_ir(dp->dev, IR_ABORT); + jtag_dev_shift_dr(dp->dev, NULL, (const uint8_t*)&request, 35); +} + diff --git a/src/adiv5_swdp.c b/src/adiv5_swdp.c index 44edcb6..63e4eb4 100644 --- a/src/adiv5_swdp.c +++ b/src/adiv5_swdp.c @@ -41,6 +41,7 @@ static uint32_t adiv5_swdp_error(ADIv5_DP_t *dp); static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t RnW, uint16_t addr, uint32_t value); +static void adiv5_swdp_abort(ADIv5_DP_t *dp, uint32_t abort); int adiv5_swdp_scan(void) { @@ -65,6 +66,7 @@ int adiv5_swdp_scan(void) dp->dp_read = adiv5_swdp_read; dp->error = adiv5_swdp_error; dp->low_access = adiv5_swdp_low_access; + dp->abort = adiv5_swdp_abort; adiv5_swdp_error(dp); adiv5_dp_init(dp); @@ -158,3 +160,8 @@ static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t RnW, return response; } +static void adiv5_swdp_abort(ADIv5_DP_t *dp, uint32_t abort) +{ + adiv5_dp_write(dp, ADIV5_DP_ABORT, abort); +} + diff --git a/src/include/adiv5.h b/src/include/adiv5.h index b6fe30f..6bebd3e 100644 --- a/src/include/adiv5.h +++ b/src/include/adiv5.h @@ -110,6 +110,7 @@ typedef struct ADIv5_DP_s { uint32_t (*error)(struct ADIv5_DP_s *dp); uint32_t (*low_access)(struct ADIv5_DP_s *dp, uint8_t RnW, uint16_t addr, uint32_t value); + void (*abort)(struct ADIv5_DP_s *dp, uint32_t abort); union { jtag_dev_t *dev; @@ -133,6 +134,11 @@ static inline uint32_t adiv5_dp_low_access(struct ADIv5_DP_s *dp, uint8_t RnW, return dp->low_access(dp, RnW, addr, value); } +static inline void adiv5_dp_abort(struct ADIv5_DP_s *dp, uint32_t abort) +{ + return dp->abort(dp, abort); +} + typedef struct ADIv5_AP_s { int refcnt; From 08c9ab54d7f6b3af8cf4399b6ed355b7fafa1bfe Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Tue, 19 Apr 2016 22:03:52 -0700 Subject: [PATCH 10/11] adiv5: Try abort sequence if DP is stalling during scan. --- src/adiv5.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/adiv5.c b/src/adiv5.c index 39cd3db..74ec425 100644 --- a/src/adiv5.c +++ b/src/adiv5.c @@ -26,6 +26,7 @@ #include "gdb_packet.h" #include "adiv5.h" #include "cortexm.h" +#include "exception.h" #ifndef DO_RESET_SEQ #define DO_RESET_SEQ 0 @@ -157,6 +158,11 @@ ADIv5_AP_t *adiv5_new_ap(ADIv5_DP_t *dp, uint8_t apsel) ap->csw = adiv5_ap_read(ap, ADIV5_AP_CSW) & ~(ADIV5_AP_CSW_SIZE_MASK | ADIV5_AP_CSW_ADDRINC_MASK); + if (ap->csw & ADIV5_AP_CSW_TRINPROG) { + gdb_out("AP transaction in progress. Target may not be usable.\n"); + ap->csw &= ~ADIV5_AP_CSW_TRINPROG; + } + DEBUG("%3d: IDR=%08X CFG=%08X BASE=%08X CSW=%08X\n", apsel, ap->idr, ap->cfg, ap->base, ap->csw); @@ -166,11 +172,19 @@ ADIv5_AP_t *adiv5_new_ap(ADIv5_DP_t *dp, uint8_t apsel) void adiv5_dp_init(ADIv5_DP_t *dp) { - uint32_t ctrlstat; + uint32_t ctrlstat = 0; adiv5_dp_ref(dp); - ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT); + volatile struct exception e; + TRY_CATCH (e, EXCEPTION_TIMEOUT) { + ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT); + } + if (e.type) { + gdb_out("DP not responding! Trying abort sequence...\n"); + adiv5_dp_abort(dp, ADIV5_DP_ABORT_DAPABORT); + ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT); + } /* Write request for system and debug power up */ adiv5_dp_write(dp, ADIV5_DP_CTRLSTAT, From 13602c5d8508c85821aaa19e596e8bb069539600 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Wed, 20 Apr 2016 11:54:01 -0700 Subject: [PATCH 11/11] cortexa: Also assert SRST to reset. --- src/cortexa.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cortexa.c b/src/cortexa.c index bf06176..d1b23dd 100644 --- a/src/cortexa.c +++ b/src/cortexa.c @@ -433,6 +433,10 @@ static void cortexa_reset(target *t) target_mem_write32(t, ZYNQ_SLCR_UNLOCK, ZYNQ_SLCR_UNLOCK_KEY); target_mem_write32(t, ZYNQ_SLCR_PSS_RST_CTRL, 1); + /* Try hard reset too */ + platform_srst_set_val(true); + platform_srst_set_val(false); + /* Spin until Xilinx reconnects us */ volatile struct exception e; do {