1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
/*
* SCLP event type
* Signal Quiesce - trigger system powerdown request
*
* Copyright IBM, Corp. 2012
*
* Authors:
* Heinz Graalfs <graalfs@de.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 <hw/qdev.h>
#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/event-facility.h"
typedef struct SignalQuiesce {
EventBufferHeader ebh;
uint16_t timeout;
uint8_t unit;
} QEMU_PACKED SignalQuiesce;
static bool can_handle_event(uint8_t type)
{
return type == 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 const VMStateDescription vmstate_sclpquiesce = {
.name = "sclpquiesce",
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
VMSTATE_BOOL(event_pending, SCLPEvent),
VMSTATE_END_OF_LIST()
}
};
typedef struct QuiesceNotifier QuiesceNotifier;
static struct QuiesceNotifier {
Notifier notifier;
SCLPEvent *event;
} qn;
static void quiesce_powerdown_req(Notifier *n, void *opaque)
{
QuiesceNotifier *qn = container_of(n, QuiesceNotifier, notifier);
SCLPEvent *event = qn->event;
event->event_pending = true;
/* trigger SCLP read operation */
sclp_service_interrupt(0);
}
static int quiesce_init(SCLPEvent *event)
{
qn.notifier.notify = quiesce_powerdown_req;
qn.event = event;
qemu_register_powerdown_notifier(&qn.notifier);
return 0;
}
static void quiesce_reset(DeviceState *dev)
{
SCLPEvent *event = SCLP_EVENT(dev);
event->event_pending = false;
}
static void quiesce_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SCLPEventClass *k = SCLP_EVENT_CLASS(klass);
dc->reset = quiesce_reset;
dc->vmsd = &vmstate_sclpquiesce;
k->init = quiesce_init;
k->get_send_mask = send_mask;
k->get_receive_mask = receive_mask;
k->can_handle_event = can_handle_event;
k->read_event_data = read_event_data;
k->write_event_data = NULL;
}
static const 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)
|