exec.c: Relax restrictions on watchpoint length and alignment

The current implementation of watchpoints requires that they
have a power of 2 length which is not greater than TARGET_PAGE_SIZE
and that their address is a multiple of their length. Watchpoints
on ARM don't fit these restrictions, so change the implementation
so they can be relaxed.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
This commit is contained in:
Peter Maydell 2014-09-12 14:06:48 +01:00
parent acf82361c6
commit 05068c0dfb
3 changed files with 33 additions and 16 deletions

44
exec.c
View file

@ -582,12 +582,10 @@ int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
int flags, CPUWatchpoint **watchpoint) int flags, CPUWatchpoint **watchpoint)
{ {
vaddr len_mask = ~(len - 1);
CPUWatchpoint *wp; CPUWatchpoint *wp;
/* sanity checks: allow power-of-2 lengths, deny unaligned watchpoints */ /* forbid ranges which are empty or run off the end of the address space */
if ((len & (len - 1)) || (addr & ~len_mask) || if (len == 0 || (addr + len - 1) <= addr) {
len == 0 || len > TARGET_PAGE_SIZE) {
error_report("tried to set invalid watchpoint at %" error_report("tried to set invalid watchpoint at %"
VADDR_PRIx ", len=%" VADDR_PRIu, addr, len); VADDR_PRIx ", len=%" VADDR_PRIu, addr, len);
return -EINVAL; return -EINVAL;
@ -595,7 +593,7 @@ int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
wp = g_malloc(sizeof(*wp)); wp = g_malloc(sizeof(*wp));
wp->vaddr = addr; wp->vaddr = addr;
wp->len_mask = len_mask; wp->len = len;
wp->flags = flags; wp->flags = flags;
/* keep all GDB-injected watchpoints in front */ /* keep all GDB-injected watchpoints in front */
@ -616,11 +614,10 @@ int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, vaddr len, int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, vaddr len,
int flags) int flags)
{ {
vaddr len_mask = ~(len - 1);
CPUWatchpoint *wp; CPUWatchpoint *wp;
QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
if (addr == wp->vaddr && len_mask == wp->len_mask if (addr == wp->vaddr && len == wp->len
&& flags == (wp->flags & ~BP_WATCHPOINT_HIT)) { && flags == (wp->flags & ~BP_WATCHPOINT_HIT)) {
cpu_watchpoint_remove_by_ref(cpu, wp); cpu_watchpoint_remove_by_ref(cpu, wp);
return 0; return 0;
@ -650,6 +647,27 @@ void cpu_watchpoint_remove_all(CPUState *cpu, int mask)
} }
} }
} }
/* Return true if this watchpoint address matches the specified
* access (ie the address range covered by the watchpoint overlaps
* partially or completely with the address range covered by the
* access).
*/
static inline bool cpu_watchpoint_address_matches(CPUWatchpoint *wp,
vaddr addr,
vaddr len)
{
/* We know the lengths are non-zero, but a little caution is
* required to avoid errors in the case where the range ends
* exactly at the top of the address space and so addr + len
* wraps round to zero.
*/
vaddr wpend = wp->vaddr + wp->len - 1;
vaddr addrend = addr + len - 1;
return !(addr > wpend || wp->vaddr > addrend);
}
#endif #endif
/* Add a breakpoint. */ /* Add a breakpoint. */
@ -861,7 +879,7 @@ hwaddr memory_region_section_get_iotlb(CPUState *cpu,
/* Make accesses to pages with watchpoints go via the /* Make accesses to pages with watchpoints go via the
watchpoint trap routines. */ watchpoint trap routines. */
QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
if (vaddr == (wp->vaddr & TARGET_PAGE_MASK)) { if (cpu_watchpoint_address_matches(wp, vaddr, TARGET_PAGE_SIZE)) {
/* Avoid trapping reads of pages with a write breakpoint. */ /* Avoid trapping reads of pages with a write breakpoint. */
if ((prot & PAGE_WRITE) || (wp->flags & BP_MEM_READ)) { if ((prot & PAGE_WRITE) || (wp->flags & BP_MEM_READ)) {
iotlb = PHYS_SECTION_WATCH + paddr; iotlb = PHYS_SECTION_WATCH + paddr;
@ -1625,7 +1643,7 @@ static const MemoryRegionOps notdirty_mem_ops = {
}; };
/* Generate a debug exception if a watchpoint has been hit. */ /* Generate a debug exception if a watchpoint has been hit. */
static void check_watchpoint(int offset, int len_mask, int flags) static void check_watchpoint(int offset, int len, int flags)
{ {
CPUState *cpu = current_cpu; CPUState *cpu = current_cpu;
CPUArchState *env = cpu->env_ptr; CPUArchState *env = cpu->env_ptr;
@ -1643,8 +1661,8 @@ static void check_watchpoint(int offset, int len_mask, int flags)
} }
vaddr = (cpu->mem_io_vaddr & TARGET_PAGE_MASK) + offset; vaddr = (cpu->mem_io_vaddr & TARGET_PAGE_MASK) + offset;
QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
if ((vaddr == (wp->vaddr & len_mask) || if (cpu_watchpoint_address_matches(wp, vaddr, len)
(vaddr & wp->len_mask) == wp->vaddr) && (wp->flags & flags)) { && (wp->flags & flags)) {
wp->flags |= BP_WATCHPOINT_HIT; wp->flags |= BP_WATCHPOINT_HIT;
if (!cpu->watchpoint_hit) { if (!cpu->watchpoint_hit) {
cpu->watchpoint_hit = wp; cpu->watchpoint_hit = wp;
@ -1670,7 +1688,7 @@ static void check_watchpoint(int offset, int len_mask, int flags)
static uint64_t watch_mem_read(void *opaque, hwaddr addr, static uint64_t watch_mem_read(void *opaque, hwaddr addr,
unsigned size) unsigned size)
{ {
check_watchpoint(addr & ~TARGET_PAGE_MASK, ~(size - 1), BP_MEM_READ); check_watchpoint(addr & ~TARGET_PAGE_MASK, size, BP_MEM_READ);
switch (size) { switch (size) {
case 1: return ldub_phys(&address_space_memory, addr); case 1: return ldub_phys(&address_space_memory, addr);
case 2: return lduw_phys(&address_space_memory, addr); case 2: return lduw_phys(&address_space_memory, addr);
@ -1682,7 +1700,7 @@ static uint64_t watch_mem_read(void *opaque, hwaddr addr,
static void watch_mem_write(void *opaque, hwaddr addr, static void watch_mem_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size) uint64_t val, unsigned size)
{ {
check_watchpoint(addr & ~TARGET_PAGE_MASK, ~(size - 1), BP_MEM_WRITE); check_watchpoint(addr & ~TARGET_PAGE_MASK, size, BP_MEM_WRITE);
switch (size) { switch (size) {
case 1: case 1:
stb_phys(&address_space_memory, addr, val); stb_phys(&address_space_memory, addr, val);

View file

@ -169,7 +169,7 @@ typedef struct CPUBreakpoint {
typedef struct CPUWatchpoint { typedef struct CPUWatchpoint {
vaddr vaddr; vaddr vaddr;
vaddr len_mask; vaddr len;
int flags; /* BP_* */ int flags; /* BP_* */
QTAILQ_ENTRY(CPUWatchpoint) entry; QTAILQ_ENTRY(CPUWatchpoint) entry;
} CPUWatchpoint; } CPUWatchpoint;

View file

@ -3458,8 +3458,7 @@ CPUArchState *cpu_copy(CPUArchState *env)
cpu_breakpoint_insert(new_cpu, bp->pc, bp->flags, NULL); cpu_breakpoint_insert(new_cpu, bp->pc, bp->flags, NULL);
} }
QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
cpu_watchpoint_insert(new_cpu, wp->vaddr, (~wp->len_mask) + 1, cpu_watchpoint_insert(new_cpu, wp->vaddr, wp->len, wp->flags, NULL);
wp->flags, NULL);
} }
#endif #endif