exec.c: Capture the memory attributes for a watchpoint hit
Capture the memory attributes for the transaction which triggered a watchpoint; this allows CPU specific code to implement features like ARM's "user-mode only WPs also hit for LDRT/STRT accesses made from privileged code". This change also correctly passes through the memory attributes to the underlying device when a watchpoint access doesn't hit. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
This commit is contained in:
parent
500131154d
commit
66b9b43c42
56
exec.c
56
exec.c
|
@ -1858,7 +1858,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, int flags)
|
static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags)
|
||||||
{
|
{
|
||||||
CPUState *cpu = current_cpu;
|
CPUState *cpu = current_cpu;
|
||||||
CPUArchState *env = cpu->env_ptr;
|
CPUArchState *env = cpu->env_ptr;
|
||||||
|
@ -1884,6 +1884,7 @@ static void check_watchpoint(int offset, int len, int flags)
|
||||||
wp->flags |= BP_WATCHPOINT_HIT_WRITE;
|
wp->flags |= BP_WATCHPOINT_HIT_WRITE;
|
||||||
}
|
}
|
||||||
wp->hitaddr = vaddr;
|
wp->hitaddr = vaddr;
|
||||||
|
wp->hitattrs = attrs;
|
||||||
if (!cpu->watchpoint_hit) {
|
if (!cpu->watchpoint_hit) {
|
||||||
cpu->watchpoint_hit = wp;
|
cpu->watchpoint_hit = wp;
|
||||||
tb_check_watchpoint(cpu);
|
tb_check_watchpoint(cpu);
|
||||||
|
@ -1905,39 +1906,54 @@ static void check_watchpoint(int offset, int len, int flags)
|
||||||
/* Watchpoint access routines. Watchpoints are inserted using TLB tricks,
|
/* Watchpoint access routines. Watchpoints are inserted using TLB tricks,
|
||||||
so these check for a hit then pass through to the normal out-of-line
|
so these check for a hit then pass through to the normal out-of-line
|
||||||
phys routines. */
|
phys routines. */
|
||||||
static uint64_t watch_mem_read(void *opaque, hwaddr addr,
|
static MemTxResult watch_mem_read(void *opaque, hwaddr addr, uint64_t *pdata,
|
||||||
unsigned size)
|
unsigned size, MemTxAttrs attrs)
|
||||||
{
|
{
|
||||||
check_watchpoint(addr & ~TARGET_PAGE_MASK, size, BP_MEM_READ);
|
MemTxResult res;
|
||||||
switch (size) {
|
uint64_t data;
|
||||||
case 1: return ldub_phys(&address_space_memory, addr);
|
|
||||||
case 2: return lduw_phys(&address_space_memory, addr);
|
|
||||||
case 4: return ldl_phys(&address_space_memory, addr);
|
|
||||||
default: abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void watch_mem_write(void *opaque, hwaddr addr,
|
check_watchpoint(addr & ~TARGET_PAGE_MASK, size, attrs, BP_MEM_READ);
|
||||||
uint64_t val, unsigned size)
|
|
||||||
{
|
|
||||||
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);
|
data = address_space_ldub(&address_space_memory, addr, attrs, &res);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
stw_phys(&address_space_memory, addr, val);
|
data = address_space_lduw(&address_space_memory, addr, attrs, &res);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
stl_phys(&address_space_memory, addr, val);
|
data = address_space_ldl(&address_space_memory, addr, attrs, &res);
|
||||||
break;
|
break;
|
||||||
default: abort();
|
default: abort();
|
||||||
}
|
}
|
||||||
|
*pdata = data;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemTxResult watch_mem_write(void *opaque, hwaddr addr,
|
||||||
|
uint64_t val, unsigned size,
|
||||||
|
MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
MemTxResult res;
|
||||||
|
|
||||||
|
check_watchpoint(addr & ~TARGET_PAGE_MASK, size, attrs, BP_MEM_WRITE);
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
address_space_stb(&address_space_memory, addr, val, attrs, &res);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
address_space_stw(&address_space_memory, addr, val, attrs, &res);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
address_space_stl(&address_space_memory, addr, val, attrs, &res);
|
||||||
|
break;
|
||||||
|
default: abort();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const MemoryRegionOps watch_mem_ops = {
|
static const MemoryRegionOps watch_mem_ops = {
|
||||||
.read = watch_mem_read,
|
.read_with_attrs = watch_mem_read,
|
||||||
.write = watch_mem_write,
|
.write_with_attrs = watch_mem_write,
|
||||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include "hw/qdev-core.h"
|
#include "hw/qdev-core.h"
|
||||||
#include "exec/hwaddr.h"
|
#include "exec/hwaddr.h"
|
||||||
|
#include "exec/memattrs.h"
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
#include "qemu/thread.h"
|
#include "qemu/thread.h"
|
||||||
#include "qemu/tls.h"
|
#include "qemu/tls.h"
|
||||||
|
@ -195,6 +196,7 @@ typedef struct CPUWatchpoint {
|
||||||
vaddr vaddr;
|
vaddr vaddr;
|
||||||
vaddr len;
|
vaddr len;
|
||||||
vaddr hitaddr;
|
vaddr hitaddr;
|
||||||
|
MemTxAttrs hitattrs;
|
||||||
int flags; /* BP_* */
|
int flags; /* BP_* */
|
||||||
QTAILQ_ENTRY(CPUWatchpoint) entry;
|
QTAILQ_ENTRY(CPUWatchpoint) entry;
|
||||||
} CPUWatchpoint;
|
} CPUWatchpoint;
|
||||||
|
|
Loading…
Reference in New Issue