Merge branch 's390-for-upstream' of git://repo.or.cz/qemu/agraf

* 's390-for-upstream' of git://repo.or.cz/qemu/agraf:
  s390: sclp ascii console support
  s390: sclp signal quiesce support
  s390: sclp event support
  s390: sclp base support
  s390: use sync regs for register transfer
  s390/kvm_stat: correct sys_perf_event_open syscall number
  s390x: fix -initrd in virtio machine
This commit is contained in:
Aurelien Jarno 2012-10-30 00:35:43 +01:00
commit 38c4718392
12 changed files with 1320 additions and 89 deletions

View File

@ -32,6 +32,7 @@
#include "exec-memory.h"
#include "hw/s390-virtio-bus.h"
#include "hw/s390x/sclp.h"
//#define DEBUG_S390
@ -184,6 +185,7 @@ static void s390_init(QEMUMachineInitArgs *args)
/* get a BUS */
s390_bus = s390_virtio_bus_init(&my_ram_size);
s390_sclp_init();
/* allocate RAM */
memory_region_init_ram(ram, "s390.ram", my_ram_size);
@ -285,8 +287,8 @@ static void s390_init(QEMUMachineInitArgs *args)
}
/* we have to overwrite values in the kernel image, which are "rom" */
memcpy(rom_ptr(INITRD_PARM_START), &initrd_offset, 8);
memcpy(rom_ptr(INITRD_PARM_SIZE), &initrd_size, 8);
stq_p(rom_ptr(INITRD_PARM_START), initrd_offset);
stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size);
}
if (rom_ptr(KERN_PARM_AREA)) {

View File

@ -1,3 +1,6 @@
obj-y = s390-virtio-bus.o s390-virtio.o
obj-y := $(addprefix ../,$(obj-y))
obj-y += sclp.o
obj-y += event-facility.o
obj-y += sclpquiesce.o sclpconsole.o

398
hw/s390x/event-facility.c Normal file
View File

@ -0,0 +1,398 @@
/*
* 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 "monitor.h"
#include "sysemu.h"
#include "sclp.h"
#include "event-facility.h"
typedef struct EventTypesBus {
BusState qbus;
} EventTypesBus;
struct SCLPEventFacility {
EventTypesBus sbus;
DeviceState *qdev;
/* 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;
QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) {
DeviceState *qdev = kid->child;
event = (SCLPEvent *) qdev;
ec = SCLP_EVENT_GET_CLASS(event);
rc = SCLP_RC_INVALID_FUNCTION;
if (ec->write_event_data &&
ec->event_type() == 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 = 0;
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)) {
rc = SCLP_RC_NORMAL_COMPLETION;
}
}
elen = be16_to_cpu(event_buf->length);
event_buf = (void *) event_buf + elen;
}
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:
if (!(sclp_cp_receive_mask & be32_to_cpu(red->mask))) {
sccb->h.response_code =
cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK);
goto out;
}
sclp_active_selection_mask = be32_to_cpu(red->mask);
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_class_init(ObjectClass *klass, void *data)
{
}
static const TypeInfo s390_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) {
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 int init_event_facility(S390SCLPDevice *sdev)
{
SCLPEventFacility *event_facility;
DeviceState *quiesce;
event_facility = g_malloc0(sizeof(SCLPEventFacility));
sdev->ef = event_facility;
sdev->sclp_command_handler = command_handler;
sdev->event_pending = event_pending;
/* Spawn a new sclp-events facility */
qbus_create_inplace(&event_facility->sbus.qbus,
TYPE_SCLP_EVENTS_BUS, (DeviceState *)sdev, NULL);
event_facility->sbus.qbus.allow_hotplug = 0;
event_facility->qdev = (DeviceState *) sdev;
quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce");
if (!quiesce) {
return -1;
}
qdev_init_nofail(quiesce);
return 0;
}
static void init_event_facility_class(ObjectClass *klass, void *data)
{
S390SCLPDeviceClass *k = SCLP_S390_DEVICE_CLASS(klass);
k->init = init_event_facility;
}
static TypeInfo s390_sclp_event_facility_info = {
.name = "s390-sclp-event-facility",
.parent = TYPE_DEVICE_S390_SCLP,
.instance_size = sizeof(S390SCLPDevice),
.class_init = init_event_facility_class,
};
static int event_qdev_init(DeviceState *qdev)
{
SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev);
SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);
return child->init(event);
}
static int event_qdev_exit(DeviceState *qdev)
{
SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev);
SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);
if (child->exit) {
child->exit(event);
}
return 0;
}
static void event_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->bus_type = TYPE_SCLP_EVENTS_BUS;
dc->unplug = qdev_simple_unplug_cb;
dc->init = event_qdev_init;
dc->exit = event_qdev_exit;
}
static TypeInfo s390_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(&s390_sclp_events_bus_info);
type_register_static(&s390_sclp_event_facility_info);
type_register_static(&s390_sclp_event_type_info);
}
type_init(register_types)

