summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--cpus.c14
-rw-r--r--hmp-commands.hx4
-rw-r--r--hw/s390x/s390-virtio-ccw.c15
-rw-r--r--qmp-commands.hx2
-rw-r--r--target-s390x/cpu-qom.h6
-rw-r--r--target-s390x/cpu.c52
-rw-r--r--target-s390x/cpu.h20
-rw-r--r--target-s390x/kvm.c40
-rw-r--r--target-s390x/misc_helper.c70
9 files changed, 204 insertions, 19 deletions
diff --git a/cpus.c b/cpus.c
index b9e5685e16..d74cc117b3 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1401,6 +1401,20 @@ void qmp_inject_nmi(Error **errp)
             apic_deliver_nmi(env->apic_state);
         }
     }
+#elif defined(TARGET_S390X)
+    CPUState *cs;
+    S390CPU *cpu;
+
+    for (cs = first_cpu; cs != NULL; cs = cs->next_cpu) {
+        cpu = S390_CPU(cs);
+        if (cpu->env.cpu_num == monitor_get_cpu_index()) {
+            if (s390_cpu_restart(S390_CPU(cs)) == -1) {
+                error_set(errp, QERR_UNSUPPORTED);
+                return;
+            }
+            break;
+        }
+    }
 #else
     error_set(errp, QERR_UNSUPPORTED);
 #endif
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 8c6b91a9c7..628807f684 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -822,7 +822,7 @@ The values that can be specified here depend on the machine type, but are
 the same that can be specified in the @code{-boot} command line option.
 ETEXI
 
