summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-06-15 13:24:50 +0100
committerPeter Maydell <peter.maydell@linaro.org>2015-06-15 13:24:51 +0100
commit46bca5404b08201bb9df1ac32bc88fc7e6db1f74 (patch)
treed4501d9f7027a6cbe51c3ab828d6f75a8f6d5087
parentf3e3b083d4c266ea864ae3c83da49d4086857679 (diff)
parent8369e339d24f365750da456588e742674c153437 (diff)
downloadfocaccia-qemu-46bca5404b08201bb9df1ac32bc88fc7e6db1f74.tar.gz
focaccia-qemu-46bca5404b08201bb9df1ac32bc88fc7e6db1f74.zip
Merge remote-tracking branch 'remotes/borntraeger/tags/s390x-20150615' into staging
s390x/kvm/watchdog

1. Implement a diag288 based watchdog
2. Fix virtio-ccw BIOS for gcc >= 4.9

# gpg: Signature made Mon Jun 15 12:36:25 2015 BST using RSA key ID B5A61C7C
# gpg: Good signature from "Christian Borntraeger (IBM) <borntraeger@de.ibm.com>"

* remotes/borntraeger/tags/s390x-20150615:
  s390/bios: build with -fdelete-null-pointer-checks
  watchdog: Add new Virtual Watchdog action INJECT-NMI
  nmi: Implement inject_nmi() for non-monitor context use
  s390x/watchdog: diag288 migration support
  s390x/kvm: diag288 instruction interception and handling
  s390x/watchdog: introduce diag288 watchdog device
  watchdog: change option wording to allow for more watchdogs

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--default-configs/s390x-softmmu.mak1
-rw-r--r--hw/core/nmi.c20
-rw-r--r--hw/watchdog/Makefile.objs1
-rw-r--r--hw/watchdog/watchdog.c10
-rw-r--r--hw/watchdog/wdt_diag288.c122
-rw-r--r--include/hw/nmi.h1
-rw-r--r--include/hw/watchdog/wdt_diag288.h36
-rw-r--r--pc-bios/s390-ccw/Makefile2
-rw-r--r--qapi-schema.json6
-rw-r--r--qemu-options.hx26
-rw-r--r--target-s390x/cpu.h1
-rw-r--r--target-s390x/kvm.c18
-rw-r--r--target-s390x/misc_helper.c29
13 files changed, 262 insertions, 11 deletions
diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak
index f9e13f177e..36e15de336 100644
--- a/default-configs/s390x-softmmu.mak
+++ b/default-configs/s390x-softmmu.mak
@@ -4,3 +4,4 @@ CONFIG_VIRTIO=y
 CONFIG_SCLPCONSOLE=y
 CONFIG_S390_FLIC=y
 CONFIG_S390_FLIC_KVM=$(CONFIG_KVM)
+CONFIG_WDT_DIAG288=y
diff --git a/hw/core/nmi.c b/hw/core/nmi.c
index 3dff020659..5260d6c1ec 100644
--- a/hw/core/nmi.c
+++ b/hw/core/nmi.c
@@ -21,6 +21,7 @@
 
 #include "hw/nmi.h"
 #include "qapi/qmp/qerror.h"
+#include "monitor/monitor.h"
 
 struct do_nmi_s {
     int cpu_index;
@@ -70,6 +71,25 @@ void nmi_monitor_handle(int cpu_index, Error **errp)
     }
 }
 