96
hw/s390x/event-facility.h Normal file
View File

@ -0,0 +1,96 @@
/*
* SCLP
* Event Facility definitions
*
* 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.
*
*/
#ifndef HW_S390_SCLP_EVENT_FACILITY_H
#define HW_S390_SCLP_EVENT_FACILITY_H
#include <hw/qdev.h>
#include "qemu-thread.h"
/* SCLP event types */
#define SCLP_EVENT_ASCII_CONSOLE_DATA 0x1a
#define SCLP_EVENT_SIGNAL_QUIESCE 0x1d
/* SCLP event masks */
#define SCLP_EVENT_MASK_SIGNAL_QUIESCE 0x00000008
#define SCLP_EVENT_MASK_MSG_ASCII 0x00000040
#define SCLP_UNCONDITIONAL_READ 0x00
#define SCLP_SELECTIVE_READ 0x01
#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 WriteEventMask {
SCCBHeader h;
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;
} QEMU_PACKED WriteEventMask;
typedef struct EventBufferHeader {
uint16_t length;
uint8_t type;
uint8_t flags;
uint16_t _reserved;
} QEMU_PACKED EventBufferHeader;
typedef struct WriteEventData {
SCCBHeader h;
EventBufferHeader ebh;
} QEMU_PACKED WriteEventData;
typedef struct ReadEventData {
SCCBHeader h;
EventBufferHeader ebh;
uint32_t mask;
} QEMU_PACKED ReadEventData;
typedef struct SCLPEvent {
DeviceState qdev;
bool event_pending;
uint32_t event_type;
char *name;
} SCLPEvent;
typedef struct SCLPEventClass {
DeviceClass parent_class;
int (*init)(SCLPEvent *event);
int (*exit)(SCLPEvent *event);
/* get SCLP's send mask */
unsigned int (*get_send_mask)(void);
/* get SCLP's receive mask */
unsigned int (*get_receive_mask)(void);
int (*read_event_data)(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
int *slen);
int (*write_event_data)(SCLPEvent *event, EventBufferHeader *evt_buf_hdr);
/* returns the supported event type */
int (*event_type)(void);
} SCLPEventClass;
#endif

163
hw/s390x/sclp.c Normal file
View File

