Compare commits
5 Commits
qdev-array
...
singlestep
Author | SHA1 | Date | |
---|---|---|---|
|
34370ee6ff | ||
|
9953b32e77 | ||
|
0da2fd84e1 | ||
|
b82417e8bf | ||
|
0b72719af8 |
@@ -2267,6 +2267,13 @@ bool kvm_arm_supports_user_irq(void)
|
|||||||
return kvm_check_extension(kvm_state, KVM_CAP_ARM_USER_IRQ);
|
return kvm_check_extension(kvm_state, KVM_CAP_ARM_USER_IRQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Whether the KVM_SET_GUEST_DEBUG ioctl supports single stepping */
|
||||||
|
int kvm_has_guestdbg_singlestep(void)
|
||||||
|
{
|
||||||
|
/* return kvm_check_extension(kvm_state, KVM_CAP_GUEST_DEBUG_SSTEP); */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef KVM_CAP_SET_GUEST_DEBUG
|
#ifdef KVM_CAP_SET_GUEST_DEBUG
|
||||||
struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu,
|
struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu,
|
||||||
target_ulong pc)
|
target_ulong pc)
|
||||||
@@ -2316,6 +2323,15 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
|
|||||||
return data.err;
|
return data.err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_set_singlestep(CPUState *cs, int enabled)
|
||||||
|
{
|
||||||
|
if (kvm_has_guestdbg_singlestep()) {
|
||||||
|
kvm_update_guest_debug(cs, 0);
|
||||||
|
} else {
|
||||||
|
kvm_arch_set_singlestep(cs, enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
|
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
|
||||||
target_ulong len, int type)
|
target_ulong len, int type)
|
||||||
{
|
{
|
||||||
|
@@ -79,6 +79,10 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
|
|||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_set_singlestep(CPUState *cs, int enabled)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
|
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
|
||||||
target_ulong len, int type)
|
target_ulong len, int type)
|
||||||
{
|
{
|
||||||
|
2
exec.c
2
exec.c
@@ -1236,7 +1236,7 @@ void cpu_single_step(CPUState *cpu, int enabled)
|
|||||||
if (cpu->singlestep_enabled != enabled) {
|
if (cpu->singlestep_enabled != enabled) {
|
||||||
cpu->singlestep_enabled = enabled;
|
cpu->singlestep_enabled = enabled;
|
||||||
if (kvm_enabled()) {
|
if (kvm_enabled()) {
|
||||||
kvm_update_guest_debug(cpu, 0);
|
kvm_set_singlestep(cpu, enabled);
|
||||||
} else {
|
} else {
|
||||||
/* must flush all the translated code to avoid inconsistencies */
|
/* must flush all the translated code to avoid inconsistencies */
|
||||||
/* XXX: only flush what is necessary */
|
/* XXX: only flush what is necessary */
|
||||||
|
@@ -214,6 +214,7 @@ int kvm_has_pit_state2(void);
|
|||||||
int kvm_has_many_ioeventfds(void);
|
int kvm_has_many_ioeventfds(void);
|
||||||
int kvm_has_gsi_routing(void);
|
int kvm_has_gsi_routing(void);
|
||||||
int kvm_has_intx_set_mask(void);
|
int kvm_has_intx_set_mask(void);
|
||||||
|
int kvm_has_guestdbg_singlestep(void);
|
||||||
|
|
||||||
int kvm_init_vcpu(CPUState *cpu);
|
int kvm_init_vcpu(CPUState *cpu);
|
||||||
int kvm_cpu_exec(CPUState *cpu);
|
int kvm_cpu_exec(CPUState *cpu);
|
||||||
@@ -246,6 +247,7 @@ bool kvm_memcrypt_enabled(void);
|
|||||||
*/
|
*/
|
||||||
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len);
|
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len);
|
||||||
|
|
||||||
|
void kvm_arch_set_singlestep(CPUState *cpu, int enabled);
|
||||||
|
|
||||||
#ifdef NEED_CPU_H
|
#ifdef NEED_CPU_H
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
@@ -258,6 +260,7 @@ int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr,
|
|||||||
target_ulong len, int type);
|
target_ulong len, int type);
|
||||||
void kvm_remove_all_breakpoints(CPUState *cpu);
|
void kvm_remove_all_breakpoints(CPUState *cpu);
|
||||||
int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap);
|
int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap);
|
||||||
|
void kvm_set_singlestep(CPUState *cs, int enabled);
|
||||||
|
|
||||||
int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr);
|
int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr);
|
||||||
int kvm_on_sigbus(int code, void *addr);
|
int kvm_on_sigbus(int code, void *addr);
|
||||||
|
@@ -12,6 +12,7 @@ stub-obj-y += get-vm-name.o
|
|||||||
stub-obj-y += iothread.o
|
stub-obj-y += iothread.o
|
||||||
stub-obj-y += iothread-lock.o
|
stub-obj-y += iothread-lock.o
|
||||||
stub-obj-y += is-daemonized.o
|
stub-obj-y += is-daemonized.o
|
||||||
|
stub-obj-y += kvm-arch-set-singlestep.o
|
||||||
stub-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
stub-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||||
stub-obj-y += machine-init-done.o
|
stub-obj-y += machine-init-done.o
|
||||||
stub-obj-y += migr-blocker.o
|
stub-obj-y += migr-blocker.o
|
||||||
|
8
stubs/kvm-arch-set-singlestep.c
Normal file
8
stubs/kvm-arch-set-singlestep.c
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "sysemu/kvm.h"
|
||||||
|
|
||||||
|
void kvm_arch_set_singlestep(CPUState *cpu, int enabled)
|
||||||
|
{
|
||||||
|
warn_report("KVM does not support single stepping");
|
||||||
|
}
|
@@ -1171,6 +1171,11 @@ struct CPUPPCState {
|
|||||||
uint32_t tm_vscr;
|
uint32_t tm_vscr;
|
||||||
uint64_t tm_dscr;
|
uint64_t tm_dscr;
|
||||||
uint64_t tm_tar;
|
uint64_t tm_tar;
|
||||||
|
|
||||||
|
/* Used for software single step */
|
||||||
|
target_ulong sstep_msr;
|
||||||
|
target_ulong sstep_srr0;
|
||||||
|
target_ulong sstep_srr1;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SET_FIT_PERIOD(a_, b_, c_, d_) \
|
#define SET_FIT_PERIOD(a_, b_, c_, d_) \
|
||||||
@@ -1266,6 +1271,7 @@ struct PPCVirtualHypervisorClass {
|
|||||||
OBJECT_GET_CLASS(PPCVirtualHypervisorClass, (obj), \
|
OBJECT_GET_CLASS(PPCVirtualHypervisorClass, (obj), \
|
||||||
TYPE_PPC_VIRTUAL_HYPERVISOR)
|
TYPE_PPC_VIRTUAL_HYPERVISOR)
|
||||||
|
|
||||||
|
target_ulong ppc_get_trace_int_handler_addr(CPUState *cs);
|
||||||
void ppc_cpu_do_interrupt(CPUState *cpu);
|
void ppc_cpu_do_interrupt(CPUState *cpu);
|
||||||
bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
|
bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
|
||||||
void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
|
void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
|
||||||
@@ -1281,6 +1287,12 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg);
|
|||||||
void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu);
|
void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu);
|
||||||
const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name);
|
const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name);
|
||||||
#endif
|
#endif
|
||||||
|
uint32_t ppc_gdb_read_insn(CPUState *cs, target_ulong addr);
|
||||||
|
uint32_t ppc_gdb_get_op(uint32_t insn);
|
||||||
|
uint32_t ppc_gdb_get_xop(uint32_t insn);
|
||||||
|
uint32_t ppc_gdb_get_spr(uint32_t insn);
|
||||||
|
uint32_t ppc_gdb_get_rt(uint32_t insn);
|
||||||
|
|
||||||
int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
|
int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
|
||||||
int cpuid, void *opaque);
|
int cpuid, void *opaque);
|
||||||
int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
|
int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
|
||||||
@@ -2227,6 +2239,10 @@ enum {
|
|||||||
PPC2_ISA300)
|
PPC2_ISA300)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define XOP_RFID 18
|
||||||
|
#define XOP_MFMSR 83
|
||||||
|
#define XOP_MTSPR 467
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Memory access type :
|
/* Memory access type :
|
||||||
* may be needed for precise access rights control and precise exceptions.
|
* may be needed for precise access rights control and precise exceptions.
|
||||||
|
@@ -107,6 +107,26 @@ static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
|
|||||||
return POWERPC_EXCP_RESET;
|
return POWERPC_EXCP_RESET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail)
|
||||||
|
{
|
||||||
|
uint64_t offset = 0;
|
||||||
|
|
||||||
|
switch (ail) {
|
||||||
|
case AIL_NONE:
|
||||||
|
break;
|
||||||
|
case AIL_0001_8000:
|
||||||
|
offset = 0x18000;
|
||||||
|
break;
|
||||||
|
case AIL_C000_0000_0000_4000:
|
||||||
|
offset = 0xc000000000004000ull;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cpu_abort(cs, "Invalid AIL combination %d\n", ail);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
/* Note that this function should be greatly optimized
|
/* Note that this function should be greatly optimized
|
||||||
* when called with a constant excp, from ppc_hw_interrupt
|
* when called with a constant excp, from ppc_hw_interrupt
|
||||||
@@ -708,17 +728,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
|||||||
/* Handle AIL */
|
/* Handle AIL */
|
||||||
if (ail) {
|
if (ail) {
|
||||||
new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
|
new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
|
||||||
switch(ail) {
|
vector |= ppc_excp_vector_offset(cs, ail);
|
||||||
case AIL_0001_8000:
|
|
||||||
vector |= 0x18000;
|
|
||||||
break;
|
|
||||||
case AIL_C000_0000_0000_4000:
|
|
||||||
vector |= 0xc000000000004000ull;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
cpu_abort(cs, "Invalid AIL combination %d\n", ail);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(TARGET_PPC64)
|
#if defined(TARGET_PPC64)
|
||||||
@@ -760,6 +770,17 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
|||||||
check_tlb_flush(env, false);
|
check_tlb_flush(env, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target_ulong ppc_get_trace_int_handler_addr(CPUState *cs)
|
||||||
|
{
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
CPUPPCState *env = &cpu->env;
|
||||||
|
int ail;
|
||||||
|
|
||||||
|
ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
|
||||||
|
return env->excp_vectors[POWERPC_EXCP_TRACE] |
|
||||||
|
ppc_excp_vector_offset(cs, ail);
|
||||||
|
}
|
||||||
|
|
||||||
void ppc_cpu_do_interrupt(CPUState *cs)
|
void ppc_cpu_do_interrupt(CPUState *cs)
|
||||||
{
|
{
|
||||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
@@ -380,3 +380,38 @@ const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
uint32_t ppc_gdb_read_insn(CPUState *cs, target_ulong addr)
|
||||||
|
{
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
CPUPPCState *env = &cpu->env;
|
||||||
|
uint32_t insn;
|
||||||
|
|
||||||
|
cpu_memory_rw_debug(cs, addr, (uint8_t *)&insn, sizeof(insn), 0);
|
||||||
|
|
||||||
|
if (msr_le) {
|
||||||
|
return ldl_le_p(&insn);
|
||||||
|
} else {
|
||||||
|
return ldl_be_p(&insn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ppc_gdb_get_op(uint32_t insn)
|
||||||
|
{
|
||||||
|
return extract32(insn, 26, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ppc_gdb_get_xop(uint32_t insn)
|
||||||
|
{
|
||||||
|
return extract32(insn, 1, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ppc_gdb_get_spr(uint32_t insn)
|
||||||
|
{
|
||||||
|
return extract32(insn, 11, 5) << 5 | extract32(insn, 16, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ppc_gdb_get_rt(uint32_t insn)
|
||||||
|
{
|
||||||
|
return extract32(insn, 21, 5);
|
||||||
|
}
|
||||||
|
314
target/ppc/kvm.c
314
target/ppc/kvm.c
@@ -1554,6 +1554,86 @@ void kvm_arch_remove_all_hw_breakpoints(void)
|
|||||||
nb_hw_breakpoint = nb_hw_watchpoint = 0;
|
nb_hw_breakpoint = nb_hw_watchpoint = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_arch_set_singlestep(CPUState *cs, int enabled)
|
||||||
|
{
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
CPUPPCState *env = &cpu->env;
|
||||||
|
target_ulong trace_handler_addr;
|
||||||
|
uint32_t insn;
|
||||||
|
bool rfid;
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_synchronize_state(cs);
|
||||||
|
insn = ppc_gdb_read_insn(cs, env->nip);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rfid needs special handling because it:
|
||||||
|
* - overwrites NIP with SRR0;
|
||||||
|
* - overwrites MSR with SRR1;
|
||||||
|
* - cannot be single stepped.
|
||||||
|
*/
|
||||||
|
rfid = ppc_gdb_get_op(insn) == 19 && ppc_gdb_get_xop(insn) == XOP_RFID;
|
||||||
|
|
||||||
|
if (rfid && kvm_find_sw_breakpoint(cs, env->spr[SPR_SRR0])) {
|
||||||
|
/*
|
||||||
|
* There is a breakpoint at the next instruction address. It
|
||||||
|
* will already cause the vm exit we need for the single step,
|
||||||
|
* so there's nothing to be done.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save the registers that will be affected by the single step
|
||||||
|
* mechanism. These will be restored after the step at
|
||||||
|
* kvm_handle_singlestep.
|
||||||
|
*/
|
||||||
|
env->sstep_msr = env->msr;
|
||||||
|
env->sstep_srr0 = env->spr[SPR_SRR0];
|
||||||
|
env->sstep_srr1 = env->spr[SPR_SRR1];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MSR_SE = 1 will cause a Trace Interrupt in the guest after the
|
||||||
|
* next instruction executes. If this is a rfid, use SRR1 instead
|
||||||
|
* of MSR.
|
||||||
|
*/
|
||||||
|
if (rfid) {
|
||||||
|
if ((env->spr[SPR_SRR1] >> MSR_SE) & 1) {
|
||||||
|
/*
|
||||||
|
* The guest is doing a single step itself. Make sure we
|
||||||
|
* restore it later.
|
||||||
|
*/
|
||||||
|
env->sstep_msr |= (1ULL << MSR_SE);
|
||||||
|
}
|
||||||
|
|
||||||
|
env->spr[SPR_SRR1] |= (1ULL << MSR_SE);
|
||||||
|
} else {
|
||||||
|
env->msr |= (1ULL << MSR_SE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We set a breakpoint at the interrupt handler address so
|
||||||
|
* that the singlestep will be seen by KVM (this is treated by
|
||||||
|
* KVM like an ordinary breakpoint) and control is returned to
|
||||||
|
* QEMU.
|
||||||
|
*/
|
||||||
|
trace_handler_addr = ppc_get_trace_int_handler_addr(cs);
|
||||||
|
|
||||||
|
if (env->nip == trace_handler_addr) {
|
||||||
|
/*
|
||||||
|
* We are trying to step over the interrupt handler
|
||||||
|
* address itself; move the breakpoint to the next
|
||||||
|
* instruction.
|
||||||
|
*/
|
||||||
|
trace_handler_addr += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm_insert_breakpoint(cs, trace_handler_addr, 4, GDB_BREAKPOINT_SW);
|
||||||
|
}
|
||||||
|
|
||||||
void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg)
|
void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg)
|
||||||
{
|
{
|
||||||
int n;
|
int n;
|
||||||
@@ -1593,70 +1673,194 @@ void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Revert any side-effects caused during single step */
|
||||||
|
static void restore_singlestep_env(CPUState *cs)
|
||||||
|
{
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
CPUPPCState *env = &cpu->env;
|
||||||
|
uint32_t insn;
|
||||||
|
int reg;
|
||||||
|
int spr;
|
||||||
|
|
||||||
|
insn = ppc_gdb_read_insn(cs, env->spr[SPR_SRR0] - 4);
|
||||||
|
|
||||||
|
env->spr[SPR_SRR0] = env->sstep_srr0;
|
||||||
|
env->spr[SPR_SRR1] = env->sstep_srr1;
|
||||||
|
|
||||||
|
if (ppc_gdb_get_op(insn) != 31) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = ppc_gdb_get_rt(insn);
|
||||||
|
|
||||||
|
switch (ppc_gdb_get_xop(insn)) {
|
||||||
|
case XOP_MTSPR:
|
||||||
|
/*
|
||||||
|
* mtspr: the guest altered the SRR, so do not use the
|
||||||
|
* pre-step value.
|
||||||
|
*/
|
||||||
|
spr = ppc_gdb_get_spr(insn);
|
||||||
|
if (spr == SPR_SRR0 || spr == SPR_SRR1) {
|
||||||
|
env->spr[spr] = env->gpr[reg];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case XOP_MFMSR:
|
||||||
|
/*
|
||||||
|
* mfmsr: clear MSR_SE bit to avoid the guest knowing
|
||||||
|
* that it is being single-stepped.
|
||||||
|
*/
|
||||||
|
env->gpr[reg] &= ~(1ULL << MSR_SE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_handle_singlestep(CPUState *cs, target_ulong address)
|
||||||
|
{
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
CPUPPCState *env = &cpu->env;
|
||||||
|
target_ulong trace_handler_addr;
|
||||||
|
|
||||||
|
if (kvm_has_guestdbg_singlestep()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_synchronize_state(cs);
|
||||||
|
trace_handler_addr = ppc_get_trace_int_handler_addr(cs);
|
||||||
|
|
||||||
|
if (address == trace_handler_addr) {
|
||||||
|
kvm_remove_breakpoint(cs, trace_handler_addr, 4, GDB_BREAKPOINT_SW);
|
||||||
|
|
||||||
|
if (env->sstep_msr & (1ULL << MSR_SE)) {
|
||||||
|
/*
|
||||||
|
* The guest expects the last instruction to have caused a
|
||||||
|
* single step, go back into the interrupt handler.
|
||||||
|
*/
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->nip = env->spr[SPR_SRR0];
|
||||||
|
/* Bits 33-36, 43-47 are set by the interrupt */
|
||||||
|
env->msr = env->spr[SPR_SRR1] & ~(1ULL << MSR_SE |
|
||||||
|
PPC_BITMASK(33, 36) |
|
||||||
|
PPC_BITMASK(43, 47));
|
||||||
|
restore_singlestep_env(cs);
|
||||||
|
|
||||||
|
} else if (address == trace_handler_addr + 4) {
|
||||||
|
/*
|
||||||
|
* A step at trace_handler_addr would interfere with the
|
||||||
|
* singlestep mechanism itself, so we have previously
|
||||||
|
* displaced the breakpoint to the next instruction.
|
||||||
|
*/
|
||||||
|
kvm_remove_breakpoint(cs, trace_handler_addr + 4, 4, GDB_BREAKPOINT_SW);
|
||||||
|
restore_singlestep_env(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_handle_hw_breakpoint(CPUState *cs,
|
||||||
|
struct kvm_debug_exit_arch *arch_info)
|
||||||
|
{
|
||||||
|
int handle = 0;
|
||||||
|
int n;
|
||||||
|
int flag = 0;
|
||||||
|
|
||||||
|
if (nb_hw_breakpoint + nb_hw_watchpoint > 0) {
|
||||||
|
if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) {
|
||||||
|
n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW);
|
||||||
|
if (n >= 0) {
|
||||||
|
handle = 1;
|
||||||
|
}
|
||||||
|
} else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ |
|
||||||
|
KVMPPC_DEBUG_WATCH_WRITE)) {
|
||||||
|
n = find_hw_watchpoint(arch_info->address, &flag);
|
||||||
|
if (n >= 0) {
|
||||||
|
handle = 1;
|
||||||
|
cs->watchpoint_hit = &hw_watchpoint;
|
||||||
|
hw_watchpoint.vaddr = hw_debug_points[n].addr;
|
||||||
|
hw_watchpoint.flags = flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_handle_sw_breakpoint(CPUState *cs, target_ulong address)
|
||||||
|
{
|
||||||
|
target_ulong trace_handler_addr;
|
||||||
|
|
||||||
|
if (kvm_has_guestdbg_singlestep()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_synchronize_state(cs);
|
||||||
|
trace_handler_addr = ppc_get_trace_int_handler_addr(cs);
|
||||||
|
|
||||||
|
if (address == trace_handler_addr) {
|
||||||
|
CPU_FOREACH(cs) {
|
||||||
|
if (cs->singlestep_enabled) {
|
||||||
|
/*
|
||||||
|
* We hit this breakpoint while another cpu is doing a
|
||||||
|
* software single step. Go back into the guest to
|
||||||
|
* give chance for the single step to finish.
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run)
|
static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
CPUState *cs = CPU(cpu);
|
||||||
CPUPPCState *env = &cpu->env;
|
CPUPPCState *env = &cpu->env;
|
||||||
struct kvm_debug_exit_arch *arch_info = &run->debug.arch;
|
struct kvm_debug_exit_arch *arch_info = &run->debug.arch;
|
||||||
int handle = 0;
|
|
||||||
int n;
|
|
||||||
int flag = 0;
|
|
||||||
|
|
||||||
if (cs->singlestep_enabled) {
|
if (cs->singlestep_enabled) {
|
||||||
handle = 1;
|
return kvm_handle_singlestep(cs, arch_info->address);
|
||||||
} else if (arch_info->status) {
|
|
||||||
if (nb_hw_breakpoint + nb_hw_watchpoint > 0) {
|
|
||||||
if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) {
|
|
||||||
n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW);
|
|
||||||
if (n >= 0) {
|
|
||||||
handle = 1;
|
|
||||||
}
|
|
||||||
} else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ |
|
|
||||||
KVMPPC_DEBUG_WATCH_WRITE)) {
|
|
||||||
n = find_hw_watchpoint(arch_info->address, &flag);
|
|
||||||
if (n >= 0) {
|
|
||||||
handle = 1;
|
|
||||||
cs->watchpoint_hit = &hw_watchpoint;
|
|
||||||
hw_watchpoint.vaddr = hw_debug_points[n].addr;
|
|
||||||
hw_watchpoint.flags = flag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (kvm_find_sw_breakpoint(cs, arch_info->address)) {
|
|
||||||
handle = 1;
|
|
||||||
} else {
|
|
||||||
/* QEMU is not able to handle debug exception, so inject
|
|
||||||
* program exception to guest;
|
|
||||||
* Yes program exception NOT debug exception !!
|
|
||||||
* When QEMU is using debug resources then debug exception must
|
|
||||||
* be always set. To achieve this we set MSR_DE and also set
|
|
||||||
* MSRP_DEP so guest cannot change MSR_DE.
|
|
||||||
* When emulating debug resource for guest we want guest
|
|
||||||
* to control MSR_DE (enable/disable debug interrupt on need).
|
|
||||||
* Supporting both configurations are NOT possible.
|
|
||||||
* So the result is that we cannot share debug resources
|
|
||||||
* between QEMU and Guest on BOOKE architecture.
|
|
||||||
* In the current design QEMU gets the priority over guest,
|
|
||||||
* this means that if QEMU is using debug resources then guest
|
|
||||||
* cannot use them;
|
|
||||||
* For software breakpoint QEMU uses a privileged instruction;
|
|
||||||
* So there cannot be any reason that we are here for guest
|
|
||||||
* set debug exception, only possibility is guest executed a
|
|
||||||
* privileged / illegal instruction and that's why we are
|
|
||||||
* injecting a program interrupt.
|
|
||||||
*/
|
|
||||||
|
|
||||||
cpu_synchronize_state(cs);
|
|
||||||
/* env->nip is PC, so increment this by 4 to use
|
|
||||||
* ppc_cpu_do_interrupt(), which set srr0 = env->nip - 4.
|
|
||||||
*/
|
|
||||||
env->nip += 4;
|
|
||||||
cs->exception_index = POWERPC_EXCP_PROGRAM;
|
|
||||||
env->error_code = POWERPC_EXCP_INVAL;
|
|
||||||
ppc_cpu_do_interrupt(cs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return handle;
|
if (arch_info->status) {
|
||||||
|
return kvm_handle_hw_breakpoint(cs, arch_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kvm_find_sw_breakpoint(cs, arch_info->address)) {
|
||||||
|
return kvm_handle_sw_breakpoint(cs, arch_info->address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QEMU is not able to handle debug exception, so inject
|
||||||
|
* program exception to guest;
|
||||||
|
* Yes program exception NOT debug exception !!
|
||||||
|
* When QEMU is using debug resources then debug exception must
|
||||||
|
* be always set. To achieve this we set MSR_DE and also set
|
||||||
|
* MSRP_DEP so guest cannot change MSR_DE.
|
||||||
|
* When emulating debug resource for guest we want guest
|
||||||
|
* to control MSR_DE (enable/disable debug interrupt on need).
|
||||||
|
* Supporting both configurations are NOT possible.
|
||||||
|
* So the result is that we cannot share debug resources
|
||||||
|
* between QEMU and Guest on BOOKE architecture.
|
||||||
|
* In the current design QEMU gets the priority over guest,
|
||||||
|
* this means that if QEMU is using debug resources then guest
|
||||||
|
* cannot use them;
|
||||||
|
* For software breakpoint QEMU uses a privileged instruction;
|
||||||
|
* So there cannot be any reason that we are here for guest
|
||||||
|
* set debug exception, only possibility is guest executed a
|
||||||
|
* privileged / illegal instruction and that's why we are
|
||||||
|
* injecting a program interrupt.
|
||||||
|
*/
|
||||||
|
cpu_synchronize_state(cs);
|
||||||
|
/*
|
||||||
|
* env->nip is PC, so increment this by 4 to use
|
||||||
|
* ppc_cpu_do_interrupt(), which set srr0 = env->nip - 4.
|
||||||
|
*/
|
||||||
|
env->nip += 4;
|
||||||
|
cs->exception_index = POWERPC_EXCP_PROGRAM;
|
||||||
|
env->error_code = POWERPC_EXCP_INVAL;
|
||||||
|
ppc_cpu_do_interrupt(cs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
||||||
|
Reference in New Issue
Block a user