]> git.proxmox.com Git - mirror_qemu.git/blobdiff - target-s390x/kvm.c
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2016-10-25' into staging
[mirror_qemu.git] / target-s390x / kvm.c
index 0305ffa9d3c1e8af6dabdc3564ceac79b358372b..7f745726bd44b13c0b2fcafed466bcc67a158589 100644 (file)
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <sys/types.h>
+#include "qemu/osdep.h"
 #include <sys/ioctl.h>
-#include <sys/mman.h>
 
 #include <linux/kvm.h>
 #include <asm/ptrace.h>
 
 #include "qemu-common.h"
+#include "cpu.h"
 #include "qemu/error-report.h"
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/kvm.h"
 #include "hw/hw.h"
-#include "cpu.h"
 #include "sysemu/device_tree.h"
 #include "qapi/qmp/qjson.h"
 #include "exec/gdbstub.h"
@@ -46,6 +45,7 @@
 #include "hw/s390x/ipl.h"
 #include "hw/s390x/ebcdic.h"
 #include "exec/memattrs.h"
+#include "hw/s390x/s390-virtio-ccw.h"
 
 /* #define DEBUG_KVM */
 
 #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
@@ -131,10 +132,13 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
     KVM_CAP_LAST_INFO
 };
 
+static QemuMutex qemu_sigp_mutex;
+
 static int cap_sync_regs;
 static int cap_async_pf;
 static int cap_mem_op;
 static int cap_s390_irq;
+static int cap_ri;
 
 static void *legacy_s390_alloc(size_t size, uint64_t *align);
 
@@ -173,20 +177,35 @@ int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, uint64_t *hw_limit)
     return kvm_vm_ioctl(s, KVM_SET_DEVICE_ATTR, &attr);
 }
 
-void kvm_s390_clear_cmma_callback(void *opaque)
+static bool kvm_s390_cmma_available(void)
+{
+    static bool initialized, value;
+
+    if (!initialized) {
+        initialized = true;
+        value = kvm_vm_check_mem_attr(kvm_state, KVM_S390_VM_MEM_ENABLE_CMMA) &&
+                kvm_vm_check_mem_attr(kvm_state, KVM_S390_VM_MEM_CLR_CMMA);
+    }
+    return value;
+}
+
+void kvm_s390_cmma_reset(void)
 {
     int rc;
-    KVMState *s = opaque;
     struct kvm_device_attr attr = {
         .group = KVM_S390_VM_MEM_CTRL,
         .attr = KVM_S390_VM_MEM_CLR_CMMA,
     };
 
-    rc = kvm_vm_ioctl(s, KVM_SET_DEVICE_ATTR, &attr);
+    if (!mem_path || !kvm_s390_cmma_available()) {
+        return;
+    }
+
+    rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr);
     trace_kvm_clear_cmma(rc);
 }
 
-static void kvm_s390_enable_cmma(KVMState *s)
+static void kvm_s390_enable_cmma(void)
 {
     int rc;
     struct kvm_device_attr attr = {
@@ -194,15 +213,7 @@ static void kvm_s390_enable_cmma(KVMState *s)
         .attr = KVM_S390_VM_MEM_ENABLE_CMMA,
     };
 
-    if (!kvm_vm_check_mem_attr(s, KVM_S390_VM_MEM_ENABLE_CMMA) ||
-        !kvm_vm_check_mem_attr(s, KVM_S390_VM_MEM_CLR_CMMA)) {
-        return;
-    }
-
-    rc = kvm_vm_ioctl(s, KVM_SET_DEVICE_ATTR, &attr);
-    if (!rc) {
-        qemu_register_reset(kvm_s390_clear_cmma_callback, s);
-    }
+    rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr);
     trace_kvm_enable_cmma(rc);
 }
 
@@ -249,10 +260,12 @@ static void kvm_s390_init_dea_kw(void)
     }
 }
 
-static void kvm_s390_init_crypto(void)
+void kvm_s390_crypto_reset(void)
 {
-    kvm_s390_init_aes_kw();
-    kvm_s390_init_dea_kw();
+    if (s390_has_feat(S390_FEAT_MSA_EXT_3)) {
+        kvm_s390_init_aes_kw();
+        kvm_s390_init_dea_kw();
+    }
 }
 
 int kvm_arch_init(MachineState *ms, KVMState *s)
@@ -262,8 +275,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
     cap_mem_op = kvm_check_extension(s, KVM_CAP_S390_MEM_OP);
     cap_s390_irq = kvm_check_extension(s, KVM_CAP_S390_INJECT_IRQ);
 