+void inject_nmi(void)
+{
+#if defined(TARGET_I386)
+    CPUState *cs;
+
+    CPU_FOREACH(cs) {
+        X86CPU *cpu = X86_CPU(cs);
+
+        if (!cpu->apic_state) {
+            cpu_interrupt(cs, CPU_INTERRUPT_NMI);
+        } else {
+            apic_deliver_nmi(cpu->apic_state);
+        }
+    }
+#else
+    nmi_monitor_handle(0, NULL);
+#endif
+}
+
 static const TypeInfo nmi_info = {
     .name          = TYPE_NMI,
     .parent        = TYPE_INTERFACE,
diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs
index 4b0374a555..72e3ffd93c 100644
--- a/hw/watchdog/Makefile.objs
+++ b/hw/watchdog/Makefile.objs
@@ -1,3 +1,4 @@
 common-obj-y += watchdog.o
 common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o
 common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o
+common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o
diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c
index 54440c91c5..8d4b0eeeb0 100644
--- a/hw/watchdog/watchdog.c
+++ b/hw/watchdog/watchdog.c
@@ -27,6 +27,7 @@
 #include "sysemu/sysemu.h"
 #include "sysemu/watchdog.h"
 #include "qapi-event.h"
+#include "hw/nmi.h"
 
 /* Possible values for action parameter. */
 #define WDT_RESET        1	/* Hard reset. */
@@ -35,6 +36,7 @@
 #define WDT_PAUSE        4	/* Pause. */
 #define WDT_DEBUG        5	/* Prints a message and continues running. */
 #define WDT_NONE         6	/* Do nothing. */
+#define WDT_NMI          7	/* Inject nmi into the guest */
 
 static int watchdog_action = WDT_RESET;
 static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
@@ -95,6 +97,8 @@ int select_watchdog_action(const char *p)
         watchdog_action = WDT_DEBUG;
     else if (strcasecmp(p, "none") == 0)
         watchdog_action = WDT_NONE;
+    else if (strcasecmp(p, "inject-nmi") == 0)
+        watchdog_action = WDT_NMI;
     else
         return -1;
 
@@ -138,5 +142,11 @@ void watchdog_perform_action(void)
     case WDT_NONE:
         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_NONE, &error_abort);
         break;
+
+    case WDT_NMI:
+        qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_INJECT_NMI,
+                                 &error_abort);
+        inject_nmi();
+        break;
     }
 }
diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c
new file mode 100644
index 0000000000..1185e0681c
--- /dev/null
+++ b/hw/watchdog/wdt_diag288.c
@@ -0,0 +1,122 @@
+/*
+ * watchdog device diag288 support
+ *
+ * Copyright IBM, Corp. 2015
+ *
+ * Authors:
+ *  Xu Wang <gesaint@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at your
+ * option) any later version.  See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "sysemu/watchdog.h"
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "hw/watchdog/wdt_diag288.h"
+
+static WatchdogTimerModel model = {
+    .wdt_name = TYPE_WDT_DIAG288,
+    .wdt_description = "diag288 device for s390x platform",
+};
+
+static const VMStateDescription vmstate_diag288 = {
+    .name = "vmstate_diag288",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_TIMER_PTR(timer, DIAG288State),
+        VMSTATE_BOOL(enabled, DIAG288State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void wdt_diag288_reset(DeviceState *dev)
+{
+    DIAG288State *diag288 = DIAG288(dev);
+
+    diag288->enabled = false;
+    timer_del(diag288->timer);
+}
+
+static void diag288_timer_expired(void *dev)
+{
+    qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
+    watchdog_perform_action();
+    wdt_diag288_reset(dev);
+}
+
+static int wdt_diag288_handle_timer(DIAG288State *diag288,
+                                     uint64_t func, uint64_t timeout)
+{
+    switch (func) {
+    case WDT_DIAG288_INIT:
+        diag288->enabled = true;
+        /* fall through */
+    case WDT_DIAG288_CHANGE:
+        if (!diag288->enabled) {
+            return -1;
+        }
+        timer_mod(diag288->timer,
+                  qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+                  timeout * get_ticks_per_sec());
+        break;
+    case WDT_DIAG288_CANCEL:
+        if (!diag288->enabled) {
+            return -1;
+        }
+        diag288->enabled = false;
+        timer_del(diag288->timer);
+        break;
+    default:
+        return -1;
+    }
+
+    return 0;
+}
+
+static void wdt_diag288_realize(DeviceState *dev, Error **errp)
+{
+    DIAG288State *diag288 = DIAG288(dev);
+
+    diag288->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, diag288_timer_expired,
+                                  dev);
+}
+
+static void wdt_diag288_unrealize(DeviceState *dev, Error **errp)
+{
+    DIAG288State *diag288 = DIAG288(dev);
+
+    timer_del(diag288->timer);
+    timer_free(diag288->timer);
+}
+
+static void wdt_diag288_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    DIAG288Class *diag288 = DIAG288_CLASS(klass);
+
+    dc->realize = wdt_diag288_realize;
+    dc->unrealize = wdt_diag288_unrealize;
+    dc->reset = wdt_diag288_reset;
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+    dc->vmsd = &vmstate_diag288;
+    diag288->handle_timer = wdt_diag288_handle_timer;
+}
+
+static const TypeInfo wdt_diag288_info = {
+    .class_init = wdt_diag288_class_init,
+    .parent = TYPE_DEVICE,
+    .name  = TYPE_WDT_DIAG288,
+    .instance_size  = sizeof(DIAG288State),
+    .class_size = sizeof(DIAG288Class),
+};
+
+static void wdt_diag288_register_types(void)
+{
+    watchdog_add_model(&model);
+    type_register_static(&wdt_diag288_info);
+}
+
+type_init(wdt_diag288_register_types)
diff --git a/include/hw/nmi.h b/include/hw/nmi.h
index b541772e1d..f4cec6257d 100644
--- a/include/hw/nmi.h
+++ b/include/hw/nmi.h
@@ -45,5 +45,6 @@ typedef struct NMIClass {
 } NMIClass;
 
 void nmi_monitor_handle(int cpu_index, Error **errp);
