aboutsummaryrefslogtreecommitdiff
path: root/target-s390x
diff options
context:
space:
mode:
Diffstat (limited to 'target-s390x')
-rw-r--r--target-s390x/cpu.c2
-rw-r--r--target-s390x/cpu.h12
-rw-r--r--target-s390x/kvm.c204
-rw-r--r--target-s390x/machine.c5
4 files changed, 218 insertions, 5 deletions
diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index d2f6312e03..e0537fa222 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -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));
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 1cdfe5edaf..da0af9453f 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -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);
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index c4c867c00f..127a3b8024 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -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:
diff --git a/target-s390x/machine.c b/target-s390x/machine.c
index fbcb0d0863..bd4cea726d 100644
--- a/target-s390x/machine.c
+++ b/target-s390x/machine.c
@@ -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()
},
};