]> Git Repo - qemu.git/commitdiff
s390x/kvm: implement handling of new SIGP orders
authorDavid Hildenbrand <[email protected]>
Tue, 24 Feb 2015 13:15:27 +0000 (14:15 +0100)
committerChristian Borntraeger <[email protected]>
Tue, 10 Mar 2015 08:26:22 +0000 (09:26 +0100)
This patch adds handling code for the following SIGP orders:
- SIGP SET ARCHITECTURE
- SIGP SET PREFIX
- SIGP STOP
- SIGP STOP AND STORE STATUS
- SIGP STORE STATUS AT ADDRESS

SIGP STOP (AND STORE STATUS) are the only orders that can stay pending forever
(and may only be interrupted by resets), so special care has to be taken about
them. Their status also has to be tracked within QEMU. This patch takes
care of migrating this status (e.g. if migration happens during a SIGP STOP).

Due to the BQL, only one VCPU is currently able to execute SIGP handlers at a
time. According to the PoP, BUSY should be returned if another SIGP order is
currently being executed on a VCPU. This can only be implemented when the BQL
does not protect all handlers. For now, all SIGP orders on all VCPUs will be
serialized, which will be okay for the first shot.

Reviewed-by: Thomas Huth <[email protected]>
Signed-off-by: Jens Freimann <[email protected]>
Signed-off-by: David Hildenbrand <[email protected]>
Message-Id: <1424783731[email protected]>
Signed-off-by: Christian Borntraeger <[email protected]>
target-s390x/cpu.c
target-s390x/cpu.h
target-s390x/kvm.c
target-s390x/machine.c

index d2f6312e03ab4f8fd12b8be9dfb7732f19372bed..e0537fa222a718edbafb7cf71aec7983401435d7 100644 (file)
@@ -96,6 +96,7 @@ static void s390_cpu_reset(CPUState *s)
 
     env->pfault_token = -1UL;
     scc->parent_reset(s);
+    cpu->env.sigp_order = 0;
     s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
     tlb_flush(s, 1);
 }
@@ -131,6 +132,7 @@ static void s390_cpu_full_reset(CPUState *s)
     CPUS390XState *env = &cpu->env;
 
     scc->parent_reset(s);
+    cpu->env.sigp_order = 0;
     s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
 
     memset(env, 0, offsetof(CPUS390XState, cpu_num));
index 1cdfe5edaf608bc9dd4db87b2ace45d34a24f774..da0af9453f08085a7613e09093d9afae8b8dc7ab 100644 (file)
@@ -157,6 +157,9 @@ typedef struct CPUS390XState {
 #define CPU_STATE_LOAD                 0x04
     uint8_t cpu_state;
 
+    /* currently processed sigp order */
+    uint8_t sigp_order;
+
 } CPUS390XState;
 
 #include "cpu-qom.h"
@@ -411,6 +414,10 @@ S390CPU *s390_cpu_addr2state(uint16_t cpu_addr);
 unsigned int s390_cpu_halt(S390CPU *cpu);
 void s390_cpu_unhalt(S390CPU *cpu);
 unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu);
+static inline uint8_t s390_cpu_get_state(S390CPU *cpu)
+{
+    return cpu->env.cpu_state;
+}
 
 /* service interrupts are floating therefore we must not pass an cpustate */
 void s390_sclp_extint(uint32_t parm);
@@ -896,6 +903,11 @@ struct sysib_322 {
 #define SIGP_STAT_INVALID_ORDER     0x00000002UL
 #define SIGP_STAT_RECEIVER_CHECK    0x00000001UL
 
+/* SIGP SET ARCHITECTURE modes */
+#define SIGP_MODE_ESA_S390 0
+#define SIGP_MODE_Z_ARCH_TRANS_ALL_PSW 1
+#define SIGP_MODE_Z_ARCH_TRANS_CUR_PSW 2
+
 void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr);
 int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
                   target_ulong *raddr, int *flags, bool exc);
index c4c867c00f99db45f47a6e49ef9c2c2bb854a100..127a3b8024c31d4515af2e02b73773b8dbb8a13e 100644 (file)
@@ -38,6 +38,7 @@
 #include "qapi/qmp/qjson.h"
 #include "monitor/monitor.h"
 #include "exec/gdbstub.h"
+#include "exec/address-spaces.h"
 #include "trace.h"
 #include "qapi-event.h"
 #include "hw/s390x/s390-pci-inst.h"