@ -0,0 +1,163 @@
/*
* SCLP Support
*
* Copyright IBM, Corp. 2012
*
* Authors:
* Christian Borntraeger <borntraeger@de.ibm.com>
* Heinz Graalfs <graalfs@linux.vnet.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 "cpu.h"
#include "kvm.h"
#include "memory.h"
#include "sclp.h"
static inline S390SCLPDevice *get_event_facility(void)
{
ObjectProperty *op = object_property_find(qdev_get_machine(),
"s390-sclp-event-facility",
NULL);
assert(op);
return op->opaque;
}
/* Provide information about the configuration, CPUs and storage */
static void read_SCP_info(SCCB *sccb)
{
ReadInfo *read_info = (ReadInfo *) sccb;
int shift = 0;
while ((ram_size >> (20 + shift)) > 65535) {
shift++;
}
read_info->rnmax = cpu_to_be16(ram_size >> (20 + shift));
read_info->rnsize = 1 << shift;
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
static void sclp_execute(SCCB *sccb, uint64_t code)
{
S390SCLPDevice *sdev = get_event_facility();
switch (code) {
case SCLP_CMDW_READ_SCP_INFO:
case SCLP_CMDW_READ_SCP_INFO_FORCED:
read_SCP_info(sccb);
break;
default:
sdev->sclp_command_handler(sdev->ef, sccb, code);
break;
}
}
int sclp_service_call(uint32_t sccb, uint64_t code)
{
int r = 0;
SCCB work_sccb;
hwaddr sccb_len = sizeof(SCCB);
/* first some basic checks on program checks */
if (cpu_physical_memory_is_io(sccb)) {
r = -PGM_ADDRESSING;
goto out;
}
if (sccb & ~0x7ffffff8ul) {
r = -PGM_SPECIFICATION;
goto out;
}
/*
* we want to work on a private copy of the sccb, to prevent guests
* from playing dirty tricks by modifying the memory content after
* the host has checked the values
*/
cpu_physical_memory_read(sccb, &work_sccb, sccb_len);
/* Valid sccb sizes */
if (be16_to_cpu(work_sccb.h.length) < sizeof(SCCBHeader) ||
be16_to_cpu(work_sccb.h.length) > SCCB_SIZE) {
r = -PGM_SPECIFICATION;
goto out;
}
sclp_execute((SCCB *)&work_sccb, code);
cpu_physical_memory_write(sccb, &work_sccb,
be16_to_cpu(work_sccb.h.length));
sclp_service_interrupt(sccb);
out:
return r;
}
void sclp_service_interrupt(uint32_t sccb)
{
S390SCLPDevice *sdev = get_event_facility();
uint32_t param = sccb & ~3;
/* Indicate whether an event is still pending */
param |= sdev->event_pending(sdev->ef) ? 1 : 0;
if (!param) {
/* No need to send an interrupt, there's nothing to be notified about */
return;
}
s390_sclp_extint(param);
}
/* qemu object creation and initialization functions */
void s390_sclp_init(void)
{
DeviceState *dev = qdev_create(NULL, "s390-sclp-event-facility");
object_property_add_child(qdev_get_machine(), "s390-sclp-event-facility",
OBJECT(dev), NULL);
qdev_init_nofail(dev);
}
static int s390_sclp_dev_init(SysBusDevice *dev)
{
int r;
S390SCLPDevice *sdev = (S390SCLPDevice *)dev;
S390SCLPDeviceClass *sclp = SCLP_S390_DEVICE_GET_CLASS(dev);
r = sclp->init(sdev);
if (!r) {
assert(sdev->event_pending);
assert(sdev->sclp_command_handler);
}
return r;
}
static void s390_sclp_device_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *dc = SYS_BUS_DEVICE_CLASS(klass);
dc->init = s390_sclp_dev_init;
}
static TypeInfo s390_sclp_device_info = {
.name = TYPE_DEVICE_S390_SCLP,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(S390SCLPDevice),
.class_init = s390_sclp_device_class_init,
.class_size = sizeof(S390SCLPDeviceClass),
.abstract = true,
};
static void s390_sclp_register_types(void)
{
type_register_static(&s390_sclp_device_info);
}
type_init(s390_sclp_register_types)

118
hw/s390x/sclp.h Normal file
View File

