From: Mike Dunn <[email protected]>
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 files changed, 51 insertions(+), 8 deletions(-)
diff --git a/src/target/xscale.c b/src/target/xscale.c
index ddc73d2..ed0eef3 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;
--
1.6.4.4
_______________________________________________
Openocd-development mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/openocd-development