arm: move KVM breakpoints helpers
These helpers will be also used for HVF. Aside from reformatting a couple of comments for 'checkpatch.pl' and updating meson to compile 'hyp_gdbstub.c', this is just code motion. Signed-off-by: Francesco Cagnin <fcagnin@quarkslab.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20230601153107.81955-2-fcagnin@quarkslab.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
369081c455
commit
0ca52a5fed
253
target/arm/hyp_gdbstub.c
Normal file
253
target/arm/hyp_gdbstub.c
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* ARM implementation of KVM and HVF hooks, 64 bit specific code
|
||||
*
|
||||
* Copyright Mian-M. Hamayun 2013, Virtual Open Systems
|
||||
* Copyright Alex Bennée 2014, Linaro
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "internals.h"
|
||||
#include "exec/gdbstub.h"
|
||||
|
||||
/* Maximum and current break/watch point counts */
|
||||
int max_hw_bps, max_hw_wps;
|
||||
GArray *hw_breakpoints, *hw_watchpoints;
|
||||
|
||||
/**
|
||||
* insert_hw_breakpoint()
|
||||
* @addr: address of breakpoint
|
||||
*
|
||||
* See ARM ARM D2.9.1 for details but here we are only going to create
|
||||
* simple un-linked breakpoints (i.e. we don't chain breakpoints
|
||||
* together to match address and context or vmid). The hardware is
|
||||
* capable of fancier matching but that will require exposing that
|
||||
* fanciness to GDB's interface
|
||||
*
|
||||
* DBGBCR<n>_EL1, Debug Breakpoint Control Registers
|
||||
*
|
||||
* 31 24 23 20 19 16 15 14 13 12 9 8 5 4 3 2 1 0
|
||||
* +------+------+-------+-----+----+------+-----+------+-----+---+
|
||||
* | RES0 | BT | LBN | SSC | HMC| RES0 | BAS | RES0 | PMC | E |
|
||||
* +------+------+-------+-----+----+------+-----+------+-----+---+
|
||||
*
|
||||
* BT: Breakpoint type (0 = unlinked address match)
|
||||
* LBN: Linked BP number (0 = unused)
|
||||
* SSC/HMC/PMC: Security, Higher and Priv access control (Table D-12)
|
||||
* BAS: Byte Address Select (RES1 for AArch64)
|
||||
* E: Enable bit
|
||||
*
|
||||
* DBGBVR<n>_EL1, Debug Breakpoint Value Registers
|
||||
*
|
||||
* 63 53 52 49 48 2 1 0
|
||||
* +------+-----------+----------+-----+
|
||||
* | RESS | VA[52:49] | VA[48:2] | 0 0 |
|
||||
* +------+-----------+----------+-----+
|
||||
*
|
||||
* Depending on the addressing mode bits the top bits of the register
|
||||
* are a sign extension of the highest applicable VA bit. Some
|
||||
* versions of GDB don't do it correctly so we ensure they are correct
|
||||
* here so future PC comparisons will work properly.
|
||||
*/
|
||||
|
||||
int insert_hw_breakpoint(target_ulong addr)
|
||||
{
|
||||
HWBreakpoint brk = {
|
||||
.bcr = 0x1, /* BCR E=1, enable */
|
||||
.bvr = sextract64(addr, 0, 53)
|
||||
};
|
||||
|
||||
if (cur_hw_bps >= max_hw_bps) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
brk.bcr = deposit32(brk.bcr, 1, 2, 0x3); /* PMC = 11 */
|
||||
brk.bcr = deposit32(brk.bcr, 5, 4, 0xf); /* BAS = RES1 */
|
||||
|
||||
g_array_append_val(hw_breakpoints, brk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* delete_hw_breakpoint()
|
||||
* @pc: address of breakpoint
|
||||
*
|
||||
* Delete a breakpoint and shuffle any above down
|
||||
*/
|
||||
|
||||
int delete_hw_breakpoint(target_ulong pc)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < hw_breakpoints->len; i++) {
|
||||
HWBreakpoint *brk = get_hw_bp(i);
|
||||
if (brk->bvr == pc) {
|
||||
g_array_remove_index(hw_breakpoints, i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_hw_watchpoint()
|
||||
* @addr: address of watch point
|
||||
* @len: size of area
|
||||
* @type: type of watch point
|
||||
*
|
||||
* See ARM ARM D2.10. As with the breakpoints we can do some advanced
|
||||
* stuff if we want to. The watch points can be linked with the break
|
||||
* points above to make them context aware. However for simplicity
|
||||
* currently we only deal with simple read/write watch points.
|
||||
*
|
||||
* D7.3.11 DBGWCR<n>_EL1, Debug Watchpoint Control Registers
|
||||
*
|
||||
* 31 29 28 24 23 21 20 19 16 15 14 13 12 5 4 3 2 1 0
|
||||
* +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+
|
||||
* | RES0 | MASK | RES0 | WT | LBN | SSC | HMC | BAS | LSC | PAC | E |
|
||||
* +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+
|
||||
*
|
||||
* MASK: num bits addr mask (0=none,01/10=res,11=3 bits (8 bytes))
|
||||
* WT: 0 - unlinked, 1 - linked (not currently used)
|
||||
* LBN: Linked BP number (not currently used)
|
||||
* SSC/HMC/PAC: Security, Higher and Priv access control (Table D2-11)
|
||||
* BAS: Byte Address Select
|
||||
* LSC: Load/Store control (01: load, 10: store, 11: both)
|
||||
* E: Enable
|
||||
*
|
||||
* The bottom 2 bits of the value register are masked. Therefore to
|
||||
* break on any sizes smaller than an unaligned word you need to set
|
||||
* MASK=0, BAS=bit per byte in question. For larger regions (^2) you
|
||||
* need to ensure you mask the address as required and set BAS=0xff
|
||||
*/
|
||||
|
||||
int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type)
|
||||
{
|
||||
HWWatchpoint wp = {
|
||||
.wcr = R_DBGWCR_E_MASK, /* E=1, enable */
|
||||
.wvr = addr & (~0x7ULL),
|
||||
.details = { .vaddr = addr, .len = len }
|
||||
};
|
||||
|
||||
if (cur_hw_wps >= max_hw_wps) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
/*
|
||||
* HMC=0 SSC=0 PAC=3 will hit EL0 or EL1, any security state,
|
||||
* valid whether EL3 is implemented or not
|
||||
*/
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, PAC, 3);
|
||||
|
||||
switch (type) {
|
||||
case GDB_WATCHPOINT_READ:
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 1);
|
||||
wp.details.flags = BP_MEM_READ;
|
||||
break;
|
||||
case GDB_WATCHPOINT_WRITE:
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 2);
|
||||
wp.details.flags = BP_MEM_WRITE;
|
||||
break;
|
||||
case GDB_WATCHPOINT_ACCESS:
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 3);
|
||||
wp.details.flags = BP_MEM_ACCESS;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
if (len <= 8) {
|
||||
/* we align the address and set the bits in BAS */
|
||||
int off = addr & 0x7;
|
||||
int bas = (1 << len) - 1;
|
||||
|
||||
wp.wcr = deposit32(wp.wcr, 5 + off, 8 - off, bas);
|
||||
} else {
|
||||
/* For ranges above 8 bytes we need to be a power of 2 */
|
||||
if (is_power_of_2(len)) {
|
||||
int bits = ctz64(len);
|
||||
|
||||
wp.wvr &= ~((1 << bits) - 1);
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, MASK, bits);
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, BAS, 0xff);
|
||||
} else {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
}
|
||||
|
||||
g_array_append_val(hw_watchpoints, wp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool check_watchpoint_in_range(int i, target_ulong addr)
|
||||
{
|
||||
HWWatchpoint *wp = get_hw_wp(i);
|
||||
uint64_t addr_top, addr_bottom = wp->wvr;
|
||||
int bas = extract32(wp->wcr, 5, 8);
|
||||
int mask = extract32(wp->wcr, 24, 4);
|
||||
|
||||
if (mask) {
|
||||
addr_top = addr_bottom + (1 << mask);
|
||||
} else {
|
||||
/*
|
||||
* BAS must be contiguous but can offset against the base
|
||||
* address in DBGWVR
|
||||
*/
|
||||
addr_bottom = addr_bottom + ctz32(bas);
|
||||
addr_top = addr_bottom + clo32(bas);
|
||||
}
|
||||
|
||||
if (addr >= addr_bottom && addr <= addr_top) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* delete_hw_watchpoint()
|
||||
* @addr: address of breakpoint
|
||||
*
|
||||
* Delete a breakpoint and shuffle any above down
|
||||
*/
|
||||
|
||||
int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < cur_hw_wps; i++) {
|
||||
if (check_watchpoint_in_range(i, addr)) {
|
||||
g_array_remove_index(hw_watchpoints, i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
bool find_hw_breakpoint(CPUState *cpu, target_ulong pc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cur_hw_bps; i++) {
|
||||
HWBreakpoint *bp = get_hw_bp(i);
|
||||
if (bp->bvr == pc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cur_hw_wps; i++) {
|
||||
if (check_watchpoint_in_range(i, addr)) {
|
||||
return &get_hw_wp(i)->details;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
@ -1447,4 +1447,54 @@ static inline bool arm_fgt_active(CPUARMState *env, int el)
|
||||
}
|
||||
|
||||
void assert_hflags_rebuild_correctly(CPUARMState *env);
|
||||
|
||||
/*
|
||||
* Although the ARM implementation of hardware assisted debugging
|
||||
* allows for different breakpoints per-core, the current GDB
|
||||
* interface treats them as a global pool of registers (which seems to
|
||||
* be the case for x86, ppc and s390). As a result we store one copy
|
||||
* of registers which is used for all active cores.
|
||||
*
|
||||
* Write access is serialised by virtue of the GDB protocol which
|
||||
* updates things. Read access (i.e. when the values are copied to the
|
||||
* vCPU) is also gated by GDB's run control.
|
||||
*
|
||||
* This is not unreasonable as most of the time debugging kernels you
|
||||
* never know which core will eventually execute your function.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint64_t bcr;
|
||||
uint64_t bvr;
|
||||
} HWBreakpoint;
|
||||
|
||||
/*
|
||||
* The watchpoint registers can cover more area than the requested
|
||||
* watchpoint so we need to store the additional information
|
||||
* somewhere. We also need to supply a CPUWatchpoint to the GDB stub
|
||||
* when the watchpoint is hit.
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t wcr;
|
||||
uint64_t wvr;
|
||||
CPUWatchpoint details;
|
||||
} HWWatchpoint;
|
||||
|
||||
/* Maximum and current break/watch point counts */
|
||||
extern int max_hw_bps, max_hw_wps;
|
||||
extern GArray *hw_breakpoints, *hw_watchpoints;
|
||||
|
||||
#define cur_hw_wps (hw_watchpoints->len)
|
||||
#define cur_hw_bps (hw_breakpoints->len)
|
||||
#define get_hw_bp(i) (&g_array_index(hw_breakpoints, HWBreakpoint, i))
|
||||
#define get_hw_wp(i) (&g_array_index(hw_watchpoints, HWWatchpoint, i))
|
||||
|
||||
bool find_hw_breakpoint(CPUState *cpu, target_ulong pc);
|
||||
int insert_hw_breakpoint(target_ulong pc);
|
||||
int delete_hw_breakpoint(target_ulong pc);
|
||||
|
||||
bool check_watchpoint_in_range(int i, target_ulong addr);
|
||||
CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr);
|
||||
int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type);
|
||||
int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type);
|
||||
#endif
|
||||
|
@ -34,46 +34,6 @@
|
||||
|
||||
static bool have_guest_debug;
|
||||
|
||||
/*
|
||||
* Although the ARM implementation of hardware assisted debugging
|
||||
* allows for different breakpoints per-core, the current GDB
|
||||
* interface treats them as a global pool of registers (which seems to
|
||||
* be the case for x86, ppc and s390). As a result we store one copy
|
||||
* of registers which is used for all active cores.
|
||||
*
|
||||
* Write access is serialised by virtue of the GDB protocol which
|
||||
* updates things. Read access (i.e. when the values are copied to the
|
||||
* vCPU) is also gated by GDB's run control.
|
||||
*
|
||||
* This is not unreasonable as most of the time debugging kernels you
|
||||
* never know which core will eventually execute your function.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint64_t bcr;
|
||||
uint64_t bvr;
|
||||
} HWBreakpoint;
|
||||
|
||||
/* The watchpoint registers can cover more area than the requested
|
||||
* watchpoint so we need to store the additional information
|
||||
* somewhere. We also need to supply a CPUWatchpoint to the GDB stub
|
||||
* when the watchpoint is hit.
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t wcr;
|
||||
uint64_t wvr;
|
||||
CPUWatchpoint details;
|
||||
} HWWatchpoint;
|
||||
|
||||
/* Maximum and current break/watch point counts */
|
||||
int max_hw_bps, max_hw_wps;
|
||||
GArray *hw_breakpoints, *hw_watchpoints;
|
||||
|
||||
#define cur_hw_wps (hw_watchpoints->len)
|
||||
#define cur_hw_bps (hw_breakpoints->len)
|
||||
#define get_hw_bp(i) (&g_array_index(hw_breakpoints, HWBreakpoint, i))
|
||||
#define get_hw_wp(i) (&g_array_index(hw_watchpoints, HWWatchpoint, i))
|
||||
|
||||
void kvm_arm_init_debug(KVMState *s)
|
||||
{
|
||||
have_guest_debug = kvm_check_extension(s,
|
||||
@ -89,217 +49,6 @@ void kvm_arm_init_debug(KVMState *s)
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_hw_breakpoint()
|
||||
* @addr: address of breakpoint
|
||||
*
|
||||
* See ARM ARM D2.9.1 for details but here we are only going to create
|
||||
* simple un-linked breakpoints (i.e. we don't chain breakpoints
|
||||
* together to match address and context or vmid). The hardware is
|
||||
* capable of fancier matching but that will require exposing that
|
||||
* fanciness to GDB's interface
|
||||
*
|
||||
* DBGBCR<n>_EL1, Debug Breakpoint Control Registers
|
||||
*
|
||||
* 31 24 23 20 19 16 15 14 13 12 9 8 5 4 3 2 1 0
|
||||
* +------+------+-------+-----+----+------+-----+------+-----+---+
|
||||
* | RES0 | BT | LBN | SSC | HMC| RES0 | BAS | RES0 | PMC | E |
|
||||
* +------+------+-------+-----+----+------+-----+------+-----+---+
|
||||
*
|
||||
* BT: Breakpoint type (0 = unlinked address match)
|
||||
* LBN: Linked BP number (0 = unused)
|
||||
* SSC/HMC/PMC: Security, Higher and Priv access control (Table D-12)
|
||||
* BAS: Byte Address Select (RES1 for AArch64)
|
||||
* E: Enable bit
|
||||
*
|
||||
* DBGBVR<n>_EL1, Debug Breakpoint Value Registers
|
||||
*
|
||||
* 63 53 52 49 48 2 1 0
|
||||
* +------+-----------+----------+-----+
|
||||
* | RESS | VA[52:49] | VA[48:2] | 0 0 |
|
||||
* +------+-----------+----------+-----+
|
||||
*
|
||||
* Depending on the addressing mode bits the top bits of the register
|
||||
* are a sign extension of the highest applicable VA bit. Some
|
||||
* versions of GDB don't do it correctly so we ensure they are correct
|
||||
* here so future PC comparisons will work properly.
|
||||
*/
|
||||
|
||||
static int insert_hw_breakpoint(target_ulong addr)
|
||||
{
|
||||
HWBreakpoint brk = {
|
||||
.bcr = 0x1, /* BCR E=1, enable */
|
||||
.bvr = sextract64(addr, 0, 53)
|
||||
};
|
||||
|
||||
if (cur_hw_bps >= max_hw_bps) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
brk.bcr = deposit32(brk.bcr, 1, 2, 0x3); /* PMC = 11 */
|
||||
brk.bcr = deposit32(brk.bcr, 5, 4, 0xf); /* BAS = RES1 */
|
||||
|
||||
g_array_append_val(hw_breakpoints, brk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* delete_hw_breakpoint()
|
||||
* @pc: address of breakpoint
|
||||
*
|
||||
* Delete a breakpoint and shuffle any above down
|
||||
*/
|
||||
|
||||
static int delete_hw_breakpoint(target_ulong pc)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < hw_breakpoints->len; i++) {
|
||||
HWBreakpoint *brk = get_hw_bp(i);
|
||||
if (brk->bvr == pc) {
|
||||
g_array_remove_index(hw_breakpoints, i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_hw_watchpoint()
|
||||
* @addr: address of watch point
|
||||
* @len: size of area
|
||||
* @type: type of watch point
|
||||
*
|
||||
* See ARM ARM D2.10. As with the breakpoints we can do some advanced
|
||||
* stuff if we want to. The watch points can be linked with the break
|
||||
* points above to make them context aware. However for simplicity
|
||||
* currently we only deal with simple read/write watch points.
|
||||
*
|
||||
* D7.3.11 DBGWCR<n>_EL1, Debug Watchpoint Control Registers
|
||||
*
|
||||
* 31 29 28 24 23 21 20 19 16 15 14 13 12 5 4 3 2 1 0
|
||||
* +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+
|
||||
* | RES0 | MASK | RES0 | WT | LBN | SSC | HMC | BAS | LSC | PAC | E |
|
||||
* +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+
|
||||
*
|
||||
* MASK: num bits addr mask (0=none,01/10=res,11=3 bits (8 bytes))
|
||||
* WT: 0 - unlinked, 1 - linked (not currently used)
|
||||
* LBN: Linked BP number (not currently used)
|
||||
* SSC/HMC/PAC: Security, Higher and Priv access control (Table D2-11)
|
||||
* BAS: Byte Address Select
|
||||
* LSC: Load/Store control (01: load, 10: store, 11: both)
|
||||
* E: Enable
|
||||
*
|
||||
* The bottom 2 bits of the value register are masked. Therefore to
|
||||
* break on any sizes smaller than an unaligned word you need to set
|
||||
* MASK=0, BAS=bit per byte in question. For larger regions (^2) you
|
||||
* need to ensure you mask the address as required and set BAS=0xff
|
||||
*/
|
||||
|
||||
static int insert_hw_watchpoint(target_ulong addr,
|
||||
target_ulong len, int type)
|
||||
{
|
||||
HWWatchpoint wp = {
|
||||
.wcr = R_DBGWCR_E_MASK, /* E=1, enable */
|
||||
.wvr = addr & (~0x7ULL),
|
||||
.details = { .vaddr = addr, .len = len }
|
||||
};
|
||||
|
||||
if (cur_hw_wps >= max_hw_wps) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
/*
|
||||
* HMC=0 SSC=0 PAC=3 will hit EL0 or EL1, any security state,
|
||||
* valid whether EL3 is implemented or not
|
||||
*/
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, PAC, 3);
|
||||
|
||||
switch (type) {
|
||||
case GDB_WATCHPOINT_READ:
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 1);
|
||||
wp.details.flags = BP_MEM_READ;
|
||||
break;
|
||||
case GDB_WATCHPOINT_WRITE:
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 2);
|
||||
wp.details.flags = BP_MEM_WRITE;
|
||||
break;
|
||||
case GDB_WATCHPOINT_ACCESS:
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 3);
|
||||
wp.details.flags = BP_MEM_ACCESS;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
if (len <= 8) {
|
||||
/* we align the address and set the bits in BAS */
|
||||
int off = addr & 0x7;
|
||||
int bas = (1 << len) - 1;
|
||||
|
||||
wp.wcr = deposit32(wp.wcr, 5 + off, 8 - off, bas);
|
||||
} else {
|
||||
/* For ranges above 8 bytes we need to be a power of 2 */
|
||||
if (is_power_of_2(len)) {
|
||||
int bits = ctz64(len);
|
||||
|
||||
wp.wvr &= ~((1 << bits) - 1);
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, MASK, bits);
|
||||
wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, BAS, 0xff);
|
||||
} else {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
}
|
||||
|
||||
g_array_append_val(hw_watchpoints, wp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool check_watchpoint_in_range(int i, target_ulong addr)
|
||||
{
|
||||
HWWatchpoint *wp = get_hw_wp(i);
|
||||
uint64_t addr_top, addr_bottom = wp->wvr;
|
||||
int bas = extract32(wp->wcr, 5, 8);
|
||||
int mask = extract32(wp->wcr, 24, 4);
|
||||
|
||||
if (mask) {
|
||||
addr_top = addr_bottom + (1 << mask);
|
||||
} else {
|
||||
/* BAS must be contiguous but can offset against the base
|
||||
* address in DBGWVR */
|
||||
addr_bottom = addr_bottom + ctz32(bas);
|
||||
addr_top = addr_bottom + clo32(bas);
|
||||
}
|
||||
|
||||
if (addr >= addr_bottom && addr <= addr_top) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* delete_hw_watchpoint()
|
||||
* @addr: address of breakpoint
|
||||
*
|
||||
* Delete a breakpoint and shuffle any above down
|
||||
*/
|
||||
|
||||
static int delete_hw_watchpoint(target_ulong addr,
|
||||
target_ulong len, int type)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < cur_hw_wps; i++) {
|
||||
if (check_watchpoint_in_range(i, addr)) {
|
||||
g_array_remove_index(hw_watchpoints, i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
int kvm_arch_insert_hw_breakpoint(target_ulong addr,
|
||||
target_ulong len, int type)
|
||||
{
|
||||
@ -364,31 +113,6 @@ bool kvm_arm_hw_debug_active(CPUState *cs)
|
||||
return ((cur_hw_wps > 0) || (cur_hw_bps > 0));
|
||||
}
|
||||
|
||||
static bool find_hw_breakpoint(CPUState *cpu, target_ulong pc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cur_hw_bps; i++) {
|
||||
HWBreakpoint *bp = get_hw_bp(i);
|
||||
if (bp->bvr == pc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cur_hw_wps; i++) {
|
||||
if (check_watchpoint_in_range(i, addr)) {
|
||||
return &get_hw_wp(i)->details;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool kvm_arm_set_device_attr(CPUState *cs, struct kvm_device_attr *attr,
|
||||
const char *name)
|
||||
{
|
||||
|
@ -8,7 +8,8 @@ arm_ss.add(files(
|
||||
))
|
||||
arm_ss.add(zlib)
|
||||
|
||||
arm_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c', 'kvm64.c'), if_false: files('kvm-stub.c'))
|
||||
arm_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c', 'kvm64.c'), if_false: files('kvm-stub.c'))
|
||||
arm_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c'))
|
||||
|
||||
arm_ss.add(when: 'TARGET_AARCH64', if_true: files(
|
||||
'cpu64.c',
|
||||
|
Loading…
Reference in New Issue
Block a user