qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 6/8] s390: sclp event facility and signal quiesce su


From: Jens Freimann
Subject: [Qemu-devel] [PATCH 6/8] s390: sclp event facility and signal quiesce support via system_powerdown
Date: Wed, 6 Jun 2012 14:05:21 +0200

From: Heinz Graalfs <address@hidden>

This patch implements a subset of the sclp event facility as well
as the signal quiesce event. This allows to gracefully shutdown
a guest by using system_powerdown. This sends a signal quiesce
signal that is interpreted by linux guests as ctrl-alt-del.
In addition the event facility is modeled using the QOM.

Signed-off-by: Heinz Graalfs <address@hidden>
Signed-off-by: Christian Borntraeger <address@hidden>
Signed-off-by: Jens Freimann <address@hidden>
---
 Makefile.target          |    1 +
 hw/s390-event-facility.c |  232 +++++++++++++++++++++++++++++++++++++++++
 hw/s390-event-facility.h |   54 ++++++++++
 hw/s390-sclp.c           |  256 +++++++++++++++++++++++++++++++++++++++++++++-
 hw/s390-sclp.h           |   98 +++++++++++++++++-
 hw/s390-virtio.c         |   11 +-
 target-s390x/op_helper.c |    3 +
 7 files changed, 649 insertions(+), 6 deletions(-)
 create mode 100644 hw/s390-event-facility.c
 create mode 100644 hw/s390-event-facility.h

diff --git a/Makefile.target b/Makefile.target
index fed2d72..f939c3a 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -375,6 +375,7 @@ obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o 
mcf5208.o mcf_fec.o
 obj-m68k-y += m68k-semi.o dummy_m68k.o
 
 obj-s390x-y = s390-virtio-bus.o s390-virtio.o s390-sclp.o
+obj-s390x-y += s390-event-facility.o
 
 obj-alpha-y = mc146818rtc.o
 obj-alpha-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o
