From 4f1296d1510715b5504f39909dadae79698aa27a Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Wed, 21 Apr 2010 13:40:51 -0400 Subject: [PATCH] xscale: add support for length arg to wp command This patch adds support for the length argument to the xscale implementation of the wp command. Per discussion with David, the length argument specifies the range of addresses over which a memory access should generate a debug exception. This patch utilizes the "mask" feature of the xscale debug hardware to implement the correct functionality of the length argument. Some limitations imposed by the hardware are: - The length must be a power of two, with a minumum of 4. - Two data breakpoint registers are available, allowing for two watchpoints. However, if the length of a watchpoint is greater than four, both registers are used (the second for a mask value), limiting the number of watchpoints to one. This patch also removes a useless call to xscale_get_reg(dbcon) in xscale_set_watchpoint() (value had already been read from the register cache, and the same previously read value is then modified and written back). I have been using and testing this patch for a couple days. Questions, corrections, criticisms of course gratefully received. --- src/target/xscale.c | 59 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/src/target/xscale.c b/src/target/xscale.c index ddc73d20..ed0eef35 100644 --- a/src/target/xscale.c +++ b/src/target/xscale.c @@ -2266,7 +2266,7 @@ static int xscale_set_watchpoint(struct target *target, struct watchpoint *watchpoint) { struct xscale_common *xscale = target_to_xscale(target); - uint8_t enable = 0; + uint32_t enable = 0; struct reg *dbcon = &xscale->reg_cache->reg_list[XSCALE_DBCON]; uint32_t dbcon_value = buf_get_u32(dbcon->value, 0, 32); @@ -2276,8 +2276,6 @@ static int xscale_set_watchpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - xscale_get_reg(dbcon); - switch (watchpoint->rw) { case WPT_READ: @@ -2293,6 +2291,24 @@ static int xscale_set_watchpoint(struct target *target, LOG_ERROR("BUG: watchpoint->rw neither read, write nor access"); } + /* For watchpoint across more than one word, both DBR registers must + be enlisted, with the second used as a mask. */ + if (watchpoint->length > 4) + { + if (xscale->dbr0_used || xscale->dbr1_used) + { + LOG_ERROR("BUG: sufficient hardware comparators unavailable"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* Write mask value to DBR1, based on the length argument. + * Address bits ignored by the comparator are those set in mask. */ + xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_DBR1], + watchpoint->length - 1); + xscale->dbr1_used = 1; + enable |= 0x100; /* DBCON[M] */ + } + if (!xscale->dbr0_used) { xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_DBR0], watchpoint->address); @@ -2312,7 +2328,7 @@ static int xscale_set_watchpoint(struct target *target, else { LOG_ERROR("BUG: no hardware comparator available"); - return ERROR_OK; + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } return ERROR_OK; @@ -2328,13 +2344,30 @@ static int xscale_add_watchpoint(struct target *target, return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - if ((watchpoint->length != 1) && (watchpoint->length != 2) && (watchpoint->length != 4)) + if (watchpoint->value) + LOG_WARNING("xscale does not support value, mask arguments; ignoring"); + + /* check that length is a power of two */ + for (uint32_t len = watchpoint->length; len != 1; len /= 2) { - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + if (len % 2) + { + LOG_ERROR("xscale requires that watchpoint length is a power of two"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } } - xscale->dbr_available--; + if (watchpoint->length == 4) /* single word watchpoint */ + { + xscale->dbr_available--; /* one DBR reg used */ + return ERROR_OK; + } + /* watchpoints across multiple words require both DBR registers */ + if (xscale->dbr_available < 2) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + xscale->dbr_available = 0; return ERROR_OK; } @@ -2359,7 +2392,14 @@ static int xscale_unset_watchpoint(struct target *target, if (watchpoint->set == 1) { - dbcon_value &= ~0x3; + if (watchpoint->length > 4) + { + dbcon_value &= ~0x103; /* clear DBCON[M] as well */ + xscale->dbr1_used = 0; /* DBR1 was used for mask */ + } + else + dbcon_value &= ~0x3; + xscale_set_reg_u32(dbcon, dbcon_value); xscale->dbr0_used = 0; } @@ -2389,6 +2429,9 @@ static int xscale_remove_watchpoint(struct target *target, struct watchpoint *wa xscale_unset_watchpoint(target, watchpoint); } + if (watchpoint->length > 4) + xscale->dbr_available++; /* both DBR regs now available */ + xscale->dbr_available++; return ERROR_OK; -- 2.39.5