@ -0,0 +1,118 @@
/*
* SCLP Support
*
* Copyright IBM, Corp. 2012
*
* Authors:
* Christian Borntraeger <borntraeger@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.
*
*/
#ifndef HW_S390_SCLP_H
#define HW_S390_SCLP_H
#include <hw/sysbus.h>
#include <hw/qdev.h>
/* SCLP command codes */
#define SCLP_CMDW_READ_SCP_INFO 0x00020001
#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
#define SCLP_CMD_READ_EVENT_DATA 0x00770005
#define SCLP_CMD_WRITE_EVENT_DATA 0x00760005
#define SCLP_CMD_READ_EVENT_DATA 0x00770005
#define SCLP_CMD_WRITE_EVENT_DATA 0x00760005
#define SCLP_CMD_WRITE_EVENT_MASK 0x00780005
/* SCLP response codes */
#define SCLP_RC_NORMAL_READ_COMPLETION 0x0010
#define SCLP_RC_NORMAL_COMPLETION 0x0020
#define SCLP_RC_INVALID_SCLP_COMMAND 0x01f0
#define SCLP_RC_CONTAINED_EQUIPMENT_CHECK 0x0340
#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
/* Service Call Control Block (SCCB) and its elements */
#define SCCB_SIZE 4096
#define SCLP_VARIABLE_LENGTH_RESPONSE 0x80
#define SCLP_EVENT_BUFFER_ACCEPTED 0x80
#define SCLP_FC_NORMAL_WRITE 0
/*
* Normally packed structures are not the right thing to do, since all code
* must take care of endianess. We cant use ldl_phys and friends for two
* reasons, though:
* - some of the embedded structures below the SCCB can appear multiple times
* at different locations, so there is no fixed offset
* - we work on a private copy of the SCCB, since there are several length
* fields, that would cause a security nightmare if we allow the guest to
* alter the structure while we parse it. We cannot use ldl_p and friends
* either without doing pointer arithmetics
* So we have to double check that all users of sclp data structures use the
* right endianess wrappers.
*/
typedef struct SCCBHeader {
uint16_t length;
uint8_t function_code;
uint8_t control_mask[3];
uint16_t response_code;
} QEMU_PACKED SCCBHeader;
#define SCCB_DATA_LEN (SCCB_SIZE - sizeof(SCCBHeader))
typedef struct ReadInfo {
SCCBHeader h;
uint16_t rnmax;
uint8_t rnsize;
} QEMU_PACKED ReadInfo;
typedef struct SCCB {
SCCBHeader h;
char data[SCCB_DATA_LEN];
} QEMU_PACKED SCCB;
static inline int sccb_data_len(SCCB *sccb)
{
return be16_to_cpu(sccb->h.length) - sizeof(sccb->h);
}
#define TYPE_DEVICE_S390_SCLP "s390-sclp-device"
#define SCLP_S390_DEVICE(obj) \
OBJECT_CHECK(S390SCLPDevice, (obj), TYPE_DEVICE_S390_SCLP)
#define SCLP_S390_DEVICE_CLASS(klass) \
OBJECT_CLASS_CHECK(S390SCLPDeviceClass, (klass), \
TYPE_DEVICE_S390_SCLP)
#define SCLP_S390_DEVICE_GET_CLASS(obj) \
OBJECT_GET_CLASS(S390SCLPDeviceClass, (obj), \
TYPE_DEVICE_S390_SCLP)
typedef struct SCLPEventFacility SCLPEventFacility;
typedef struct S390SCLPDevice {
SysBusDevice busdev;
SCLPEventFacility *ef;
void (*sclp_command_handler)(SCLPEventFacility *ef, SCCB *sccb,
uint64_t code);
bool (*event_pending)(SCLPEventFacility *ef);
} S390SCLPDevice;
typedef struct S390SCLPDeviceClass {
DeviceClass qdev;
int (*init)(S390SCLPDevice *sdev);
} S390SCLPDeviceClass;
void s390_sclp_init(void);
void sclp_service_interrupt(uint32_t sccb);
#endif

306
hw/s390x/sclpconsole.c Normal file
View File