-    kvm_s390_enable_cmma(s);
-
     if (!kvm_check_extension(s, KVM_CAP_S390_GMAP)
         || !kvm_check_extension(s, KVM_CAP_S390_COW)) {
         phys_mem_set_alloc(legacy_s390_alloc);
@@ -272,6 +283,13 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
     kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0);
     kvm_vm_enable_cap(s, KVM_CAP_S390_VECTOR_REGISTERS, 0);
     kvm_vm_enable_cap(s, KVM_CAP_S390_USER_STSI, 0);
+    if (ri_allowed()) {
+        if (kvm_vm_enable_cap(s, KVM_CAP_S390_RI, 0) == 0) {
+            cap_ri = 1;
+        }
+    }
+
+    qemu_mutex_init(&qemu_sigp_mutex);
 
     return 0;
 }
@@ -301,8 +319,6 @@ void kvm_s390_reset_vcpu(S390CPU *cpu)
     if (kvm_vcpu_ioctl(cs, KVM_S390_INITIAL_RESET, NULL)) {
         error_report("Initial CPU reset failed on CPU %i", cs->cpu_index);
     }
-
-    kvm_s390_init_crypto();
 }
 
 static int can_sync_regs(CPUState *cs, int regs)
@@ -346,6 +362,12 @@ int kvm_arch_put_registers(CPUState *cs, int level)
         }
         cs->kvm_run->s.regs.fpc = env->fpc;
         cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_VRS;
+    } else if (can_sync_regs(cs, KVM_SYNC_FPRS)) {
+        for (i = 0; i < 16; i++) {
+            cs->kvm_run->s.regs.fprs[i] = get_freg(env, i)->ll;
+        }
+        cs->kvm_run->s.regs.fpc = env->fpc;
+        cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_FPRS;
     } else {
         /* Floating point */
         for (i = 0; i < 16; i++) {
@@ -384,6 +406,11 @@ int kvm_arch_put_registers(CPUState *cs, int level)
         kvm_set_one_reg(cs, KVM_REG_S390_PP, &env->pp);
     }
 
+    if (can_sync_regs(cs, KVM_SYNC_RICCB)) {
+        memcpy(cs->kvm_run->s.regs.riccb, env->riccb, 64);
+        cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_RICCB;
+    }
+
     /* pfault parameters */
     if (can_sync_regs(cs, KVM_SYNC_PFAULT)) {
         cs->kvm_run->s.regs.pft = env->pfault_token;
@@ -486,6 +513,11 @@ int kvm_arch_get_registers(CPUState *cs)
             env->vregs[i][1].ll = cs->kvm_run->s.regs.vrs[i][1];
         }
         env->fpc = cs->kvm_run->s.regs.fpc;
+    } else if (can_sync_regs(cs, KVM_SYNC_FPRS)) {
+        for (i = 0; i < 16; i++) {
+            get_freg(env, i)->ll = cs->kvm_run->s.regs.fprs[i];
+        }
+        env->fpc = cs->kvm_run->s.regs.fpc;
     } else {
         r = kvm_vcpu_ioctl(cs, KVM_GET_FPU, &fpu);
         if (r < 0) {
@@ -521,6 +553,10 @@ int kvm_arch_get_registers(CPUState *cs)
         kvm_get_one_reg(cs, KVM_REG_S390_PP, &env->pp);
     }
 
+    if (can_sync_regs(cs, KVM_SYNC_RICCB)) {
+        memcpy(env->riccb, cs->kvm_run->s.regs.riccb, 64);
+    }
+
     /* pfault parameters */
     if (can_sync_regs(cs, KVM_SYNC_PFAULT)) {
         env->pfault_token = cs->kvm_run->s.regs.pft;
@@ -643,16 +679,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;
@@ -660,14 +717,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;
     }
 
@@ -929,17 +986,6 @@ void kvm_s390_floating_interrupt(struct kvm_s390_irq *irq)
     __kvm_s390_floating_interrupt(irq);
 }
 