diff --git a/hw/s390-event-facility.c b/hw/s390-event-facility.c
new file mode 100644
index 0000000..b8106a6
--- /dev/null
+++ b/hw/s390-event-facility.c
@@ -0,0 +1,232 @@
+/*
+ * SCLP Event Facility
+ *
+ * Copyright IBM Corp. 2007,2012
+ * Author: Heinz Graalfs <address@hidden>
+ *
+ * This file is licensed under the terms of the GNU General Public License(GPL)
+ */
+
+#include "iov.h"
+#include "monitor.h"
+#include "qemu-queue.h"
+#include "sysbus.h"
+#include "sysemu.h"
+
+#include "s390-sclp.h"
+#include "s390-event-facility.h"
+
+struct SCLPDevice {
+    const char *name;
+    bool vm_running;
+};
+
+struct SCLP {
+    BusState qbus;
+    SCLPEventFacility *event_facility;
+};
+
+struct SCLPEventFacility {
+    SCLPDevice sdev;
+    SCLP sbus;
+    DeviceState *qdev;
+    void *opaque;
+};
+
+typedef struct SCLPConsole {
+    SCLPEvent event;
+    CharDriverState *chr;
+} SCLPConsole;
+
+static void sclpef_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
+{
+    SCLPEvent *event = DO_UPCAST(SCLPEvent, dev, qdev);
+
+    monitor_printf(mon, "%*sid %d\n", indent, "", event->id);
+}
+
+static unsigned int send_mask_quiesce(void)
+{
+    return SCLP_EVENT_MASK_SIGNAL_QUIESCE;
+}
+
+static unsigned int receive_mask_quiesce(void)
+{
+    return 0;
+}
+
+static void s390_signal_quiesce(void *opaque, int n, int level)
+{
+    sclp_enable_signal_quiesce();
+    sclp_service_interrupt(opaque, 0);
+}
+
+unsigned int sclpef_send_mask(SCLPDevice *sdev)
+{
+    unsigned int mask;
+    DeviceState *dev;
+    SCLPEventFacility *event_facility;
+    SCLPEventClass *cons;
+
+    event_facility = DO_UPCAST(SCLPEventFacility, sdev, sdev);
+
+    mask = 0;
+
+    QTAILQ_FOREACH(dev, &event_facility->sbus.qbus.children, sibling) {
+        cons = SCLP_EVENT_GET_CLASS((SCLPEvent *) dev);
+        mask |= cons->get_send_mask();
+    }
+    return mask;
+}
+
+unsigned int sclpef_receive_mask(SCLPDevice *sdev)
+{
+    unsigned int mask;
+    DeviceState *dev;
+    SCLPEventClass *cons;
+    SCLPEventFacility *event_facility;
+
+    event_facility = DO_UPCAST(SCLPEventFacility, sdev, sdev);
+
+    mask = 0;
+
+    QTAILQ_FOREACH(dev, &event_facility->sbus.qbus.children, sibling) {
+        cons = SCLP_EVENT_GET_CLASS((SCLPEvent *) dev);
+        mask |= cons->get_receive_mask();
+    }
+    return mask;
+}
+
+static struct BusInfo sclp_bus_info = {
+    .name      = "s390-sclp-bus",
+    .size      = sizeof(SCLPS390Bus),
+    .print_dev = sclpef_bus_dev_print,
+    .props      = (Property[]) {
+        DEFINE_PROP_STRING("name", SCLPEvent, name),
+        DEFINE_PROP_END_OF_LIST()
+    }
+};
+
+static int sclpef_qdev_init(DeviceState *qdev)
+{
+    int rc;
+    SCLPEvent *event = DO_UPCAST(SCLPEvent, dev, qdev);
+    SCLPEventClass *cons = SCLP_EVENT_GET_CLASS(event);
+    SCLP *bus = DO_UPCAST(SCLP, qbus, qdev->parent_bus);
+
+    event->evt_fac = bus->event_facility;
+    rc = cons->init(event);
+
+    return rc;
+}
+
+static int sclpef_qdev_exit(DeviceState *qdev)
+{
+    SCLPEvent *event = DO_UPCAST(SCLPEvent, dev, qdev);
+    SCLPEventClass *cons = SCLP_EVENT_GET_CLASS(event);
+    if (cons->exit) {
+        cons->exit(event);
+    }
+    return 0;
+}
+
+static SCLPDevice *sclpef_common_init(const char *name, size_t struct_size)
+{
+    SCLPDevice *sdev;
+
+    sdev = malloc(struct_size);
+    if (!sdev) {
+        return NULL;
+    }
+    sdev->vm_running = runstate_is_running();
+    sdev->name = name;
+
+    return sdev;
+}
+
+void sclpef_enable_irqs(SCLPDevice *sdev, void *opaque)
+{
+    SCLPEventFacility *event_facility;
+
+    event_facility = DO_UPCAST(SCLPEventFacility, sdev, sdev);
+    qemu_system_powerdown = *qemu_allocate_irqs(s390_signal_quiesce,
+                                                opaque, 1);
+    event_facility->opaque = opaque;
+}
+
+SCLPDevice *sclpef_init_event_facility(DeviceState *dev)
+{
+    DeviceState *quiesce;
+    SCLPDevice *sdev;
+    SCLPEventFacility *event_facility;
+
+    sdev = sclpef_common_init("sclp-event-facility",
+            sizeof(SCLPEventFacility));
+
+    if (!sdev) {
+        return NULL;
+    }
+    event_facility = DO_UPCAST(SCLPEventFacility, sdev, sdev);
+
+    /* Spawn a new sclp-facility */
+    qbus_create_inplace(&event_facility->sbus.qbus, &sclp_bus_info, dev, NULL);
+    event_facility->sbus.qbus.allow_hotplug = 0;
+    event_facility->sbus.event_facility = event_facility;
+    event_facility->qdev = dev;
+    event_facility->opaque = NULL;
+    quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce");
+
+    if (!quiesce) {
+        return NULL;
+    }
+
+    return sdev;
+}
+
+static void sclpef_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    dc->init = sclpef_qdev_init;
+    dc->bus_info = &sclp_bus_info;
+    dc->exit = sclpef_qdev_exit;
+    dc->unplug = qdev_simple_unplug_cb;
+}
+
+static TypeInfo sclp_event_facility_type_info = {
+    .name = TYPE_SCLP_EVENT,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(SCLPEvent),
+    .class_size = sizeof(SCLPEventClass),
+    .class_init = sclpef_class_init,
+    .abstract = true,
+};
+
+static int sclpquiesce_initfn(SCLPEvent *event)
+{
+    event->id = ID_QUIESCE;
+
+    return 0;
+}
+
+static void sclpef_quiesce_class_init(ObjectClass *klass, void *data)
+{
+    SCLPEventClass *k = SCLP_EVENT_CLASS(klass);
+
+    k->init = sclpquiesce_initfn;
+    k->get_send_mask = send_mask_quiesce;
+    k->get_receive_mask = receive_mask_quiesce;
+}
+
+static TypeInfo sclp_quiesce_info = {
+    .name          = "sclpquiesce",
+    .parent        = TYPE_SCLP_EVENT,
+    .instance_size = sizeof(SCLPEvent),
+    .class_init    = sclpef_quiesce_class_init,
+};
+
+static void sclpef_register_types(void)
+{
+    type_register_static(&sclp_event_facility_type_info);
+    type_register_static(&sclp_quiesce_info);
+}
+type_init(sclpef_register_types)
diff --git a/hw/s390-event-facility.h b/hw/s390-event-facility.h
new file mode 100644
index 0000000..40d4049
--- /dev/null
+++ b/hw/s390-event-facility.h
@@ -0,0 +1,54 @@
+/*
+ * SCLP definitions
+ *
+ * Copyright IBM Corp. 2007,2012
+ * Author: Heinz Graalfs <address@hidden>
+ *
+ * This file is licensed under the terms of the GNU General Public License(GPL)
+ */
+
+#ifndef _QEMU_SCLP_EVENT_H
+#define _QEMU_SCLP_EVENT_H
+
+#include "qdev.h"
+#include "qemu-common.h"
+
+#define ID_QUIESCE SCLP_EVENT_SIGNAL_QUIESCE
+
+#define TYPE_SCLP_EVENT "s390-sclp-event-type"
+#define SCLP_EVENT(obj) \
+     OBJECT_CHECK(SCLPEvent, (obj), TYPE_SCLP_EVENT)
+#define SCLP_EVENT_CLASS(klass) \
+     OBJECT_CLASS_CHECK(SCLPEventClass, (klass), TYPE_SCLP_EVENT)
+#define SCLP_EVENT_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(SCLPEventClass, (obj), TYPE_SCLP_EVENT)
+
+typedef struct SCLP SCLP;
+typedef struct SCLPEventFacility SCLPEventFacility;
+typedef struct SCLPEvent SCLPEvent;
+typedef struct SCLPDevice SCLPDevice;
+
+typedef struct SCLPEventClass {
+    DeviceClass parent_class;
+    int (*init)(SCLPEvent *event);
+    int (*exit)(SCLPEvent *event);
+    unsigned int (*get_send_mask)(void);
+    unsigned int (*get_receive_mask)(void);
+} SCLPEventClass;
+
+struct SCLPEvent {
+    DeviceState dev;
+    SCLPEventFacility *evt_fac;
+    char *name;
+    uint32_t id;
+};
+
+SCLPDevice *sclpef_init_event_facility(DeviceState *dev);
+
+void sclpef_enable_irqs(SCLPDevice *sdev, void *opaque);
+
+void sclpef_set_masks(void);
+unsigned int sclpef_send_mask(SCLPDevice *sdev);
+unsigned int sclpef_receive_mask(SCLPDevice *sdev);
+
+#endif
diff --git a/hw/s390-sclp.c b/hw/s390-sclp.c
index c046441..683a709 100644
--- a/hw/s390-sclp.c
+++ b/hw/s390-sclp.c
@@ -7,7 +7,20 @@
 
 #include "cpu.h"
 #include "kvm.h"
