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 void cortexa_halt_request(target *t);
static int cortexa_set_hw_bp(target *t, target_addr addr, uint8_t len);
static int cortexa_clear_hw_bp(target *t, target_addr addr, uint8_t len);
static int cortexa_breakwatch_set(target *t, struct breakwatch *);
static int cortexa_breakwatch_clear(target *t, struct breakwatch *);
static uint32_t bp_bas(uint32_t addr, uint8_t len);
static void apb_write(target *t, uint16_t reg, uint32_t val);
@ -68,7 +68,7 @@ struct cortexa_priv {
uint64_t d[16];
} reg_cache;
unsigned hw_breakpoint_max;
unsigned hw_breakpoint[16];
bool hw_breakpoint[16];
uint32_t bpc0;
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->regs_size = sizeof(priv->reg_cache);
t->set_hw_bp = cortexa_set_hw_bp;
t->clear_hw_bp = cortexa_clear_hw_bp;
t->breakwatch_set = cortexa_breakwatch_set;
t->breakwatch_clear = cortexa_breakwatch_clear;
return true;
}
@ -673,45 +673,75 @@ static uint32_t bp_bas(uint32_t addr, uint8_t len)
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;
unsigned i;
for(i = 0; i < priv->hw_breakpoint_max; i++)
if((priv->hw_breakpoint[i] & 1) == 0) break;
switch (bw->type) {
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);
uint32_t bpc = bp_bas(addr, len) | DBGBCR_EN;
apb_write(t, DBGBCR(i), bpc);
if (i == 0)
priv->bpc0 = bpc;
bw->reserved[0] = i;
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;
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;
unsigned i = bw->reserved[0];
switch (bw->type) {
case TARGET_BREAK_SOFT:
switch (bw->size) {
case 2:
target_mem_write16(t, bw->addr, i);
return 0;
case 4:
target_mem_write32(t, bw->addr, i);
return 0;
default:
return -1;
}
case TARGET_BREAK_HARD:
priv->hw_breakpoint[i] = false;
apb_write(t, DBGBCR(i), 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 int cortexm_fault_unwind(target *t);
static int cortexm_set_hw_bp(target *t, target_addr addr, uint8_t len);
static int cortexm_clear_hw_bp(target *t, target_addr addr, uint8_t len);
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 int cortexm_breakwatch_set(target *t, struct breakwatch *);
static int cortexm_breakwatch_clear(target *t, struct breakwatch *);
static target_addr cortexm_check_watch(target *t);
#define CORTEXM_MAX_WATCHPOINTS 4 /* architecture says up to 15, no implementation has > 4 */
@ -74,15 +70,11 @@ struct cortexm_priv {
bool stepping;
bool on_bkpt;
/* Watchpoint unit status */
struct wp_unit_s {
uint32_t addr;
uint8_t type;
uint8_t size;
} hw_watchpoint[CORTEXM_MAX_WATCHPOINTS];
bool hw_watchpoint[CORTEXM_MAX_WATCHPOINTS];
unsigned flash_patch_revision;
unsigned hw_watchpoint_max;
/* Breakpoint unit status */
uint32_t hw_breakpoint[CORTEXM_MAX_BREAKPOINTS];
bool hw_breakpoint[CORTEXM_MAX_BREAKPOINTS];
unsigned hw_breakpoint_max;
/* Copy of DEMCR for vector-catch */
uint32_t demcr;
@ -240,6 +232,9 @@ bool cortexm_probe(ADIv5_AP_t *ap)
t->halt_resume = cortexm_halt_resume;
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);
/* Probe for FP extension */
@ -320,18 +315,12 @@ bool cortexm_attach(target *t)
/* Clear any stale watchpoints */
for(i = 0; i < priv->hw_watchpoint_max; i++) {
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 */
target_mem_write32(t, CORTEXM_FPB_CTRL,
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);
@ -650,130 +639,110 @@ int cortexm_run_stub(target *t, uint32_t loadaddr,
return bkpt_instr & 0xff;
}
/* The following routines implement hardware breakpoints.
* The Flash Patch and Breakpoint (FPB) system is used. */
/* The following routines implement hardware breakpoints and watchpoints.
* 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;
struct cortexm_priv *priv = t->priv;
uint32_t val = addr;
unsigned i;
if (priv->flash_patch_revision == 0) {
val = addr & 0x1FFFFFFC;
val |= (addr & 2)?0x80000000:0x40000000;
switch (len) {
case 1:
return CORTEXM_DWT_MASK_BYTE;
case 2:
return CORTEXM_DWT_MASK_HALFWORD;
case 4:
return CORTEXM_DWT_MASK_WORD;
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;
struct cortexm_priv *priv = t->priv;
unsigned i;
uint32_t x = 0;
for(i = 0; i < priv->hw_breakpoint_max; i++)
if((priv->hw_breakpoint[i] & ~1) == addr) break;
if ((t->target_options & TOPT_FLAVOUR_V6M) == 0)
x = CORTEXM_DWT_FUNC_DATAVSIZE_WORD;
if(i == priv->hw_breakpoint_max) return -1;
priv->hw_breakpoint[i] = 0;
target_mem_write32(t, CORTEXM_FPB_COMP(i), 0);
return 0;
switch (type) {
case TARGET_WATCH_WRITE:
return CORTEXM_DWT_FUNC_FUNC_WRITE | x;
case TARGET_WATCH_READ:
return CORTEXM_DWT_FUNC_FUNC_READ | x;
case TARGET_WATCH_ACCESS:
return CORTEXM_DWT_FUNC_FUNC_ACCESS | x;
default:
return -1;
}
}
/* The following routines implement hardware watchpoints.
* 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)
static int cortexm_breakwatch_set(target *t, struct breakwatch *bw)
{
struct cortexm_priv *priv = t->priv;
unsigned i;
uint32_t val = bw->addr;
switch(len) { /* Convert bytes size to mask size */
case 1: len = CORTEXM_DWT_MASK_BYTE; break;
case 2: len = CORTEXM_DWT_MASK_HALFWORD; break;
case 4: len = CORTEXM_DWT_MASK_WORD; break;
default:
switch (bw->type) {
case TARGET_BREAK_HARD:
if (priv->flash_patch_revision == 0) {
val &= 0x1FFFFFFC;
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;
}
switch(type) { /* Convert gdb type to function 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:
priv->hw_breakpoint[i] = true;
target_mem_write32(t, CORTEXM_FPB_COMP(i), val);
bw->reserved[0] = i;
return 0;
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;
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
cortexm_clear_hw_wp(target *t, uint8_t type, target_addr addr, uint8_t len)
static int cortexm_breakwatch_clear(target *t, struct breakwatch *bw)
{
struct cortexm_priv *priv = t->priv;
unsigned i;
switch(len) {
case 1: len = CORTEXM_DWT_MASK_BYTE; break;
case 2: len = CORTEXM_DWT_MASK_HALFWORD; break;
case 4: len = CORTEXM_DWT_MASK_WORD; break;
default:
return -1;
unsigned i = bw->reserved[0];
switch (bw->type) {
case TARGET_BREAK_HARD:
priv->hw_breakpoint[i] = false;
target_mem_write32(t, CORTEXM_FPB_COMP(i), 0);
return 0;
case TARGET_WATCH_WRITE:
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)
@ -783,14 +752,15 @@ static target_addr cortexm_check_watch(target *t)
for(i = 0; i < priv->hw_watchpoint_max; i++)
/* 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)) &
CORTEXM_DWT_FUNC_MATCHED))
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[])

View File

@ -73,6 +73,11 @@ void target_list_free(void)
free(target_list->flash);
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);
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,
enum target_breakwatch type, target_addr addr, size_t len)
{
switch (type) {
case TARGET_BREAK_HARD:
if (t->set_hw_bp)
return t->set_hw_bp(t, addr, len);
case TARGET_WATCH_WRITE:
case TARGET_WATCH_READ:
case TARGET_WATCH_ACCESS:
if (t->set_hw_wp)
return t->set_hw_wp(t, type, addr, len);
default:
break;
struct breakwatch bw = {
.type = type,
.addr = addr,
.size = len,
};
int ret = 1;
if (t->breakwatch_set)
ret = t->breakwatch_set(t, &bw);
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,
enum target_breakwatch type, target_addr addr, size_t len)
{
switch (type) {
case TARGET_BREAK_HARD:
if (t->set_hw_bp)
return t->set_hw_bp(t, addr, len);
case TARGET_WATCH_WRITE:
case TARGET_WATCH_READ:
case TARGET_WATCH_ACCESS:
if (t->set_hw_wp)
return t->set_hw_wp(t, type, addr, len);
default:
break;
struct breakwatch *bwp = NULL, *bw;
int ret = 1;
for (bw = t->bw_list; bw; bw = bw->next, bwp = bw)
if ((bw->type == type) &&
(bw->addr == addr) &&
(bw->size == len))
break;
if (bw == NULL)
return -1;
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 */

View File

@ -68,6 +68,14 @@ struct target_command_s {
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 {
bool attached;
struct target_controller *tc;
@ -96,11 +104,9 @@ struct target_s {
void (*halt_resume)(target *t, bool step);
/* Break-/watchpoint functions */
int (*set_hw_bp)(target *t, target_addr addr, uint8_t len);
int (*clear_hw_bp)(target *t, target_addr addr, uint8_t len);
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);
int (*breakwatch_set)(target *t, struct breakwatch*);
int (*breakwatch_clear)(target *t, struct breakwatch*);
struct breakwatch *bw_list;
/* target-defined options */
unsigned target_options;