-void kvm_s390_virtio_irq(int config_change, uint64_t token)
-{
-    struct kvm_s390_irq irq = {
-        .type = KVM_S390_INT_VIRTIO,
-        .u.ext.ext_params = config_change,
-        .u.ext.ext_params2 = token,
-    };
-
-    kvm_s390_floating_interrupt(&irq);
-}
-
 void kvm_s390_service_interrupt(uint32_t parm)
 {
     struct kvm_s390_irq irq = {
@@ -1299,7 +1345,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;
@@ -1343,7 +1389,6 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
 }
 
 typedef struct SigpInfo {
-    S390CPU *cpu;
     uint64_t param;
     int cc;
     uint64_t *status_reg;
@@ -1356,38 +1401,40 @@ static void set_sigp_status(SigpInfo *si, uint64_t status)
     si->cc = SIGP_CC_STATUS_STORED;
 }
 
-static void sigp_start(void *arg)
+static void sigp_start(CPUState *cs, void *arg)
 {
+    S390CPU *cpu = S390_CPU(cs);
     SigpInfo *si = arg;
 
-    if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) {
+    if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
         return;
     }
 
-    s390_cpu_set_state(CPU_STATE_OPERATING, si->cpu);
+    s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
-static void sigp_stop(void *arg)
+static void sigp_stop(CPUState *cs, void *arg)
 {
+    S390CPU *cpu = S390_CPU(cs);
     SigpInfo *si = arg;
     struct kvm_s390_irq irq = {
         .type = KVM_S390_SIGP_STOP,
     };
 
-    if (s390_cpu_get_state(si->cpu) != CPU_STATE_OPERATING) {
+    if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) {
         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
         return;
     }
 
     /* disabled wait - sleeping in user space */
-    if (CPU(si->cpu)->halted) {
-        s390_cpu_set_state(CPU_STATE_STOPPED, si->cpu);
+    if (cs->halted) {
+        s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
     } else {
         /* execute the stop function */
-        si->cpu->env.sigp_order = SIGP_STOP;
-        kvm_s390_vcpu_interrupt(si->cpu, &irq);
+        cpu->env.sigp_order = SIGP_STOP;
+        kvm_s390_vcpu_interrupt(cpu, &irq);
     }
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
 }
@@ -1437,7 +1484,7 @@ static int kvm_s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch)
         cpu_physical_memory_write(offsetof(LowCore, ar_access_id), &ar_id, 1);
     }
     for (i = 0; i < 16; ++i) {
-        *((uint64 *)mem + i) = get_freg(&cpu->env, i)->ll;
+        *((uint64_t *)mem + i) = get_freg(&cpu->env, i)->ll;
     }
     memcpy(mem + 128, &cpu->env.regs, 128);
     memcpy(mem + 256, &cpu->env.psw, 16);
@@ -1454,65 +1501,67 @@ static int kvm_s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch)
     return 0;
 }
 
