target: Restructure internal break/watch handling.

cortexa: Implement soft breakpoints.
This commit is contained in:
Gareth McMullin 2016-07-07 11:49:47 +12:00
parent 9136cf4c98
commit 9aacc18f60
4 changed files with 213 additions and 186 deletions

View File

@ -48,8 +48,8 @@ static void cortexa_reset(target *t);
static enum target_halt_reason cortexa_halt_poll(target *t, target_addr *watch); static enum target_halt_reason cortexa_halt_poll(target *t, target_addr *watch);
static void cortexa_halt_request(target *t); static void cortexa_halt_request(target *t);
static int cortexa_set_hw_bp(target *t, target_addr addr, uint8_t len); static int cortexa_breakwatch_set(target *t, struct breakwatch *);
static int cortexa_clear_hw_bp(target *t, target_addr addr, uint8_t len); static int cortexa_breakwatch_clear(target *t, struct breakwatch *);
static uint32_t bp_bas(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 void apb_write(target *t, uint16_t reg, uint32_t val);
@ -68,7 +68,7 @@ struct cortexa_priv {
uint64_t d[16]; uint64_t d[16];
} reg_cache; } reg_cache;
unsigned hw_breakpoint_max; unsigned hw_breakpoint_max;
unsigned hw_breakpoint[16]; bool hw_breakpoint[16];
uint32_t bpc0; uint32_t bpc0;
bool mmu_fault; bool mmu_fault;
}; };
@ -393,8 +393,8 @@ bool cortexa_probe(ADIv5_AP_t *apb, uint32_t debug_base)
t->halt_resume = cortexa_halt_resume; t->halt_resume = cortexa_halt_resume;
t->regs_size = sizeof(priv->reg_cache); t->regs_size = sizeof(priv->reg_cache);
t->set_hw_bp = cortexa_set_hw_bp; t->breakwatch_set = cortexa_breakwatch_set;
t->clear_hw_bp = cortexa_clear_hw_bp; t->breakwatch_clear = cortexa_breakwatch_clear;
return true; return true;
} }
@ -673,45 +673,75 @@ static uint32_t bp_bas(uint32_t addr, uint8_t len)
return DBGBCR_BAS_LOW_HW; return DBGBCR_BAS_LOW_HW;
} }
static int cortexa_set_hw_bp(target *t, target_addr addr, uint8_t len) static int cortexa_breakwatch_set(target *t, struct breakwatch *bw)
{ {
struct cortexa_priv *priv = t->priv; struct cortexa_priv *priv = t->priv;
unsigned i; unsigned i;
for(i = 0; i < priv->hw_breakpoint_max; i++) switch (bw->type) {
if((priv->hw_breakpoint[i] & 1) == 0) break; case TARGET_BREAK_SOFT:
switch (bw->size) {
case 2:
bw->reserved[0] = target_mem_read16(t, bw->addr);
target_mem_write16(t, bw->addr, 0xBE00);
return 0;
case 4:
bw->reserved[0] = target_mem_read32(t, bw->addr);
target_mem_write32(t, bw->addr, 0xE1200070);
return 0;
default:
return -1;
}
case TARGET_BREAK_HARD:
if ((bw->size != 4) && (bw->size != 2))
return -1;
if(i == priv->hw_breakpoint_max) return -1; for (i = 0; i < priv->hw_breakpoint_max; i++)
if ((priv->hw_breakpoint[i] & 1) == 0)
break;
priv->hw_breakpoint[i] = addr | 1; if (i == priv->hw_breakpoint_max)
return -1;
apb_write(t, DBGBVR(i), addr & ~3); bw->reserved[0] = i;
uint32_t bpc = bp_bas(addr, len) | DBGBCR_EN;
apb_write(t, DBGBCR(i), bpc);
if (i == 0)
priv->bpc0 = bpc;
return 0; priv->hw_breakpoint[i] = true;
apb_write(t, DBGBVR(i), bw->addr & ~3);
uint32_t bpc = bp_bas(bw->addr, bw->size) | DBGBCR_EN;
apb_write(t, DBGBCR(i), bpc);
if (i == 0)
priv->bpc0 = bpc;
return 0;
default:
return 1;
}
} }
static int cortexa_clear_hw_bp(target *t, target_addr addr, uint8_t len) static int cortexa_breakwatch_clear(target *t, struct breakwatch *bw)
{ {
struct cortexa_priv *priv = t->priv; struct cortexa_priv *priv = t->priv;
unsigned i; unsigned i = bw->reserved[0];
switch (bw->type) {
(void)len; case TARGET_BREAK_SOFT:
switch (bw->size) {
for (i = 0; i < priv->hw_breakpoint_max; i++) case 2:
if ((priv->hw_breakpoint[i] & ~1) == addr) target_mem_write16(t, bw->addr, i);
break; return 0;
if (i == priv->hw_breakpoint_max) case 4:
return -1; target_mem_write32(t, bw->addr, i);
return 0;
priv->hw_breakpoint[i] = 0; default:
return -1;
apb_write(t, DBGBCR(i), 0); }
if (i == 0) case TARGET_BREAK_HARD:
priv->bpc0 = 0; priv->hw_breakpoint[i] = false;
apb_write(t, DBGBCR(i), 0);
return 0; if (i == 0)
priv->bpc0 = 0;
return 0;
default:
return 1;
}
} }

