mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
Several features, fixes and cleanups for kvm/s390:
- sclp event facility: cleanup structure. This allows to use realize/unrealize as well as migration support via vmsd - reboot: Two fixes that make reboot much more reliable - ipl: make elf loading more robust - flic interrupt controller: This allows to migrate floating interrupts, as well as clear them on reset etc. - enable async_pf feature of KVM on s390 - several sclp fixes and cleanups - several sigp fixes and cleanups -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJTDwZVAAoJEBF7vIC1phx8lx4P/Rv+UVD9XDFFF8yHuye1am40 NpRGjdarQ/9QUkS4gqyKwYUvIjAClk5id7U2d5zrfdc8XC49AH0ZhVFMdRupaOon AUqXjOXD5zAh9bfUcewg1EK1P1VuKcp0hyh0jFlIqk9Xmidw8N5guQ6iBoTqGJD5 UYTp0PuSqIjY1RCuF4fCTCurzRd1+J2oKcQBip7BSWlVuWZlg2/hPxoIraLezlz2 huwOU9tkSGXwSRv4C6fCcukEwlqnvkE6W0MCrHrcb2T8xYwAR2Jjs0TsscbKxb+t lIjZRiCxBrFwOLUqGN8DMYtZPffR+cigZ5bYb4o3PPJ0DQL4vLQVd8SPMPrdJhbb M7UOaeTclSTQuzmM/Uuc1pmrFc8PDq0dg50dT3weH2bW8aSgyqutYGpmUcm1Q6kq JLFuyswOBr1vS9o0TlBunP4+TqJJrnGvtIQ4EbRZm7zP78mBaIIrUcAZlbgOI+XI cSjtFXkBOCz0j28J9GSHrsWMC7RQ179TGdcH/FjDpu0dNDOxH7eH5gZPQoQDAqwC SjstqJdIFnd0qxOB1EqcgMUxbSqQYq3hoGvJ644ZrMA3T5trBn0fSw3J9ZU/qAK7 EvOKRacMfcacIj4l0aEQgpwqVmktwIYnkfetX/QAKw/4AImJz/R9GRkmYgjCfOH8 /CUfXM71zWLEdv1o5uJ5 =toIt -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/borntraeger/tags/kvm-s390-20140227' into staging Several features, fixes and cleanups for kvm/s390: - sclp event facility: cleanup structure. This allows to use realize/unrealize as well as migration support via vmsd - reboot: Two fixes that make reboot much more reliable - ipl: make elf loading more robust - flic interrupt controller: This allows to migrate floating interrupts, as well as clear them on reset etc. - enable async_pf feature of KVM on s390 - several sclp fixes and cleanups - several sigp fixes and cleanups * remotes/borntraeger/tags/kvm-s390-20140227: (22 commits) s390x/ipl: Fix crash of ELF images with arbitrary entry points s390x/kvm: Rework priv instruction handlers s390x/kvm: Add missing SIGP CPU RESET order s390x/kvm: Rework SIGP INITIAL CPU RESET handler s390x/cpu: Use ioctl to reset state in the kernel s390-ccw.img: new binary rom to match latest fixes s390-ccw.img: Fix sporadic errors with ccw boot image - initialize css s390-ccw.img: Fix sporadic reboot hangs: Initialize next_idx s390x/event-facility: exploit realize/unrealize s390x/event-facility: add support for live migration s390x/event-facility: code restructure s390x/event-facility: some renaming s390x/sclp: Fixed setting of condition code register s390x/sclp: Add missing checks to SCLP handler s390x/sclp: Fixed the size of sccb and code parameter s390x/eventfacility: mask out commands s390x/virtio-hcall: Specification exception for illegal subcodes s390x/virtio-hcall: Add range check for hypervisor call s390x/kvm: Fixed bad SIGP SET-ARCHITECTURE handler s390x/async_pf: Check for apf extension and enable pfault ... Conflicts: linux-headers/linux/kvm.h Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
c2cb92f9ea
22 changed files with 757 additions and 214 deletions
|
@ -1,2 +1,3 @@
|
|||
CONFIG_VIRTIO=y
|
||||
CONFIG_SCLPCONSOLE=y
|
||||
CONFIG_S390_FLIC=$(CONFIG_KVM)
|
||||
|
|
|
@ -25,3 +25,4 @@ obj-$(CONFIG_SH4) += sh_intc.o
|
|||
obj-$(CONFIG_XICS) += xics.o
|
||||
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
||||
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
||||
|
|
322
hw/intc/s390_flic.c
Normal file
322
hw/intc/s390_flic.c
Normal file
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* QEMU S390x KVM floating interrupt controller (flic)
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Jens Freimann <jfrei@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 <sys/ioctl.h>
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "migration/qemu-file.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define FLIC_SAVE_INITIAL_SIZE getpagesize()
|
||||
#define FLIC_FAILED (-1UL)
|
||||
#define FLIC_SAVEVM_VERSION 1
|
||||
|
||||
void s390_flic_init(void)
|
||||
{
|
||||
DeviceState *dev;
|
||||
int r;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
dev = qdev_create(NULL, "s390-flic");
|
||||
object_property_add_child(qdev_get_machine(), "s390-flic",
|
||||
OBJECT(dev), NULL);
|
||||
r = qdev_init(dev);
|
||||
if (r) {
|
||||
error_report("flic: couldn't create qdev");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* flic_get_all_irqs - store all pending irqs in buffer
|
||||
* @buf: pointer to buffer which is passed to kernel
|
||||
* @len: length of buffer
|
||||
* @flic: pointer to flic device state
|
||||
*
|
||||
* Returns: -ENOMEM if buffer is too small,
|
||||
* -EINVAL if attr.group is invalid,
|
||||
* -EFAULT if copying to userspace failed,
|
||||
* on success return number of stored interrupts
|
||||
*/
|
||||
static int flic_get_all_irqs(KVMS390FLICState *flic,
|
||||
void *buf, int len)
|
||||
{
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_GET_ALL_IRQS,
|
||||
.addr = (uint64_t) buf,
|
||||
.attr = len,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
|
||||
|
||||
return rc == -1 ? -errno : rc;
|
||||
}
|
||||
|
||||
static void flic_enable_pfault(KVMS390FLICState *flic)
|
||||
{
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_APF_ENABLE,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
if (rc) {
|
||||
fprintf(stderr, "flic: couldn't enable pfault\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void flic_disable_wait_pfault(KVMS390FLICState *flic)
|
||||
{
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
if (rc) {
|
||||
fprintf(stderr, "flic: couldn't disable pfault\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** flic_enqueue_irqs - returns 0 on success
|
||||
* @buf: pointer to buffer which is passed to kernel
|
||||
* @len: length of buffer
|
||||
* @flic: pointer to flic device state
|
||||
*
|
||||
* Returns: -EINVAL if attr.group is unknown
|
||||
*/
|
||||
static int flic_enqueue_irqs(void *buf, uint64_t len,
|
||||
KVMS390FLICState *flic)
|
||||
{
|
||||
int rc;
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_ENQUEUE,
|
||||
.addr = (uint64_t) buf,
|
||||
.attr = len,
|
||||
};
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
return rc ? -errno : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __get_all_irqs - store all pending irqs in buffer
|
||||
* @flic: pointer to flic device state
|
||||
* @buf: pointer to pointer to a buffer
|
||||
* @len: length of buffer
|
||||
*
|
||||
* Returns: return value of flic_get_all_irqs
|
||||
* Note: Retry and increase buffer size until flic_get_all_irqs
|
||||
* either returns a value >= 0 or a negative error code.
|
||||
* -ENOMEM is an exception, which means the buffer is too small
|
||||
* and we should try again. Other negative error codes can be
|
||||
* -EFAULT and -EINVAL which we ignore at this point
|
||||
*/
|
||||
static int __get_all_irqs(KVMS390FLICState *flic,
|
||||
void **buf, int len)
|
||||
{
|
||||
int r;
|
||||
|
||||
do {
|
||||
/* returns -ENOMEM if buffer is too small and number
|
||||
* of queued interrupts on success */
|
||||
r = flic_get_all_irqs(flic, *buf, len);
|
||||
if (r >= 0) {
|
||||
break;
|
||||
}
|
||||
len *= 2;
|
||||
*buf = g_try_realloc(*buf, len);
|
||||
if (!buf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
} while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_flic_save - Save pending floating interrupts
|
||||
* @f: QEMUFile containing migration state
|
||||
* @opaque: pointer to flic device state
|
||||
*
|
||||
* Note: Pass buf and len to kernel. Start with one page and
|
||||
* increase until buffer is sufficient or maxium size is
|
||||
* reached
|
||||
*/
|
||||
static void kvm_flic_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
KVMS390FLICState *flic = opaque;
|
||||
int len = FLIC_SAVE_INITIAL_SIZE;
|
||||
void *buf;
|
||||
int count;
|
||||
|
||||
flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
|
||||
|
||||
buf = g_try_malloc0(len);
|
||||
if (!buf) {
|
||||
/* Storing FLIC_FAILED into the count field here will cause the
|
||||
* target system to fail when attempting to load irqs from the
|
||||
* migration state */
|
||||
error_report("flic: couldn't allocate memory");
|
||||
qemu_put_be64(f, FLIC_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
count = __get_all_irqs(flic, &buf, len);
|
||||
if (count < 0) {
|
||||
error_report("flic: couldn't retrieve irqs from kernel, rc %d",
|
||||
count);
|
||||
/* Storing FLIC_FAILED into the count field here will cause the
|
||||
* target system to fail when attempting to load irqs from the
|
||||
* migration state */
|
||||
qemu_put_be64(f, FLIC_FAILED);
|
||||
} else {
|
||||
qemu_put_be64(f, count);
|
||||
qemu_put_buffer(f, (uint8_t *) buf,
|
||||
count * sizeof(struct kvm_s390_irq));
|
||||
}
|
||||
g_free(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_flic_load - Load pending floating interrupts
|
||||
* @f: QEMUFile containing migration state
|
||||
* @opaque: pointer to flic device state
|
||||
* @version_id: version id for migration
|
||||
*
|
||||
* Returns: value of flic_enqueue_irqs, -EINVAL on error
|
||||
* Note: Do nothing when no interrupts where stored
|
||||
* in QEMUFile
|
||||
*/
|
||||
static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
uint64_t len = 0;
|
||||
uint64_t count = 0;
|
||||
void *buf = NULL;
|
||||
int r = 0;
|
||||
|
||||
if (version_id != FLIC_SAVEVM_VERSION) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flic_enable_pfault((struct KVMS390FLICState *) opaque);
|
||||
|
||||
count = qemu_get_be64(f);
|
||||
len = count * sizeof(struct kvm_s390_irq);
|
||||
if (count == FLIC_FAILED) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (count == 0) {
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
buf = g_try_malloc0(len);
|
||||
if (!buf) {
|
||||
r = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
|
||||
r = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
|
||||
|
||||
out_free:
|
||||
g_free(buf);
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
|
||||
struct kvm_create_device cd = {0};
|
||||
int ret;
|
||||
|
||||
flic_state->fd = -1;
|
||||
if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
|
||||
trace_flic_no_device_api(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
cd.type = KVM_DEV_TYPE_FLIC;
|
||||
ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
|
||||
if (ret < 0) {
|
||||
trace_flic_create_device(errno);
|
||||
return;
|
||||
}
|
||||
flic_state->fd = cd.fd;
|
||||
|
||||
/* Register savevm handler for floating interrupts */
|
||||
register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
|
||||
kvm_flic_load, (void *) flic_state);
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
|
||||
|
||||
unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_reset(DeviceState *dev)
|
||||
{
|
||||
KVMS390FLICState *flic = KVM_S390_FLIC(dev);
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_CLEAR_IRQS,
|
||||
};
|
||||
int rc = 0;
|
||||
|
||||
if (flic->fd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
flic_disable_wait_pfault(flic);
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
if (rc) {
|
||||
trace_flic_reset_failed(errno);
|
||||
}
|
||||
|
||||
flic_enable_pfault(flic);
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = kvm_s390_flic_realize;
|
||||
dc->unrealize = kvm_s390_flic_unrealize;
|
||||
dc->reset = kvm_s390_flic_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo kvm_s390_flic_info = {
|
||||
.name = TYPE_KVM_S390_FLIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(KVMS390FLICState),
|
||||
.class_init = kvm_s390_flic_class_init,
|
||||
};
|
||||
|
||||
static void kvm_s390_flic_register_types(void)
|
||||
{
|
||||
type_register_static(&kvm_s390_flic_info);
|
||||
}
|
||||
|
||||
type_init(kvm_s390_flic_register_types)
|
|
@ -21,13 +21,13 @@
|
|||
#include "hw/s390x/sclp.h"
|
||||
#include "hw/s390x/event-facility.h"
|
||||
|
||||
typedef struct EventTypesBus {
|
||||
typedef struct SCLPEventsBus {
|
||||
BusState qbus;
|
||||
} EventTypesBus;
|
||||
} SCLPEventsBus;
|
||||
|
||||
struct SCLPEventFacility {
|
||||
EventTypesBus sbus;
|
||||
DeviceState *qdev;
|
||||
SysBusDevice parent_obj;
|
||||
SCLPEventsBus sbus;
|
||||
/* guest' receive mask */
|
||||
unsigned int receive_mask;
|
||||
};
|
||||
|
@ -291,7 +291,7 @@ static void sclp_events_bus_class_init(ObjectClass *klass, void *data)
|
|||
{
|
||||
}
|
||||
|
||||
static const TypeInfo s390_sclp_events_bus_info = {
|
||||
static const TypeInfo sclp_events_bus_info = {
|
||||
.name = TYPE_SCLP_EVENTS_BUS,
|
||||
.parent = TYPE_BUS,
|
||||
.class_init = sclp_events_bus_class_init,
|
||||
|
@ -299,7 +299,7 @@ static const TypeInfo s390_sclp_events_bus_info = {
|
|||
|
||||
static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code)
|
||||
{
|
||||
switch (code) {
|
||||
switch (code & SCLP_CMD_CODE_MASK) {
|
||||
case SCLP_CMD_READ_EVENT_DATA:
|
||||
read_event_data(ef, sccb);
|
||||
break;
|
||||
|
@ -315,21 +315,26 @@ static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code)
|
|||
}
|
||||
}
|
||||
|
||||
static int init_event_facility(S390SCLPDevice *sdev)
|
||||
static const VMStateDescription vmstate_event_facility = {
|
||||
.name = "vmstate-event-facility",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.minimum_version_id_old = 0,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(receive_mask, SCLPEventFacility),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static int init_event_facility(SCLPEventFacility *event_facility)
|
||||
{
|
||||
SCLPEventFacility *event_facility;
|
||||
DeviceState *sdev = DEVICE(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 */
|
||||
/* Spawn a new bus for SCLP events */
|
||||
qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
|
||||
TYPE_SCLP_EVENTS_BUS, DEVICE(sdev), NULL);
|
||||
TYPE_SCLP_EVENTS_BUS, sdev, NULL);
|
||||
event_facility->sbus.qbus.allow_hotplug = 0;
|
||||
event_facility->qdev = (DeviceState *) sdev;
|
||||
|
||||
quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce");
|
||||
if (!quiesce) {
|
||||
|
@ -346,43 +351,57 @@ static int init_event_facility(S390SCLPDevice *sdev)
|
|||
|
||||
static void reset_event_facility(DeviceState *dev)
|
||||
{
|
||||
S390SCLPDevice *sdev = SCLP_S390_DEVICE(dev);
|
||||
SCLPEventFacility *sdev = EVENT_FACILITY(dev);
|
||||
|
||||
sdev->ef->receive_mask = 0;
|
||||
sdev->receive_mask = 0;
|
||||
}
|
||||
|
||||
static void init_event_facility_class(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
S390SCLPDeviceClass *k = SCLP_S390_DEVICE_CLASS(klass);
|
||||
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;
|
||||
k->init = init_event_facility;
|
||||
k->command_handler = command_handler;
|
||||
k->event_pending = event_pending;
|
||||
}
|
||||
|
||||
static const TypeInfo s390_sclp_event_facility_info = {
|
||||
.name = "s390-sclp-event-facility",
|
||||
.parent = TYPE_DEVICE_S390_SCLP,
|
||||
.instance_size = sizeof(S390SCLPDevice),
|
||||
static const TypeInfo sclp_event_facility_info = {
|
||||
.name = TYPE_SCLP_EVENT_FACILITY,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SCLPEventFacility),
|
||||
.class_init = init_event_facility_class,
|
||||
.class_size = sizeof(SCLPEventFacilityClass),
|
||||
};
|
||||
|
||||
static int event_qdev_init(DeviceState *qdev)
|
||||
static void event_realize(DeviceState *qdev, Error **errp)
|
||||
{
|
||||
SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev);
|
||||
SCLPEvent *event = SCLP_EVENT(qdev);
|
||||
SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);
|
||||
|
||||
return child->init(event);
|
||||
if (child->init) {
|
||||
int rc = child->init(event);
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "SCLP event initialization failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int event_qdev_exit(DeviceState *qdev)
|
||||
static void event_unrealize(DeviceState *qdev, Error **errp)
|
||||
{
|
||||
SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev);
|
||||
SCLPEvent *event = SCLP_EVENT(qdev);
|
||||
SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);
|
||||
if (child->exit) {
|
||||
child->exit(event);
|
||||
int rc = child->exit(event);
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "SCLP event exit failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void event_class_init(ObjectClass *klass, void *data)
|
||||
|
@ -391,11 +410,11 @@ static void event_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
dc->bus_type = TYPE_SCLP_EVENTS_BUS;
|
||||
dc->unplug = qdev_simple_unplug_cb;
|
||||
dc->init = event_qdev_init;
|
||||
dc->exit = event_qdev_exit;
|
||||
dc->realize = event_realize;
|
||||
dc->unrealize = event_unrealize;
|
||||
}
|
||||
|
||||
static const TypeInfo s390_sclp_event_type_info = {
|
||||
static const TypeInfo sclp_event_type_info = {
|
||||
.name = TYPE_SCLP_EVENT,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(SCLPEvent),
|
||||
|
@ -406,9 +425,9 @@ static const TypeInfo s390_sclp_event_type_info = {
|
|||
|
||||
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_register_static(&sclp_events_bus_info);
|
||||
type_register_static(&sclp_event_facility_info);
|
||||
type_register_static(&sclp_event_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types)
|
||||
|
|
|
@ -95,7 +95,8 @@ static int s390_ipl_init(SysBusDevice *dev)
|
|||
}
|
||||
return 0;
|
||||
} else {
|
||||
kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, NULL,
|
||||
uint64_t pentry = KERN_IMAGE_START;
|
||||
kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL,
|
||||
NULL, 1, ELF_MACHINE, 0);
|
||||
if (kernel_size == -1) {
|
||||
kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
|
||||
|
@ -104,15 +105,19 @@ static int s390_ipl_init(SysBusDevice *dev)
|
|||
fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
|
||||
return -1;
|
||||
}
|
||||
/* we have to overwrite values in the kernel image, which are "rom" */
|
||||
strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);
|
||||
|
||||
/*
|
||||
* we can not rely on the ELF entry point, since up to 3.2 this
|
||||
* value was 0x800 (the SALIPL loader) and it wont work. For
|
||||
* all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine.
|
||||
* Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the
|
||||
* kernel parameters here as well. Note: For old kernels (up to 3.2)
|
||||
* we can not rely on the ELF entry point - it was 0x800 (the SALIPL
|
||||
* loader) and it won't work. For this case we force it to 0x10000, too.
|
||||
*/
|
||||
ipl->start_addr = KERN_IMAGE_START;
|
||||
if (pentry == KERN_IMAGE_START || pentry == 0x800) {
|
||||
ipl->start_addr = KERN_IMAGE_START;
|
||||
/* Overwrite parameters in the kernel image, which are "rom" */
|
||||
strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);
|
||||
} else {
|
||||
ipl->start_addr = pentry;
|
||||
}
|
||||
}
|
||||
if (ipl->initrd) {
|
||||
ram_addr_t initrd_offset;
|
||||
|
|
|
@ -13,13 +13,14 @@
|
|||
#include "exec/address-spaces.h"
|
||||
#include "s390-virtio.h"
|
||||
#include "hw/s390x/sclp.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
#include "ioinst.h"
|
||||
#include "css.h"
|
||||
#include "virtio-ccw.h"
|
||||
|
||||
void io_subsystem_reset(void)
|
||||
{
|
||||
DeviceState *css, *sclp;
|
||||
DeviceState *css, *sclp, *flic;
|
||||
|
||||
css = DEVICE(object_resolve_path_type("", "virtual-css-bridge", NULL));
|
||||
if (css) {
|
||||
|
@ -30,6 +31,10 @@ void io_subsystem_reset(void)
|
|||
if (sclp) {
|
||||
qdev_reset_all(sclp);
|
||||
}
|
||||
flic = DEVICE(object_resolve_path_type("", "s390-flic", NULL));
|
||||
if (flic) {
|
||||
qdev_reset_all(flic);
|
||||
}
|
||||
}
|
||||
|
||||
static int virtio_ccw_hcall_notify(const uint64_t *args)
|
||||
|
@ -99,6 +104,7 @@ static void ccw_init(QEMUMachineInitArgs *args)
|
|||
s390_sclp_init();
|
||||
s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline,
|
||||
args->initrd_filename, "s390-ccw.img");
|
||||
s390_flic_init();
|
||||
|
||||
/* register hypercalls */
|
||||
virtio_ccw_register_hcalls();
|
||||
|
|
|
@ -26,11 +26,15 @@ void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn)
|
|||
|
||||
int s390_virtio_hypercall(CPUS390XState *env)
|
||||
{
|
||||
s390_virtio_fn fn = s390_diag500_table[env->regs[1]];
|
||||
s390_virtio_fn fn;
|
||||
|
||||
if (!fn) {
|
||||
return -EINVAL;
|
||||
if (env->regs[1] < MAX_DIAG_SUBCODES) {
|
||||
fn = s390_diag500_table[env->regs[1]];
|
||||
if (fn) {
|
||||
env->regs[2] = fn(&env->regs[2]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return fn(&env->regs[2]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include "hw/s390x/s390-virtio-bus.h"
|
||||
#include "hw/s390x/sclp.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
#include "hw/s390x/s390-virtio.h"
|
||||
|
||||
//#define DEBUG_S390
|
||||
|
@ -251,6 +252,7 @@ static void s390_init(QEMUMachineInitArgs *args)
|
|||
s390_sclp_init();
|
||||
s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline,
|
||||
args->initrd_filename, ZIPL_FILENAME);
|
||||
s390_flic_init();
|
||||
|
||||
/* register hypercalls */
|
||||
s390_virtio_register_hcalls();
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
#include "sysemu/sysemu.h"
|
||||
|
||||
#include "hw/s390x/sclp.h"
|
||||
#include "hw/s390x/event-facility.h"
|
||||
|
||||
static inline S390SCLPDevice *get_event_facility(void)
|
||||
static inline SCLPEventFacility *get_event_facility(void)
|
||||
{
|
||||
ObjectProperty *op = object_property_find(qdev_get_machine(),
|
||||
"s390-sclp-event-facility",
|
||||
TYPE_SCLP_EVENT_FACILITY,
|
||||
NULL);
|
||||
assert(op);
|
||||
return op->opaque;
|
||||
|
@ -89,9 +90,10 @@ static void sclp_read_cpu_info(SCCB *sccb)
|
|||
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
|
||||
}
|
||||
|
||||
static void sclp_execute(SCCB *sccb, uint64_t code)
|
||||
static void sclp_execute(SCCB *sccb, uint32_t code)
|
||||
{
|
||||
S390SCLPDevice *sdev = get_event_facility();
|
||||
SCLPEventFacility *ef = get_event_facility();
|
||||
SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
|
||||
|
||||
switch (code & SCLP_CMD_CODE_MASK) {
|
||||
case SCLP_CMDW_READ_SCP_INFO:
|
||||
|
@ -102,12 +104,12 @@ static void sclp_execute(SCCB *sccb, uint64_t code)
|
|||
sclp_read_cpu_info(sccb);
|
||||
break;
|
||||
default:
|
||||
sdev->sclp_command_handler(sdev->ef, sccb, code);
|
||||
efc->command_handler(ef, sccb, code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int sclp_service_call(uint32_t sccb, uint64_t code)
|
||||
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
|
||||
{
|
||||
int r = 0;
|
||||
SCCB work_sccb;
|
||||
|
@ -115,11 +117,16 @@ int sclp_service_call(uint32_t sccb, uint64_t code)
|
|||
hwaddr sccb_len = sizeof(SCCB);
|
||||
|
||||
/* first some basic checks on program checks */
|
||||
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||
r = -PGM_PRIVILEGED;
|
||||
goto out;
|
||||
}
|
||||
if (cpu_physical_memory_is_io(sccb)) {
|
||||
r = -PGM_ADDRESSING;
|
||||
goto out;
|
||||
}
|
||||
if (sccb & ~0x7ffffff8ul) {
|
||||
if ((sccb & ~0x1fffUL) == 0 || (sccb & ~0x1fffUL) == env->psa
|
||||
|| (sccb & ~0x7ffffff8UL) != 0) {
|
||||
r = -PGM_SPECIFICATION;
|
||||
goto out;
|
||||
}
|
||||
|
@ -151,11 +158,13 @@ out:
|
|||
|
||||
void sclp_service_interrupt(uint32_t sccb)
|
||||
{
|
||||
S390SCLPDevice *sdev = get_event_facility();
|
||||
SCLPEventFacility *ef = get_event_facility();
|
||||
SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
|
||||
|
||||
uint32_t param = sccb & ~3;
|
||||
|
||||
/* Indicate whether an event is still pending */
|
||||
param |= sdev->event_pending(sdev->ef) ? 1 : 0;
|
||||
param |= efc->event_pending(ef) ? 1 : 0;
|
||||
|
||||
if (!param) {
|
||||
/* No need to send an interrupt, there's nothing to be notified about */
|
||||
|
@ -168,47 +177,9 @@ void sclp_service_interrupt(uint32_t sccb)
|
|||
|
||||
void s390_sclp_init(void)
|
||||
{
|
||||
DeviceState *dev = qdev_create(NULL, "s390-sclp-event-facility");
|
||||
DeviceState *dev = qdev_create(NULL, TYPE_SCLP_EVENT_FACILITY);
|
||||
|
||||
object_property_add_child(qdev_get_machine(), "s390-sclp-event-facility",
|
||||
object_property_add_child(qdev_get_machine(), TYPE_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 const 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)
|
||||
|
|
|
@ -176,4 +176,23 @@ typedef struct SCLPEventClass {
|
|||
bool (*can_handle_event)(uint8_t type);
|
||||
} SCLPEventClass;
|
||||
|
||||
#define TYPE_SCLP_EVENT_FACILITY "s390-sclp-event-facility"
|
||||
#define EVENT_FACILITY(obj) \
|
||||
OBJECT_CHECK(SCLPEventFacility, (obj), TYPE_SCLP_EVENT_FACILITY)
|
||||
#define EVENT_FACILITY_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(SCLPEventFacilityClass, (klass), \
|
||||
TYPE_SCLP_EVENT_FACILITY)
|
||||
#define EVENT_FACILITY_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(SCLPEventFacilityClass, (obj), \
|
||||
TYPE_SCLP_EVENT_FACILITY)
|
||||
|
||||
typedef struct SCLPEventFacility SCLPEventFacility;
|
||||
|
||||
typedef struct SCLPEventFacilityClass {
|
||||
DeviceClass parent_class;
|
||||
int (*init)(SCLPEventFacility *ef);
|
||||
void (*command_handler)(SCLPEventFacility *ef, SCCB *sccb, uint64_t code);
|
||||
bool (*event_pending)(SCLPEventFacility *ef);
|
||||
} SCLPEventFacilityClass;
|
||||
|
||||
#endif
|
||||
|
|
33
include/hw/s390x/s390_flic.h
Normal file
33
include/hw/s390x/s390_flic.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* QEMU S390x KVM floating interrupt controller (flic)
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Jens Freimann <jfrei@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.
|
||||
*/
|
||||
|
||||
#ifndef __KVM_S390_FLIC_H
|
||||
#define __KVM_S390_FLIC_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_KVM_S390_FLIC "s390-flic"
|
||||
#define KVM_S390_FLIC(obj) \
|
||||
OBJECT_CHECK(KVMS390FLICState, (obj), TYPE_KVM_S390_FLIC)
|
||||
|
||||
typedef struct KVMS390FLICState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
uint32_t fd;
|
||||
} KVMS390FLICState;
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
void s390_flic_init(void);
|
||||
#else
|
||||
static inline void s390_flic_init(void) { }
|
||||
#endif
|
||||
|
||||
#endif /* __KVM_S390_FLIC_H */
|
|
@ -161,30 +161,6 @@ 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);
|
||||
|
|
|
@ -16,6 +16,22 @@
|
|||
|
||||
#define __KVM_S390
|
||||
|
||||
/* Device control API: s390-specific devices */
|
||||
#define KVM_DEV_FLIC_GET_ALL_IRQS 1
|
||||
#define KVM_DEV_FLIC_ENQUEUE 2
|
||||
#define KVM_DEV_FLIC_CLEAR_IRQS 3
|
||||
#define KVM_DEV_FLIC_APF_ENABLE 4
|
||||
#define KVM_DEV_FLIC_APF_DISABLE_WAIT 5
|
||||
/*
|
||||
* We can have up to 4*64k pending subchannels + 8 adapter interrupts,
|
||||
* as well as up to ASYNC_PF_PER_VCPU*KVM_MAX_VCPUS pfault done interrupts.
|
||||
* There are also sclp and machine checks. This gives us
|
||||
* sizeof(kvm_s390_irq)*(4*65536+8+64*64+1+1) = 72 * 266250 = 19170000
|
||||
* Lets round up to 8192 pages.
|
||||
*/
|
||||
#define KVM_S390_MAX_FLOAT_IRQS 266250
|
||||
#define KVM_S390_FLIC_MAX_BUFFER 0x2000000
|
||||
|
||||
/* for KVM_GET_REGS and KVM_SET_REGS */
|
||||
struct kvm_regs {
|
||||
/* general purpose regs for s390 */
|
||||
|
@ -57,4 +73,7 @@ struct kvm_sync_regs {
|
|||
#define KVM_REG_S390_EPOCHDIFF (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x2)
|
||||
#define KVM_REG_S390_CPU_TIMER (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x3)
|
||||
#define KVM_REG_S390_CLOCK_COMP (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x4)
|
||||
#define KVM_REG_S390_PFTOKEN (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x5)
|
||||
#define KVM_REG_S390_PFCOMPARE (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x6)
|
||||
#define KVM_REG_S390_PFSELECT (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x7)
|
||||
#endif
|
||||
|
|
|
@ -413,6 +413,8 @@ struct kvm_s390_psw {
|
|||
#define KVM_S390_PROGRAM_INT 0xfffe0001u
|
||||
#define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u
|
||||
#define KVM_S390_RESTART 0xfffe0003u
|
||||
#define KVM_S390_INT_PFAULT_INIT 0xfffe0004u
|
||||
#define KVM_S390_INT_PFAULT_DONE 0xfffe0005u
|
||||
#define KVM_S390_MCHK 0xfffe1000u
|
||||
#define KVM_S390_INT_VIRTIO 0xffff2603u
|
||||
#define KVM_S390_INT_SERVICE 0xffff2401u
|
||||
|
@ -434,6 +436,69 @@ struct kvm_s390_interrupt {
|
|||
__u64 parm64;
|
||||
};
|
||||
|
||||
struct kvm_s390_io_info {
|
||||
__u16 subchannel_id;
|
||||
__u16 subchannel_nr;
|
||||
__u32 io_int_parm;
|
||||
__u32 io_int_word;
|
||||
};
|
||||
|
||||
struct kvm_s390_ext_info {
|
||||
__u32 ext_params;
|
||||
__u32 pad;
|
||||
__u64 ext_params2;
|
||||
};
|
||||
|
||||
struct kvm_s390_pgm_info {
|
||||
__u64 trans_exc_code;
|
||||
__u64 mon_code;
|
||||
__u64 per_address;
|
||||
__u32 data_exc_code;
|
||||
__u16 code;
|
||||
__u16 mon_class_nr;
|
||||
__u8 per_code;
|
||||
__u8 per_atmid;
|
||||
__u8 exc_access_id;
|
||||
__u8 per_access_id;
|
||||
__u8 op_access_id;
|
||||
__u8 pad[3];
|
||||
};
|
||||
|
||||
struct kvm_s390_prefix_info {
|
||||
__u32 address;
|
||||
};
|
||||
|
||||
struct kvm_s390_extcall_info {
|
||||
__u16 code;
|
||||
};
|
||||
|
||||
struct kvm_s390_emerg_info {
|
||||
__u16 code;
|
||||
};
|
||||
|
||||
struct kvm_s390_mchk_info {
|
||||
__u64 cr14;
|
||||
__u64 mcic;
|
||||
__u64 failing_storage_address;
|
||||
__u32 ext_damage_code;
|
||||
__u32 pad;
|
||||
__u8 fixed_logout[16];
|
||||
};
|
||||
|
||||
struct kvm_s390_irq {
|
||||
__u64 type;
|
||||
union {
|
||||
struct kvm_s390_io_info io;
|
||||
struct kvm_s390_ext_info ext;
|
||||
struct kvm_s390_pgm_info pgm;
|
||||
struct kvm_s390_emerg_info emerg;
|
||||
struct kvm_s390_extcall_info extcall;
|
||||
struct kvm_s390_prefix_info prefix;
|
||||
struct kvm_s390_mchk_info mchk;
|
||||
char reserved[64];
|
||||
} u;
|
||||
};
|
||||
|
||||
/* for KVM_SET_GUEST_DEBUG */
|
||||
|
||||
#define KVM_GUESTDBG_ENABLE 0x00000001
|
||||
|
@ -855,6 +920,7 @@ struct kvm_device_attr {
|
|||
#define KVM_DEV_VFIO_GROUP_ADD 1
|
||||
#define KVM_DEV_VFIO_GROUP_DEL 2
|
||||
#define KVM_DEV_TYPE_ARM_VGIC_V2 5
|
||||
#define KVM_DEV_TYPE_FLIC 6
|
||||
|
||||
/*
|
||||
* ioctls for VM fds
|
||||
|
|
Binary file not shown.
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include "s390-ccw.h"
|
||||
|
||||
struct subchannel_id blk_schid;
|
||||
char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE)));
|
||||
uint64_t boot_value;
|
||||
|
||||
|
@ -23,13 +22,13 @@ void virtio_panic(const char *string)
|
|||
|
||||
static void virtio_setup(uint64_t dev_info)
|
||||
{
|
||||
struct subchannel_id blk_schid = { .one = 1 };
|
||||
struct schib schib;
|
||||
int i;
|
||||
int r;
|
||||
bool found = false;
|
||||
bool check_devno = false;
|
||||
uint16_t dev_no = -1;
|
||||
blk_schid.one = 1;
|
||||
|
||||
if (dev_info != -1) {
|
||||
check_devno = true;
|
||||
|
|
|
@ -124,6 +124,7 @@ static void vring_init(struct vring *vr, unsigned int num, void *p,
|
|||
vr->used->flags = VRING_USED_F_NO_NOTIFY;
|
||||
vr->used->idx = 0;
|
||||
vr->used_idx = 0;
|
||||
vr->next_idx = 0;
|
||||
|
||||
debug_print_addr("init vr", vr);
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ static void s390_cpu_reset(CPUState *s)
|
|||
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
|
||||
env->pfault_token = -1UL;
|
||||
s390_del_running_cpu(cpu);
|
||||
scc->parent_reset(s);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
@ -105,6 +106,17 @@ static void s390_cpu_initial_reset(CPUState *s)
|
|||
/* architectured initial values for CR 0 and 14 */
|
||||
env->cregs[0] = CR0_RESET;
|
||||
env->cregs[14] = CR14_RESET;
|
||||
|
||||
env->pfault_token = -1UL;
|
||||
|
||||
#if defined(CONFIG_KVM)
|
||||
/* Reset state inside the kernel that we cannot access yet from QEMU. */
|
||||
if (kvm_enabled()) {
|
||||
if (kvm_vcpu_ioctl(s, KVM_S390_INITIAL_RESET, NULL)) {
|
||||
perror("Initial CPU reset failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* CPUClass:reset() */
|
||||
|
@ -123,6 +135,9 @@ static void s390_cpu_full_reset(CPUState *s)
|
|||
/* architectured initial values for CR 0 and 14 */
|
||||
env->cregs[0] = CR0_RESET;
|
||||
env->cregs[14] = CR14_RESET;
|
||||
|
||||
env->pfault_token = -1UL;
|
||||
|
||||
/* set halted to 1 to make sure we can add the cpu in
|
||||
* s390_ipl_cpu code, where CPUState::halted is set back to 0
|
||||
* after incrementing the cpu counter */
|
||||
|
|
|
@ -121,6 +121,10 @@ typedef struct CPUS390XState {
|
|||
uint64_t cputm;
|
||||
uint32_t todpr;
|
||||
|
||||
uint64_t pfault_token;
|
||||
uint64_t pfault_compare;
|
||||
uint64_t pfault_select;
|
||||
|
||||
CPU_COMMON
|
||||
|
||||
/* reset does memset(0) up to here */
|
||||
|
@ -959,7 +963,7 @@ struct sysib_322 {
|
|||
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(uint32_t sccb, uint64_t code);
|
||||
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code);
|
||||
uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst,
|
||||
uint64_t vr);
|
||||
|
||||
|
|
|
@ -53,25 +53,28 @@
|
|||
#define IPA0_B9 0xb900
|
||||
#define IPA0_EB 0xeb00
|
||||
|
||||
#define PRIV_SCLP_CALL 0x20
|
||||
#define PRIV_CSCH 0x30
|
||||
#define PRIV_HSCH 0x31
|
||||
#define PRIV_MSCH 0x32
|
||||
#define PRIV_SSCH 0x33
|
||||
#define PRIV_STSCH 0x34
|
||||
#define PRIV_TSCH 0x35
|
||||
#define PRIV_TPI 0x36
|
||||
#define PRIV_SAL 0x37
|
||||
#define PRIV_RSCH 0x38
|
||||
#define PRIV_STCRW 0x39
|
||||
#define PRIV_STCPS 0x3a
|
||||
#define PRIV_RCHP 0x3b
|
||||
#define PRIV_SCHM 0x3c
|
||||
#define PRIV_CHSC 0x5f
|
||||
#define PRIV_SIGA 0x74
|
||||
#define PRIV_XSCH 0x76
|
||||
#define PRIV_SQBS 0x8a
|
||||
#define PRIV_EQBS 0x9c
|
||||
#define PRIV_B2_SCLP_CALL 0x20
|
||||
#define PRIV_B2_CSCH 0x30
|
||||
#define PRIV_B2_HSCH 0x31
|
||||
#define PRIV_B2_MSCH 0x32
|
||||
#define PRIV_B2_SSCH 0x33
|
||||
#define PRIV_B2_STSCH 0x34
|
||||
#define PRIV_B2_TSCH 0x35
|
||||
#define PRIV_B2_TPI 0x36
|
||||
#define PRIV_B2_SAL 0x37
|
||||
#define PRIV_B2_RSCH 0x38
|
||||
#define PRIV_B2_STCRW 0x39
|
||||
#define PRIV_B2_STCPS 0x3a
|
||||
#define PRIV_B2_RCHP 0x3b
|
||||
#define PRIV_B2_SCHM 0x3c
|
||||
#define PRIV_B2_CHSC 0x5f
|
||||
#define PRIV_B2_SIGA 0x74
|
||||
#define PRIV_B2_XSCH 0x76
|
||||
|
||||
#define PRIV_EB_SQBS 0x8a
|
||||
|
||||
#define PRIV_B9_EQBS 0x9c
|
||||
|
||||
#define DIAG_IPL 0x308
|
||||
#define DIAG_KVM_HYPERCALL 0x500
|
||||
#define DIAG_KVM_BREAKPOINT 0x501
|
||||
|
@ -87,12 +90,14 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
|||
};
|
||||
|
||||
static int cap_sync_regs;
|
||||
static int cap_async_pf;
|
||||
|
||||
static void *legacy_s390_alloc(size_t size);
|
||||
|
||||
int kvm_arch_init(KVMState *s)
|
||||
{
|
||||
cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS);
|
||||
cap_async_pf = kvm_check_extension(s, KVM_CAP_ASYNC_PF);
|
||||
if (!kvm_check_extension(s, KVM_CAP_S390_GMAP)
|
||||
|| !kvm_check_extension(s, KVM_CAP_S390_COW)) {
|
||||
phys_mem_set_alloc(legacy_s390_alloc);
|
||||
|
@ -178,6 +183,29 @@ int kvm_arch_put_registers(CPUState *cs, int level)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (cap_async_pf) {
|
||||
reg.id = KVM_REG_S390_PFTOKEN;
|
||||
reg.addr = (__u64)&(env->pfault_token);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg.id = KVM_REG_S390_PFCOMPARE;
|
||||
reg.addr = (__u64)&(env->pfault_compare);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg.id = KVM_REG_S390_PFSELECT;
|
||||
reg.addr = (__u64)&(env->pfault_select);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (cap_sync_regs &&
|
||||
cs->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS &&
|
||||
cs->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) {
|
||||
|
@ -282,6 +310,29 @@ int kvm_arch_get_registers(CPUState *cs)
|
|||
return r;
|
||||
}
|
||||
|
||||
if (cap_async_pf) {
|
||||
reg.id = KVM_REG_S390_PFTOKEN;
|
||||
reg.addr = (__u64)&(env->pfault_token);
|
||||
r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
reg.id = KVM_REG_S390_PFCOMPARE;
|
||||
reg.addr = (__u64)&(env->pfault_compare);
|
||||
r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
reg.id = KVM_REG_S390_PFSELECT;
|
||||
reg.addr = (__u64)&(env->pfault_select);
|
||||
r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -392,117 +443,128 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run,
|
|||
uint16_t ipbh0)
|
||||
{
|
||||
CPUS390XState *env = &cpu->env;
|
||||
uint32_t sccb;
|
||||
uint64_t code;
|
||||
uint64_t sccb;
|
||||
uint32_t code;
|
||||
int r = 0;
|
||||
|
||||
cpu_synchronize_state(CPU(cpu));
|
||||
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||
enter_pgmcheck(cpu, PGM_PRIVILEGED);
|
||||
return 0;
|
||||
}
|
||||
sccb = env->regs[ipbh0 & 0xf];
|
||||
code = env->regs[(ipbh0 & 0xf0) >> 4];
|
||||
|
||||
r = sclp_service_call(sccb, code);
|
||||
r = sclp_service_call(env, sccb, code);
|
||||
if (r < 0) {
|
||||
enter_pgmcheck(cpu, -r);
|
||||
} else {
|
||||
setcc(cpu, r);
|
||||
}
|
||||
setcc(cpu, r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_handle_css_inst(S390CPU *cpu, struct kvm_run *run,
|
||||
uint8_t ipa0, uint8_t ipa1, uint8_t ipb)
|
||||
static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
|
||||
{
|
||||
CPUS390XState *env = &cpu->env;
|
||||
|
||||
if (ipa0 != 0xb2) {
|
||||
/* Not handled for now. */
|
||||
return -1;
|
||||
}
|
||||
int rc = 0;
|
||||
uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16;
|
||||
|
||||
cpu_synchronize_state(CPU(cpu));
|
||||
|
||||
switch (ipa1) {
|
||||
case PRIV_XSCH:
|
||||
case PRIV_B2_XSCH:
|
||||
ioinst_handle_xsch(cpu, env->regs[1]);
|
||||
break;
|
||||
case PRIV_CSCH:
|
||||
case PRIV_B2_CSCH:
|
||||
ioinst_handle_csch(cpu, env->regs[1]);
|
||||
break;
|
||||
case PRIV_HSCH:
|
||||
case PRIV_B2_HSCH:
|
||||
ioinst_handle_hsch(cpu, env->regs[1]);
|
||||
break;
|
||||
case PRIV_MSCH:
|
||||
case PRIV_B2_MSCH:
|
||||
ioinst_handle_msch(cpu, env->regs[1], run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_SSCH:
|
||||
case PRIV_B2_SSCH:
|
||||
ioinst_handle_ssch(cpu, env->regs[1], run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_STCRW:
|
||||
case PRIV_B2_STCRW:
|
||||
ioinst_handle_stcrw(cpu, run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_STSCH:
|
||||
case PRIV_B2_STSCH:
|
||||
ioinst_handle_stsch(cpu, env->regs[1], run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_TSCH:
|
||||
case PRIV_B2_TSCH:
|
||||
/* We should only get tsch via KVM_EXIT_S390_TSCH. */
|
||||
fprintf(stderr, "Spurious tsch intercept\n");
|
||||
break;
|
||||
case PRIV_CHSC:
|
||||
case PRIV_B2_CHSC:
|
||||
ioinst_handle_chsc(cpu, run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_TPI:
|
||||
case PRIV_B2_TPI:
|
||||
/* This should have been handled by kvm already. */
|
||||
fprintf(stderr, "Spurious tpi intercept\n");
|
||||
break;
|
||||
case PRIV_SCHM:
|
||||
case PRIV_B2_SCHM:
|
||||
ioinst_handle_schm(cpu, env->regs[1], env->regs[2],
|
||||
run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_RSCH:
|
||||
case PRIV_B2_RSCH:
|
||||
ioinst_handle_rsch(cpu, env->regs[1]);
|
||||
break;
|
||||
case PRIV_RCHP:
|
||||
case PRIV_B2_RCHP:
|
||||
ioinst_handle_rchp(cpu, env->regs[1]);
|
||||
break;
|
||||
case PRIV_STCPS:
|
||||
case PRIV_B2_STCPS:
|
||||
/* We do not provide this instruction, it is suppressed. */
|
||||
break;
|
||||
case PRIV_SAL:
|
||||
case PRIV_B2_SAL:
|
||||
ioinst_handle_sal(cpu, env->regs[1]);
|
||||
break;
|
||||
case PRIV_SIGA:
|
||||
case PRIV_B2_SIGA:
|
||||
/* Not provided, set CC = 3 for subchannel not operational */
|
||||
setcc(cpu, 3);
|
||||
break;
|
||||
case PRIV_B2_SCLP_CALL:
|
||||
rc = kvm_sclp_service_call(cpu, run, ipbh0);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
rc = -1;
|
||||
DPRINTF("KVM: unhandled PRIV: 0xb2%x\n", ipa1);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int handle_priv(S390CPU *cpu, struct kvm_run *run,
|
||||
uint8_t ipa0, uint8_t ipa1)
|
||||
static int handle_b9(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
|
||||
{
|
||||
int r = 0;
|
||||
uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16;
|
||||
uint8_t ipb = run->s390_sieic.ipb & 0xff;
|
||||
|
||||
DPRINTF("KVM: PRIV: %d\n", ipa1);
|
||||
switch (ipa1) {
|
||||
case PRIV_SCLP_CALL:
|
||||
r = kvm_sclp_service_call(cpu, run, ipbh0);
|
||||
break;
|
||||
default:
|
||||
r = kvm_handle_css_inst(cpu, run, ipa0, ipa1, ipb);
|
||||
if (r == -1) {
|
||||
DPRINTF("KVM: unhandled PRIV: 0x%x\n", ipa1);
|
||||
}
|
||||
break;
|
||||
case PRIV_B9_EQBS:
|
||||
/* just inject exception */
|
||||
r = -1;
|
||||
break;
|
||||
default:
|
||||
r = -1;
|
||||
DPRINTF("KVM: unhandled PRIV: 0xb9%x\n", ipa1);
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int handle_eb(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
switch (ipa1) {
|
||||
case PRIV_EB_SQBS:
|
||||
/* just inject exception */
|
||||
r = -1;
|
||||
break;
|
||||
default:
|
||||
r = -1;
|
||||
DPRINTF("KVM: unhandled PRIV: 0xeb%x\n", ipa1);
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
|
@ -511,11 +573,16 @@ static int handle_priv(S390CPU *cpu, struct kvm_run *run,
|
|||
static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
|
||||
{
|
||||
CPUS390XState *env = &cpu->env;
|
||||
int ret;
|
||||
|
||||
cpu_synchronize_state(CPU(cpu));
|
||||
env->regs[2] = s390_virtio_hypercall(env);
|
||||
ret = s390_virtio_hypercall(env);
|
||||
if (ret == -EINVAL) {
|
||||
enter_pgmcheck(cpu, PGM_SPECIFICATION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run)
|
||||
|
@ -576,25 +643,22 @@ int kvm_s390_cpu_restart(S390CPU *cpu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int s390_cpu_initial_reset(S390CPU *cpu)
|
||||
static void sigp_initial_cpu_reset(void *arg)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
int i;
|
||||
CPUState *cpu = arg;
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
|
||||
|
||||
s390_del_running_cpu(cpu);
|
||||
if (kvm_vcpu_ioctl(cs, KVM_S390_INITIAL_RESET, NULL) < 0) {
|
||||
perror("cannot init reset vcpu");
|
||||
}
|
||||
cpu_synchronize_state(cpu);
|
||||
scc->initial_cpu_reset(cpu);
|
||||
}
|
||||
|
||||
/* Manually zero out all registers */
|
||||
cpu_synchronize_state(cs);
|
||||
for (i = 0; i < 16; i++) {
|
||||
env->regs[i] = 0;
|
||||
}
|
||||
static void sigp_cpu_reset(void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
|
||||
|
||||
DPRINTF("DONE: SIGP initial reset: %p\n", env);
|
||||
return 0;
|
||||
cpu_synchronize_state(cpu);
|
||||
scc->cpu_reset(cpu);
|
||||
}
|
||||
|
||||
#define SIGP_ORDER_MASK 0x000000ff
|
||||
|
@ -628,10 +692,17 @@ static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
|
|||
cc = kvm_s390_cpu_restart(target_cpu);
|
||||
break;
|
||||
case SIGP_SET_ARCH:
|
||||
/* make the caller panic */
|
||||
return -1;
|
||||
*statusreg &= 0xffffffff00000000UL;
|
||||
*statusreg |= SIGP_STAT_INVALID_PARAMETER;
|
||||
cc = 1; /* status stored */
|
||||
break;
|
||||
case SIGP_INITIAL_CPU_RESET:
|
||||
cc = s390_cpu_initial_reset(target_cpu);
|
||||
run_on_cpu(CPU(target_cpu), sigp_initial_cpu_reset, CPU(target_cpu));
|
||||
cc = 0;
|
||||
break;
|
||||
case SIGP_CPU_RESET:
|
||||
run_on_cpu(CPU(target_cpu), sigp_cpu_reset, CPU(target_cpu));
|
||||
cc = 0;
|
||||
break;
|
||||
default:
|
||||
DPRINTF("KVM: unknown SIGP: 0x%x\n", order_code);
|
||||
|
@ -656,9 +727,13 @@ static void handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
|||
run->s390_sieic.ipa, run->s390_sieic.ipb);
|
||||
switch (ipa0) {
|
||||
case IPA0_B2:
|
||||
r = handle_b2(cpu, run, ipa1);
|
||||
break;
|
||||
case IPA0_B9:
|
||||
r = handle_b9(cpu, run, ipa1);
|
||||
break;
|
||||
case IPA0_EB:
|
||||
r = handle_priv(cpu, run, ipa0 >> 8, ipa1);
|
||||
r = handle_eb(cpu, run, ipa1);
|
||||
break;
|
||||
case IPA0_DIAG:
|
||||
r = handle_diag(cpu, run, run->s390_sieic.ipb);
|
||||
|
|
|
@ -93,7 +93,7 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
|
|||
/* SCLP service call */
|
||||
uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
|
||||
{
|
||||
int r = sclp_service_call(r1, r2);
|
||||
int r = sclp_service_call(env, r1, r2);
|
||||
if (r < 0) {
|
||||
program_interrupt(env, -r, 4);
|
||||
return 0;
|
||||
|
|
|
@ -1162,6 +1162,11 @@ css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc,
|
|||
virtio_ccw_interpret_ccw(int cssid, int ssid, int schid, int cmd_code) "VIRTIO-CCW: %x.%x.%04x: interpret command %x"
|
||||
virtio_ccw_new_device(int cssid, int ssid, int schid, int devno, const char *devno_mode) "VIRTIO-CCW: add subchannel %x.%x.%04x, devno %04x (%s)"
|
||||
|
||||
# hw/intc/s390_flic.c
|
||||
flic_create_device(int err) "flic: create device failed %d"
|
||||
flic_no_device_api(int err) "flic: no Device Contral API support %d"
|
||||
flic_reset_failed(int err) "flic: reset failed %d"
|
||||
|
||||
# migration.c
|
||||
migrate_set_state(int new_state) "new state %d"
|
||||
|
||||
|
|
Loading…
Reference in a new issue