-static void sigp_stop_and_store_status(void *arg)
+static void sigp_stop_and_store_status(CPUState *cs, void *arg)
 {
+    S390CPU *cpu = S390_CPU(cs);
     SigpInfo *si = arg;
     struct kvm_s390_irq irq = {
         .type = KVM_S390_SIGP_STOP,
     };
 
     /* disabled wait - sleeping in user space */
-    if (s390_cpu_get_state(si->cpu) == CPU_STATE_OPERATING &&
-        CPU(si->cpu)->halted) {
-        s390_cpu_set_state(CPU_STATE_STOPPED, si->cpu);
+    if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) {
+        s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
     }
 
-    switch (s390_cpu_get_state(si->cpu)) {
+    switch (s390_cpu_get_state(cpu)) {
     case CPU_STATE_OPERATING:
-        si->cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
-        kvm_s390_vcpu_interrupt(si->cpu, &irq);
+        cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
+        kvm_s390_vcpu_interrupt(cpu, &irq);
         /* store will be performed when handling the stop intercept */
         break;
     case CPU_STATE_STOPPED:
         /* already stopped, just store the status */
-        cpu_synchronize_state(CPU(si->cpu));
-        kvm_s390_store_status(si->cpu, KVM_S390_STORE_STATUS_DEF_ADDR, true);
+        cpu_synchronize_state(cs);
+        kvm_s390_store_status(cpu, KVM_S390_STORE_STATUS_DEF_ADDR, true);
         break;
     }
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
-static void sigp_store_status_at_address(void *arg)
+static void sigp_store_status_at_address(CPUState *cs, void *arg)
 {
+    S390CPU *cpu = S390_CPU(cs);
     SigpInfo *si = arg;
     uint32_t address = si->param & 0x7ffffe00u;
 
     /* cpu has to be stopped */
-    if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) {
+    if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
         set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
         return;
     }
 
-    cpu_synchronize_state(CPU(si->cpu));
+    cpu_synchronize_state(cs);
 
-    if (kvm_s390_store_status(si->cpu, address, false)) {
+    if (kvm_s390_store_status(cpu, address, false)) {
         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
         return;
     }
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
-static void sigp_store_adtl_status(void *arg)
+static void sigp_store_adtl_status(CPUState *cs, void *arg)
 {
+    S390CPU *cpu = S390_CPU(cs);
     SigpInfo *si = arg;
 
-    if (!kvm_check_extension(kvm_state, KVM_CAP_S390_VECTOR_REGISTERS)) {
+    if (!s390_has_feat(S390_FEAT_VECTOR)) {
         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
         return;
     }
 
     /* cpu has to be stopped */
-    if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) {
+    if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
         set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
         return;
     }
@@ -1523,31 +1572,32 @@ static void sigp_store_adtl_status(void *arg)
         return;
     }
 
-    cpu_synchronize_state(CPU(si->cpu));
+    cpu_synchronize_state(cs);
 
-    if (kvm_s390_store_adtl_status(si->cpu, si->param)) {
+    if (kvm_s390_store_adtl_status(cpu, si->param)) {
         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
         return;
     }
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
-static void sigp_restart(void *arg)
+static void sigp_restart(CPUState *cs, void *arg)
 {
+    S390CPU *cpu = S390_CPU(cs);
     SigpInfo *si = arg;
     struct kvm_s390_irq irq = {
         .type = KVM_S390_RESTART,
     };
 
-    switch (s390_cpu_get_state(si->cpu)) {
+    switch (s390_cpu_get_state(cpu)) {
     case CPU_STATE_STOPPED:
         /* the restart irq has to be delivered prior to any other pending irq */
-        cpu_synchronize_state(CPU(si->cpu));
-        do_restart_interrupt(&si->cpu->env);
-        s390_cpu_set_state(CPU_STATE_OPERATING, si->cpu);
+        cpu_synchronize_state(cs);
+        do_restart_interrupt(&cpu->env);
+        s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
         break;
     case CPU_STATE_OPERATING:
-        kvm_s390_vcpu_interrupt(si->cpu, &irq);
+        kvm_s390_vcpu_interrupt(cpu, &irq);
         break;
     }
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
@@ -1555,20 +1605,18 @@ static void sigp_restart(void *arg)
 
 int kvm_s390_cpu_restart(S390CPU *cpu)
 {
-    SigpInfo si = {
-        .cpu = cpu,
-    };
+    SigpInfo si = {};
 
     run_on_cpu(CPU(cpu), sigp_restart, &si);
     DPRINTF("DONE: KVM cpu restart: %p\n", &cpu->env);
     return 0;
 }
 
-static void sigp_initial_cpu_reset(void *arg)
+static void sigp_initial_cpu_reset(CPUState *cs, void *arg)
 {
+    S390CPU *cpu = S390_CPU(cs);
+    S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
     SigpInfo *si = arg;
-    CPUState *cs = CPU(si->cpu);
-    S390CPUClass *scc = S390_CPU_GET_CLASS(si->cpu);
 
     cpu_synchronize_state(cs);
     scc->initial_cpu_reset(cs);
@@ -1576,11 +1624,11 @@ static void sigp_initial_cpu_reset(void *arg)
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
-static void sigp_cpu_reset(void *arg)
+static void sigp_cpu_reset(CPUState *cs, void *arg)
 {
+    S390CPU *cpu = S390_CPU(cs);
+    S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
     SigpInfo *si = arg;
-    CPUState *cs = CPU(si->cpu);
-    S390CPUClass *scc = S390_CPU_GET_CLASS(si->cpu);
 
     cpu_synchronize_state(cs);
     scc->cpu_reset(cs);
@@ -1588,12 +1636,13 @@ static void sigp_cpu_reset(void *arg)
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
-static void sigp_set_prefix(void *arg)
+static void sigp_set_prefix(CPUState *cs, void *arg)
 {
+    S390CPU *cpu = S390_CPU(cs);
     SigpInfo *si = arg;
     uint32_t addr = si->param & 0x7fffe000u;
 
-    cpu_synchronize_state(CPU(si->cpu));
+    cpu_synchronize_state(cs);
 
     if (!address_space_access_valid(&address_space_memory, addr,
                                     sizeof(struct LowCore), false)) {
@@ -1602,13 +1651,13 @@ static void sigp_set_prefix(void *arg)
     }
 
     /* cpu has to be stopped */
-    if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) {
+    if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
         set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
         return;
     }
 
-    si->cpu->env.psa = addr;
-    cpu_synchronize_post_init(CPU(si->cpu));
+    cpu->env.psa = addr;
+    cpu_synchronize_post_init(cs);
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
@@ -1616,7 +1665,6 @@ static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order,
                                   uint64_t param, uint64_t *status_reg)
 {
     SigpInfo si = {
-        .cpu = dst_cpu,
         .param = param,
         .status_reg = status_reg,
     };
@@ -1732,6 +1780,11 @@ static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
     status_reg = &env->regs[r1];
     param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1];
 
+    if (qemu_mutex_trylock(&qemu_sigp_mutex)) {
+        ret = SIGP_CC_BUSY;
+        goto out;
+    }
+
     switch (order) {
     case SIGP_SET_ARCH:
         ret = sigp_set_architecture(cpu, param, status_reg);
@@ -1741,7 +1794,9 @@ static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
         dst_cpu = s390_cpu_addr2state(env->regs[r3]);
         ret = handle_sigp_single_dst(dst_cpu, order, param, status_reg);
     }
+    qemu_mutex_unlock(&qemu_sigp_mutex);
 
+out:
     trace_kvm_sigp_finished(order, CPU(cpu)->cpu_index,
                             dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret);
 
@@ -1853,6 +1908,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);
@@ -1938,7 +2001,7 @@ static void insert_stsi_3_2_2(S390CPU *cpu, __u64 addr, uint8_t ar)
         strcpy((char *)sysib.ext_names[0], "KVMguest");
     }
     /* Insert UUID */
-    memcpy(sysib.vm[0].uuid, qemu_uuid, sizeof(sysib.vm[0].uuid));
+    memcpy(sysib.vm[0].uuid, &qemu_uuid, sizeof(sysib.vm[0].uuid));
 
     s390_cpu_virt_mem_write(cpu, addr, ar, &sysib, sizeof(sysib));
 }
@@ -2059,18 +2122,37 @@ void kvm_s390_io_interrupt(uint16_t subchannel_id,
     if (io_int_word & IO_INT_WORD_AI) {
         irq.type = KVM_S390_INT_IO(1, 0, 0, 0);
     } else {
-        irq.type = ((subchannel_id & 0xff00) << 24) |
-            ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16);
+        irq.type = KVM_S390_INT_IO(0, (subchannel_id & 0xff00) >> 8,
+                                      (subchannel_id & 0x0006),
+                                      subchannel_nr);
     }
     kvm_s390_floating_interrupt(&irq);
 }
 
+static uint64_t build_channel_report_mcic(void)
+{
+    uint64_t mcic;
+
+    /* subclass: indicate channel report pending */
+    mcic = MCIC_SC_CP |
+    /* subclass modifiers: none */
+    /* storage errors: none */
+    /* validity bits: no damage */
+        MCIC_VB_WP | MCIC_VB_MS | MCIC_VB_PM | MCIC_VB_IA | MCIC_VB_FP |
+        MCIC_VB_GR | MCIC_VB_CR | MCIC_VB_ST | MCIC_VB_AR | MCIC_VB_PR |
+        MCIC_VB_FC | MCIC_VB_CT | MCIC_VB_CC;
+    if (s390_has_feat(S390_FEAT_VECTOR)) {
+        mcic |= MCIC_VB_VR;
+    }
+    return mcic;
+}
+
 void kvm_s390_crw_mchk(void)
 {
     struct kvm_s390_irq irq = {
         .type = KVM_S390_MCHK,
         .u.mchk.cr14 = 1 << 28,
-        .u.mchk.mcic = 0x00400f1d40330000ULL,
+        .u.mchk.mcic = build_channel_report_mcic(),
     };
     kvm_s390_floating_interrupt(&irq);
 }
@@ -2122,6 +2204,11 @@ int kvm_s390_get_memslot_count(KVMState *s)
     return kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS);
 }
 
+int kvm_s390_get_ri(void)
+{
+    return cap_ri;
+}
+
 int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state)
 {
     struct kvm_mp_state mp_state = {};
@@ -2211,10 +2298,10 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
                              uint64_t address, uint32_t data, PCIDevice *dev)
 {
     S390PCIBusDevice *pbdev;
-    uint32_t fid = data >> ZPCI_MSI_VEC_BITS;
+    uint32_t idx = data >> ZPCI_MSI_VEC_BITS;
     uint32_t vec = data & ZPCI_MSI_VEC_MASK;
 
-    pbdev = s390_pci_find_dev_by_fid(fid);
+    pbdev = s390_pci_find_dev_by_idx(idx);
     if (!pbdev) {
         DPRINTF("add_msi_route no dev\n");
         return -ENODEV;
@@ -2232,7 +2319,335 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
     return 0;
 }
 
+int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route,
+                                int vector, PCIDevice *dev)
+{
+    return 0;
+}
+
+int kvm_arch_release_virq_post(int virq)
+{
+    return 0;
+}
+
 int kvm_arch_msi_data_to_gsi(uint32_t data)
 {
     abort();
 }
