Compare commits

...

5 Commits

Author SHA1 Message Date
Fabiano Rosas
34370ee6ff target/ppc: support single stepping with KVM HV
The hardware singlestep mechanism in POWER works via a Trace Interrupt
(0xd00) that happens after any instruction executes, whenever MSR_SE =
1 (PowerISA Section 6.5.15 - Trace Interrupt).

However, with kvm_hv, the Trace Interrupt happens inside the guest and
KVM has no visibility of it. Therefore, when the gdbstub uses the
KVM_SET_GUEST_DEBUG ioctl to enable singlestep, KVM simply ignores it.

This patch takes advantage of the Trace Interrupt to perform the step
inside the guest, but uses a breakpoint at the Trace Interrupt handler
to return control to KVM. The exit is treated by KVM as a regular
breakpoint and it returns to the host (and QEMU eventually).

Before signalling GDB, QEMU sets the Next Instruction Pointer to the
instruction following the one being stepped and restores the MSR,
SRR0, SRR1 values from before the step, effectively skipping the
interrupt handler execution and hiding the trace interrupt breakpoint
from GDB.

This approach works with both of GDB's 'scheduler-locking' options
(off, step).

Note:

- kvm_arch_set_singlestep happens after GDB asks for a single step,
  while the vcpus are stopped.

- kvm_handle_singlestep executes after the step, during the handling
  of the Emulation Assist Interrupt (breakpoint).

Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
2019-02-28 16:12:49 -03:00
Fabiano Rosas
9953b32e77 target/ppc: Refactor kvm_handle_debug
There are four scenarios being handled in this function:

- single stepping
- hardware breakpoints
- software breakpoints
- fallback (no debug supported)

A future patch will add code to handle specific single step and
software breakpoints cases so let's split each scenario into its own
function now to avoid hurting readability.

Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru>
2019-02-28 16:12:39 -03:00
Fabiano Rosas
0da2fd84e1 target/ppc: Move handling of hardware breakpoints to a separate function
This is in preparation for a refactoring of the kvm_handle_debug
function in the next patch.

Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
2019-02-28 16:12:37 -03:00
Fabiano Rosas
b82417e8bf kvm-all: Introduce kvm_set_singlestep
For single stepping (via KVM) of a guest vcpu to work, KVM needs not
only to support the SET_GUEST_DEBUG ioctl but to also recognize the
KVM_GUESTDBG_SINGLESTEP bit in the control field of the
kvm_guest_debug struct.

This patch adds support for querying the single step capability so
that QEMU can decide what to do for the platforms that do not have
such support.

This will allow architecture-specific implementations of a fallback
mechanism for single stepping in cases where KVM does not support it.

Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
2019-02-28 16:10:58 -03:00
Fabiano Rosas
0b72719af8 target/ppc: Move exception vector offset computation into a function
Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru>
2019-02-28 16:10:29 -03:00
10 changed files with 375 additions and 67 deletions

View File

@@ -2267,6 +2267,13 @@ bool kvm_arm_supports_user_irq(void)
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
struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu,
target_ulong pc)
@@ -2316,6 +2323,15 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
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,
target_ulong len, int type)
{

View File

@@ -79,6 +79,10 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
return -ENOSYS;
}
void kvm_set_singlestep(CPUState *cs, int enabled)
{
}
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
target_ulong len, int type)
{

2
exec.c
View File

@@ -1236,7 +1236,7 @@ void cpu_single_step(CPUState *cpu, int enabled)
if (cpu->singlestep_enabled != enabled) {
cpu->singlestep_enabled = enabled;
if (kvm_enabled()) {
kvm_update_guest_debug(cpu, 0);
kvm_set_singlestep(cpu, enabled);
} else {
/* must flush all the translated code to avoid inconsistencies */
/* XXX: only flush what is necessary */

View File

@@ -214,6 +214,7 @@ int kvm_has_pit_state2(void);
int kvm_has_many_ioeventfds(void);
int kvm_has_gsi_routing(void);
int kvm_has_intx_set_mask(void);
int kvm_has_guestdbg_singlestep(void);
int kvm_init_vcpu(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);
void kvm_arch_set_singlestep(CPUState *cpu, int enabled);
#ifdef NEED_CPU_H
#include "cpu.h"
@@ -258,6 +260,7 @@ int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr,
target_ulong len, int type);
void kvm_remove_all_breakpoints(CPUState *cpu);
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(int code, void *addr);

View File

@@ -12,6 +12,7 @@ stub-obj-y += get-vm-name.o
stub-obj-y += iothread.o
stub-obj-y += iothread-lock.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-y += machine-init-done.o
stub-obj-y += migr-blocker.o

View 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");
}

View File

@@ -1171,6 +1171,11 @@ struct CPUPPCState {
uint32_t tm_vscr;
uint64_t tm_dscr;
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_) \
@@ -1266,6 +1271,7 @@ struct PPCVirtualHypervisorClass {
OBJECT_GET_CLASS(PPCVirtualHypervisorClass, (obj), \
TYPE_PPC_VIRTUAL_HYPERVISOR)
target_ulong ppc_get_trace_int_handler_addr(CPUState *cs);
void ppc_cpu_do_interrupt(CPUState *cpu);
bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
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);
const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name);
#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 cpuid, void *opaque);
int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
@@ -2227,6 +2239,10 @@ enum {
PPC2_ISA300)
};
#define XOP_RFID 18
#define XOP_MFMSR 83
#define XOP_MTSPR 467
/*****************************************************************************/
/* Memory access type :
* may be needed for precise access rights control and precise exceptions.

View File

@@ -107,6 +107,26 @@ static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
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
* 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 */
if (ail) {
new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
switch(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;
}
vector |= ppc_excp_vector_offset(cs, ail);
}
#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);
}
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)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);

View File

@@ -380,3 +380,38 @@ const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name)
return NULL;
}
#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);
}

View File

@@ -1554,6 +1554,86 @@ void kvm_arch_remove_all_hw_breakpoints(void)
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)
{
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)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
struct kvm_debug_exit_arch *arch_info = &run->debug.arch;
int handle = 0;
int n;
int flag = 0;
if (cs->singlestep_enabled) {
handle = 1;
} 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 kvm_handle_singlestep(cs, arch_info->address);
}
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)