some s390 patches:
- Enable irqfds on s390 via the new adapter interrupt routing type. As a prereq, fix the kvm enable_cap helpers for some compilers and split the s390 flic into kvm and non-kvm parts. - Enable software and hardware debugging support on s390. This needs a kernel headers update. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJTezzuAAoJEN7Pa5PG8C+vujQQAIYfi5sdGIomu6sneNW/BFDY 9mik3q6gqLIOWbyVpPP2UyY6LdIhIFlurjHgaM86MQjNLpP1SU8hqoHBYf1dKi8K 59krakj3RlI6S3kbJc/kljUJZ3PM/GChPWS2SA8G7qY0zyK91flhHBlB/hvE/ar6 XwcL5YOJV3XTglVgS3UKVwuOpZwnP8LzUKOytZ141O/u1Eigc/yag/sWZmY90XxY FUVGbf7pWoOQiiYCEUfAVC3oaCa0wlXLy8jPF3/jeSTr40rjCOeBcV+f+QpLHHSj 2KLktaGWl6COqAzvH2mNNy4SdLLCvFi9TfCjKNTMRnIwoEaZT2MjVDe+47aqTMNs p54dbRLOBdFsJ5Z32TWBlFPMKlgsqlVubhhmMVa+zfOGI5JiSX62rimpSESJ3G/H 5NN3WoO6qTrC6JBfu1AZ/dhxehwnPjrQ2dFHtevH1BOj6avn0vi3l2mbHJtdRxnD Txsbi5v5S1K7HKhsqScQabFlAn0ZwhF/rs+6Ssr1lMegYpJMWR2V1AtTFSi1cuGw NY5tRNB+slufIn5GOBnJemyriigg0B/G0QggyqBuehRZ7WjKnKVdfBZhiOePl8ff 2+2ixsGIBNIgsLKvw7zVaWWZH5OHH7+ZXuFmV1O3fqMqwHPysCQE/kAYrbyMzxlF Sb0gKUcqMrYMa1EjDmpf =hrU2 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20140520' into staging some s390 patches: - Enable irqfds on s390 via the new adapter interrupt routing type. As a prereq, fix the kvm enable_cap helpers for some compilers and split the s390 flic into kvm and non-kvm parts. - Enable software and hardware debugging support on s390. This needs a kernel headers update. # gpg: Signature made Tue 20 May 2014 12:30:54 BST using RSA key ID C6F02FAF # gpg: Can't check signature: public key not found * remotes/cohuck/tags/s390x-20140520: s390x/kvm: hw debugging support via guest PER facility s390x/kvm: software breakpoint support s390x: remove duplicate definitions of DIAG 501 linux-headers: update s390x/virtio-ccw: wire up irq routing and irqfds s390x/virtio-ccw: reference-counted indicators s390x: add I/O adapter registration s390x: split flic into kvm and non-kvm parts kvm: Fix enable_cap helpers on older gcc Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
45e66b7beb
@ -1,3 +1,4 @@
|
||||
CONFIG_VIRTIO=y
|
||||
CONFIG_SCLPCONSOLE=y
|
||||
CONFIG_S390_FLIC=$(CONFIG_KVM)
|
||||
CONFIG_S390_FLIC=y
|
||||
CONFIG_S390_FLIC_KVM=$(CONFIG_KVM)
|
||||
|
@ -26,3 +26,4 @@ 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
|
||||
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
|
||||
|
@ -1,322 +1,103 @@
|
||||
/*
|
||||
* QEMU S390x KVM floating interrupt controller (flic)
|
||||
* QEMU S390x floating interrupt controller (flic)
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
|
||||
* Cornelia Huck <cornelia.huck@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 <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
|
||||
S390FLICState *s390_get_flic(void)
|
||||
{
|
||||
S390FLICState *fs;
|
||||
|
||||
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL));
|
||||
if (!fs) {
|
||||
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, NULL));
|
||||
}
|
||||
return fs;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
dev = s390_flic_kvm_create();
|
||||
if (!dev) {
|
||||
dev = qdev_create(NULL, TYPE_QEMU_S390_FLIC);
|
||||
object_property_add_child(qdev_get_machine(), TYPE_QEMU_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)
|
||||
static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
|
||||
uint8_t isc, bool swap,
|
||||
bool is_maskable)
|
||||
{
|
||||
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;
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flic_enable_pfault(KVMS390FLICState *flic)
|
||||
static int qemu_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
|
||||
uint64_t map_addr, bool do_map)
|
||||
{
|
||||
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");
|
||||
}
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flic_disable_wait_pfault(KVMS390FLICState *flic)
|
||||
static int qemu_s390_add_adapter_routes(S390FLICState *fs,
|
||||
AdapterRoutes *routes)
|
||||
{
|
||||
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");
|
||||
}
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
static void qemu_s390_release_adapter_routes(S390FLICState *fs,
|
||||
AdapterRoutes *routes)
|
||||
{
|
||||
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)
|
||||
static void qemu_s390_flic_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
int r;
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
|
||||
|
||||
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;
|
||||
fsc->register_io_adapter = qemu_s390_register_io_adapter;
|
||||
fsc->io_adapter_map = qemu_s390_io_adapter_map;
|
||||
fsc->add_adapter_routes = qemu_s390_add_adapter_routes;
|
||||
fsc->release_adapter_routes = qemu_s390_release_adapter_routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 const TypeInfo qemu_s390_flic_info = {
|
||||
.name = TYPE_QEMU_S390_FLIC,
|
||||
.parent = TYPE_S390_FLIC_COMMON,
|
||||
.instance_size = sizeof(QEMUS390FLICState),
|
||||
.class_init = qemu_s390_flic_class_init,
|
||||
};
|
||||
|
||||
static void kvm_s390_flic_register_types(void)
|
||||
static const TypeInfo s390_flic_common_info = {
|
||||
.name = TYPE_S390_FLIC_COMMON,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(S390FLICState),
|
||||
.class_size = sizeof(S390FLICStateClass),
|
||||
};
|
||||
|
||||
static void qemu_s390_flic_register_types(void)
|
||||
{
|
||||
type_register_static(&kvm_s390_flic_info);
|
||||
type_register_static(&s390_flic_common_info);
|
||||
type_register_static(&qemu_s390_flic_info);
|
||||
}
|
||||
|
||||
type_init(kvm_s390_flic_register_types)
|
||||
type_init(qemu_s390_flic_register_types)
|
||||
|
420
hw/intc/s390_flic_kvm.c
Normal file
420
hw/intc/s390_flic_kvm.c
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
* QEMU S390x KVM floating interrupt controller (flic)
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
|
||||
* Cornelia Huck <cornelia.huck@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 <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 "hw/s390x/adapter.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define FLIC_SAVE_INITIAL_SIZE getpagesize()
|
||||
#define FLIC_FAILED (-1UL)
|
||||
#define FLIC_SAVEVM_VERSION 1
|
||||
|
||||
typedef struct KVMS390FLICState {
|
||||
S390FLICState parent_obj;
|
||||
|
||||
uint32_t fd;
|
||||
} KVMS390FLICState;
|
||||
|
||||
DeviceState *s390_flic_kvm_create(void)
|
||||
{
|
||||
DeviceState *dev = NULL;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
|
||||
object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
|
||||
OBJECT(dev), NULL);
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
|
||||
uint8_t isc, bool swap,
|
||||
bool is_maskable)
|
||||
{
|
||||
struct kvm_s390_io_adapter adapter = {
|
||||
.id = id,
|
||||
.isc = isc,
|
||||
.maskable = is_maskable,
|
||||
.swap = swap,
|
||||
};
|
||||
KVMS390FLICState *flic = KVM_S390_FLIC(fs);
|
||||
int r, ret;
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_ADAPTER_REGISTER,
|
||||
.addr = (uint64_t)&adapter,
|
||||
};
|
||||
|
||||
if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
ret = r ? -errno : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
|
||||
uint64_t map_addr, bool do_map)
|
||||
{
|
||||
struct kvm_s390_io_adapter_req req = {
|
||||
.id = id,
|
||||
.type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
|
||||
.addr = map_addr,
|
||||
};
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_ADAPTER_MODIFY,
|
||||
.addr = (uint64_t)&req,
|
||||
};
|
||||
KVMS390FLICState *flic = KVM_S390_FLIC(fs);
|
||||
int r;
|
||||
|
||||
if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
return r ? -errno : 0;
|
||||
}
|
||||
|
||||
static int kvm_s390_add_adapter_routes(S390FLICState *fs,
|
||||
AdapterRoutes *routes)
|
||||
{
|
||||
int ret, i;
|
||||
uint64_t ind_offset = routes->adapter.ind_offset;
|
||||
|
||||
for (i = 0; i < routes->num_routes; i++) {
|
||||
ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
|
||||
if (ret < 0) {
|
||||
goto out_undo;
|
||||
}
|
||||
routes->gsi[i] = ret;
|
||||
routes->adapter.ind_offset++;
|
||||
}
|
||||
/* Restore passed-in structure to original state. */
|
||||
routes->adapter.ind_offset = ind_offset;
|
||||
return 0;
|
||||
out_undo:
|
||||
while (--i >= 0) {
|
||||
kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
|
||||
routes->gsi[i] = -1;
|
||||
}
|
||||
routes->adapter.ind_offset = ind_offset;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void kvm_s390_release_adapter_routes(S390FLICState *fs,
|
||||
AdapterRoutes *routes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < routes->num_routes; i++) {
|
||||
if (routes->gsi[i] >= 0) {
|
||||
kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
|
||||
routes->gsi[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
|
||||
|
||||
dc->realize = kvm_s390_flic_realize;
|
||||
dc->unrealize = kvm_s390_flic_unrealize;
|
||||
dc->reset = kvm_s390_flic_reset;
|
||||
fsc->register_io_adapter = kvm_s390_register_io_adapter;
|
||||
fsc->io_adapter_map = kvm_s390_io_adapter_map;
|
||||
fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
|
||||
fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
|
||||
}
|
||||
|
||||
static const TypeInfo kvm_s390_flic_info = {
|
||||
.name = TYPE_KVM_S390_FLIC,
|
||||
.parent = TYPE_S390_FLIC_COMMON,
|
||||
.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)
|
@ -16,6 +16,7 @@
|
||||
#include "ioinst.h"
|
||||
#include "css.h"
|
||||
#include "trace.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
|
||||
typedef struct CrwContainer {
|
||||
CRW crw;
|
||||
@ -39,6 +40,13 @@ typedef struct CssImage {
|
||||
ChpInfo chpids[MAX_CHPID + 1];
|
||||
} CssImage;
|
||||
|
||||
typedef struct IoAdapter {
|
||||
uint32_t id;
|
||||
uint8_t type;
|
||||
uint8_t isc;
|
||||
QTAILQ_ENTRY(IoAdapter) sibling;
|
||||
} IoAdapter;
|
||||
|
||||
typedef struct ChannelSubSys {
|
||||
QTAILQ_HEAD(, CrwContainer) pending_crws;
|
||||
bool do_crw_mchk;
|
||||
@ -49,6 +57,7 @@ typedef struct ChannelSubSys {
|
||||
uint64_t chnmon_area;
|
||||
CssImage *css[MAX_CSSID + 1];
|
||||
uint8_t default_cssid;
|
||||
QTAILQ_HEAD(, IoAdapter) io_adapters;
|
||||
} ChannelSubSys;
|
||||
|
||||
static ChannelSubSys *channel_subsys;
|
||||
@ -69,6 +78,46 @@ int css_create_css_image(uint8_t cssid, bool default_image)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
|
||||
bool maskable, uint32_t *id)
|
||||
{
|
||||
IoAdapter *adapter;
|
||||
bool found = false;
|
||||
int ret;
|
||||
S390FLICState *fs = s390_get_flic();
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||
|
||||
*id = 0;
|
||||
QTAILQ_FOREACH(adapter, &channel_subsys->io_adapters, sibling) {
|
||||
if ((adapter->type == type) && (adapter->isc == isc)) {
|
||||
*id = adapter->id;
|
||||
found = true;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
if (adapter->id >= *id) {
|
||||
*id = adapter->id + 1;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
goto out;
|
||||
}
|
||||
adapter = g_new0(IoAdapter, 1);
|
||||
ret = fsc->register_io_adapter(fs, *id, isc, swap, maskable);
|
||||
if (ret == 0) {
|
||||
adapter->id = *id;
|
||||
adapter->isc = isc;
|
||||
adapter->type = type;
|
||||
QTAILQ_INSERT_TAIL(&channel_subsys->io_adapters, adapter, sibling);
|
||||
} else {
|
||||
g_free(adapter);
|
||||
fprintf(stderr, "Unexpected error %d when registering adapter %d\n",
|
||||
ret, *id);
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint16_t css_build_subchannel_id(SubchDev *sch)
|
||||
{
|
||||
if (channel_subsys->max_cssid > 0) {
|
||||
@ -1235,6 +1284,7 @@ static void css_init(void)
|
||||
channel_subsys->do_crw_mchk = true;
|
||||
channel_subsys->crws_lost = false;
|
||||
channel_subsys->chnmon_active = false;
|
||||
QTAILQ_INIT(&channel_subsys->io_adapters);
|
||||
}
|
||||
machine_init(css_init);
|
||||
|
||||
|
@ -98,4 +98,8 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
|
||||
int hotplugged, int add);
|
||||
void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
|
||||
void css_adapter_interrupt(uint8_t isc);
|
||||
|
||||
#define CSS_IO_ADAPTER_VIRTIO 1
|
||||
int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
|
||||
bool maskable, uint32_t *id);
|
||||
#endif
|
||||
|
@ -21,12 +21,77 @@
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
#include "hw/s390x/adapter.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
|
||||
#include "ioinst.h"
|
||||
#include "css.h"
|
||||
#include "virtio-ccw.h"
|
||||
#include "trace.h"
|
||||
|
||||
static QTAILQ_HEAD(, IndAddr) indicator_addresses =
|
||||
QTAILQ_HEAD_INITIALIZER(indicator_addresses);
|
||||
|
||||
static IndAddr *get_indicator(hwaddr ind_addr, int len)
|
||||
{
|
||||
IndAddr *indicator;
|
||||
|
||||
QTAILQ_FOREACH(indicator, &indicator_addresses, sibling) {
|
||||
if (indicator->addr == ind_addr) {
|
||||
indicator->refcnt++;
|
||||
return indicator;
|
||||
}
|
||||
}
|
||||
indicator = g_new0(IndAddr, 1);
|
||||
indicator->addr = ind_addr;
|
||||
indicator->len = len;
|
||||
indicator->refcnt = 1;
|
||||
QTAILQ_INSERT_TAIL(&indicator_addresses, indicator, sibling);
|
||||
return indicator;
|
||||
}
|
||||
|
||||
static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr,
|
||||
bool do_map)
|
||||
{
|
||||
S390FLICState *fs = s390_get_flic();
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||
|
||||
return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map);
|
||||
}
|
||||
|
||||
static void release_indicator(AdapterInfo *adapter, IndAddr *indicator)
|
||||
{
|
||||
assert(indicator->refcnt > 0);
|
||||
indicator->refcnt--;
|
||||
if (indicator->refcnt > 0) {
|
||||
return;
|
||||
}
|
||||
QTAILQ_REMOVE(&indicator_addresses, indicator, sibling);
|
||||
if (indicator->map) {
|
||||
s390_io_adapter_map(adapter, indicator->map, false);
|
||||
}
|
||||
g_free(indicator);
|
||||
}
|
||||
|
||||
static int map_indicator(AdapterInfo *adapter, IndAddr *indicator)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (indicator->map) {
|
||||
return 0; /* already mapped is not an error */
|
||||
}
|
||||
indicator->map = indicator->addr;
|
||||
ret = s390_io_adapter_map(adapter, indicator->map, true);
|
||||
if ((ret != 0) && (ret != -ENOSYS)) {
|
||||
goto out_err;
|
||||
}
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
indicator->map = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
|
||||
VirtioCcwDevice *dev);
|
||||
|
||||
@ -445,7 +510,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||
ret = -EFAULT;
|
||||
} else {
|
||||
indicators = ldq_phys(&address_space_memory, ccw.cda);
|
||||
dev->indicators = indicators;
|
||||
dev->indicators = get_indicator(indicators, sizeof(uint64_t));
|
||||
sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
|
||||
ret = 0;
|
||||
}
|
||||
@ -465,7 +530,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||
ret = -EFAULT;
|
||||
} else {
|
||||
indicators = ldq_phys(&address_space_memory, ccw.cda);
|
||||
dev->indicators2 = indicators;
|
||||
dev->indicators2 = get_indicator(indicators, sizeof(uint64_t));
|
||||
sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
|
||||
ret = 0;
|
||||
}
|
||||
@ -517,13 +582,20 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||
ret = -EFAULT;
|
||||
} else {
|
||||
len = hw_len;
|
||||
dev->summary_indicator = thinint->summary_indicator;
|
||||
dev->indicators = thinint->device_indicator;
|
||||
dev->summary_indicator =
|
||||
get_indicator(thinint->summary_indicator, sizeof(uint8_t));
|
||||
dev->indicators = get_indicator(thinint->device_indicator,
|
||||
thinint->ind_bit / 8 + 1);
|
||||
dev->thinint_isc = thinint->isc;
|
||||
dev->ind_bit = thinint->ind_bit;
|
||||
dev->routes.adapter.ind_offset = thinint->ind_bit;
|
||||
dev->routes.adapter.summary_offset = 7;
|
||||
cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len);
|
||||
sch->thinint_active = ((dev->indicators != 0) &&
|
||||
(dev->summary_indicator != 0));
|
||||
ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO,
|
||||
dev->thinint_isc, true, false,
|
||||
&dev->routes.adapter.adapter_id);
|
||||
assert(ret == 0);
|
||||
sch->thinint_active = ((dev->indicators != NULL) &&
|
||||
(dev->summary_indicator != NULL));
|
||||
sch->curr_status.scsw.count = ccw.count - len;
|
||||
ret = 0;
|
||||
}
|
||||
@ -554,7 +626,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev)
|
||||
sch->driver_data = dev;
|
||||
dev->sch = sch;
|
||||
|
||||
dev->indicators = 0;
|
||||
dev->indicators = NULL;
|
||||
|
||||
/* Initialize subchannel structure. */
|
||||
sch->channel_prog = 0x0;
|
||||
@ -693,7 +765,10 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev)
|
||||
css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
|
||||
g_free(sch);
|
||||
}
|
||||
dev->indicators = 0;
|
||||
if (dev->indicators) {
|
||||
release_indicator(&dev->routes.adapter, dev->indicators);
|
||||
dev->indicators = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -950,17 +1025,19 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
|
||||
* ind_bit indicates the start of the indicators in a big
|
||||
* endian notation.
|
||||
*/
|
||||
virtio_set_ind_atomic(sch, dev->indicators +
|
||||
(dev->ind_bit + vector) / 8,
|
||||
0x80 >> ((dev->ind_bit + vector) % 8));
|
||||
if (!virtio_set_ind_atomic(sch, dev->summary_indicator,
|
||||
uint64_t ind_bit = dev->routes.adapter.ind_offset;
|
||||
|
||||
virtio_set_ind_atomic(sch, dev->indicators->addr +
|
||||
(ind_bit + vector) / 8,
|
||||
0x80 >> ((ind_bit + vector) % 8));
|
||||
if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr,
|
||||
0x01)) {
|
||||
css_adapter_interrupt(dev->thinint_isc);
|
||||
}
|
||||
} else {
|
||||
indicators = ldq_phys(&address_space_memory, dev->indicators);
|
||||
indicators = ldq_phys(&address_space_memory, dev->indicators->addr);
|
||||
indicators |= 1ULL << vector;
|
||||
stq_phys(&address_space_memory, dev->indicators, indicators);
|
||||
stq_phys(&address_space_memory, dev->indicators->addr, indicators);
|
||||
css_conditional_io_interrupt(sch);
|
||||
}
|
||||
} else {
|
||||
@ -968,9 +1045,9 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
|
||||
return;
|
||||
}
|
||||
vector = 0;
|
||||
indicators = ldq_phys(&address_space_memory, dev->indicators2);
|
||||
indicators = ldq_phys(&address_space_memory, dev->indicators2->addr);
|
||||
indicators |= 1ULL << vector;
|
||||
stq_phys(&address_space_memory, dev->indicators2, indicators);
|
||||
stq_phys(&address_space_memory, dev->indicators2->addr, indicators);
|
||||
css_conditional_io_interrupt(sch);
|
||||
}
|
||||
}
|
||||
@ -991,9 +1068,18 @@ static void virtio_ccw_reset(DeviceState *d)
|
||||
virtio_ccw_stop_ioeventfd(dev);
|
||||
virtio_reset(vdev);
|
||||
css_reset_sch(dev->sch);
|
||||
dev->indicators = 0;
|
||||
dev->indicators2 = 0;
|
||||
dev->summary_indicator = 0;
|
||||
if (dev->indicators) {
|
||||
release_indicator(&dev->routes.adapter, dev->indicators);
|
||||
dev->indicators = NULL;
|
||||
}
|
||||
if (dev->indicators2) {
|
||||
release_indicator(&dev->routes.adapter, dev->indicators2);
|
||||
dev->indicators2 = NULL;
|
||||
}
|
||||
if (dev->summary_indicator) {
|
||||
release_indicator(&dev->routes.adapter, dev->summary_indicator);
|
||||
dev->summary_indicator = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
|
||||
@ -1027,6 +1113,79 @@ static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign)
|
||||
return virtio_ccw_set_guest2host_notifier(dev, n, assign, false);
|
||||
}
|
||||
|
||||
static int virtio_ccw_get_mappings(VirtioCcwDevice *dev)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!dev->sch->thinint_active) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = map_indicator(&dev->routes.adapter, dev->summary_indicator);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
r = map_indicator(&dev->routes.adapter, dev->indicators);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
dev->routes.adapter.summary_addr = dev->summary_indicator->map;
|
||||
dev->routes.adapter.ind_addr = dev->indicators->map;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs)
|
||||
{
|
||||
int i;
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||
int ret;
|
||||
S390FLICState *fs = s390_get_flic();
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||
|
||||
ret = virtio_ccw_get_mappings(dev);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
for (i = 0; i < nvqs; i++) {
|
||||
if (!virtio_queue_get_num(vdev, i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dev->routes.num_routes = i;
|
||||
return fsc->add_adapter_routes(fs, &dev->routes);
|
||||
}
|
||||
|
||||
static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs)
|
||||
{
|
||||
S390FLICState *fs = s390_get_flic();
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||
|
||||
fsc->release_adapter_routes(fs, &dev->routes);
|
||||
}
|
||||
|
||||
static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n)
|
||||
{
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||
VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
|
||||
|
||||
return kvm_irqchip_add_irqfd_notifier(kvm_state, notifier, NULL,
|
||||
dev->routes.gsi[n]);
|
||||
}
|
||||
|
||||
static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n)
|
||||
{
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||
VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
|
||||
int ret;
|
||||
|
||||
ret = kvm_irqchip_remove_irqfd_notifier(kvm_state, notifier,
|
||||
dev->routes.gsi[n]);
|
||||
assert(ret == 0);
|
||||
}
|
||||
|
||||
static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
||||
bool assign, bool with_irqfd)
|
||||
{
|
||||
@ -1042,11 +1201,17 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
||||
return r;
|
||||
}
|
||||
virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
|
||||
/* We do not support irqfd for classic I/O interrupts, because the
|
||||
* classic interrupts are intermixed with the subchannel status, that
|
||||
* is queried with test subchannel. We want to use vhost, though.
|
||||
* Lets make sure to have vhost running and wire up the irq fd to
|
||||
* land in qemu (and only the irq fd) in this code.
|
||||
if (with_irqfd) {
|
||||
r = virtio_ccw_add_irqfd(dev, n);
|
||||
if (r) {
|
||||
virtio_queue_set_guest_notifier_fd_handler(vq, false,
|
||||
with_irqfd);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* We do not support individual masking for channel devices, so we
|
||||
* need to manually trigger any guest masking callbacks here.
|
||||
*/
|
||||
if (k->guest_notifier_mask) {
|
||||
k->guest_notifier_mask(vdev, n, false);
|
||||
@ -1060,6 +1225,9 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
||||
if (k->guest_notifier_mask) {
|
||||
k->guest_notifier_mask(vdev, n, true);
|
||||
}
|
||||
if (with_irqfd) {
|
||||
virtio_ccw_remove_irqfd(dev, n);
|
||||
}
|
||||
virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
|
||||
event_notifier_cleanup(notifier);
|
||||
}
|
||||
@ -1071,24 +1239,39 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
|
||||
{
|
||||
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||
bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled();
|
||||
int r, n;
|
||||
|
||||
if (with_irqfd && assigned) {
|
||||
/* irq routes need to be set up before assigning irqfds */
|
||||
r = virtio_ccw_setup_irqroutes(dev, nvqs);
|
||||
if (r < 0) {
|
||||
goto irqroute_error;
|
||||
}
|
||||
}
|
||||
for (n = 0; n < nvqs; n++) {
|
||||
if (!virtio_queue_get_num(vdev, n)) {
|
||||
break;
|
||||
}
|
||||
/* false -> true, as soon as irqfd works */
|
||||
r = virtio_ccw_set_guest_notifier(dev, n, assigned, false);
|
||||
r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd);
|
||||
if (r < 0) {
|
||||
goto assign_error;
|
||||
}
|
||||
}
|
||||
if (with_irqfd && !assigned) {
|
||||
/* release irq routes after irqfds have been released */
|
||||
virtio_ccw_release_irqroutes(dev, nvqs);
|
||||
}
|
||||
return 0;
|
||||
|
||||
assign_error:
|
||||
while (--n >= 0) {
|
||||
virtio_ccw_set_guest_notifier(dev, n, !assigned, false);
|
||||
}
|
||||
irqroute_error:
|
||||
if (with_irqfd && assigned) {
|
||||
virtio_ccw_release_irqroutes(dev, nvqs);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <hw/virtio/virtio-balloon.h>
|
||||
#include <hw/virtio/virtio-rng.h>
|
||||
#include <hw/virtio/virtio-bus.h>
|
||||
#include <hw/s390x/s390_flic.h>
|
||||
|
||||
#define VIRTUAL_CSSID 0xfe
|
||||
|
||||
@ -75,6 +76,14 @@ typedef struct VirtIOCCWDeviceClass {
|
||||
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1
|
||||
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT)
|
||||
|
||||
typedef struct IndAddr {
|
||||
hwaddr addr;
|
||||
uint64_t map;
|
||||
unsigned long refcnt;
|
||||
int len;
|
||||
QTAILQ_ENTRY(IndAddr) sibling;
|
||||
} IndAddr;
|
||||
|
||||
struct VirtioCcwDevice {
|
||||
DeviceState parent_obj;
|
||||
SubchDev *sch;
|
||||
@ -85,10 +94,11 @@ struct VirtioCcwDevice {
|
||||
bool ioeventfd_disabled;
|
||||
uint32_t flags;
|
||||
uint8_t thinint_isc;
|
||||
AdapterRoutes routes;
|
||||
/* Guest provided values: */
|
||||
hwaddr indicators;
|
||||
hwaddr indicators2;
|
||||
hwaddr summary_indicator;
|
||||
IndAddr *indicators;
|
||||
IndAddr *indicators2;
|
||||
IndAddr *summary_indicator;
|
||||
uint64_t ind_bit;
|
||||
};
|
||||
|
||||
|
23
include/hw/s390x/adapter.h
Normal file
23
include/hw/s390x/adapter.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* s390 adapter definitions
|
||||
*
|
||||
* Copyright 2013,2014 IBM Corp.
|
||||
* Author(s): Cornelia Huck <cornelia.huck@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 S390X_ADAPTER_H
|
||||
#define S390X_ADAPTER_H
|
||||
|
||||
struct AdapterInfo {
|
||||
uint64_t ind_addr;
|
||||
uint64_t summary_addr;
|
||||
uint64_t ind_offset;
|
||||
uint32_t summary_offset;
|
||||
uint32_t adapter_id;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,33 +1,76 @@
|
||||
/*
|
||||
* QEMU S390x KVM floating interrupt controller (flic)
|
||||
* QEMU S390x floating interrupt controller (flic)
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
|
||||
* Cornelia Huck <cornelia.huck@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 __KVM_S390_FLIC_H
|
||||
#define __KVM_S390_FLIC_H
|
||||
#ifndef __HW_S390_FLIC_H
|
||||
#define __HW_S390_FLIC_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/s390x/adapter.h"
|
||||
#include "hw/virtio/virtio.h"
|
||||
|
||||
#define TYPE_KVM_S390_FLIC "s390-flic"
|
||||
typedef struct AdapterRoutes {
|
||||
AdapterInfo adapter;
|
||||
int num_routes;
|
||||
int gsi[VIRTIO_PCI_QUEUE_MAX];
|
||||
} AdapterRoutes;
|
||||
|
||||
#define TYPE_S390_FLIC_COMMON "s390-flic"
|
||||
#define S390_FLIC_COMMON(obj) \
|
||||
OBJECT_CHECK(S390FLICState, (obj), TYPE_S390_FLIC_COMMON)
|
||||
|
||||
typedef struct S390FLICState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
} S390FLICState;
|
||||
|
||||
#define S390_FLIC_COMMON_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(S390FLICStateClass, (klass), TYPE_S390_FLIC_COMMON)
|
||||
#define S390_FLIC_COMMON_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(S390FLICStateClass, (obj), TYPE_S390_FLIC_COMMON)
|
||||
|
||||
typedef struct S390FLICStateClass {
|
||||
DeviceClass parent_class;
|
||||
|
||||
int (*register_io_adapter)(S390FLICState *fs, uint32_t id, uint8_t isc,
|
||||
bool swap, bool maskable);
|
||||
int (*io_adapter_map)(S390FLICState *fs, uint32_t id, uint64_t map_addr,
|
||||
bool do_map);
|
||||
int (*add_adapter_routes)(S390FLICState *fs, AdapterRoutes *routes);
|
||||
void (*release_adapter_routes)(S390FLICState *fs, AdapterRoutes *routes);
|
||||
} S390FLICStateClass;
|
||||
|
||||
#define TYPE_KVM_S390_FLIC "s390-flic-kvm"
|
||||
#define KVM_S390_FLIC(obj) \
|
||||
OBJECT_CHECK(KVMS390FLICState, (obj), TYPE_KVM_S390_FLIC)
|
||||
|
||||
typedef struct KVMS390FLICState {
|
||||
SysBusDevice parent_obj;
|
||||
#define TYPE_QEMU_S390_FLIC "s390-flic-qemu"
|
||||
#define QEMU_S390_FLIC(obj) \
|
||||
OBJECT_CHECK(QEMUS390FLICState, (obj), TYPE_QEMU_S390_FLIC)
|
||||
|
||||
uint32_t fd;
|
||||
} KVMS390FLICState;
|
||||
typedef struct QEMUS390FLICState {
|
||||
S390FLICState parent_obj;
|
||||
} QEMUS390FLICState;
|
||||
|
||||
void s390_flic_init(void);
|
||||
|
||||
S390FLICState *s390_get_flic(void);
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
void s390_flic_init(void);
|
||||
DeviceState *s390_flic_kvm_create(void);
|
||||
#else
|
||||
static inline void s390_flic_init(void) { }
|
||||
static inline DeviceState *s390_flic_kvm_create(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __KVM_S390_FLIC_H */
|
||||
#endif /* __HW_S390_FLIC_H */
|
||||
|
@ -74,5 +74,6 @@ typedef struct SHPCDevice SHPCDevice;
|
||||
typedef struct FWCfgState FWCfgState;
|
||||
typedef struct PcGuestInfo PcGuestInfo;
|
||||
typedef struct Range Range;
|
||||
typedef struct AdapterInfo AdapterInfo;
|
||||
|
||||
#endif /* QEMU_TYPEDEFS_H */
|
||||
|
@ -300,7 +300,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension);
|
||||
}; \
|
||||
uint64_t args_tmp[] = { __VA_ARGS__ }; \
|
||||
int i; \
|
||||
for (i = 0; i < ARRAY_SIZE(args_tmp) && \
|
||||
for (i = 0; i < (int)ARRAY_SIZE(args_tmp) && \
|
||||
i < ARRAY_SIZE(cap.args); i++) { \
|
||||
cap.args[i] = args_tmp[i]; \
|
||||
} \
|
||||
@ -315,7 +315,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension);
|
||||
}; \
|
||||
uint64_t args_tmp[] = { __VA_ARGS__ }; \
|
||||
int i; \
|
||||
for (i = 0; i < ARRAY_SIZE(args_tmp) && \
|
||||
for (i = 0; i < (int)ARRAY_SIZE(args_tmp) && \
|
||||
i < ARRAY_SIZE(cap.args); i++) { \
|
||||
cap.args[i] = args_tmp[i]; \
|
||||
} \
|
||||
@ -363,6 +363,8 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg);
|
||||
int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg);
|
||||
void kvm_irqchip_release_virq(KVMState *s, int virq);
|
||||
|
||||
int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter);
|
||||
|
||||
int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n,
|
||||
EventNotifier *rn, int virq);
|
||||
int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq);
|
||||
|
38
kvm-all.c
38
kvm-all.c
@ -27,6 +27,7 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/s390x/adapter.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "qemu/bswap.h"
|
||||
@ -1236,6 +1237,35 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int rfd, int virq,
|
||||
return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd);
|
||||
}
|
||||
|
||||
int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
|
||||
{
|
||||
struct kvm_irq_routing_entry kroute;
|
||||
int virq;
|
||||
|
||||
if (!kvm_gsi_routing_enabled()) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
virq = kvm_irqchip_get_virq(s);
|
||||
if (virq < 0) {
|
||||
return virq;
|
||||
}
|
||||
|
||||
kroute.gsi = virq;
|
||||
kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER;
|
||||
kroute.flags = 0;
|
||||
kroute.u.adapter.summary_addr = adapter->summary_addr;
|
||||
kroute.u.adapter.ind_addr = adapter->ind_addr;
|
||||
kroute.u.adapter.summary_offset = adapter->summary_offset;
|
||||
kroute.u.adapter.ind_offset = adapter->ind_offset;
|
||||
kroute.u.adapter.adapter_id = adapter->adapter_id;
|
||||
|
||||
kvm_add_routing_entry(s, &kroute);
|
||||
kvm_irqchip_commit_routes(s);
|
||||
|
||||
return virq;
|
||||
}
|
||||
|
||||
#else /* !KVM_CAP_IRQ_ROUTING */
|
||||
|
||||
void kvm_init_irq_routing(KVMState *s)
|
||||
@ -1256,6 +1286,11 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign)
|
||||
{
|
||||
abort();
|
||||
@ -1285,7 +1320,8 @@ static int kvm_irqchip_create(KVMState *s)
|
||||
int ret;
|
||||
|
||||
if (!qemu_opt_get_bool(qemu_get_machine_opts(), "kernel_irqchip", true) ||
|
||||
!kvm_check_extension(s, KVM_CAP_IRQCHIP)) {
|
||||
(!kvm_check_extension(s, KVM_CAP_IRQCHIP) &&
|
||||
(kvm_vm_enable_cap(s, KVM_CAP_S390_IRQCHIP, 0) < 0))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -136,6 +136,11 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n,
|
||||
EventNotifier *rn, int virq)
|
||||
{
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/types.h>
|
||||
|
||||
#define __KVM_S390
|
||||
#define __KVM_HAVE_GUEST_DEBUG
|
||||
|
||||
/* Device control API: s390-specific devices */
|
||||
#define KVM_DEV_FLIC_GET_ALL_IRQS 1
|
||||
@ -54,6 +55,13 @@ struct kvm_s390_io_adapter_req {
|
||||
__u64 addr;
|
||||
};
|
||||
|
||||
/* kvm attr_group on vm fd */
|
||||
#define KVM_S390_VM_MEM_CTRL 0
|
||||
|
||||
/* kvm attributes for mem_ctrl */
|
||||
#define KVM_S390_VM_MEM_ENABLE_CMMA 0
|
||||
#define KVM_S390_VM_MEM_CLR_CMMA 1
|
||||
|
||||
/* for KVM_GET_REGS and KVM_SET_REGS */
|
||||
struct kvm_regs {
|
||||
/* general purpose regs for s390 */
|
||||
@ -72,11 +80,31 @@ struct kvm_fpu {
|
||||
__u64 fprs[16];
|
||||
};
|
||||
|
||||
#define KVM_GUESTDBG_USE_HW_BP 0x00010000
|
||||
|
||||
#define KVM_HW_BP 1
|
||||
#define KVM_HW_WP_WRITE 2
|
||||
#define KVM_SINGLESTEP 4
|
||||
|
||||
struct kvm_debug_exit_arch {
|
||||
__u64 addr;
|
||||
__u8 type;
|
||||
__u8 pad[7]; /* Should be set to 0 */
|
||||
};
|
||||
|
||||
struct kvm_hw_breakpoint {
|
||||
__u64 addr;
|
||||
__u64 phys_addr;
|
||||
__u64 len;
|
||||
__u8 type;
|
||||
__u8 pad[7]; /* Should be set to 0 */
|
||||
};
|
||||
|
||||
/* for KVM_SET_GUEST_DEBUG */
|
||||
struct kvm_guest_debug_arch {
|
||||
__u32 nr_hw_bp;
|
||||
__u32 pad; /* Should be set to 0 */
|
||||
struct kvm_hw_breakpoint *hw_bp;
|
||||
};
|
||||
|
||||
#define KVM_SYNC_PREFIX (1UL << 0)
|
||||
|
@ -416,6 +416,8 @@ struct kvm_s390_psw {
|
||||
#define KVM_S390_INT_PFAULT_INIT 0xfffe0004u
|
||||
#define KVM_S390_INT_PFAULT_DONE 0xfffe0005u
|
||||
#define KVM_S390_MCHK 0xfffe1000u
|
||||
#define KVM_S390_INT_CLOCK_COMP 0xffff1004u
|
||||
#define KVM_S390_INT_CPU_TIMER 0xffff1005u
|
||||
#define KVM_S390_INT_VIRTIO 0xffff2603u
|
||||
#define KVM_S390_INT_SERVICE 0xffff2401u
|
||||
#define KVM_S390_INT_EMERGENCY 0xffff1201u
|
||||
@ -515,6 +517,7 @@ enum {
|
||||
kvm_ioeventfd_flag_nr_pio,
|
||||
kvm_ioeventfd_flag_nr_deassign,
|
||||
kvm_ioeventfd_flag_nr_virtio_ccw_notify,
|
||||
kvm_ioeventfd_flag_nr_fast_mmio,
|
||||
kvm_ioeventfd_flag_nr_max,
|
||||
};
|
||||
|
||||
@ -529,7 +532,7 @@ enum {
|
||||
struct kvm_ioeventfd {
|
||||
__u64 datamatch;
|
||||
__u64 addr; /* legal pio/mmio address */
|
||||
__u32 len; /* 1, 2, 4, or 8 bytes */
|
||||
__u32 len; /* 1, 2, 4, or 8 bytes; or 0 to ignore length */
|
||||
__s32 fd;
|
||||
__u32 flags;
|
||||
__u8 pad[36];
|
||||
@ -743,6 +746,8 @@ struct kvm_ppc_smmu_info {
|
||||
#define KVM_CAP_IOAPIC_POLARITY_IGNORED 97
|
||||
#define KVM_CAP_ENABLE_CAP_VM 98
|
||||
#define KVM_CAP_S390_IRQCHIP 99
|
||||
#define KVM_CAP_IOEVENTFD_NO_LENGTH 100
|
||||
#define KVM_CAP_VM_ATTRIBUTES 101
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
|
@ -86,6 +86,7 @@ int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
|
||||
int s390_cpu_write_elf64_qemunote(WriteCoreDumpFunction f,
|
||||
CPUState *cpu, void *opaque);
|
||||
hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
|
||||
hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr);
|
||||
int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||
int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||
|
||||
|
@ -489,6 +489,18 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr)
|
||||
return raddr;
|
||||
}
|
||||
|
||||
hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr)
|
||||
{
|
||||
hwaddr phys_addr;
|
||||
target_ulong page;
|
||||
|
||||
page = vaddr & TARGET_PAGE_MASK;
|
||||
phys_addr = cpu_get_phys_page_debug(cs, page);
|
||||
phys_addr += (vaddr & ~TARGET_PAGE_MASK);
|
||||
|
||||
return phys_addr;
|
||||
}
|
||||
|
||||
void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
|
||||
{
|
||||
if (mask & PSW_MASK_WAIT) {
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "trace.h"
|
||||
|
||||
/* #define DEBUG_KVM */
|
||||
@ -86,6 +87,14 @@
|
||||
#define ICPT_CPU_STOP 0x28
|
||||
#define ICPT_IO 0x40
|
||||
|
||||
static CPUWatchpoint hw_watchpoint;
|
||||
/*
|
||||
* We don't use a list because this structure is also used to transmit the
|
||||
* hardware breakpoints to the kernel.
|
||||
*/
|
||||
static struct kvm_hw_breakpoint *hw_breakpoints;
|
||||
static int nb_hw_breakpoints;
|
||||
|
||||
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
||||
KVM_CAP_LAST_INFO
|
||||
};
|
||||
@ -320,12 +329,16 @@ static void *legacy_s390_alloc(size_t size)
|
||||
return mem == MAP_FAILED ? NULL : mem;
|
||||
}
|
||||
|
||||
/* DIAG 501 is used for sw breakpoints */
|
||||
static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
|
||||
|
||||
int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
|
||||
{
|
||||
static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
|
||||
|
||||
if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) ||
|
||||
cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)diag_501, 4, 1)) {
|
||||
if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn,
|
||||
sizeof(diag_501), 0) ||
|
||||
cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)diag_501,
|
||||
sizeof(diag_501), 1)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
@ -333,38 +346,140 @@ int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
|
||||
|
||||
int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
|
||||
{
|
||||
uint8_t t[4];
|
||||
static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
|
||||
uint8_t t[sizeof(diag_501)];
|
||||
|
||||
if (cpu_memory_rw_debug(cs, bp->pc, t, 4, 0)) {
|
||||
if (cpu_memory_rw_debug(cs, bp->pc, t, sizeof(diag_501), 0)) {
|
||||
return -EINVAL;
|
||||
} else if (memcmp(t, diag_501, 4)) {
|
||||
} else if (memcmp(t, diag_501, sizeof(diag_501))) {
|
||||
return -EINVAL;
|
||||
} else if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) {
|
||||
} else if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn,
|
||||
sizeof(diag_501), 1)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kvm_hw_breakpoint *find_hw_breakpoint(target_ulong addr,
|
||||
int len, int type)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < nb_hw_breakpoints; n++) {
|
||||
if (hw_breakpoints[n].addr == addr && hw_breakpoints[n].type == type &&
|
||||
(hw_breakpoints[n].len == len || len == -1)) {
|
||||
return &hw_breakpoints[n];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int insert_hw_breakpoint(target_ulong addr, int len, int type)
|
||||
{
|
||||
int size;
|
||||
|
||||
if (find_hw_breakpoint(addr, len, type)) {
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
size = (nb_hw_breakpoints + 1) * sizeof(struct kvm_hw_breakpoint);
|
||||
|
||||
if (!hw_breakpoints) {
|
||||
nb_hw_breakpoints = 0;
|
||||
hw_breakpoints = (struct kvm_hw_breakpoint *)g_try_malloc(size);
|
||||
} else {
|
||||
hw_breakpoints =
|
||||
(struct kvm_hw_breakpoint *)g_try_realloc(hw_breakpoints, size);
|
||||
}
|
||||
|
||||
if (!hw_breakpoints) {
|
||||
nb_hw_breakpoints = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hw_breakpoints[nb_hw_breakpoints].addr = addr;
|
||||
hw_breakpoints[nb_hw_breakpoints].len = len;
|
||||
hw_breakpoints[nb_hw_breakpoints].type = type;
|
||||
|
||||
nb_hw_breakpoints++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_insert_hw_breakpoint(target_ulong addr,
|
||||
target_ulong len, int type)
|
||||
{
|
||||
return -ENOSYS;
|
||||
switch (type) {
|
||||
case GDB_BREAKPOINT_HW:
|
||||
type = KVM_HW_BP;
|
||||
break;
|
||||
case GDB_WATCHPOINT_WRITE:
|
||||
if (len < 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
type = KVM_HW_WP_WRITE;
|
||||
break;
|
||||
default:
|
||||
return -ENOSYS;
|
||||
}
|
||||
return insert_hw_breakpoint(addr, len, type);
|
||||
}
|
||||
|
||||
int kvm_arch_remove_hw_breakpoint(target_ulong addr,
|
||||
target_ulong len, int type)
|
||||
{
|
||||
return -ENOSYS;
|
||||
int size;
|
||||
struct kvm_hw_breakpoint *bp = find_hw_breakpoint(addr, len, type);
|
||||
|
||||
if (bp == NULL) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
nb_hw_breakpoints--;
|
||||
if (nb_hw_breakpoints > 0) {
|
||||
/*
|
||||
* In order to trim the array, move the last element to the position to
|
||||
* be removed - if necessary.
|
||||
*/
|
||||
if (bp != &hw_breakpoints[nb_hw_breakpoints]) {
|
||||
*bp = hw_breakpoints[nb_hw_breakpoints];
|
||||
}
|
||||
size = nb_hw_breakpoints * sizeof(struct kvm_hw_breakpoint);
|
||||
hw_breakpoints =
|
||||
(struct kvm_hw_breakpoint *)g_realloc(hw_breakpoints, size);
|
||||
} else {
|
||||
g_free(hw_breakpoints);
|
||||
hw_breakpoints = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arch_remove_all_hw_breakpoints(void)
|
||||
{
|
||||
nb_hw_breakpoints = 0;
|
||||
g_free(hw_breakpoints);
|
||||
hw_breakpoints = NULL;
|
||||
}
|
||||
|
||||
void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (nb_hw_breakpoints > 0) {
|
||||
dbg->arch.nr_hw_bp = nb_hw_breakpoints;
|
||||
dbg->arch.hw_bp = hw_breakpoints;
|
||||
|
||||
for (i = 0; i < nb_hw_breakpoints; ++i) {
|
||||
hw_breakpoints[i].phys_addr = s390_cpu_get_phys_addr_debug(cpu,
|
||||
hw_breakpoints[i].addr);
|
||||
}
|
||||
dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
|
||||
} else {
|
||||
dbg->arch.nr_hw_bp = 0;
|
||||
dbg->arch.hw_bp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run)
|
||||
@ -579,6 +694,22 @@ static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run)
|
||||
handle_diag_308(&cpu->env, r1, r3);
|
||||
}
|
||||
|
||||
static int handle_sw_breakpoint(S390CPU *cpu, struct kvm_run *run)
|
||||
{
|
||||
CPUS390XState *env = &cpu->env;
|
||||
unsigned long pc;
|
||||
|
||||
cpu_synchronize_state(CPU(cpu));
|
||||
|
||||
pc = env->psw.addr - 4;
|
||||
if (kvm_find_sw_breakpoint(CPU(cpu), pc)) {
|
||||
env->psw.addr = pc;
|
||||
return EXCP_DEBUG;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
#define DIAG_KVM_CODE_MASK 0x000000000000ffff
|
||||
|
||||
static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
|
||||
@ -599,7 +730,7 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
|
||||
r = handle_hypercall(cpu, run);
|
||||
break;
|
||||
case DIAG_KVM_BREAKPOINT:
|
||||
sleep(10);
|
||||
r = handle_sw_breakpoint(cpu, run);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code);
|
||||
@ -701,7 +832,7 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
||||
static int handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
||||
{
|
||||
unsigned int ipa0 = (run->s390_sieic.ipa & 0xff00);
|
||||
uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff;
|
||||
@ -728,8 +859,11 @@ static void handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
r = 0;
|
||||
enter_pgmcheck(cpu, 0x0001);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool is_special_wait_psw(CPUState *cs)
|
||||
@ -749,7 +883,7 @@ static int handle_intercept(S390CPU *cpu)
|
||||
(long)cs->kvm_run->psw_addr);
|
||||
switch (icpt_code) {
|
||||
case ICPT_INSTRUCTION:
|
||||
handle_instruction(cpu, run);
|
||||
r = handle_instruction(cpu, run);
|
||||
break;
|
||||
case ICPT_WAITPSW:
|
||||
/* disabled wait, since enabled wait is handled in kernel */
|
||||
@ -830,7 +964,36 @@ static int handle_tsch(S390CPU *cpu)
|
||||
|
||||
static int kvm_arch_handle_debug_exit(S390CPU *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
CPUState *cs = CPU(cpu);
|
||||
struct kvm_run *run = cs->kvm_run;
|
||||
|
||||
int ret = 0;
|
||||
struct kvm_debug_exit_arch *arch_info = &run->debug.arch;
|
||||
|
||||
switch (arch_info->type) {
|
||||
case KVM_HW_WP_WRITE:
|
||||
if (find_hw_breakpoint(arch_info->addr, -1, arch_info->type)) {
|
||||
cs->watchpoint_hit = &hw_watchpoint;
|
||||
hw_watchpoint.vaddr = arch_info->addr;
|
||||
hw_watchpoint.flags = BP_MEM_WRITE;
|
||||
ret = EXCP_DEBUG;
|
||||
}
|
||||
break;
|
||||
case KVM_HW_BP:
|
||||
if (find_hw_breakpoint(arch_info->addr, -1, arch_info->type)) {
|
||||
ret = EXCP_DEBUG;
|
||||
}
|
||||
break;
|
||||
case KVM_SINGLESTEP:
|
||||
if (cs->singlestep_enabled) {
|
||||
ret = EXCP_DEBUG;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -ENOSYS;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
||||
@ -911,6 +1074,16 @@ void kvm_s390_enable_css_support(S390CPU *cpu)
|
||||
|
||||
void kvm_arch_init_irq_routing(KVMState *s)
|
||||
{
|
||||
/*
|
||||
* Note that while irqchip capabilities generally imply that cpustates
|
||||
* are handled in-kernel, it is not true for s390 (yet); therefore, we
|
||||
* have to override the common code kvm_halt_in_kernel_allowed setting.
|
||||
*/
|
||||
if (kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) {
|
||||
kvm_irqfds_allowed = true;
|
||||
kvm_gsi_routing_allowed = true;
|
||||
kvm_halt_in_kernel_allowed = false;
|
||||
}
|
||||
}
|
||||
|
||||
int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
|
||||
|
Loading…
Reference in New Issue
Block a user