+
+static inline int test_bit_inv(long nr, const unsigned long *addr)
+{
+    return test_bit(BE_BIT_NR(nr), addr);
+}
+
+static inline void set_bit_inv(long nr, unsigned long *addr)
+{
+    set_bit(BE_BIT_NR(nr), addr);
+}
+
+static int query_cpu_subfunc(S390FeatBitmap features)
+{
+    struct kvm_s390_vm_cpu_subfunc prop;
+    struct kvm_device_attr attr = {
+        .group = KVM_S390_VM_CPU_MODEL,
+        .attr = KVM_S390_VM_CPU_MACHINE_SUBFUNC,
+        .addr = (uint64_t) &prop,
+    };
+    int rc;
+
+    rc = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr);
+    if (rc) {
+        return  rc;
+    }
+
+    /*
+     * We're going to add all subfunctions now, if the corresponding feature
+     * is available that unlocks the query functions.
+     */
+    s390_add_from_feat_block(features, S390_FEAT_TYPE_PLO, prop.plo);
+    if (test_bit(S390_FEAT_TOD_CLOCK_STEERING, features)) {
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_PTFF, prop.ptff);
+    }
+    if (test_bit(S390_FEAT_MSA, features)) {
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_KMAC, prop.kmac);
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_KMC, prop.kmc);
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_KM, prop.km);
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_KIMD, prop.kimd);
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_KLMD, prop.klmd);
+    }
+    if (test_bit(S390_FEAT_MSA_EXT_3, features)) {
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_PCKMO, prop.pckmo);
+    }
+    if (test_bit(S390_FEAT_MSA_EXT_4, features)) {
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_KMCTR, prop.kmctr);
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_KMF, prop.kmf);
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_KMO, prop.kmo);
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_PCC, prop.pcc);
+    }
+    if (test_bit(S390_FEAT_MSA_EXT_5, features)) {
+        s390_add_from_feat_block(features, S390_FEAT_TYPE_PPNO, prop.ppno);
+    }
+    return 0;
+}
+
+static int configure_cpu_subfunc(const S390FeatBitmap features)
+{
+    struct kvm_s390_vm_cpu_subfunc prop = {};
+    struct kvm_device_attr attr = {
+        .group = KVM_S390_VM_CPU_MODEL,
+        .attr = KVM_S390_VM_CPU_PROCESSOR_SUBFUNC,
+        .addr = (uint64_t) &prop,
+    };
+
+    if (!kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL,
+                           KVM_S390_VM_CPU_PROCESSOR_SUBFUNC)) {
+        /* hardware support might be missing, IBC will handle most of this */
+        return 0;
+    }
+
+    s390_fill_feat_block(features, S390_FEAT_TYPE_PLO, prop.plo);
+    if (test_bit(S390_FEAT_TOD_CLOCK_STEERING, features)) {
+        s390_fill_feat_block(features, S390_FEAT_TYPE_PTFF, prop.ptff);
+        prop.ptff[0] |= 0x80; /* query is always available */
+    }
+    if (test_bit(S390_FEAT_MSA, features)) {
+        s390_fill_feat_block(features, S390_FEAT_TYPE_KMAC, prop.kmac);
+        prop.kmac[0] |= 0x80; /* query is always available */
+        s390_fill_feat_block(features, S390_FEAT_TYPE_KMC, prop.kmc);
+        prop.kmc[0] |= 0x80; /* query is always available */
+        s390_fill_feat_block(features, S390_FEAT_TYPE_KM, prop.km);
+        prop.km[0] |= 0x80; /* query is always available */
+        s390_fill_feat_block(features, S390_FEAT_TYPE_KIMD, prop.kimd);
+        prop.kimd[0] |= 0x80; /* query is always available */
+        s390_fill_feat_block(features, S390_FEAT_TYPE_KLMD, prop.klmd);
+        prop.klmd[0] |= 0x80; /* query is always available */
+    }
+    if (test_bit(S390_FEAT_MSA_EXT_3, features)) {
+        s390_fill_feat_block(features, S390_FEAT_TYPE_PCKMO, prop.pckmo);
+        prop.pckmo[0] |= 0x80; /* query is always available */
+    }
+    if (test_bit(S390_FEAT_MSA_EXT_4, features)) {
+        s390_fill_feat_block(features, S390_FEAT_TYPE_KMCTR, prop.kmctr);
+        prop.kmctr[0] |= 0x80; /* query is always available */
+        s390_fill_feat_block(features, S390_FEAT_TYPE_KMF, prop.kmf);
+        prop.kmf[0] |= 0x80; /* query is always available */
+        s390_fill_feat_block(features, S390_FEAT_TYPE_KMO, prop.kmo);
+        prop.kmo[0] |= 0x80; /* query is always available */
+        s390_fill_feat_block(features, S390_FEAT_TYPE_PCC, prop.pcc);
+        prop.pcc[0] |= 0x80; /* query is always available */
+    }
+    if (test_bit(S390_FEAT_MSA_EXT_5, features)) {
+        s390_fill_feat_block(features, S390_FEAT_TYPE_PPNO, prop.ppno);
+        prop.ppno[0] |= 0x80; /* query is always available */
+    }
+    return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr);
+}
+
+static int kvm_to_feat[][2] = {
+    { KVM_S390_VM_CPU_FEAT_ESOP, S390_FEAT_ESOP },
+    { KVM_S390_VM_CPU_FEAT_SIEF2, S390_FEAT_SIE_F2 },
+    { KVM_S390_VM_CPU_FEAT_64BSCAO , S390_FEAT_SIE_64BSCAO },
+    { KVM_S390_VM_CPU_FEAT_SIIF, S390_FEAT_SIE_SIIF },
+    { KVM_S390_VM_CPU_FEAT_GPERE, S390_FEAT_SIE_GPERE },
+    { KVM_S390_VM_CPU_FEAT_GSLS, S390_FEAT_SIE_GSLS },
+    { KVM_S390_VM_CPU_FEAT_IB, S390_FEAT_SIE_IB },
+    { KVM_S390_VM_CPU_FEAT_CEI, S390_FEAT_SIE_CEI },
+    { KVM_S390_VM_CPU_FEAT_IBS, S390_FEAT_SIE_IBS },
+    { KVM_S390_VM_CPU_FEAT_SKEY, S390_FEAT_SIE_SKEY },
+    { KVM_S390_VM_CPU_FEAT_CMMA, S390_FEAT_SIE_CMMA },
+    { KVM_S390_VM_CPU_FEAT_PFMFI, S390_FEAT_SIE_PFMFI},
+    { KVM_S390_VM_CPU_FEAT_SIGPIF, S390_FEAT_SIE_SIGPIF},
+};
+
+static int query_cpu_feat(S390FeatBitmap features)
+{
+    struct kvm_s390_vm_cpu_feat prop;
+    struct kvm_device_attr attr = {
+        .group = KVM_S390_VM_CPU_MODEL,
+        .attr = KVM_S390_VM_CPU_MACHINE_FEAT,
+        .addr = (uint64_t) &prop,
+    };
+    int rc;
+    int i;
+
+    rc = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr);
+    if (rc) {
+        return  rc;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(kvm_to_feat); i++) {
+        if (test_bit_inv(kvm_to_feat[i][0], (unsigned long *)prop.feat)) {
+            set_bit(kvm_to_feat[i][1], features);
+        }
+    }
+    return 0;
+}
+
+static int configure_cpu_feat(const S390FeatBitmap features)
+{
+    struct kvm_s390_vm_cpu_feat prop = {};
+    struct kvm_device_attr attr = {
+        .group = KVM_S390_VM_CPU_MODEL,
+        .attr = KVM_S390_VM_CPU_PROCESSOR_FEAT,
+        .addr = (uint64_t) &prop,
+    };
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(kvm_to_feat); i++) {
+        if (test_bit(kvm_to_feat[i][1], features)) {
+            set_bit_inv(kvm_to_feat[i][0], (unsigned long *)prop.feat);
+        }
+    }
+    return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr);
+}
+
+bool kvm_s390_cpu_models_supported(void)
+{
+    if (!cpu_model_allowed()) {
+        /* compatibility machines interfere with the cpu model */
+        return false;
+    }
+    return kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL,
+                             KVM_S390_VM_CPU_MACHINE) &&
+           kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL,
+                             KVM_S390_VM_CPU_PROCESSOR) &&
+           kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL,
+                             KVM_S390_VM_CPU_MACHINE_FEAT) &&
+           kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL,
+                             KVM_S390_VM_CPU_PROCESSOR_FEAT) &&
+           kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL,
+                             KVM_S390_VM_CPU_MACHINE_SUBFUNC);
+}
+
+void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
+{
+    struct kvm_s390_vm_cpu_machine prop = {};
+    struct kvm_device_attr attr = {
+        .group = KVM_S390_VM_CPU_MODEL,
+        .attr = KVM_S390_VM_CPU_MACHINE,
+        .addr = (uint64_t) &prop,
+    };
+    uint16_t unblocked_ibc = 0, cpu_type = 0;
+    int rc;
+
+    memset(model, 0, sizeof(*model));
+
+    if (!kvm_s390_cpu_models_supported()) {
+        error_setg(errp, "KVM doesn't support CPU models");
+        return;
+    }
+
+    /* query the basic cpu model properties */
+    rc = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr);
+    if (rc) {
+        error_setg(errp, "KVM: Error querying host CPU model: %d", rc);
+        return;
+    }
+
+    cpu_type = cpuid_type(prop.cpuid);
+    if (has_ibc(prop.ibc)) {
+        model->lowest_ibc = lowest_ibc(prop.ibc);
+        unblocked_ibc = unblocked_ibc(prop.ibc);
+    }
+    model->cpu_id = cpuid_id(prop.cpuid);
+    model->cpu_ver = 0xff;
+
+    /* get supported cpu features indicated via STFL(E) */
+    s390_add_from_feat_block(model->features, S390_FEAT_TYPE_STFL,
+                             (uint8_t *) prop.fac_mask);
+    /* dat-enhancement facility 2 has no bit but was introduced with stfle */
+    if (test_bit(S390_FEAT_STFLE, model->features)) {
+        set_bit(S390_FEAT_DAT_ENH_2, model->features);
+    }
+    /* get supported cpu features indicated e.g. via SCLP */
+    rc = query_cpu_feat(model->features);
+    if (rc) {
+        error_setg(errp, "KVM: Error querying CPU features: %d", rc);
+        return;
+    }
+    /* get supported cpu subfunctions indicated via query / test bit */
+    rc = query_cpu_subfunc(model->features);
+    if (rc) {
+        error_setg(errp, "KVM: Error querying CPU subfunctions: %d", rc);
+        return;
+    }
+
+    /* with cpu model support, CMM is only indicated if really available */
+    if (kvm_s390_cmma_available()) {
+        set_bit(S390_FEAT_CMM, model->features);
+    }
+
+    if (s390_known_cpu_type(cpu_type)) {
+        /* we want the exact model, even if some features are missing */
+        model->def = s390_find_cpu_def(cpu_type, ibc_gen(unblocked_ibc),
+                                       ibc_ec_ga(unblocked_ibc), NULL);
+    } else {
+        /* model unknown, e.g. too new - search using features */
+        model->def = s390_find_cpu_def(0, ibc_gen(unblocked_ibc),
+                                       ibc_ec_ga(unblocked_ibc),
+                                       model->features);
+    }
+    if (!model->def) {
+        error_setg(errp, "KVM: host CPU model could not be identified");
+        return;
+    }
+    /* strip of features that are not part of the maximum model */
+    bitmap_and(model->features, model->features, model->def->full_feat,
+               S390_FEAT_MAX);
+}
+
+void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp)
+{
+    struct kvm_s390_vm_cpu_processor prop  = {
+        .fac_list = { 0 },
+    };
+    struct kvm_device_attr attr = {
+        .group = KVM_S390_VM_CPU_MODEL,
+        .attr = KVM_S390_VM_CPU_PROCESSOR,
+        .addr = (uint64_t) &prop,
+    };
+    int rc;
+
+    if (!model) {
+        /* compatibility handling if cpu models are disabled */
+        if (kvm_s390_cmma_available() && !mem_path) {
+            kvm_s390_enable_cmma();
+        }
+        return;
+    }
+    if (!kvm_s390_cpu_models_supported()) {
+        error_setg(errp, "KVM doesn't support CPU models");
+        return;
+    }
+    prop.cpuid = s390_cpuid_from_cpu_model(model);
+    prop.ibc = s390_ibc_from_cpu_model(model);
+    /* configure cpu features indicated via STFL(e) */
+    s390_fill_feat_block(model->features, S390_FEAT_TYPE_STFL,
+                         (uint8_t *) prop.fac_list);
+    rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr);
+    if (rc) {
+        error_setg(errp, "KVM: Error configuring the CPU model: %d", rc);
+        return;
+    }
+    /* configure cpu features indicated e.g. via SCLP */
+    rc = configure_cpu_feat(model->features);
+    if (rc) {
+        error_setg(errp, "KVM: Error configuring CPU features: %d", rc);
+        return;
+    }
+    /* configure cpu subfunctions indicated via query / test bit */
+    rc = configure_cpu_subfunc(model->features);
+    if (rc) {
+        error_setg(errp, "KVM: Error configuring CPU subfunctions: %d", rc);
+        return;
+    }
+    /* enable CMM via CMMA - disable on hugetlbfs */
+    if (test_bit(S390_FEAT_CMM, model->features)) {
+        if (mem_path) {
+            error_report("Warning: CMM will not be enabled because it is not "
+                         "compatible to hugetlbfs.");
+        } else {
+            kvm_s390_enable_cmma();
+        }
+    }
+}