@@ -1133,6 +1134,114 @@ static void sigp_start(void *arg)
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
+static void sigp_stop(void *arg)
+{
+    SigpInfo *si = arg;
+    struct kvm_s390_irq irq = {
+        .type = KVM_S390_SIGP_STOP,
+    };
+
+    if (s390_cpu_get_state(si->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);
+    } else {
+        /* execute the stop function */
+        si->cpu->env.sigp_order = SIGP_STOP;
+        kvm_s390_vcpu_interrupt(si->cpu, &irq);
+    }
+    si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+}
+
+#define KVM_S390_STORE_STATUS_DEF_ADDR offsetof(LowCore, floating_pt_save_area)
+#define SAVE_AREA_SIZE 512
+static int kvm_s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch)
+{
+    static const uint8_t ar_id = 1;
+    uint64_t ckc = cpu->env.ckc >> 8;
+    void *mem;
+    hwaddr len = SAVE_AREA_SIZE;
+
+    mem = cpu_physical_memory_map(addr, &len, 1);
+    if (!mem) {
+        return -EFAULT;
+    }
+    if (len != SAVE_AREA_SIZE) {
+        cpu_physical_memory_unmap(mem, len, 1, 0);
+        return -EFAULT;
+    }
+
+    if (store_arch) {
+        cpu_physical_memory_write(offsetof(LowCore, ar_access_id), &ar_id, 1);
+    }
+    memcpy(mem, &cpu->env.fregs, 128);
+    memcpy(mem + 128, &cpu->env.regs, 128);
+    memcpy(mem + 256, &cpu->env.psw, 16);
+    memcpy(mem + 280, &cpu->env.psa, 4);
+    memcpy(mem + 284, &cpu->env.fpc, 4);
+    memcpy(mem + 292, &cpu->env.todpr, 4);
+    memcpy(mem + 296, &cpu->env.cputm, 8);
+    memcpy(mem + 304, &ckc, 8);
+    memcpy(mem + 320, &cpu->env.aregs, 64);
+    memcpy(mem + 384, &cpu->env.cregs, 128);
+
+    cpu_physical_memory_unmap(mem, len, 1, len);
+
+    return 0;
+}
+
+static void sigp_stop_and_store_status(void *arg)
+{
+    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);
+    }
+
+    switch (s390_cpu_get_state(si->cpu)) {
+    case CPU_STATE_OPERATING:
+        si->cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
+        kvm_s390_vcpu_interrupt(si->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);
+        break;
+    }
+    si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+}
+
+static void sigp_store_status_at_address(void *arg)
+{
+    SigpInfo *si = arg;
+    uint32_t address = si->param & 0x7ffffe00u;
+
+    /* cpu has to be stopped */
+    if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) {
+        set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
+        return;
+    }
+
+    cpu_synchronize_state(CPU(si->cpu));
+
+    if (kvm_s390_store_status(si->cpu, address, false)) {
+        set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
+        return;
+    }
+    si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+}
+
 static void sigp_restart(void *arg)
 {
     SigpInfo *si = arg;
@@ -1180,6 +1289,30 @@ static void sigp_cpu_reset(void *arg)
     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
+static void sigp_set_prefix(void *arg)
+{
+    SigpInfo *si = arg;
+    uint32_t addr = si->param & 0x7fffe000u;
+
+    cpu_synchronize_state(CPU(si->cpu));
+
+    if (!address_space_access_valid(&address_space_memory, addr,
+                                    sizeof(struct LowCore), false)) {
+        set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
+        return;
+    }
+
+    /* cpu has to be stopped */
+    if (s390_cpu_get_state(si->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));
+    si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+}
+
 static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order,
                                   uint64_t param, uint64_t *status_reg)
 {
@@ -1194,12 +1327,32 @@ static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order,
         return SIGP_CC_NOT_OPERATIONAL;
     }
 
+    /* only resets can break pending orders */
+    if (dst_cpu->env.sigp_order != 0 &&
+        order != SIGP_CPU_RESET &&
+        order != SIGP_INITIAL_CPU_RESET) {
+        return SIGP_CC_BUSY;
+    }
+
     switch (order) {
     case SIGP_START:
         run_on_cpu(CPU(dst_cpu), sigp_start, &si);
         break;
+    case SIGP_STOP:
+        run_on_cpu(CPU(dst_cpu), sigp_stop, &si);
+        break;
     case SIGP_RESTART:
         run_on_cpu(CPU(dst_cpu), sigp_restart, &si);
+        break;
+    case SIGP_STOP_STORE_STATUS:
+        run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, &si);
+        break;
+    case SIGP_STORE_STATUS_ADDR:
+        run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, &si);
+        break;
+    case SIGP_SET_PREFIX:
+        run_on_cpu(CPU(dst_cpu), sigp_set_prefix, &si);
+        break;
     case SIGP_INITIAL_CPU_RESET:
         run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, &si);
         break;