@ -0,0 +1,306 @@
/*
* SCLP event type
* Ascii Console Data (VT220 Console)
*
* 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 "qemu-thread.h"
#include "sclp.h"
#include "event-facility.h"
typedef struct ASCIIConsoleData {
EventBufferHeader ebh;
char data[0];
} QEMU_PACKED ASCIIConsoleData;
/* max size for ASCII data in 4K SCCB page */
#define SIZE_BUFFER_VT220 4080
typedef struct SCLPConsole {
SCLPEvent event;
CharDriverState *chr;
/* io vector */
uint8_t *iov; /* iov buffer pointer */
uint8_t *iov_sclp; /* pointer to SCLP read offset */
uint8_t *iov_bs; /* pointer byte stream read offset */
uint32_t iov_data_len; /* length of byte stream in buffer */
uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */
qemu_irq irq_read_vt220;
} SCLPConsole;
/* character layer call-back functions */
/* Return number of bytes that fit into iov buffer */
static int chr_can_read(void *opaque)
{
int can_read;
SCLPConsole *scon = opaque;
can_read = SIZE_BUFFER_VT220 - scon->iov_data_len;
return can_read;
}
/* Receive n bytes from character layer, save in iov buffer,
* and set event pending */
static void receive_from_chr_layer(SCLPConsole *scon, const uint8_t *buf,
int size)
{
assert(scon->iov);
/* read data must fit into current buffer */
assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len);
/* put byte-stream from character layer into buffer */
memcpy(scon->iov_bs, buf, size);
scon->iov_data_len += size;
scon->iov_sclp_rest += size;
scon->iov_bs += size;
scon->event.event_pending = true;
}
/* Send data from a char device over to the guest */
static void chr_read(void *opaque, const uint8_t *buf, int size)
{
SCLPConsole *scon = opaque;
assert(scon);
receive_from_chr_layer(scon, buf, size);
/* trigger SCLP read operation */
qemu_irq_raise(scon->irq_read_vt220);
}
static void chr_event(void *opaque, int event)
{
SCLPConsole *scon = opaque;
switch (event) {
case CHR_EVENT_OPENED:
if (!scon->iov) {
scon->iov = g_malloc0(SIZE_BUFFER_VT220);
scon->iov_sclp = scon->iov;
scon->iov_bs = scon->iov;
scon->iov_data_len = 0;
scon->iov_sclp_rest = 0;
}
break;
case CHR_EVENT_CLOSED:
if (scon->iov) {
g_free(scon->iov);
scon->iov = NULL;
}
break;
}
}
/* functions to be called by event facility */
static int event_type(void)
{
return SCLP_EVENT_ASCII_CONSOLE_DATA;
}
static unsigned int send_mask(void)
{
return SCLP_EVENT_MASK_MSG_ASCII;
}
static unsigned int receive_mask(void)
{
return SCLP_EVENT_MASK_MSG_ASCII;
}
/* triggered by SCLP's read_event_data -
* copy console data byte-stream into provided (SCLP) buffer
*/
static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size,
int avail)
{
SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event);
/* first byte is hex 0 saying an ascii string follows */
*buf++ = '\0';
avail--;
/* if all data fit into provided SCLP buffer */
if (avail >= cons->iov_sclp_rest) {
/* copy character byte-stream to SCLP buffer */
memcpy(buf, cons->iov_sclp, cons->iov_sclp_rest);
*size = cons->iov_sclp_rest + 1;
cons->iov_sclp = cons->iov;
cons->iov_bs = cons->iov;
cons->iov_data_len = 0;
cons->iov_sclp_rest = 0;
event->event_pending = false;
/* data provided and no more data pending */
} else {
/* if provided buffer is too small, just copy part */
memcpy(buf, cons->iov_sclp, avail);
*size = avail + 1;
cons->iov_sclp_rest -= avail;
cons->iov_sclp += avail;
/* more data pending */
}
}
static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
int *slen)
{
int avail;
size_t src_len;
uint8_t *to;
ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
if (!event->event_pending) {
/* no data pending */
return 0;
}
to = (uint8_t *)&acd->data;
avail = *slen - sizeof(ASCIIConsoleData);
get_console_data(event, to, &src_len, avail);
acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len);
acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA;
acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED;
*slen = avail - src_len;
return 1;
}
/* triggered by SCLP's write_event_data
* - write console data into character layer
* returns < 0 if an error occured
*/
static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf,
size_t len)
{
ssize_t ret = 0;
const uint8_t *iov_offset;
SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
if (!scon->chr) {
/* If there's no backend, we can just say we consumed all data. */
return len;
}
iov_offset = buf;
while (len > 0) {
ret = qemu_chr_fe_write(scon->chr, buf, len);
if (ret == 0) {
/* a pty doesn't seem to be connected - no error */
len = 0;
} else if (ret == -EAGAIN || (ret > 0 && ret < len)) {
len -= ret;
iov_offset += ret;
} else {
len = 0;
}
}
return ret;
}
static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
{
int rc;
int length;
ssize_t written;
ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader);
written = write_console_data(event, (uint8_t *)acd->data, length);
rc = SCLP_RC_NORMAL_COMPLETION;
/* set event buffer accepted flag */
evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED;
/* written will be zero if a pty is not connected - don't treat as error */
if (written < 0) {
/* event buffer not accepted due to error in character layer */
evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED);
rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK;
}
return rc;
}
static void trigger_ascii_console_data(void *env, int n, int level)
{
sclp_service_interrupt(0);
}
/* qemu object creation and initialization functions */
/* tell character layer our call-back functions */
static int console_init(SCLPEvent *event)
{
static bool console_available;
SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
if (console_available) {
error_report("Multiple VT220 operator consoles are not supported");
return -1;
}
console_available = true;
event->event_type = SCLP_EVENT_ASCII_CONSOLE_DATA;
if (scon->chr) {
qemu_chr_add_handlers(scon->chr, chr_can_read,
chr_read, chr_event, scon);
}
scon->irq_read_vt220 = *qemu_allocate_irqs(trigger_ascii_console_data,
NULL, 1);
return 0;
}
static int console_exit(SCLPEvent *event)
{
return 0;
}
static Property console_properties[] = {
DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
DEFINE_PROP_END_OF_LIST(),
};
static void console_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SCLPEventClass *ec = SCLP_EVENT_CLASS(klass);
dc->props = console_properties;
ec->init = console_init;
ec->exit = console_exit;
ec->get_send_mask = send_mask;
ec->get_receive_mask = receive_mask;
ec->event_type = event_type;
ec->read_event_data = read_event_data;
ec->write_event_data = write_event_data;
}
static TypeInfo sclp_console_info = {
.name = "sclpconsole",
.parent = TYPE_SCLP_EVENT,
.instance_size = sizeof(SCLPConsole),
.class_init = console_class_init,
.class_size = sizeof(SCLPEventClass),
};
static void register_types(void)
{
type_register_static(&sclp_console_info);
}
type_init(register_types)