+void inject_nmi(void);
 
 #endif /* NMI_H */
diff --git a/include/hw/watchdog/wdt_diag288.h b/include/hw/watchdog/wdt_diag288.h
new file mode 100644
index 0000000000..7f3fd450dc
--- /dev/null
+++ b/include/hw/watchdog/wdt_diag288.h
@@ -0,0 +1,36 @@
+#ifndef WDT_DIAG288_H
+#define WDT_DIAG288_H
+
+#include "hw/qdev.h"
+
+#define TYPE_WDT_DIAG288 "diag288"
+#define DIAG288(obj) \
+    OBJECT_CHECK(DIAG288State, (obj), TYPE_WDT_DIAG288)
+#define DIAG288_CLASS(klass) \
+    OBJECT_CLASS_CHECK(DIAG288Class, (klass), TYPE_WDT_DIAG288)
+#define DIAG288_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(DIAG288Class, (obj), TYPE_WDT_DIAG288)
+
+#define WDT_DIAG288_INIT      0
+#define WDT_DIAG288_CHANGE    1
+#define WDT_DIAG288_CANCEL    2
+
+typedef struct DIAG288State {
+    /*< private >*/
+    DeviceState parent_obj;
+    QEMUTimer *timer;
+    bool enabled;
+
+    /*< public >*/
+} DIAG288State;
+
+typedef struct DIAG288Class {
+    /*< private >*/
+    DeviceClass parent_class;
+
+    /*< public >*/
+    int (*handle_timer)(DIAG288State *dev,
+                        uint64_t func, uint64_t timeout);
+} DIAG288Class;
+
+#endif  /* WDT_DIAG288_H */
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 009bb8de1c..746603a315 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -10,7 +10,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
 .PHONY : all clean build-all
 
 OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o
-CFLAGS += -fPIE -fno-stack-protector -ffreestanding
+CFLAGS += -fPIE -fno-stack-protector -ffreestanding -fno-delete-null-pointer-checks
 LDFLAGS += -Wl,-pie -nostdlib
 
 build-all: s390-ccw.img
diff --git a/qapi-schema.json b/qapi-schema.json
index bcc604b813..106008cdeb 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3746,10 +3746,14 @@
 #
 # @none: nothing is done
 #
+# @inject-nmi: a non-maskable interrupt is injected into the first VCPU (all
+#              VCPUS on x86) (since 2.4)
+#
 # Since: 2.1
 ##
 { 'enum': 'WatchdogExpirationAction',
-  'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none' ] }
+  'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none',
+            'inject-nmi' ] }
 
 ##
 # @IoOperationType
diff --git a/qemu-options.hx b/qemu-options.hx
index 289ddcb944..bf1683e5b4 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3153,7 +3153,7 @@ when the shift value is high (how high depends on the host machine).
 ETEXI
 
 DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
-    "-watchdog i6300esb|ib700\n" \
+    "-watchdog model\n" \
     "                enable virtual hardware watchdog [default=none]\n",
     QEMU_ARCH_ALL)
 STEXI
@@ -3161,16 +3161,24 @@ STEXI
 @findex -watchdog
 Create a virtual hardware watchdog device.  Once enabled (by a guest
 action), the watchdog must be periodically polled by an agent inside