@@ -1214,6 +1367,48 @@ static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order,
     return si.cc;
 }
 
+static int sigp_set_architecture(S390CPU *cpu, uint32_t param,
+                                 uint64_t *status_reg)
+{
+    CPUState *cur_cs;
+    S390CPU *cur_cpu;
+
+    /* due to the BQL, we are the only active cpu */
+    CPU_FOREACH(cur_cs) {
+        cur_cpu = S390_CPU(cur_cs);
+        if (cur_cpu->env.sigp_order != 0) {
+            return SIGP_CC_BUSY;
+        }
+        cpu_synchronize_state(cur_cs);
+        /* all but the current one have to be stopped */
+        if (cur_cpu != cpu &&
+            s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) {
+            *status_reg &= 0xffffffff00000000ULL;
+            *status_reg |= SIGP_STAT_INCORRECT_STATE;
+            return SIGP_CC_STATUS_STORED;
+        }
+    }
+
+    switch (param & 0xff) {
+    case SIGP_MODE_ESA_S390:
+        /* not supported */
+        return SIGP_CC_NOT_OPERATIONAL;
+    case SIGP_MODE_Z_ARCH_TRANS_ALL_PSW:
+    case SIGP_MODE_Z_ARCH_TRANS_CUR_PSW:
+        CPU_FOREACH(cur_cs) {
+            cur_cpu = S390_CPU(cur_cs);
+            cur_cpu->env.pfault_token = -1UL;
+        }
+        break;
+    default:
+        *status_reg &= 0xffffffff00000000ULL;
+        *status_reg |= SIGP_STAT_INVALID_PARAMETER;
+        return SIGP_CC_STATUS_STORED;
+    }
+
+    return SIGP_CC_ORDER_CODE_ACCEPTED;
+}
+
 #define SIGP_ORDER_MASK 0x000000ff
 
 static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
@@ -1236,9 +1431,7 @@ static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
 
     switch (order) {
     case SIGP_SET_ARCH:
-        *status_reg &= 0xffffffff00000000ULL;
-        *status_reg |= SIGP_STAT_INVALID_PARAMETER;
-        ret = SIGP_CC_STATUS_STORED;
+        ret = sigp_set_architecture(cpu, param, status_reg);
         break;
     default:
         /* all other sigp orders target a single vcpu */
@@ -1357,6 +1550,11 @@ static int handle_intercept(S390CPU *cpu)
             if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) {
                 qemu_system_shutdown_request();
             }
+            if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) {
+                kvm_s390_store_status(cpu, KVM_S390_STORE_STATUS_DEF_ADDR,
+                                      true);
+            }
+            cpu->env.sigp_order = 0;
             r = EXCP_HALTED;
             break;
         case ICPT_SOFT_INTERCEPT:
index fbcb0d08633b925a678096de5493fffd80b1ab0e..bd4cea726d0a4d5bf231bb40c196079fd621d5e6 100644 (file)
@@ -36,8 +36,8 @@ static int cpu_post_load(void *opaque, int version_id)
 const VMStateDescription vmstate_s390_cpu = {
     .name = "cpu",
     .post_load = cpu_post_load,
-    .version_id = 1,
-    .minimum_version_id = 1,
+    .version_id = 2,
+    .minimum_version_id = 2,
     .fields      = (VMStateField[]) {
         VMSTATE_UINT64(env.fregs[0].ll, S390CPU),
         VMSTATE_UINT64(env.fregs[1].ll, S390CPU),
@@ -71,6 +71,7 @@ const VMStateDescription vmstate_s390_cpu = {
         VMSTATE_UINT32_ARRAY(env.aregs, S390CPU, 16),
         VMSTATE_UINT64_ARRAY(env.cregs, S390CPU, 16),
         VMSTATE_UINT8(env.cpu_state, S390CPU),
+        VMSTATE_UINT8(env.sigp_order, S390CPU),
         VMSTATE_END_OF_LIST()
      },
 };
This page took 0.037502 seconds and 4 git commands to generate.