From: Heinz Graalfs <graa...@linux.vnet.ibm.com> This implements the sclp signal quiesce event via the SCLP Event Facility. This allows to gracefully shutdown a guest by using system_powerdown. It creates a service interrupt that will trigger a Read Event Data command from the guest. This code will then add an event that is interpreted by linux guests as ctrl-alt-del.
Signed-off-by: Heinz Graalfs <graa...@linux.vnet.ibm.com> Signed-off-by: Christian Borntraeger <borntrae...@de.ibm.com> --- hw/s390-event-facility.c | 6 +++ hw/s390-sclpquiesce.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++ hw/s390x/Makefile.objs | 2 +- 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 hw/s390-sclpquiesce.c diff --git a/hw/s390-event-facility.c b/hw/s390-event-facility.c index 42ac102..e3c9c56 100644 --- a/hw/s390-event-facility.c +++ b/hw/s390-event-facility.c @@ -335,6 +335,7 @@ static int command_handler(SCCB *sccb, uint64_t code) static int init_event_facility(S390SCLPDevice *sdev) { SCLPEventFacility *event_facility; + DeviceState *quiesce; event_facility = g_malloc0(sizeof(SCLPEventFacility)); sdev->instance = event_facility; @@ -348,6 +349,11 @@ static int init_event_facility(S390SCLPDevice *sdev) event_facility->sbus.event_facility = event_facility; event_facility->qdev = (DeviceState *) sdev; + quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce"); + if (!quiesce) { + return -1; + } + qdev_init_nofail(quiesce); return 0; } diff --git a/hw/s390-sclpquiesce.c b/hw/s390-sclpquiesce.c new file mode 100644 index 0000000..405664a --- /dev/null +++ b/hw/s390-sclpquiesce.c @@ -0,0 +1,113 @@ +/* + * SCLP event type + * Signal Quiesce - trigger system powerdown request + * + * Copyright IBM, Corp. 2007, 2012 + * + * Authors: + * Heinz Graalfs <graa...@de.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#include "qdev.h" +#include "sysemu.h" + +#include "s390-sclp.h" +#include "s390-event-facility.h" + +typedef struct SignalQuiesce { + EventBufferHeader ebh; + uint16_t timeout; + uint8_t unit; +} __attribute__((packed)) SignalQuiesce; + +static int event_type(void) +{ + return SCLP_EVENT_SIGNAL_QUIESCE; +} + +static unsigned int send_mask(void) +{ + return SCLP_EVENT_MASK_SIGNAL_QUIESCE; +} + +static unsigned int receive_mask(void) +{ + return 0; +} + +static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, + int *slen) +{ + SignalQuiesce *sq = (SignalQuiesce *) evt_buf_hdr; + + if (*slen < sizeof(SignalQuiesce)) { + return 0; + } + + if (!event->event_pending) { + return 0; + } + event->event_pending = false; + + sq->ebh.length = cpu_to_be16(sizeof(SignalQuiesce)); + sq->ebh.type = SCLP_EVENT_SIGNAL_QUIESCE; + sq->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED; + /* + * system_powerdown does not have a timeout. Fortunately the + * timeout value is currently ignored by Linux, anyway + */ + sq->timeout = cpu_to_be16(0); + sq->unit = cpu_to_be16(0); + *slen -= sizeof(SignalQuiesce); + + return 1; +} + +static void trigger_signal_quiesce(void *opaque, int n, int level) +{ + SCLPEvent *event = opaque; + + event->event_pending = true; + /* trigger SCLP read operation */ + sclp_service_interrupt(0); +} + +static int quiesce_init(SCLPEvent *event) +{ + event->event_type = SCLP_EVENT_SIGNAL_QUIESCE; + qemu_system_powerdown = *qemu_allocate_irqs(trigger_signal_quiesce, + event, 1); + qemu_mutex_init(&event->lock); + + return 0; +} + +static void quiesce_class_init(ObjectClass *klass, void *data) +{ + SCLPEventClass *k = SCLP_EVENT_CLASS(klass); + + k->init = quiesce_init; + + k->get_send_mask = send_mask; + k->get_receive_mask = receive_mask; + k->event_type = event_type; + k->read_event_data = read_event_data; + k->write_event_data = NULL; +} + +static TypeInfo sclp_quiesce_info = { + .name = "sclpquiesce", + .parent = TYPE_SCLP_EVENT, + .instance_size = sizeof(SCLPEvent), + .class_init = quiesce_class_init, + .class_size = sizeof(SCLPEventClass), +}; + +static void register_types(void) +{ + type_register_static(&sclp_quiesce_info); +} +type_init(register_types) diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 5ebde3b..a3ab189 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -1,4 +1,4 @@ obj-y = s390-virtio-bus.o s390-virtio.o -obj-y += s390-sclp.o s390-event-facility.o +obj-y += s390-sclp.o s390-event-facility.o s390-sclpquiesce.o obj-y := $(addprefix ../,$(obj-y)) -- 1.7.10.5