-#if defined(TARGET_I386)
+#if defined(TARGET_I386) || defined(TARGET_S390X)
     {
         .name       = "nmi",
         .args_type  = "",
@@ -834,7 +834,7 @@ ETEXI
 STEXI
 @item nmi @var{cpu}
 @findex nmi
-Inject an NMI on the given CPU (x86 only).
+Inject an NMI (x86) or RESTART (s390x) on the given CPU.
 
 ETEXI
 
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index aebbbf1755..8fd46a92c9 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -17,6 +17,21 @@
 #include "css.h"
 #include "virtio-ccw.h"
 
+void io_subsystem_reset(void)
+{
+    DeviceState *css, *sclp;
+
+    css = DEVICE(object_resolve_path_type("", "virtual-css-bridge", NULL));
+    if (css) {
+        qdev_reset_all(css);
+    }
+    sclp = DEVICE(object_resolve_path_type("",
+                  "s390-sclp-event-facility", NULL));
+    if (sclp) {
+        qdev_reset_all(sclp);
+    }
+}
+
 static int virtio_ccw_hcall_notify(const uint64_t *args)
 {
     uint64_t subch_id = args[0];
diff --git a/qmp-commands.hx b/qmp-commands.hx
index cf47e3fe72..bb09e72712 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -487,7 +487,7 @@ Example:
 <- { "return": {} }
 
 Note: inject-nmi fails when the guest doesn't support injecting.
-      Currently, only x86 guests do.
+      Currently, only x86 (NMI) and s390x (RESTART) guests do.
 
 EQMP
 
diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h
index cbe2341b3b..ac0460eb30 100644
--- a/target-s390x/cpu-qom.h
+++ b/target-s390x/cpu-qom.h
@@ -36,6 +36,9 @@
  * S390CPUClass:
  * @parent_realize: The parent class' realize handler.
  * @parent_reset: The parent class' reset handler.
+ * @load_normal: Performs a load normal.
+ * @cpu_reset: Performs a CPU reset.
+ * @initial_cpu_reset: Performs an initial CPU reset.
  *
  * An S/390 CPU model.
  */
@@ -46,6 +49,9 @@ typedef struct S390CPUClass {
 
     DeviceRealize parent_realize;
     void (*parent_reset)(CPUState *cpu);
+    void (*load_normal)(CPUState *cpu);
+    void (*cpu_reset)(CPUState *cpu);
+    void (*initial_cpu_reset)(CPUState *cpu);
 } S390CPUClass;
 
 /**
diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index 5cc99387b2..3c89f8a767 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -65,7 +65,18 @@ static void s390_cpu_set_pc(CPUState *cs, vaddr value)
     cpu->env.psw.addr = value;
 }
 
-/* CPUClass::reset() */
+#if !defined(CONFIG_USER_ONLY)
+/* S390CPUClass::load_normal() */
+static void s390_cpu_load_normal(CPUState *s)
+{
+    S390CPU *cpu = S390_CPU(s);
+    cpu->env.psw.addr = ldl_phys(4) & PSW_MASK_ESA_ADDR;
+    cpu->env.psw.mask = PSW_MASK_32 | PSW_MASK_64;
+    s390_add_running_cpu(cpu);
+}
+#endif
+
+/* S390CPUClass::cpu_reset() */
 static void s390_cpu_reset(CPUState *s)
 {
     S390CPU *cpu = S390_CPU(s);
@@ -73,6 +84,37 @@ static void s390_cpu_reset(CPUState *s)
     CPUS390XState *env = &cpu->env;
 
     s390_del_running_cpu(cpu);
+    scc->parent_reset(s);
+#if !defined(CONFIG_USER_ONLY)
+    s->halted = 1;
+#endif
+    tlb_flush(env, 1);
+}
+
+/* S390CPUClass::initial_reset() */
+static void s390_cpu_initial_reset(CPUState *s)
+{
+    S390CPU *cpu = S390_CPU(s);
+    CPUS390XState *env = &cpu->env;
+
+    s390_cpu_reset(s);
+    /* initial reset does not touch regs,fregs and aregs */
+    memset(&env->fpc, 0, offsetof(CPUS390XState, breakpoints) -
+                         offsetof(CPUS390XState, fpc));
+
+    /* architectured initial values for CR 0 and 14 */
+    env->cregs[0] = CR0_RESET;
+    env->cregs[14] = CR14_RESET;
+}
+
+/* CPUClass:reset() */
+static void s390_cpu_full_reset(CPUState *s)
+{
+    S390CPU *cpu = S390_CPU(s);
+    S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
+    CPUS390XState *env = &cpu->env;
+
+    s390_del_running_cpu(cpu);
 
     scc->parent_reset(s);
 
@@ -169,8 +211,12 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data)
     dc->realize = s390_cpu_realizefn;
 
     scc->parent_reset = cc->reset;
-    cc->reset = s390_cpu_reset;
-
+#if !defined(CONFIG_USER_ONLY)
+    scc->load_normal = s390_cpu_load_normal;
+#endif
+    scc->cpu_reset = s390_cpu_reset;
+    scc->initial_cpu_reset = s390_cpu_initial_reset;
+    cc->reset = s390_cpu_full_reset;
     cc->do_interrupt = s390_cpu_do_interrupt;
     cc->dump_state = s390_cpu_dump_state;
     cc->set_pc = s390_cpu_set_pc;
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 65bef8625f..8be5648806 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -228,6 +228,8 @@ typedef struct CPUS390XState {
 #undef PSW_MASK_CC
 #undef PSW_MASK_PM
 #undef PSW_MASK_64
+#undef PSW_MASK_32
+#undef PSW_MASK_ESA_ADDR
 
 #define PSW_MASK_PER            0x4000000000000000ULL
 #define PSW_MASK_DAT            0x0400000000000000ULL
@@ -243,6 +245,7 @@ typedef struct CPUS390XState {
 #define PSW_MASK_PM             0x00000F0000000000ULL
 #define PSW_MASK_64             0x0000000100000000ULL
 #define PSW_MASK_32             0x0000000080000000ULL
+#define PSW_MASK_ESA_ADDR       0x000000007fffffffULL
 
 #undef PSW_ASC_PRIMARY
 #undef PSW_ASC_ACCREG
@@ -400,6 +403,7 @@ void cpu_unlock(void);
 typedef struct SubchDev SubchDev;
 
 #ifndef CONFIG_USER_ONLY
+extern void io_subsystem_reset(void);
 SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
                          uint16_t schid);
 bool css_subch_visible(SubchDev *sch);
@@ -1047,6 +1051,9 @@ uint32_t set_cc_nz_f64(float64 v);
 uint32_t set_cc_nz_f128(float128 v);
 
 /* misc_helper.c */
+#ifndef CONFIG_USER_ONLY
+void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3);
+#endif
 void program_interrupt(CPUS390XState *env, uint32_t code, int ilen);
 void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp,
                                      uintptr_t retaddr);
@@ -1062,6 +1069,7 @@ void kvm_s390_enable_css_support(S390CPU *cpu);
 int kvm_s390_get_registers_partial(CPUState *cpu);
 int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
                                     int vq, bool assign);