+#include "hw/sysbus.h"
 #include "hw/s390-sclp.h"
+#include "hw/s390-event-facility.h"
+
+/* Host capabilites */
+static unsigned int sclp_send_mask;
+static unsigned int sclp_receive_mask;
+
+/* Guest capabilities */
+static unsigned int sclp_cp_send_mask;
+static unsigned int sclp_cp_receive_mask;
+
+static int quiesce;
+static int event_pending;
 
 int sclp_read_info(CPUS390XState *env, struct sccb *sccb)
 {
@@ -23,20 +36,257 @@ int sclp_read_info(CPUS390XState *env, struct sccb *sccb)
     return 0;
 }
 
+static int signal_quiesce_event(struct sccb *sccb, int *slen)
+{
+    if (!quiesce) {
+        return 0;
+    }
+
+    if (*slen < sizeof(struct signal_quiesce)) {
+        event_pending = 1;
+        return 0;
+    }
+
+    sccb->c.read.quiesce.h.length = cpu_to_be16(sizeof(struct signal_quiesce));
+    sccb->c.read.quiesce.h.type = SCLP_EVENT_SIGNAL_QUIESCE;
+    sccb->c.read.quiesce.h.flags &= ~SCLP_EVENT_BUFFER_ACCEPTED;
+    /*
+     * system_powerdown does not have a timeout. Fortunately the
+     * timeout value is curently ignored by Linux, anyway
+     */
+    sccb->c.read.quiesce.timeout = cpu_to_be16(0);
+    sccb->c.read.quiesce.unit = cpu_to_be16(0);
+
+    quiesce = 0;
+    *slen -= sizeof(struct signal_quiesce);
+    return 1;
+}
+
+void sclp_enable_signal_quiesce(void)
+{
+    quiesce = 1;
+    event_pending = 1;
+}
+
+static void sclp_set_masks(void)
+{
+    SCLPS390EventFacility *evt_fac;
+
+    if (!sclp_bus) {
+        return;
+    }
+    evt_fac = sclp_bus->event_facility;
+
+    assert(evt_fac);
+
+    sclp_send_mask = sclpef_send_mask(evt_fac->sdev);
+    sclp_receive_mask = sclpef_receive_mask(evt_fac->sdev);
+ }
+
+int sclp_read_event_data(CPUS390XState *env, struct sccb *sccb)
+{
+    unsigned int sclp_active_selection_mask;
+    int slen;
+
+    if (be16_to_cpu(sccb->h.length) != SCCB_SIZE) {
+        sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH);
+        goto out;
+    }
+
+    switch (sccb->h.function_code) {
+    case SCLP_UNCONDITIONAL_READ:
+        sclp_active_selection_mask = sclp_cp_receive_mask;
+        break;
+    case SCLP_SELECTIVE_READ:
+        if (!(sclp_cp_receive_mask & be32_to_cpu(sccb->c.read.mask))) {
+            sccb->h.response_code = 
cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK);
+            goto out;
+        }
+        sclp_active_selection_mask = be32_to_cpu(sccb->c.read.mask);
+        break;
+    default:
+        sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION);
+        goto out;
+    }
+
+    slen = sizeof(sccb->c.data);
+    sccb->h.response_code = cpu_to_be16(SCLP_RC_NO_EVENT_BUFFERS_STORED);
+
+    if (sclp_active_selection_mask & SCLP_EVENT_MASK_SIGNAL_QUIESCE) {
+        if (signal_quiesce_event(sccb, &slen)) {
+            sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
+        }
+    }
+
+    if (sccb->h.control_mask[2] & SCLP_VARIABLE_LENGTH_RESPONSE) {
+        sccb->h.control_mask[2] &= ~SCLP_VARIABLE_LENGTH_RESPONSE;
+        sccb->h.length = cpu_to_be16(SCCB_SIZE - slen);
+    }
+
+out:
+    return 0;
+}
+
+int sclp_write_event_mask(CPUS390XState *env, struct sccb *sccb)
+{
+    /* Attention: We assume that Linux uses 4-byte masks, what it actually
+       does. Architecture allows for masks of variable size, though */
+    if (be16_to_cpu(sccb->c.we_mask.mask_length) != 4) {
+        sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_MASK_LENGTH);
+        goto out;
+    }
+
+    /* keep track of the guest's capability masks */
+    sclp_cp_send_mask = be32_to_cpu(sccb->c.we_mask.cp_send_mask);
+    sclp_cp_receive_mask = be32_to_cpu(sccb->c.we_mask.cp_receive_mask);
+
+    sclp_set_masks();
+
+    /* return the SCLP's capability masks to the guest */
+    sccb->c.we_mask.send_mask = cpu_to_be32(sclp_send_mask);
+    sccb->c.we_mask.receive_mask = cpu_to_be32(sclp_receive_mask);
+
+    sccb->h.response_code = cpu_to_be32(SCLP_RC_NORMAL_COMPLETION);
+
+out:
+    return 0;
+}
+
 void sclp_service_interrupt(CPUS390XState *env, uint32_t sccb)
 {
-    if (!sccb) {
+    if (!event_pending && !sccb) {
         return;
     }
 
     if (kvm_enabled()) {
 #ifdef CONFIG_KVM
         kvm_s390_interrupt_internal(env, KVM_S390_INT_SERVICE,
-                                    (sccb & ~3), 0, 1);
+                                    (sccb & ~3) + event_pending, 0, 1);
 #endif
     } else {
         env->psw.addr += 4;
-        cpu_inject_ext(env, EXT_SERVICE, (sccb & ~3), 0);
+        cpu_inject_ext(env, EXT_SERVICE, (sccb & ~3) + event_pending, 0);
+    }
+    event_pending = 0;
+}
+
+struct BusInfo s390_sclp_bus_info = {
+    .name       = "s390-sclp",
+    .size       = sizeof(SCLPS390Bus),
+};
+
+SCLPS390Bus *s390_sclp_bus_init(void)
+{
+    SCLPS390Bus *bus;
+    BusState *bus_state;
+    DeviceState *dev;
+
+    dev = qdev_create(NULL, "s390-sclp-bridge");
+    qdev_init_nofail(dev);
+    bus_state = qbus_create(&s390_sclp_bus_info, dev, "s390-sclp-bus");
+    bus = DO_UPCAST(SCLPS390Bus, bus, bus_state);
+
+    bus_state->allow_hotplug = 0;
+
+    return bus;
+}
+
+static int s390_sclp_device_init(SCLPS390EventFacility *dev, SCLPDevice *sdev)
+{
+    dev->sdev = sdev;
+
+    return 0;
+}
+
+static int s390_sclp_init(SCLPS390EventFacility *dev)
+{
+    int rc;
+    SCLPS390Bus *bus;
+    SCLPDevice *sdev;
+
+    bus = DO_UPCAST(SCLPS390Bus, bus, dev->qdev.parent_bus);
+
+    sdev = sclpef_init_event_facility((DeviceState *)dev);
+    if (!sdev) {
+        return -1;
     }
+
+    rc = s390_sclp_device_init(dev, sdev);
+    if (!rc) {
+        bus->event_facility = dev;
+    }
+
+    return rc;
+}
+
+static void s390_sclp_class_init(ObjectClass *klass, void *data)
+{
+    SCLPS390EventFacilityClass *k = SCLP_S390_EVENT_FACILITY_CLASS(klass);
+
+    k->init = s390_sclp_init;
+}
+
+static TypeInfo s390_sclp_event_facility = {
+    .name          = "sclp-event-facility",
+    .parent        = TYPE_SCLP_S390_EVENT_FACILITY,
+    .instance_size = sizeof(SCLPS390EventFacility),
+    .class_init    = s390_sclp_class_init,
+};
+
+static int s390_sclp_busdev_init(DeviceState *dev)
+{
+    SCLPS390EventFacility *_dev = (SCLPS390EventFacility *)dev;
+    SCLPS390EventFacilityClass *evt_fac =
+        SCLP_S390_EVENT_FACILITY_GET_CLASS(dev);
+
+    return evt_fac->init(_dev);
 }
 
