From: Heinz Graalfs <graa...@linux.vnet.ibm.com> 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 <graa...@linux.vnet.ibm.com> Signed-off-by: Christian Borntraeger <borntrae...@de.ibm.com> Signed-off-by: Jens Freimann <jf...@de.ibm.com> --- 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 <graa...@de.ibm.com> + * + * 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 <graa...@de.ibm.com> + * + * 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