View File

@ -56,12 +56,8 @@ static enum target_halt_reason cortexm_halt_poll(target *t, target_addr *watch);
static void cortexm_halt_request(target *t); static void cortexm_halt_request(target *t);
static int cortexm_fault_unwind(target *t); static int cortexm_fault_unwind(target *t);
static int cortexm_set_hw_bp(target *t, target_addr addr, uint8_t len); static int cortexm_breakwatch_set(target *t, struct breakwatch *);
static int cortexm_clear_hw_bp(target *t, target_addr addr, uint8_t len); static int cortexm_breakwatch_clear(target *t, struct breakwatch *);
static int cortexm_set_hw_wp(target *t, uint8_t type, target_addr addr, uint8_t len);
static int cortexm_clear_hw_wp(target *t, uint8_t type, target_addr addr, uint8_t len);
static target_addr cortexm_check_watch(target *t); static target_addr cortexm_check_watch(target *t);
#define CORTEXM_MAX_WATCHPOINTS 4 /* architecture says up to 15, no implementation has > 4 */ #define CORTEXM_MAX_WATCHPOINTS 4 /* architecture says up to 15, no implementation has > 4 */
@ -74,15 +70,11 @@ struct cortexm_priv {
bool stepping; bool stepping;
bool on_bkpt; bool on_bkpt;
/* Watchpoint unit status */ /* Watchpoint unit status */
struct wp_unit_s { bool hw_watchpoint[CORTEXM_MAX_WATCHPOINTS];
uint32_t addr;
uint8_t type;
uint8_t size;
} hw_watchpoint[CORTEXM_MAX_WATCHPOINTS];
unsigned flash_patch_revision; unsigned flash_patch_revision;
unsigned hw_watchpoint_max; unsigned hw_watchpoint_max;
/* Breakpoint unit status */ /* Breakpoint unit status */
uint32_t hw_breakpoint[CORTEXM_MAX_BREAKPOINTS]; bool hw_breakpoint[CORTEXM_MAX_BREAKPOINTS];
unsigned hw_breakpoint_max; unsigned hw_breakpoint_max;
/* Copy of DEMCR for vector-catch */ /* Copy of DEMCR for vector-catch */
uint32_t demcr; uint32_t demcr;
@ -240,6 +232,9 @@ bool cortexm_probe(ADIv5_AP_t *ap)
t->halt_resume = cortexm_halt_resume; t->halt_resume = cortexm_halt_resume;
t->regs_size = sizeof(regnum_cortex_m); t->regs_size = sizeof(regnum_cortex_m);
t->breakwatch_set = cortexm_breakwatch_set;
t->breakwatch_clear = cortexm_breakwatch_clear;
target_add_commands(t, cortexm_cmd_list, cortexm_driver_str); target_add_commands(t, cortexm_cmd_list, cortexm_driver_str);
/* Probe for FP extension */ /* Probe for FP extension */
@ -320,18 +315,12 @@ bool cortexm_attach(target *t)
/* Clear any stale watchpoints */ /* Clear any stale watchpoints */
for(i = 0; i < priv->hw_watchpoint_max; i++) { for(i = 0; i < priv->hw_watchpoint_max; i++) {
target_mem_write32(t, CORTEXM_DWT_FUNC(i), 0); target_mem_write32(t, CORTEXM_DWT_FUNC(i), 0);
priv->hw_watchpoint[i].type = 0; priv->hw_watchpoint[i] = 0;
} }
/* Flash Patch Control Register: set ENABLE */ /* Flash Patch Control Register: set ENABLE */
target_mem_write32(t, CORTEXM_FPB_CTRL, target_mem_write32(t, CORTEXM_FPB_CTRL,
CORTEXM_FPB_CTRL_KEY | CORTEXM_FPB_CTRL_ENABLE); CORTEXM_FPB_CTRL_KEY | CORTEXM_FPB_CTRL_ENABLE);
t->set_hw_bp = cortexm_set_hw_bp;
t->clear_hw_bp = cortexm_clear_hw_bp;
/* Data Watchpoint and Trace */
t->set_hw_wp = cortexm_set_hw_wp;
t->clear_hw_wp = cortexm_clear_hw_wp;
platform_srst_set_val(false); platform_srst_set_val(false);
@ -650,130 +639,110 @@ int cortexm_run_stub(target *t, uint32_t loadaddr,
return bkpt_instr & 0xff; return bkpt_instr & 0xff;
} }
/* The following routines implement hardware breakpoints. /* The following routines implement hardware breakpoints and watchpoints.
* The Flash Patch and Breakpoint (FPB) system is used. */ * The Flash Patch and Breakpoint (FPB) and Data Watch and Trace (DWT)
* systems are used. */
static int cortexm_set_hw_bp(target *t, target_addr addr, uint8_t len) static uint32_t dwt_mask(size_t len)
{ {
(void)len; switch (len) {
struct cortexm_priv *priv = t->priv; case 1:
uint32_t val = addr; return CORTEXM_DWT_MASK_BYTE;
unsigned i; case 2:
return CORTEXM_DWT_MASK_HALFWORD;
if (priv->flash_patch_revision == 0) { case 4:
val = addr & 0x1FFFFFFC; return CORTEXM_DWT_MASK_WORD;
val |= (addr & 2)?0x80000000:0x40000000; default:
return -1;
} }
val |= 1;
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;
target_mem_write32(t, CORTEXM_FPB_COMP(i), val);
return 0;
} }
static int cortexm_clear_hw_bp(target *t, target_addr addr, uint8_t len) static uint32_t dwt_func(target *t, enum target_breakwatch type)
{ {
(void)len; uint32_t x = 0;
struct cortexm_priv *priv = t->priv;
unsigned i;
for(i = 0; i < priv->hw_breakpoint_max; i++) if ((t->target_options & TOPT_FLAVOUR_V6M) == 0)
if((priv->hw_breakpoint[i] & ~1) == addr) break; x = CORTEXM_DWT_FUNC_DATAVSIZE_WORD;
if(i == priv->hw_breakpoint_max) return -1; switch (type) {
case TARGET_WATCH_WRITE:
priv->hw_breakpoint[i] = 0; return CORTEXM_DWT_FUNC_FUNC_WRITE | x;
case TARGET_WATCH_READ:
target_mem_write32(t, CORTEXM_FPB_COMP(i), 0); return CORTEXM_DWT_FUNC_FUNC_READ | x;
case TARGET_WATCH_ACCESS:
return 0; return CORTEXM_DWT_FUNC_FUNC_ACCESS | x;
default:
return -1;
}
} }
/* The following routines implement hardware watchpoints. static int cortexm_breakwatch_set(target *t, struct breakwatch *bw)
* The Data Watch and Trace (DWT) system is used. */
static int
cortexm_set_hw_wp(target *t, uint8_t type, target_addr addr, uint8_t len)
{ {
struct cortexm_priv *priv = t->priv; struct cortexm_priv *priv = t->priv;
unsigned i; unsigned i;
uint32_t val = bw->addr;
switch(len) { /* Convert bytes size to mask size */ switch (bw->type) {
case 1: len = CORTEXM_DWT_MASK_BYTE; break; case TARGET_BREAK_HARD:
case 2: len = CORTEXM_DWT_MASK_HALFWORD; break; if (priv->flash_patch_revision == 0) {
case 4: len = CORTEXM_DWT_MASK_WORD; break; val &= 0x1FFFFFFC;
default: val |= (bw->addr & 2)?0x80000000:0x40000000;
}
val |= 1;
for(i = 0; i < priv->hw_breakpoint_max; i++)
if (!priv->hw_breakpoint[i])
break;
if (i == priv->hw_breakpoint_max)
return -1; return -1;
}
switch(type) { /* Convert gdb type to function type */ priv->hw_breakpoint[i] = true;
case 2: type = CORTEXM_DWT_FUNC_FUNC_WRITE; break; target_mem_write32(t, CORTEXM_FPB_COMP(i), val);
case 3: type = CORTEXM_DWT_FUNC_FUNC_READ; break; bw->reserved[0] = i;
case 4: type = CORTEXM_DWT_FUNC_FUNC_ACCESS; break; return 0;
default:
case TARGET_WATCH_WRITE:
case TARGET_WATCH_READ:
case TARGET_WATCH_ACCESS:
for(i = 0; i < priv->hw_watchpoint_max; i++)
if (!priv->hw_watchpoint[i])
break;
if (i == priv->hw_watchpoint_max)
return -1; return -1;
priv->hw_watchpoint[i] = true;
target_mem_write32(t, CORTEXM_DWT_COMP(i), val);
target_mem_write32(t, CORTEXM_DWT_MASK(i), dwt_mask(bw->size));
target_mem_write32(t, CORTEXM_DWT_FUNC(i), dwt_func(t, bw->type));
bw->reserved[0] = i;
return 0;
default:
return 1;
} }
for(i = 0; i < priv->hw_watchpoint_max; i++)
if((priv->hw_watchpoint[i].type == 0) &&
((target_mem_read32(t, CORTEXM_DWT_FUNC(i)) & 0xF) == 0))
break;
if(i == priv->hw_watchpoint_max) return -2;
priv->hw_watchpoint[i].type = type;
priv->hw_watchpoint[i].addr = addr;
priv->hw_watchpoint[i].size = len;
target_mem_write32(t, CORTEXM_DWT_COMP(i), addr);
target_mem_write32(t, CORTEXM_DWT_MASK(i), len);
target_mem_write32(t, CORTEXM_DWT_FUNC(i), type |
((t->target_options & TOPT_FLAVOUR_V6M) ? 0: CORTEXM_DWT_FUNC_DATAVSIZE_WORD));
return 0;
} }
static int static int cortexm_breakwatch_clear(target *t, struct breakwatch *bw)
cortexm_clear_hw_wp(target *t, uint8_t type, target_addr addr, uint8_t len)
{ {
struct cortexm_priv *priv = t->priv; struct cortexm_priv *priv = t->priv;
unsigned i; unsigned i = bw->reserved[0];
switch (bw->type) {
switch(len) { case TARGET_BREAK_HARD:
case 1: len = CORTEXM_DWT_MASK_BYTE; break; priv->hw_breakpoint[i] = false;
case 2: len = CORTEXM_DWT_MASK_HALFWORD; break; target_mem_write32(t, CORTEXM_FPB_COMP(i), 0);
case 4: len = CORTEXM_DWT_MASK_WORD; break; return 0;
default: case TARGET_WATCH_WRITE:
return -1; case TARGET_WATCH_READ:
case TARGET_WATCH_ACCESS:
priv->hw_watchpoint[i] = false;
target_mem_write32(t, CORTEXM_DWT_FUNC(i), 0);
return 0;
default:
return 1;
} }
switch(type) {
case 2: type = CORTEXM_DWT_FUNC_FUNC_WRITE; break;
case 3: type = CORTEXM_DWT_FUNC_FUNC_READ; break;
case 4: type = CORTEXM_DWT_FUNC_FUNC_ACCESS; break;
default:
return -1;
}
for(i = 0; i < priv->hw_watchpoint_max; i++)
if((priv->hw_watchpoint[i].addr == addr) &&
(priv->hw_watchpoint[i].type == type) &&
(priv->hw_watchpoint[i].size == len)) break;
if(i == priv->hw_watchpoint_max) return -2;
priv->hw_watchpoint[i].type = 0;
target_mem_write32(t, CORTEXM_DWT_FUNC(i), 0);
return 0;
} }
static target_addr cortexm_check_watch(target *t) static target_addr cortexm_check_watch(target *t)
@ -783,14 +752,15 @@ static target_addr cortexm_check_watch(target *t)
for(i = 0; i < priv->hw_watchpoint_max; i++) for(i = 0; i < priv->hw_watchpoint_max; i++)
/* if SET and MATCHED then break */ /* if SET and MATCHED then break */
if(priv->hw_watchpoint[i].type && if(priv->hw_watchpoint[i] &&
(target_mem_read32(t, CORTEXM_DWT_FUNC(i)) & (target_mem_read32(t, CORTEXM_DWT_FUNC(i)) &
CORTEXM_DWT_FUNC_MATCHED)) CORTEXM_DWT_FUNC_MATCHED))
break; break;
if(i == priv->hw_watchpoint_max) return 0; if (i == priv->hw_watchpoint_max)
return 0;
return priv->hw_watchpoint[i].addr; return target_mem_read32(t, CORTEXM_DWT_COMP(i));
} }
static bool cortexm_vector_catch(target *t, int argc, char *argv[]) static bool cortexm_vector_catch(target *t, int argc, char *argv[])