+static void sclp_s390_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->init = s390_sclp_busdev_init;
+    dc->bus_info = &s390_sclp_bus_info;
+}
+
+static TypeInfo sclp_s390_device_info = {
+    .name = TYPE_SCLP_S390_EVENT_FACILITY,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(SCLPS390EventFacility),
+    .class_init = sclp_s390_device_class_init,
+    .class_size = sizeof(SCLPS390EventFacilityClass),
+    .abstract = true,
+};
+
+/***************** S390 SCLP Bus Bridge Device *******************/
+/* Only required to have the sclp bus as child in the system bus */
+
+static int s390_sclp_bridge_init(SysBusDevice *dev)
+{
+    return 0;
+}
+
+static void s390_sclp_bridge_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = s390_sclp_bridge_init;
+    dc->no_user = 1;
+}
+
+static TypeInfo s390_sclp_bridge_info = {
+    .name          = "s390-sclp-bridge",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SysBusDevice),
+    .class_init    = s390_sclp_bridge_class_init,
+};
+
+static void s390_sclp_register_types(void)
+{
+    type_register_static(&sclp_s390_device_info);
+    type_register_static(&s390_sclp_event_facility);
+    type_register_static(&s390_sclp_bridge_info);
+}
+type_init(s390_sclp_register_types)
diff --git a/hw/s390-sclp.h b/hw/s390-sclp.h
index e335b21..f61421b 100644
--- a/hw/s390-sclp.h
+++ b/hw/s390-sclp.h
@@ -1,13 +1,69 @@
 #include <stdint.h>
 #include <qemu-common.h>
 
