From 9aacc18f6033c742ac042dab17a02c04f5b5bd4b Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Thu, 7 Jul 2016 11:49:47 +1200 Subject: [PATCH] target: Restructure internal break/watch handling. cortexa: Implement soft breakpoints. --- src/target/cortexa.c | 98 ++++++++++------ src/target/cortexm.c | 216 +++++++++++++++-------------------- src/target/target.c | 69 +++++++---- src/target/target_internal.h | 16 ++- 4 files changed, 213 insertions(+), 186 deletions(-) diff --git a/src/target/cortexa.c b/src/target/cortexa.c index a1af318..a9c8593 100644 --- a/src/target/cortexa.c +++ b/src/target/cortexa.c @@ -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; + } } diff --git a/src/target/cortexm.c b/src/target/cortexm.c index b0add80..d768fde 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -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[]) diff --git a/src/target/target.c b/src/target/target.c index 7f8e828..94aaa1e 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -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 */ diff --git a/src/target/target_internal.h b/src/target/target_internal.h index 13a131a..8ead4a2 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -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;