-the guest or else the guest will be restarted.
+the guest or else the guest will be restarted. Choose a model for
+which your guest has drivers.
 
-The @var{model} is the model of hardware watchdog to emulate.  Choices
-for model are: @code{ib700} (iBASE 700) which is a very simple ISA
-watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
-controller hub) which is a much more featureful PCI-based dual-timer
-watchdog.  Choose a model for which your guest has drivers.
-
-Use @code{-watchdog help} to list available hardware models.  Only one
+The @var{model} is the model of hardware watchdog to emulate. Use
+@code{-watchdog help} to list available hardware models. Only one
 watchdog can be enabled for a guest.
+
+The following models may be available:
+@table @option
+@item ib700
+iBASE 700 is a very simple ISA watchdog with a single timer.
+@item i6300esb
+Intel 6300ESB I/O controller hub is a much more featureful PCI-based
+dual-timer watchdog.
+@item diag288
+A virtual watchdog for s390x backed by the diagnose 288 hypercall
+(currently KVM only).
+@end table
 ETEXI
 
 DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 584e74b89a..d63eb51186 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -1100,6 +1100,7 @@ uint32_t set_cc_nz_f128(float128 v);
 
 /* misc_helper.c */
 #ifndef CONFIG_USER_ONLY
+int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3);
 void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3);
 #endif
 void program_interrupt(CPUS390XState *env, uint32_t code, int ilen);
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index f6f61b9619..b02ff8d61d 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -98,6 +98,7 @@
 #define PRIV_E3_MPCIFC                  0xd0
 #define PRIV_E3_STPCIFC                 0xd4
 
+#define DIAG_TIMEREVENT                 0x288
 #define DIAG_IPL                        0x308
 #define DIAG_KVM_HYPERCALL              0x500
 #define DIAG_KVM_BREAKPOINT             0x501
@@ -1267,6 +1268,20 @@ static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
     return ret;
 }
 
+static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run)
+{
+    uint64_t r1, r3;
+    int rc;
+
+    cpu_synchronize_state(CPU(cpu));
+    r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
+    r3 = run->s390_sieic.ipa & 0x000f;
+    rc = handle_diag_288(&cpu->env, r1, r3);
+    if (rc) {
+        enter_pgmcheck(cpu, PGM_SPECIFICATION);
+    }
+}
+
 static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run)
 {
     uint64_t r1, r3;
@@ -1306,6 +1321,9 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
      */
     func_code = decode_basedisp_rs(&cpu->env, ipb, NULL) & DIAG_KVM_CODE_MASK;
     switch (func_code) {
+    case DIAG_TIMEREVENT:
+        kvm_handle_diag_288(cpu, run);
+        break;
     case DIAG_IPL:
         kvm_handle_diag_308(cpu, run);
         break;
diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c
index b375ab724b..6711504221 100644
--- a/target-s390x/misc_helper.c
+++ b/target-s390x/misc_helper.c
@@ -30,6 +30,7 @@
 #include <linux/kvm.h>
 #endif
 #include "exec/cpu_ldst.h"
+#include "hw/watchdog/wdt_diag288.h"
 
 #if !defined(CONFIG_USER_ONLY)
 #include "sysemu/cpus.h"
@@ -153,6 +154,34 @@ static int load_normal_reset(S390CPU *cpu)
     return 0;
 }
 
+int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
+{
+    uint64_t func = env->regs[r1];
+    uint64_t timeout = env->regs[r1 + 1];
+    uint64_t action = env->regs[r3];
+    Object *obj;
+    DIAG288State *diag288;
+    DIAG288Class *diag288_class;
+
+    if (r1 % 2 || action != 0) {
+        return -1;
+    }
+
+    /* Timeout must be more than 15 seconds except for timer deletion */
+    if (func != WDT_DIAG288_CANCEL && timeout < 15) {
+        return -1;
+    }
+
+    obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
+    if (!obj) {
+        return -1;
+    }
+
+    diag288 = DIAG288(obj);
+    diag288_class = DIAG288_GET_CLASS(diag288);
+    return diag288_class->handle_timer(diag288, func, timeout);
+}
+
 #define DIAG_308_RC_OK              0x0001
 #define DIAG_308_RC_NO_CONF         0x0102
 #define DIAG_308_RC_INVALID         0x0402