+#include "sysbus.h"
+#include "hw/s390-event-facility.h"
 
 /* SCLP command codes */
 #define SCLP_CMDW_READ_SCP_INFO                 0x00020001
 #define SCLP_CMDW_READ_SCP_INFO_FORCED          0x00120001
+#define SCLP_CMD_WRITE_EVENT_MASK               0x00780005
 
 /* SCLP response codes */
-#define SCLP_RC_SCCB_RESOURCE_INSUFFICENT       0x07f0
+#define SCLP_RC_NORMAL_COMPLETION               0x0020
+#define SCLP_RC_INSUFFICIENT_SCCB_LENGTH        0x0300
+#define SCLP_RC_INVALID_FUNCTION                0x40f0
+#define SCLP_RC_NO_EVENT_BUFFERS_STORED         0x60f0
+#define SCLP_RC_INVALID_SELECTION_MASK          0x70f0
+#define SCLP_RC_INCONSISTENT_LENGTHS            0x72f0
+#define SCLP_RC_EVENT_BUFFER_SYNTAX_ERROR       0x73f0
+#define SCLP_RC_INVALID_MASK_LENGTH             0x74f0
+
+/* SCLP event types */
+#define SCLP_EVENT_SIGNAL_QUIESCE               0x1d
+
+/* SCLP event masks */
+#define SCLP_EVENT_MASK_SIGNAL_QUIESCE          0x00000008
+#define SCLP_EVENT_MASK_MSG                     0x40000000
+
+#define SCLP_UNCONDITIONAL_READ                 0x00
+#define SCLP_SELECTIVE_READ                     0x01
+
+#define SCLP_VARIABLE_LENGTH_RESPONSE           0x80
+
+#define SCCB_SIZE 4096
+
+/* Service Call Control Block (SCCB) and its elements */
+
+struct write_event_mask {
+    uint16_t _reserved;
+    uint16_t mask_length;
+    uint32_t cp_receive_mask;
+    uint32_t cp_send_mask;
+    uint32_t send_mask;
+    uint32_t receive_mask;
+} __attribute__((packed));
+
+struct event_buffer_header {
+    uint16_t length;
+    uint8_t  type;
+#define SCLP_EVENT_BUFFER_ACCEPTED              0x80
+    uint8_t  flags;
+    uint16_t _reserved;
+} __attribute__((packed));
+
+struct signal_quiesce {
+    struct event_buffer_header h;
+    uint16_t timeout;
+    uint8_t unit;
+} __attribute__((packed));
+
+struct read_event_data {
+    union {
+        struct signal_quiesce quiesce;
+        uint32_t mask;
+    };
+} __attribute__((packed));
 
 struct sccb_header {
     uint16_t length;
@@ -22,13 +78,51 @@ struct read_info_sccb {
     uint8_t rnsize;
 } __attribute__((packed));
 
+#define SCCB_DATA_LEN 4088
+
 struct sccb {
     struct sccb_header h;
     union {
         struct read_info_sccb read_info;
-        char data[4088];
+        struct read_event_data read;
+        struct write_event_mask we_mask;
+        char data[SCCB_DATA_LEN];
     } c;
  } __attribute__((packed));
 
+void sclp_enable_signal_quiesce(void);
 int sclp_read_info(CPUS390XState *env, struct sccb *sccb);
+int sclp_read_event_data(CPUS390XState *env, struct sccb *sccb);
+int sclp_write_event_mask(CPUS390XState *env, struct sccb *sccb);
 void sclp_service_interrupt(CPUS390XState *env, uint32_t sccb);
+
+#define TYPE_SCLP_S390_EVENT_FACILITY "s390-sclp-event-facility"
+#define SCLP_S390_EVENT_FACILITY(obj) \
+     OBJECT_CHECK(SCLPS390EventFacility, (obj), TYPE_SCLP_S390_EVENT_FACILITY)
+#define SCLP_S390_EVENT_FACILITY_CLASS(klass) \
+     OBJECT_CLASS_CHECK(SCLPS390EventFacilityClass, (klass), \
+             TYPE_SCLP_S390_EVENT_FACILITY)
+#define SCLP_S390_EVENT_FACILITY_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(SCLPS390EventFacilityClass, (obj), \
+             TYPE_SCLP_S390_EVENT_FACILITY)
+
+typedef struct SCLPS390EventFacility SCLPS390EventFacility;
+
+typedef struct SCLPS390EventFacilityClass {
+    DeviceClass qdev;
+    int (*init)(SCLPS390EventFacility *dev);
+} SCLPS390EventFacilityClass;
+
+struct SCLPS390EventFacility {
+    DeviceState qdev;
+    SCLPDevice *sdev;
+};
+
+typedef struct SCLPS390Bus {
+    BusState bus;
+    SCLPS390EventFacility *event_facility;
+} SCLPS390Bus;
+
+extern SCLPS390Bus *sclp_bus;
+
+SCLPS390Bus *s390_sclp_bus_init(void);
diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
index c0e19fd..0babf27 100644
--- a/hw/s390-virtio.c
+++ b/hw/s390-virtio.c
@@ -32,6 +32,7 @@
 #include "exec-memory.h"
 
 #include "hw/s390-virtio-bus.h"