123
hw/s390x/sclpquiesce.c Normal file
View File

@ -0,0 +1,123 @@
/*
* 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.h"
#include "sclp.h"
#include "event-facility.h"
typedef struct SignalQuiesce {
EventBufferHeader ebh;
uint16_t timeout;
uint8_t unit;
} QEMU_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;
}
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)
{
event->event_type = SCLP_EVENT_SIGNAL_QUIESCE;
qn.notifier.notify = quiesce_powerdown_req;
qn.event = event;
qemu_register_powerdown_notifier(&qn.notifier);
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)

View File

@ -170,6 +170,12 @@ vendor_exit_reasons = {
'IBM/S390': s390_exit_reasons,
}
syscall_numbers = {
'IBM/S390': 331,
}
sc_perf_evt_open = 298
exit_reasons = None
for line in file('/proc/cpuinfo').readlines():
@ -177,7 +183,8 @@ for line in file('/proc/cpuinfo').readlines():
for flag in line.split():
if flag in vendor_exit_reasons:
exit_reasons = vendor_exit_reasons[flag]
if flag in syscall_numbers:
sc_perf_evt_open = syscall_numbers[flag]
filters = {
'kvm_exit': ('exit_reason', exit_reasons)
}
@ -206,7 +213,7 @@ class perf_event_attr(ctypes.Structure):
('bp_len', ctypes.c_uint64),
]
def _perf_event_open(attr, pid, cpu, group_fd, flags):
return syscall(298, ctypes.pointer(attr), ctypes.c_int(pid),
return syscall(sc_perf_evt_open, ctypes.pointer(attr), ctypes.c_int(pid),
ctypes.c_int(cpu), ctypes.c_int(group_fd),
ctypes.c_long(flags))

View File

@ -596,17 +596,6 @@ static inline const char *cc_name(int cc_op)
return cc_names[cc_op];
}
/* SCLP PV interface defines */
#define SCLP_CMDW_READ_SCP_INFO 0x00020001
#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
#define SCP_LENGTH 0x00
#define SCP_FUNCTION_CODE 0x02
#define SCP_CONTROL_MASK 0x03
#define SCP_RESPONSE_CODE 0x06
#define SCP_MEM_CODE 0x08
#define SCP_INCREMENT 0x0a
typedef struct LowCore
{
/* prefix area: defined by architecture */
@ -955,7 +944,7 @@ static inline void ebcdic_put(uint8_t *p, const char *ascii, int len)
void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr);
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
target_ulong *raddr, int *flags);
int sclp_service_call(CPUS390XState *env, uint32_t sccb, uint64_t code);
int sclp_service_call(uint32_t sccb, uint64_t code);
uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst,
uint64_t vr);

