diff options
Diffstat (limited to 'qemu/hw/s390x/event-facility.c')
-rw-r--r-- | qemu/hw/s390x/event-facility.c | 449 |
1 files changed, 0 insertions, 449 deletions
diff --git a/qemu/hw/s390x/event-facility.c b/qemu/hw/s390x/event-facility.c deleted file mode 100644 index 34b2faf01..000000000 --- a/qemu/hw/s390x/event-facility.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * SCLP - * Event Facility - * handles SCLP event types - * - Signal Quiesce - system power down - * - ASCII Console Data - VT220 read and write - * - * 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 "qemu/osdep.h" -#include "qapi/error.h" -#include "sysemu/sysemu.h" - -#include "hw/s390x/sclp.h" -#include "hw/s390x/event-facility.h" - -typedef struct SCLPEventsBus { - BusState qbus; -} SCLPEventsBus; - -struct SCLPEventFacility { - SysBusDevice parent_obj; - SCLPEventsBus sbus; - /* guest' receive mask */ - unsigned int receive_mask; -}; - -/* return true if any child has event pending set */ -static bool event_pending(SCLPEventFacility *ef) -{ - BusChild *kid; - SCLPEvent *event; - SCLPEventClass *event_class; - - QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - event = DO_UPCAST(SCLPEvent, qdev, qdev); - event_class = SCLP_EVENT_GET_CLASS(event); - if (event->event_pending && - event_class->get_send_mask() & ef->receive_mask) { - return true; - } - } - return false; -} - -static unsigned int get_host_send_mask(SCLPEventFacility *ef) -{ - unsigned int mask; - BusChild *kid; - SCLPEventClass *child; - - mask = 0; - - QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - child = SCLP_EVENT_GET_CLASS((SCLPEvent *) qdev); - mask |= child->get_send_mask(); - } - return mask; -} - -static unsigned int get_host_receive_mask(SCLPEventFacility *ef) -{ - unsigned int mask; - BusChild *kid; - SCLPEventClass *child; - - mask = 0; - - QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - child = SCLP_EVENT_GET_CLASS((SCLPEvent *) qdev); - mask |= child->get_receive_mask(); - } - return mask; -} - -static uint16_t write_event_length_check(SCCB *sccb) -{ - int slen; - unsigned elen = 0; - EventBufferHeader *event; - WriteEventData *wed = (WriteEventData *) sccb; - - event = (EventBufferHeader *) &wed->ebh; - for (slen = sccb_data_len(sccb); slen > 0; slen -= elen) { - elen = be16_to_cpu(event->length); - if (elen < sizeof(*event) || elen > slen) { - return SCLP_RC_EVENT_BUFFER_SYNTAX_ERROR; - } - event = (void *) event + elen; - } - if (slen) { - return SCLP_RC_INCONSISTENT_LENGTHS; - } - return SCLP_RC_NORMAL_COMPLETION; -} - -static uint16_t handle_write_event_buf(SCLPEventFacility *ef, - EventBufferHeader *event_buf, SCCB *sccb) -{ - uint16_t rc; - BusChild *kid; - SCLPEvent *event; - SCLPEventClass *ec; - - rc = SCLP_RC_INVALID_FUNCTION; - - QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - event = (SCLPEvent *) qdev; - ec = SCLP_EVENT_GET_CLASS(event); - - if (ec->write_event_data && - ec->can_handle_event(event_buf->type)) { - rc = ec->write_event_data(event, event_buf); - break; - } - } - return rc; -} - -static uint16_t handle_sccb_write_events(SCLPEventFacility *ef, SCCB *sccb) -{ - uint16_t rc; - int slen; - unsigned elen = 0; - EventBufferHeader *event_buf; - WriteEventData *wed = (WriteEventData *) sccb; - - event_buf = &wed->ebh; - rc = SCLP_RC_NORMAL_COMPLETION; - - /* loop over all contained event buffers */ - for (slen = sccb_data_len(sccb); slen > 0; slen -= elen) { - elen = be16_to_cpu(event_buf->length); - - /* in case of a previous error mark all trailing buffers - * as not accepted */ - if (rc != SCLP_RC_NORMAL_COMPLETION) { - event_buf->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED); - } else { - rc = handle_write_event_buf(ef, event_buf, sccb); - } - event_buf = (void *) event_buf + elen; - } - return rc; -} - -static void write_event_data(SCLPEventFacility *ef, SCCB *sccb) -{ - if (sccb->h.function_code != SCLP_FC_NORMAL_WRITE) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); - goto out; - } - if (be16_to_cpu(sccb->h.length) < 8) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); - goto out; - } - /* first do a sanity check of the write events */ - sccb->h.response_code = cpu_to_be16(write_event_length_check(sccb)); - - /* if no early error, then execute */ - if (sccb->h.response_code == be16_to_cpu(SCLP_RC_NORMAL_COMPLETION)) { - sccb->h.response_code = - cpu_to_be16(handle_sccb_write_events(ef, sccb)); - } - -out: - return; -} - -static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb, - unsigned int mask) -{ - uint16_t rc; - int slen; - unsigned elen; - BusChild *kid; - SCLPEvent *event; - SCLPEventClass *ec; - EventBufferHeader *event_buf; - ReadEventData *red = (ReadEventData *) sccb; - - event_buf = &red->ebh; - event_buf->length = 0; - slen = sizeof(sccb->data); - - rc = SCLP_RC_NO_EVENT_BUFFERS_STORED; - - QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - event = (SCLPEvent *) qdev; - ec = SCLP_EVENT_GET_CLASS(event); - - if (mask & ec->get_send_mask()) { - if (ec->read_event_data(event, event_buf, &slen)) { - elen = be16_to_cpu(event_buf->length); - event_buf = (EventBufferHeader *) ((char *)event_buf + elen); - rc = SCLP_RC_NORMAL_COMPLETION; - } - } - } - - if (sccb->h.control_mask[2] & SCLP_VARIABLE_LENGTH_RESPONSE) { - /* architecture suggests to reset variable-length-response bit */ - sccb->h.control_mask[2] &= ~SCLP_VARIABLE_LENGTH_RESPONSE; - /* with a new length value */ - sccb->h.length = cpu_to_be16(SCCB_SIZE - slen); - } - return rc; -} - -static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) -{ - unsigned int sclp_active_selection_mask; - unsigned int sclp_cp_receive_mask; - - ReadEventData *red = (ReadEventData *) sccb; - - if (be16_to_cpu(sccb->h.length) != SCCB_SIZE) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); - goto out; - } - - sclp_cp_receive_mask = ef->receive_mask; - - /* get active selection mask */ - switch (sccb->h.function_code) { - case SCLP_UNCONDITIONAL_READ: - sclp_active_selection_mask = sclp_cp_receive_mask; - break; - case SCLP_SELECTIVE_READ: - sclp_active_selection_mask = be32_to_cpu(red->mask); - if (!sclp_cp_receive_mask || - (sclp_active_selection_mask & ~sclp_cp_receive_mask)) { - sccb->h.response_code = - cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK); - goto out; - } - break; - default: - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); - goto out; - } - sccb->h.response_code = cpu_to_be16( - handle_sccb_read_events(ef, sccb, sclp_active_selection_mask)); - -out: - return; -} - -static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) -{ - WriteEventMask *we_mask = (WriteEventMask *) 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(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 */ - ef->receive_mask = be32_to_cpu(we_mask->cp_receive_mask); - - /* return the SCLP's capability masks to the guest */ - we_mask->send_mask = cpu_to_be32(get_host_send_mask(ef)); - we_mask->receive_mask = cpu_to_be32(get_host_receive_mask(ef)); - - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); - -out: - return; -} - -/* qemu object creation and initialization functions */ - -#define TYPE_SCLP_EVENTS_BUS "s390-sclp-events-bus" - -static void sclp_events_bus_realize(BusState *bus, Error **errp) -{ - BusChild *kid; - - /* TODO: recursive realization has to be done in common code */ - QTAILQ_FOREACH(kid, &bus->children, sibling) { - DeviceState *dev = kid->child; - - object_property_set_bool(OBJECT(dev), true, "realized", errp); - if (*errp) { - return; - } - } -} - -static void sclp_events_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *bc = BUS_CLASS(klass); - - bc->realize = sclp_events_bus_realize; -} - -static const TypeInfo sclp_events_bus_info = { - .name = TYPE_SCLP_EVENTS_BUS, - .parent = TYPE_BUS, - .class_init = sclp_events_bus_class_init, -}; - -static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code) -{ - switch (code & SCLP_CMD_CODE_MASK) { - case SCLP_CMD_READ_EVENT_DATA: - read_event_data(ef, sccb); - break; - case SCLP_CMD_WRITE_EVENT_DATA: - write_event_data(ef, sccb); - break; - case SCLP_CMD_WRITE_EVENT_MASK: - write_event_mask(ef, sccb); - break; - default: - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - break; - } -} - -static const VMStateDescription vmstate_event_facility = { - .name = "vmstate-event-facility", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(receive_mask, SCLPEventFacility), - VMSTATE_END_OF_LIST() - } -}; - -static void init_event_facility(Object *obj) -{ - SCLPEventFacility *event_facility = EVENT_FACILITY(obj); - DeviceState *sdev = DEVICE(obj); - Object *new; - - /* Spawn a new bus for SCLP events */ - qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus), - TYPE_SCLP_EVENTS_BUS, sdev, NULL); - - new = object_new(TYPE_SCLP_QUIESCE); - object_property_add_child(obj, TYPE_SCLP_QUIESCE, new, NULL); - object_unref(new); - qdev_set_parent_bus(DEVICE(new), &event_facility->sbus.qbus); - - new = object_new(TYPE_SCLP_CPU_HOTPLUG); - object_property_add_child(obj, TYPE_SCLP_CPU_HOTPLUG, new, NULL); - object_unref(new); - qdev_set_parent_bus(DEVICE(new), &event_facility->sbus.qbus); - /* the facility will automatically realize the devices via the bus */ -} - -static void reset_event_facility(DeviceState *dev) -{ - SCLPEventFacility *sdev = EVENT_FACILITY(dev); - - sdev->receive_mask = 0; -} - -static void init_event_facility_class(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(sbdc); - SCLPEventFacilityClass *k = EVENT_FACILITY_CLASS(dc); - - dc->reset = reset_event_facility; - dc->vmsd = &vmstate_event_facility; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - k->command_handler = command_handler; - k->event_pending = event_pending; -} - -static const TypeInfo sclp_event_facility_info = { - .name = TYPE_SCLP_EVENT_FACILITY, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_init = init_event_facility, - .instance_size = sizeof(SCLPEventFacility), - .class_init = init_event_facility_class, - .class_size = sizeof(SCLPEventFacilityClass), -}; - -static void event_realize(DeviceState *qdev, Error **errp) -{ - SCLPEvent *event = SCLP_EVENT(qdev); - SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event); - - if (child->init) { - int rc = child->init(event); - if (rc < 0) { - error_setg(errp, "SCLP event initialization failed."); - return; - } - } -} - -static void event_unrealize(DeviceState *qdev, Error **errp) -{ - SCLPEvent *event = SCLP_EVENT(qdev); - SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event); - if (child->exit) { - int rc = child->exit(event); - if (rc < 0) { - error_setg(errp, "SCLP event exit failed."); - return; - } - } -} - -static void event_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->bus_type = TYPE_SCLP_EVENTS_BUS; - dc->realize = event_realize; - dc->unrealize = event_unrealize; -} - -static const TypeInfo sclp_event_type_info = { - .name = TYPE_SCLP_EVENT, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SCLPEvent), - .class_init = event_class_init, - .class_size = sizeof(SCLPEventClass), - .abstract = true, -}; - -static void register_types(void) -{ - type_register_static(&sclp_events_bus_info); - type_register_static(&sclp_event_facility_info); - type_register_static(&sclp_event_type_info); -} - -type_init(register_types) |