summaryrefslogtreecommitdiff
path: root/target-s390x
diff options
context:
space:
mode:
Diffstat (limited to 'target-s390x')
-rw-r--r--target-s390x/Makefile.objs2
-rw-r--r--target-s390x/cpu-qom.h4
-rw-r--r--target-s390x/cpu.c114
-rw-r--r--target-s390x/cpu.h41
-rw-r--r--target-s390x/helper.c19
-rw-r--r--target-s390x/kvm.c58
-rw-r--r--target-s390x/machine.c76
7 files changed, 257 insertions, 57 deletions
diff --git a/target-s390x/Makefile.objs b/target-s390x/Makefile.objs
index f8731463ff..2c5749447f 100644
--- a/target-s390x/Makefile.objs
+++ b/target-s390x/Makefile.objs
@@ -1,5 +1,5 @@
obj-y += translate.o helper.o cpu.o interrupt.o
obj-y += int_helper.o fpu_helper.o cc_helper.o mem_helper.o misc_helper.o
obj-y += gdbstub.o
-obj-$(CONFIG_SOFTMMU) += ioinst.o arch_dump.o
+obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o arch_dump.o
obj-$(CONFIG_KVM) += kvm.o
diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h
index 4f7d4cbe14..8b376df1b7 100644
--- a/target-s390x/cpu-qom.h
+++ b/target-s390x/cpu-qom.h
@@ -77,6 +77,10 @@ static inline S390CPU *s390_env_get_cpu(CPUS390XState *env)
#define ENV_OFFSET offsetof(S390CPU, env)
+#ifndef CONFIG_USER_ONLY
+extern const struct VMStateDescription vmstate_s390_cpu;
+#endif
+
void s390_cpu_do_interrupt(CPUState *cpu);
bool s390_cpu_exec_interrupt(CPUState *cpu, int int_req);
void s390_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index 2cfeb829a1..d2f6312e03 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -26,7 +26,9 @@
#include "cpu.h"
#include "qemu-common.h"
#include "qemu/timer.h"
+#include "qemu/error-report.h"
#include "hw/hw.h"
+#include "trace.h"
#ifndef CONFIG_USER_ONLY
#include "sysemu/arch_init.h"
#endif
@@ -81,7 +83,7 @@ static void s390_cpu_load_normal(CPUState *s)
S390CPU *cpu = S390_CPU(s);
cpu->env.psw.addr = ldl_phys(s->as, 4) & PSW_MASK_ESA_ADDR;
cpu->env.psw.mask = PSW_MASK_32 | PSW_MASK_64;
- s390_add_running_cpu(cpu);
+ s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
}
#endif
@@ -93,11 +95,8 @@ static void s390_cpu_reset(CPUState *s)
CPUS390XState *env = &cpu->env;
env->pfault_token = -1UL;
- s390_del_running_cpu(cpu);
scc->parent_reset(s);
-#if !defined(CONFIG_USER_ONLY)
- s->halted = 1;
-#endif
+ s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
tlb_flush(s, 1);
}
@@ -118,14 +117,10 @@ static void s390_cpu_initial_reset(CPUState *s)
env->pfault_token = -1UL;
-#if defined(CONFIG_KVM)
/* Reset state inside the kernel that we cannot access yet from QEMU. */
if (kvm_enabled()) {
- if (kvm_vcpu_ioctl(s, KVM_S390_INITIAL_RESET, NULL)) {
- perror("Initial CPU reset failed");
- }
+ kvm_s390_reset_vcpu(cpu);
}
-#endif
}
/* CPUClass:reset() */
@@ -135,9 +130,8 @@ static void s390_cpu_full_reset(CPUState *s)
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
CPUS390XState *env = &cpu->env;
- s390_del_running_cpu(cpu);
-
scc->parent_reset(s);
+ s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
memset(env, 0, offsetof(CPUS390XState, cpu_num));
@@ -147,16 +141,10 @@ static void s390_cpu_full_reset(CPUState *s)
env->pfault_token = -1UL;
- /* set halted to 1 to make sure we can add the cpu in
- * s390_ipl_cpu code, where CPUState::halted is set back to 0
- * after incrementing the cpu counter */
-#if !defined(CONFIG_USER_ONLY)
- s->halted = 1;
-
+ /* Reset state inside the kernel that we cannot access yet from QEMU. */
if (kvm_enabled()) {
kvm_s390_reset_vcpu(cpu);
}
-#endif
tlb_flush(s, 1);
}
@@ -206,10 +194,7 @@ static void s390_cpu_initfn(Object *obj)
env->tod_basetime = 0;
env->tod_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu);
env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu);
- /* set CPUState::halted state to 1 to avoid decrementing the running
- * cpu counter in s390_cpu_reset to a negative number at
- * initial ipl */
- cs->halted = 1;
+ s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
#endif
env->cpu_num = cpu_num++;
env->ext_index = -1;
@@ -229,10 +214,83 @@ static void s390_cpu_finalize(Object *obj)
#endif
}
-static const VMStateDescription vmstate_s390_cpu = {
- .name = "cpu",
- .unmigratable = 1,
-};
+#if !defined(CONFIG_USER_ONLY)
+static bool disabled_wait(CPUState *cpu)
+{
+ return cpu->halted && !(S390_CPU(cpu)->env.psw.mask &
+ (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK));
+}
+
+static unsigned s390_count_running_cpus(void)
+{
+ CPUState *cpu;
+ int nr_running = 0;
+
+ CPU_FOREACH(cpu) {
+ uint8_t state = S390_CPU(cpu)->env.cpu_state;
+ if (state == CPU_STATE_OPERATING ||
+ state == CPU_STATE_LOAD) {
+ if (!disabled_wait(cpu)) {
+ nr_running++;
+ }
+ }
+ }
+
+ return nr_running;
+}
+
+unsigned int s390_cpu_halt(S390CPU *cpu)
+{
+ CPUState *cs = CPU(cpu);
+ trace_cpu_halt(cs->cpu_index);
+
+ if (!cs->halted) {
+ cs->halted = 1;
+ cs->exception_index = EXCP_HLT;
+ }
+
+ return s390_count_running_cpus();
+}
+
+void s390_cpu_unhalt(S390CPU *cpu)
+{
+ CPUState *cs = CPU(cpu);
+ trace_cpu_unhalt(cs->cpu_index);
+
+ if (cs->halted) {
+ cs->halted = 0;
+ cs->exception_index = -1;
+ }
+}
+
+unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
+ {
+ trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state);
+
+ switch (cpu_state) {
+ case CPU_STATE_STOPPED:
+ case CPU_STATE_CHECK_STOP:
+ /* halt the cpu for common infrastructure */
+ s390_cpu_halt(cpu);
+ break;
+ case CPU_STATE_OPERATING:
+ case CPU_STATE_LOAD:
+ /* unhalt the cpu for common infrastructure */
+ s390_cpu_unhalt(cpu);
+ break;
+ default:
+ error_report("Requested CPU state is not a valid S390 CPU state: %u",
+ cpu_state);
+ exit(1);
+ }
+ if (kvm_enabled() && cpu->env.cpu_state != cpu_state) {
+ kvm_s390_set_cpu_state(cpu, cpu_state);
+ }
+ cpu->env.cpu_state = cpu_state;
+
+ return s390_count_running_cpus();
+}
+#endif
static void s390_cpu_class_init(ObjectClass *oc, void *data)
{
@@ -260,11 +318,11 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data)
cc->handle_mmu_fault = s390_cpu_handle_mmu_fault;
#else
cc->get_phys_page_debug = s390_cpu_get_phys_page_debug;
+ cc->vmsd = &vmstate_s390_cpu;
cc->write_elf64_note = s390_cpu_write_elf64_note;
cc->write_elf64_qemunote = s390_cpu_write_elf64_qemunote;
cc->cpu_exec_interrupt = s390_cpu_exec_interrupt;
#endif
- dc->vmsd = &vmstate_s390_cpu;
cc->gdb_num_core_regs = S390_NUM_CORE_REGS;
cc->gdb_core_xml_file = "s390x-core64.xml";
}
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 62940c398a..fe2f95d084 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -141,6 +141,20 @@ typedef struct CPUS390XState {
QEMUTimer *tod_timer;
QEMUTimer *cpu_timer;
+
+ /*
+ * The cpu state represents the logical state of a cpu. In contrast to other
+ * architectures, there is a difference between a halt and a stop on s390.
+ * If all cpus are either stopped (including check stop) or in the disabled
+ * wait state, the vm can be shut down.
+ */
+#define CPU_STATE_UNINITIALIZED 0x00
+#define CPU_STATE_STOPPED 0x01
+#define CPU_STATE_CHECK_STOP 0x02
+#define CPU_STATE_OPERATING 0x03
+#define CPU_STATE_LOAD 0x04
+ uint8_t cpu_state;
+
} CPUS390XState;
#include "cpu-qom.h"
@@ -375,16 +389,12 @@ int s390_virtio_hypercall(CPUS390XState *env);
void s390_virtio_irq(int config_change, uint64_t token);
#ifdef CONFIG_KVM
-void kvm_s390_reset_vcpu(S390CPU *cpu);
void kvm_s390_virtio_irq(int config_change, uint64_t token);
void kvm_s390_service_interrupt(uint32_t parm);
void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq);
void kvm_s390_floating_interrupt(struct kvm_s390_irq *irq);
int kvm_s390_inject_flic(struct kvm_s390_irq *irq);
#else
-static inline void kvm_s390_reset_vcpu(S390CPU *cpu)
-{
-}
static inline void kvm_s390_virtio_irq(int config_change, uint64_t token)
{
}
@@ -393,8 +403,9 @@ static inline void kvm_s390_service_interrupt(uint32_t parm)
}
#endif
S390CPU *s390_cpu_addr2state(uint16_t cpu_addr);
-void s390_add_running_cpu(S390CPU *cpu);
-unsigned s390_del_running_cpu(S390CPU *cpu);
+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);
/* service interrupts are floating therefore we must not pass an cpustate */
void s390_sclp_extint(uint32_t parm);
@@ -403,11 +414,16 @@ void s390_sclp_extint(uint32_t parm);
extern const hwaddr virtio_size;
#else
-static inline void s390_add_running_cpu(S390CPU *cpu)
+static inline unsigned int s390_cpu_halt(S390CPU *cpu)
{
+ return 0;
}
-static inline unsigned s390_del_running_cpu(S390CPU *cpu)
+static inline void s390_cpu_unhalt(S390CPU *cpu)
+{
+}
+
+static inline unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
{
return 0;
}
@@ -1052,6 +1068,8 @@ int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
int kvm_s390_cpu_restart(S390CPU *cpu);
int kvm_s390_get_memslot_count(KVMState *s);
void kvm_s390_clear_cmma_callback(void *opaque);
+int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state);
+void kvm_s390_reset_vcpu(S390CPU *cpu);
#else
static inline void kvm_s390_io_interrupt(uint16_t subchannel_id,
uint16_t subchannel_nr,
@@ -1082,6 +1100,13 @@ static inline int kvm_s390_get_memslot_count(KVMState *s)
{
return MAX_AVAIL_SLOTS;
}
+static inline int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state)
+{
+ return -ENOSYS;
+}
+static inline void kvm_s390_reset_vcpu(S390CPU *cpu)
+{
+}
#endif
static inline void cmma_reset(S390CPU *cpu)
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
index e21afe6b46..09aec7b42e 100644
--- a/target-s390x/helper.c
+++ b/target-s390x/helper.c
@@ -504,23 +504,18 @@ hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr)
void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
{
+ env->psw.addr = addr;
+ env->psw.mask = mask;
+ env->cc_op = (mask >> 44) & 3;
+
if (mask & PSW_MASK_WAIT) {
S390CPU *cpu = s390_env_get_cpu(env);
- CPUState *cs = CPU(cpu);
- if (!(mask & (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK))) {
- if (s390_del_running_cpu(cpu) == 0) {
+ if (s390_cpu_halt(cpu) == 0) {
#ifndef CONFIG_USER_ONLY
- qemu_system_shutdown_request();
+ qemu_system_shutdown_request();
#endif
- }
}
- cs->halted = 1;
- cs->exception_index = EXCP_HLT;
}
-
- env->psw.addr = addr;
- env->psw.mask = mask;
- env->cc_op = (mask >> 44) & 3;
}
static uint64_t get_psw_mask(CPUS390XState *env)
@@ -818,7 +813,7 @@ void s390_cpu_do_interrupt(CPUState *cs)
qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
__func__, cs->exception_index, env->psw.addr);
- s390_add_running_cpu(cpu);
+ s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
/* handle machine checks */
if ((env->psw.mask & PSW_MASK_MCHECK) &&
(cs->exception_index == -1)) {
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index a85a480c6a..5b10a255ed 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -181,9 +181,10 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu)
return cpu->cpu_index;
}
-int kvm_arch_init_vcpu(CPUState *cpu)
+int kvm_arch_init_vcpu(CPUState *cs)
{
- /* nothing todo yet */
+ S390CPU *cpu = S390_CPU(cs);
+ kvm_s390_set_cpu_state(cpu, cpu->env.cpu_state);
return 0;
}
@@ -197,7 +198,7 @@ void kvm_s390_reset_vcpu(S390CPU *cpu)
* Before this ioctl cpu_synchronize_state() is called in common kvm
* code (kvm-all) */
if (kvm_vcpu_ioctl(cs, KVM_S390_INITIAL_RESET, NULL)) {
- perror("Can't reset vcpu\n");
+ error_report("Initial CPU reset failed on CPU %i\n", cs->cpu_index);
}
}
@@ -921,7 +922,7 @@ static void sigp_cpu_start(void *arg)
CPUState *cs = arg;
S390CPU *cpu = S390_CPU(cs);
- s390_add_running_cpu(cpu);
+ s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
DPRINTF("DONE: KVM cpu start: %p\n", &cpu->env);
}
@@ -934,7 +935,7 @@ static void sigp_cpu_restart(void *arg)
};
kvm_s390_vcpu_interrupt(cpu, &irq);
- s390_add_running_cpu(cpu);
+ s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
}
int kvm_s390_cpu_restart(S390CPU *cpu)
@@ -951,6 +952,7 @@ static void sigp_initial_cpu_reset(void *arg)
cpu_synchronize_state(cpu);
scc->initial_cpu_reset(cpu);
+ cpu_synchronize_post_reset(cpu);
}
static void sigp_cpu_reset(void *arg)
@@ -960,6 +962,7 @@ static void sigp_cpu_reset(void *arg)
cpu_synchronize_state(cpu);
scc->cpu_reset(cpu);
+ cpu_synchronize_post_reset(cpu);
}
#define SIGP_ORDER_MASK 0x000000ff
@@ -1074,7 +1077,7 @@ static void unmanageable_intercept(S390CPU *cpu, const char *str, int pswoffset)
error_report("Unmanageable %s! CPU%i new PSW: 0x%016lx:%016lx",
str, cs->cpu_index, ldq_phys(cs->as, cpu->env.psa + pswoffset),
ldq_phys(cs->as, cpu->env.psa + pswoffset + 8));
- s390_del_running_cpu(cpu);
+ s390_cpu_halt(cpu);
guest_panicked();
}
@@ -1103,7 +1106,8 @@ static int handle_intercept(S390CPU *cpu)
break;
case ICPT_WAITPSW:
/* disabled wait, since enabled wait is handled in kernel */
- if (s390_del_running_cpu(cpu) == 0) {
+ cpu_synchronize_state(cs);
+ if (s390_cpu_halt(cpu) == 0) {
if (is_special_wait_psw(cs)) {
qemu_system_shutdown_request();
} else {
@@ -1113,7 +1117,7 @@ static int handle_intercept(S390CPU *cpu)
r = EXCP_HALTED;
break;
case ICPT_CPU_STOP:
- if (s390_del_running_cpu(cpu) == 0) {
+ if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) {
qemu_system_shutdown_request();
}
r = EXCP_HALTED;
@@ -1320,3 +1324,41 @@ int kvm_s390_get_memslot_count(KVMState *s)
{
return kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS);
}
+
+int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state)
+{
+ struct kvm_mp_state mp_state = {};
+ int ret;
+
+ /* the kvm part might not have been initialized yet */
+ if (CPU(cpu)->kvm_state == NULL) {
+ return 0;
+ }
+
+ switch (cpu_state) {
+ case CPU_STATE_STOPPED:
+ mp_state.mp_state = KVM_MP_STATE_STOPPED;
+ break;
+ case CPU_STATE_CHECK_STOP:
+ mp_state.mp_state = KVM_MP_STATE_CHECK_STOP;
+ break;
+ case CPU_STATE_OPERATING:
+ mp_state.mp_state = KVM_MP_STATE_OPERATING;
+ break;
+ case CPU_STATE_LOAD:
+ mp_state.mp_state = KVM_MP_STATE_LOAD;
+ break;
+ default:
+ error_report("Requested CPU state is not a valid S390 CPU state: %u",
+ cpu_state);
+ exit(1);
+ }
+
+ ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state);
+ if (ret) {
+ trace_kvm_failed_cpu_state_set(CPU(cpu)->cpu_index, cpu_state,
+ strerror(-ret));
+ }
+
+ return ret;
+}
diff --git a/target-s390x/machine.c b/target-s390x/machine.c
new file mode 100644
index 0000000000..fbcb0d0863
--- /dev/null
+++ b/target-s390x/machine.c
@@ -0,0 +1,76 @@
+/*
+ * S390x machine definitions and functions
+ *
+ * Copyright IBM Corp. 2014
+ *
+ * Authors:
+ * Thomas Huth <thuth@linux.vnet.ibm.com>
+ * Christian Borntraeger <borntraeger@de.ibm.com>
+ * Jason J. Herne <jjherne@us.ibm.com>
+ *
+ * This work is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include "hw/hw.h"
+#include "cpu.h"
+#include "sysemu/kvm.h"
+
+static int cpu_post_load(void *opaque, int version_id)
+{
+ S390CPU *cpu = opaque;
+
+ /*
+ * As the cpu state is pushed to kvm via kvm_set_mp_state rather
+ * than via cpu_synchronize_state, we need update kvm here.
+ */
+ if (kvm_enabled()) {
+ kvm_s390_set_cpu_state(cpu, cpu->env.cpu_state);
+ }
+
+ return 0;
+}
+
+const VMStateDescription vmstate_s390_cpu = {
+ .name = "cpu",
+ .post_load = cpu_post_load,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(env.fregs[0].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[1].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[2].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[3].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[4].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[5].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[6].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[7].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[8].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[9].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[10].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[11].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[12].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[13].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[14].ll, S390CPU),
+ VMSTATE_UINT64(env.fregs[15].ll, S390CPU),
+ VMSTATE_UINT64_ARRAY(env.regs, S390CPU, 16),
+ VMSTATE_UINT64(env.psw.mask, S390CPU),
+ VMSTATE_UINT64(env.psw.addr, S390CPU),
+ VMSTATE_UINT64(env.psa, S390CPU),
+ VMSTATE_UINT32(env.fpc, S390CPU),
+ VMSTATE_UINT32(env.todpr, S390CPU),
+ VMSTATE_UINT64(env.pfault_token, S390CPU),
+ VMSTATE_UINT64(env.pfault_compare, S390CPU),
+ VMSTATE_UINT64(env.pfault_select, S390CPU),
+ VMSTATE_UINT64(env.cputm, S390CPU),
+ VMSTATE_UINT64(env.ckc, S390CPU),
+ VMSTATE_UINT64(env.gbea, S390CPU),
+ VMSTATE_UINT64(env.pp, S390CPU),
+ VMSTATE_UINT32_ARRAY(env.aregs, S390CPU, 16),
+ VMSTATE_UINT64_ARRAY(env.cregs, S390CPU, 16),
+ VMSTATE_UINT8(env.cpu_state, S390CPU),
+ VMSTATE_END_OF_LIST()
+ },
+};