View File

@ -60,15 +60,15 @@
#define SIGP_STORE_STATUS_ADDR 0x0e
#define SIGP_SET_ARCH 0x12
#define SCLP_CMDW_READ_SCP_INFO 0x00020001
#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
KVM_CAP_LAST_INFO
};
static int cap_sync_regs;
int kvm_arch_init(KVMState *s)
{
cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS);
return 0;
}
@ -90,48 +90,117 @@ void kvm_arch_reset_vcpu(CPUS390XState *env)
int kvm_arch_put_registers(CPUS390XState *env, int level)
{
struct kvm_sregs sregs;
struct kvm_regs regs;
int ret;
int i;
ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
if (ret < 0) {
return ret;
}
for (i = 0; i < 16; i++) {
regs.gprs[i] = env->regs[i];
}
ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, &regs);
if (ret < 0) {
return ret;
}
/* always save the PSW and the GPRS*/
env->kvm_run->psw_addr = env->psw.addr;
env->kvm_run->psw_mask = env->psw.mask;
return ret;
if (cap_sync_regs && env->kvm_run->kvm_valid_regs & KVM_SYNC_GPRS) {
for (i = 0; i < 16; i++) {
env->kvm_run->s.regs.gprs[i] = env->regs[i];
env->kvm_run->kvm_dirty_regs |= KVM_SYNC_GPRS;
}
} else {
for (i = 0; i < 16; i++) {
regs.gprs[i] = env->regs[i];
}
ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, &regs);
if (ret < 0) {
return ret;
}
}
/* Do we need to save more than that? */
if (level == KVM_PUT_RUNTIME_STATE) {
return 0;
}
if (cap_sync_regs &&
env->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS &&
env->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) {
for (i = 0; i < 16; i++) {
env->kvm_run->s.regs.acrs[i] = env->aregs[i];
env->kvm_run->s.regs.crs[i] = env->cregs[i];
}
env->kvm_run->kvm_dirty_regs |= KVM_SYNC_ACRS;
env->kvm_run->kvm_dirty_regs |= KVM_SYNC_CRS;
} else {
for (i = 0; i < 16; i++) {
sregs.acrs[i] = env->aregs[i];
sregs.crs[i] = env->cregs[i];
}
ret = kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs);
if (ret < 0) {
return ret;
}
}
/* Finally the prefix */
if (cap_sync_regs && env->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) {
env->kvm_run->s.regs.prefix = env->psa;
env->kvm_run->kvm_dirty_regs |= KVM_SYNC_PREFIX;
} else {
/* prefix is only supported via sync regs */
}
return 0;
}
int kvm_arch_get_registers(CPUS390XState *env)
{
int ret;
struct kvm_sregs sregs;
struct kvm_regs regs;
int ret;
int i;
ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
if (ret < 0) {
return ret;
}
for (i = 0; i < 16; i++) {
env->regs[i] = regs.gprs[i];
}
/* get the PSW */
env->psw.addr = env->kvm_run->psw_addr;
env->psw.mask = env->kvm_run->psw_mask;
/* the GPRS */
if (cap_sync_regs && env->kvm_run->kvm_valid_regs & KVM_SYNC_GPRS) {
for (i = 0; i < 16; i++) {
env->regs[i] = env->kvm_run->s.regs.gprs[i];
}
} else {
ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
if (ret < 0) {
return ret;
}
for (i = 0; i < 16; i++) {
env->regs[i] = regs.gprs[i];
}
}
/* The ACRS and CRS */
if (cap_sync_regs &&
env->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS &&
env->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) {
for (i = 0; i < 16; i++) {
env->aregs[i] = env->kvm_run->s.regs.acrs[i];
env->cregs[i] = env->kvm_run->s.regs.crs[i];
}
} else {
ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs);
if (ret < 0) {
return ret;
}
for (i = 0; i < 16; i++) {
env->aregs[i] = sregs.acrs[i];
env->cregs[i] = sregs.crs[i];
}
}
/* Finally the prefix */
if (cap_sync_regs && env->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) {
env->psa = env->kvm_run->s.regs.prefix;
} else {
/* no prefix without sync regs */
}
return 0;
}
@ -272,7 +341,7 @@ static int kvm_sclp_service_call(CPUS390XState *env, struct kvm_run *run,
sccb = env->regs[ipbh0 & 0xf];
code = env->regs[(ipbh0 & 0xf0) >> 4];
r = sclp_service_call(env, sccb, code);
r = sclp_service_call(sccb, code);
if (r < 0) {
enter_pgmcheck(env, -r);
}

View File

@ -67,55 +67,12 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilc)
}
}
/*
* ret < 0 indicates program check, ret = 0, 1, 2, 3 -> cc
*/
int sclp_service_call(CPUS390XState *env, uint32_t sccb, uint64_t code)
{
int r = 0;
int shift = 0;
#ifdef DEBUG_HELPER
printf("sclp(0x%x, 0x%" PRIx64 ")\n", sccb, code);
#endif
/* basic checks */
if (cpu_physical_memory_is_io(sccb)) {
return -PGM_ADDRESSING;
}
if (sccb & ~0x7ffffff8ul) {
return -PGM_SPECIFICATION;
}
switch (code) {
case SCLP_CMDW_READ_SCP_INFO:
case SCLP_CMDW_READ_SCP_INFO_FORCED:
while ((ram_size >> (20 + shift)) > 65535) {
shift++;
}
stw_phys(sccb + SCP_MEM_CODE, ram_size >> (20 + shift));
stb_phys(sccb + SCP_INCREMENT, 1 << shift);
stw_phys(sccb + SCP_RESPONSE_CODE, 0x10);
s390_sclp_extint(sccb & ~3);
break;
default:
#ifdef DEBUG_HELPER
printf("KVM: invalid sclp call 0x%x / 0x%" PRIx64 "x\n", sccb, code);
#endif
r = 3;
break;
}
return r;
}
/* SCLP service call */
uint32_t HELPER(servc)(CPUS390XState *env, uint32_t r1, uint64_t r2)
{
int r;
r = sclp_service_call(env, r1, r2);
r = sclp_service_call(r1, r2);
if (r < 0) {
program_interrupt(env, -r, 4);
return 0;