+#include "hw/s390-sclp.h"
 
 //#define DEBUG_S390
 
@@ -60,7 +61,9 @@
 
 #define MAX_BLK_DEVS                    10
 
-static VirtIOS390Bus *s390_bus;
+VirtIOS390Bus *s390_bus;
+SCLPS390Bus *sclp_bus;
+
 static CPUS390XState **ipi_states;
 
 CPUS390XState *s390_cpu_addr2state(uint16_t cpu_addr)
@@ -170,6 +173,7 @@ static void s390_init(ram_addr_t my_ram_size,
     target_phys_addr_t virtio_region_len;
     target_phys_addr_t virtio_region_start;
     int i;
+    DeviceState *dev;
 
     /* s390x ram size detection needs a 16bit multiplier + an increment. So
        guests > 64GB can be specified in 2MB steps etc. */
@@ -183,6 +187,7 @@ static void s390_init(ram_addr_t my_ram_size,
 
     /* get a BUS */
     s390_bus = s390_virtio_bus_init(&my_ram_size);
+    sclp_bus = s390_sclp_bus_init();
 
     /* allocate RAM */
     memory_region_init_ram(ram, "s390.ram", my_ram_size);
@@ -325,6 +330,10 @@ static void s390_init(ram_addr_t my_ram_size,
         qdev_prop_set_drive_nofail(dev, "drive", dinfo->bdrv);
         qdev_init_nofail(dev);
     }
+
+    dev = qdev_create((BusState *)sclp_bus, "sclp-event-facility");
+    qdev_init_nofail(dev);
+    sclpef_enable_irqs(sclp_bus->event_facility->sdev, ipi_states[0]);
 }
 
 static QEMUMachine s390_machine = {
diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c
index 74bd9ad..3e5eff4 100644
--- a/target-s390x/op_helper.c
+++ b/target-s390x/op_helper.c
@@ -2391,6 +2391,9 @@ int sclp_service_call(CPUS390XState *env, uint32_t sccb, 
uint64_t code)
         case SCLP_CMDW_READ_SCP_INFO_FORCED:
             r = sclp_read_info(env, &work_sccb);
             break;
+        case SCLP_CMD_WRITE_EVENT_MASK:
+            r = sclp_write_event_mask(env, &work_sccb);
+            break;
         default:
 #ifdef DEBUG_HELPER
             printf("KVM: invalid sclp call 0x%x / 0x%" PRIx64 "x\n", sccb, 
code);
-- 
1.7.10.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]