aboutsummaryrefslogtreecommitdiff
path: root/target-s390x
diff options
context:
space:
mode:
Diffstat (limited to 'target-s390x')
-rw-r--r--target-s390x/cpu.h2
-rw-r--r--target-s390x/kvm.c50
2 files changed, 42 insertions, 10 deletions
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index c216bdacef..eeff1b0081 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -394,6 +394,8 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc,
((env->psw.mask & PSW_MASK_32) ? FLAG_MASK_32 : 0);
}
+#define MAX_ILEN 6
+
/* While the PoO talks about ILC (a number between 1-3) what is actually
stored in LowCore is shifted left one bit (an even between 2-6). As
this is the actual length of the insn and therefore more useful, that
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 80ac6215fb..4341d54969 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -109,6 +109,7 @@
#define ICPT_WAITPSW 0x1c
#define ICPT_SOFT_INTERCEPT 0x24
#define ICPT_CPU_STOP 0x28
+#define ICPT_OPEREXC 0x2c
#define ICPT_IO 0x40
#define NR_LOCAL_IRQS 32
@@ -665,16 +666,37 @@ static void *legacy_s390_alloc(size_t size, uint64_t *align)
return mem == MAP_FAILED ? NULL : mem;
}
-/* DIAG 501 is used for sw breakpoints */
-static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
+static uint8_t const *sw_bp_inst;
+static uint8_t sw_bp_ilen;
+
+static void determine_sw_breakpoint_instr(void)
+{
+ /* DIAG 501 is used for sw breakpoints with old kernels */
+ static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
+ /* Instruction 0x0000 is used for sw breakpoints with recent kernels */
+ static const uint8_t instr_0x0000[] = {0x00, 0x00};
+
+ if (sw_bp_inst) {
+ return;
+ }
+ if (kvm_vm_enable_cap(kvm_state, KVM_CAP_S390_USER_INSTR0, 0)) {
+ sw_bp_inst = diag_501;
+ sw_bp_ilen = sizeof(diag_501);
+ DPRINTF("KVM: will use 4-byte sw breakpoints.\n");
+ } else {
+ sw_bp_inst = instr_0x0000;
+ sw_bp_ilen = sizeof(instr_0x0000);
+ DPRINTF("KVM: will use 2-byte sw breakpoints.\n");
+ }
+}
int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
{
+ determine_sw_breakpoint_instr();
if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn,
- sizeof(diag_501), 0) ||
- cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)diag_501,
- sizeof(diag_501), 1)) {
+ sw_bp_ilen, 0) ||
+ cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)sw_bp_inst, sw_bp_ilen, 1)) {
return -EINVAL;
}
return 0;
@@ -682,14 +704,14 @@ int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
{
- uint8_t t[sizeof(diag_501)];
+ uint8_t t[MAX_ILEN];
- if (cpu_memory_rw_debug(cs, bp->pc, t, sizeof(diag_501), 0)) {
+ if (cpu_memory_rw_debug(cs, bp->pc, t, sw_bp_ilen, 0)) {
return -EINVAL;
- } else if (memcmp(t, diag_501, sizeof(diag_501))) {
+ } else if (memcmp(t, sw_bp_inst, sw_bp_ilen)) {
return -EINVAL;
} else if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn,
- sizeof(diag_501), 1)) {
+ sw_bp_ilen, 1)) {
return -EINVAL;
}
@@ -1310,7 +1332,7 @@ static int handle_sw_breakpoint(S390CPU *cpu, struct kvm_run *run)
cpu_synchronize_state(CPU(cpu));
- pc = env->psw.addr - 4;
+ pc = env->psw.addr - sw_bp_ilen;
if (kvm_find_sw_breakpoint(CPU(cpu), pc)) {
env->psw.addr = pc;
return EXCP_DEBUG;
@@ -1864,6 +1886,14 @@ static int handle_intercept(S390CPU *cpu)
cpu->env.sigp_order = 0;
r = EXCP_HALTED;
break;
+ case ICPT_OPEREXC:
+ /* currently only instr 0x0000 after enabled via capability */
+ r = handle_sw_breakpoint(cpu, run);
+ if (r == -ENOENT) {
+ enter_pgmcheck(cpu, PGM_OPERATION);
+ r = 0;
+ }
+ break;
case ICPT_SOFT_INTERCEPT:
fprintf(stderr, "KVM unimplemented icpt SOFT\n");
exit(1);