+int kvm_s390_cpu_restart(S390CPU *cpu);
 #else
 static inline void kvm_s390_io_interrupt(S390CPU *cpu,
                                         uint16_t subchannel_id,
@@ -1086,8 +1094,20 @@ static inline int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier,
 {
     return -ENOSYS;
 }
+static inline int kvm_s390_cpu_restart(S390CPU *cpu)
+{
+    return -ENOSYS;
+}
 #endif
 
+static inline int s390_cpu_restart(S390CPU *cpu)
+{
+    if (kvm_enabled()) {
+        return kvm_s390_cpu_restart(cpu);
+    }
+    return -ENOSYS;
+}
+
 static inline void s390_io_interrupt(S390CPU *cpu,
                                      uint16_t subchannel_id,
                                      uint16_t subchannel_nr,
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 26d18e3bcf..185c8f5a45 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -72,6 +72,7 @@
 #define PRIV_XSCH                       0x76
 #define PRIV_SQBS                       0x8a
 #define PRIV_EQBS                       0x9c
+#define DIAG_IPL                        0x308
 #define DIAG_KVM_HYPERCALL              0x500
 #define DIAG_KVM_BREAKPOINT             0x501
 
@@ -578,32 +579,45 @@ static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
     return 0;
 }
 
+static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run)
+{
+    uint64_t r1, r3;
+
+    cpu_synchronize_state(CPU(cpu));
+    r1 = (run->s390_sieic.ipa & 0x00f0) >> 8;
+    r3 = run->s390_sieic.ipa & 0x000f;
+    handle_diag_308(&cpu->env, r1, r3);
+}
+
 static int handle_diag(S390CPU *cpu, struct kvm_run *run, int ipb_code)
 {
     int r = 0;
 
     switch (ipb_code) {
-        case DIAG_KVM_HYPERCALL:
-            r = handle_hypercall(cpu, run);
-            break;
-        case DIAG_KVM_BREAKPOINT:
-            sleep(10);
-            break;
-        default:
-            DPRINTF("KVM: unknown DIAG: 0x%x\n", ipb_code);
-            r = -1;
-            break;
+    case DIAG_IPL:
+        kvm_handle_diag_308(cpu, run);
+        break;
+    case DIAG_KVM_HYPERCALL:
+        r = handle_hypercall(cpu, run);
+        break;
+    case DIAG_KVM_BREAKPOINT:
+        sleep(10);
+        break;
+    default:
+        DPRINTF("KVM: unknown DIAG: 0x%x\n", ipb_code);
+        r = -1;
+        break;
     }
 
     return r;
 }
 
-static int s390_cpu_restart(S390CPU *cpu)
+int kvm_s390_cpu_restart(S390CPU *cpu)
 {
     kvm_s390_interrupt(cpu, KVM_S390_RESTART, 0);
     s390_add_running_cpu(cpu);
     qemu_cpu_kick(CPU(cpu));
-    DPRINTF("DONE: SIGP cpu restart: %p\n", &cpu->env);
+    DPRINTF("DONE: KVM cpu restart: %p\n", &cpu->env);
     return 0;
 }
 
@@ -672,7 +686,7 @@ static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
 
     switch (order_code) {
         case SIGP_RESTART:
-            r = s390_cpu_restart(target_cpu);
+            r = kvm_s390_cpu_restart(target_cpu);
             break;
         case SIGP_STORE_STATUS_ADDR:
             r = s390_store_status(target_env, parameter);
diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c
index 454960aa01..4afd7dab1c 100644
--- a/target-s390x/misc_helper.c
+++ b/target-s390x/misc_helper.c
@@ -31,6 +31,7 @@
 
 #if !defined(CONFIG_USER_ONLY)
 #include "exec/softmmu_exec.h"
+#include "sysemu/cpus.h"
 #include "sysemu/sysemu.h"
 #endif
 
@@ -179,6 +180,75 @@ uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
     return r;
 }
 
+#ifndef CONFIG_USER_ONLY
+static void cpu_reset_all(void)
+{
+    CPUState *cpu;
+    S390CPUClass *scc;
+
+    for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) {
+        scc = S390_CPU_GET_CLASS(CPU(cpu));
+        scc->cpu_reset(CPU(cpu));
+    }
+}
+
+static int load_normal_reset(S390CPU *cpu)
+{
+    S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
+
+    pause_all_vcpus();
+    cpu_synchronize_all_states();
+    cpu_reset_all();
+    io_subsystem_reset();
+    scc->initial_cpu_reset(CPU(cpu));
+    scc->load_normal(CPU(cpu));
+    cpu_synchronize_all_post_reset();
+    resume_all_vcpus();
+    return 0;
+}
+
+#define DIAG_308_RC_NO_CONF         0x0102
+#define DIAG_308_RC_INVALID         0x0402
+void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3)
+{
+    uint64_t addr =  env->regs[r1];
+    uint64_t subcode = env->regs[r3];
+
+    if (env->psw.mask & PSW_MASK_PSTATE) {
+        program_interrupt(env, PGM_PRIVILEGED, ILEN_LATER_INC);
+        return;
+    }
+
+    if ((subcode & ~0x0ffffULL) || (subcode > 6)) {
+        program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER_INC);
+        return;
+    }
+
+    switch (subcode) {
+    case 1:
+        load_normal_reset(s390_env_get_cpu(env));
+        break;
+    case 5:
+        if ((r1 & 1) || (addr & 0x0fffULL)) {
+            program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER_INC);
+            return;
+        }
+        env->regs[r1+1] = DIAG_308_RC_INVALID;
+        return;
+    case 6:
+        if ((r1 & 1) || (addr & 0x0fffULL)) {
+            program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER_INC);
+            return;
+        }
+        env->regs[r1+1] = DIAG_308_RC_NO_CONF;
+        return;
+    default:
+        hw_error("Unhandled diag308 subcode %" PRIx64, subcode);
+        break;
+    }
+}
+#endif
+
 /* DIAG */
 uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem,
                       uint64_t code)