summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnthony Liguori <anthony@codemonkey.ws>2013-08-30 12:25:56 -0500
committerAnthony Liguori <anthony@codemonkey.ws>2013-08-30 12:25:56 -0500
commitb95fdc0e99e9b5c98bb8e2aee9eaffe160f1031b (patch)
tree3134edaa7e64cf853b2ab83e41c82a8f18dc5eca
parentb5d54bd42158b90b239bb6ce9c13072eb3a53fd2 (diff)
parent7f7f975295bc19829b3bd26cadc7f1c9eadb7c6b (diff)
downloadhqemu-b95fdc0e99e9b5c98bb8e2aee9eaffe160f1031b.zip
hqemu-b95fdc0e99e9b5c98bb8e2aee9eaffe160f1031b.tar.gz
Merge remote-tracking branch 'borntraeger/tags/kdump' into staging
This is a set of patches dealing with kdump support for s390x/kvm. kdump on s390x uses subcode 1 of diagnose 0x308 to put the hardware in a defined state. This is different from a full reset, since it does not touch all CPU registers. These patches define the cpu resets, the subsystem reset a load function and also wires up the "nmi" command to issue a RESTART interrupt as defined in the z/Architecture principles of operation. This allows recent guest kernels with properly setup userspace to trigger kdump: - via guest crash - via nmi from the host # gpg: Signature made Fri 30 Aug 2013 07:19:18 AM CDT using RSA key ID B5A61C7C # gpg: Can't check signature: public key not found # By Christian Borntraeger (5) and Eugene (jno) Dvurechenski (2) # Via Christian Borntraeger * borntraeger/tags/kdump: s390: wire up nmi command to raise a RESTART interrupt on S390 s390: Implement load normal reset s390/cpu: split CPU reset into architectured functions s390: provide a cpu load normal function s390: provide I/O subsystem reset s390/kvm: basic implementation of diagnose 308 subcode 6 s390x/kvm: Fix switch/case indentation for handle_diag Message-id: 1377810649-47484-1-git-send-email-borntraeger@de.ibm.com
-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 b9e5685..d74cc11 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 8c6b91a..628807f 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 aebbbf1..8fd46a9 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 cf47e3f..bb09e72 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 cbe2341..ac0460e 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 5cc9938..3c89f8a 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 65bef86..8be5648 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 26d18e3..185c8f5 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 454960a..4afd7da 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)
OpenPOWER on IntegriCloud