View File

@ -73,6 +73,11 @@ void target_list_free(void)
free(target_list->flash); free(target_list->flash);
target_list->flash = next; target_list->flash = next;
} }
while (target_list->bw_list) {
void * next = target_list->bw_list->next;
free(target_list->bw_list);
target_list->bw_list = next;
}
free(target_list); free(target_list);
target_list = t; target_list = t;
} }
@ -316,37 +321,53 @@ void target_halt_resume(target *t, bool step) { t->halt_resume(t, step); }
int target_breakwatch_set(target *t, int target_breakwatch_set(target *t,
enum target_breakwatch type, target_addr addr, size_t len) enum target_breakwatch type, target_addr addr, size_t len)
{ {
switch (type) { struct breakwatch bw = {
case TARGET_BREAK_HARD: .type = type,
if (t->set_hw_bp) .addr = addr,
return t->set_hw_bp(t, addr, len); .size = len,
case TARGET_WATCH_WRITE: };
case TARGET_WATCH_READ: int ret = 1;
case TARGET_WATCH_ACCESS:
if (t->set_hw_wp) if (t->breakwatch_set)
return t->set_hw_wp(t, type, addr, len); ret = t->breakwatch_set(t, &bw);
default:
break; if (ret == 0) {
/* Success, make a heap copy and add to list */
struct breakwatch *bwm = malloc(sizeof bw);
memcpy(bwm, &bw, sizeof(bw));
bwm->next = t->bw_list;
t->bw_list = bwm;
} }
return 1;
return ret;
} }
int target_breakwatch_clear(target *t, int target_breakwatch_clear(target *t,
enum target_breakwatch type, target_addr addr, size_t len) enum target_breakwatch type, target_addr addr, size_t len)
{ {
switch (type) { struct breakwatch *bwp = NULL, *bw;
case TARGET_BREAK_HARD: int ret = 1;
if (t->set_hw_bp) for (bw = t->bw_list; bw; bw = bw->next, bwp = bw)
return t->set_hw_bp(t, addr, len); if ((bw->type == type) &&
case TARGET_WATCH_WRITE: (bw->addr == addr) &&
case TARGET_WATCH_READ: (bw->size == len))
case TARGET_WATCH_ACCESS: break;
if (t->set_hw_wp)
return t->set_hw_wp(t, type, addr, len); if (bw == NULL)
default: return -1;
break;
if (t->breakwatch_clear)
ret = t->breakwatch_clear(t, bw);
if (ret == 0) {
if (bwp == NULL) {
t->bw_list = bw->next;
} else {
bwp->next = bw->next;
}
free(bw);
} }
return 1; return ret;
} }
/* Accessor functions */ /* Accessor functions */

View File

@ -68,6 +68,14 @@ struct target_command_s {
struct target_command_s *next; struct target_command_s *next;
}; };
struct breakwatch {
struct breakwatch *next;
enum target_breakwatch type;
target_addr addr;
size_t size;
uint32_t reserved[4]; /* for use by the implementing driver */
};
struct target_s { struct target_s {
bool attached; bool attached;
struct target_controller *tc; struct target_controller *tc;
@ -96,11 +104,9 @@ struct target_s {
void (*halt_resume)(target *t, bool step); void (*halt_resume)(target *t, bool step);
/* Break-/watchpoint functions */ /* Break-/watchpoint functions */
int (*set_hw_bp)(target *t, target_addr addr, uint8_t len); int (*breakwatch_set)(target *t, struct breakwatch*);
int (*clear_hw_bp)(target *t, target_addr addr, uint8_t len); int (*breakwatch_clear)(target *t, struct breakwatch*);
struct breakwatch *bw_list;
int (*set_hw_wp)(target *t, uint8_t type, target_addr addr, uint8_t len);
int (*clear_hw_wp)(target *t, uint8_t type, target_addr addr, uint8_t len);
/* target-defined options */ /* target-defined options */
unsigned target_options; unsigned target_options;