From 61c7bbd236b90da7531fcf957223a2456670d44d Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 12 May 2014 11:51:37 +0200 Subject: [PATCH 1/9] kvm: Fix enable_cap helpers on older gcc Commit 40f1ee27aa1 introduced handy helpers for enable_cap calls on vcpu and vm level. Unfortunately some older gcc versions (4.7.1, 4.6) seem to choke on signedness detection in inline created variables: target-ppc/kvm.c: In function 'kvmppc_booke_watchdog_enable': target-ppc/kvm.c:1302:21: error: comparison of unsigned expression < 0 is always false [-Werror=type-limits] target-ppc/kvm.c: In function 'kvmppc_set_papr': target-ppc/kvm.c:1504:21: error: comparison of unsigned expression < 0 is always false [-Werror=type-limits] However - thanks to Thomas Huth for the suggestion - we can just cast the offending potentially 0 value to a signed type, making the comparison signed. Reviewed-by: Thomas Huth Acked-by: Cornelia Huck Signed-off-by: Alexander Graf Signed-off-by: Cornelia Huck --- include/sysemu/kvm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index e7ad9d159a..25c8a2bdfd 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.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]; \ } \ From 7b35d0c44cae3dcce6347a0729a416c2929cd4bb Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 17 Apr 2014 15:59:48 +0200 Subject: [PATCH 2/9] s390x: split flic into kvm and non-kvm parts Introduce a common parent class for both cases, where kvm and non-kvm can hook up callbacks. This will be used by follow-on patches for adapter registration and mapping. We now always have a flic, regardless of whether we use kvm; the non-kvm implementation just doesn't do anything. Reviewed-by: Jens Freimann Reviewed-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- default-configs/s390x-softmmu.mak | 3 +- hw/intc/Makefile.objs | 1 + hw/intc/s390_flic.c | 328 ++++-------------------------- hw/intc/s390_flic_kvm.c | 325 +++++++++++++++++++++++++++++ include/hw/s390x/s390_flic.h | 51 ++++- 5 files changed, 404 insertions(+), 304 deletions(-) create mode 100644 hw/intc/s390_flic_kvm.c diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index d843dc0d57..126d88dc15 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -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) diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index c8a2318d56..843864a3ef 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -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 diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index b2ef3e3f8e..7dc8c7d145 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -1,322 +1,66 @@ /* - * QEMU S390x KVM floating interrupt controller (flic) + * QEMU S390x floating interrupt controller (flic) * * Copyright 2014 IBM Corp. * Author(s): Jens Freimann + * Cornelia Huck * * 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 #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) -{ - 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 const TypeInfo qemu_s390_flic_info = { + .name = TYPE_QEMU_S390_FLIC, + .parent = TYPE_S390_FLIC_COMMON, + .instance_size = sizeof(QEMUS390FLICState), }; -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) diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c new file mode 100644 index 0000000000..70c1710ec3 --- /dev/null +++ b/hw/intc/s390_flic_kvm.c @@ -0,0 +1,325 @@ +/* + * QEMU S390x KVM floating interrupt controller (flic) + * + * Copyright 2014 IBM Corp. + * Author(s): Jens Freimann + * Cornelia Huck + * + * 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 +#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 + +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; +} + +/** + * 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_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) diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 497b219e30..3bc60fd77f 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -1,33 +1,62 @@ /* - * QEMU S390x KVM floating interrupt controller (flic) + * QEMU S390x floating interrupt controller (flic) * * Copyright 2014 IBM Corp. * Author(s): Jens Freimann + * Cornelia Huck * * 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" -#define TYPE_KVM_S390_FLIC "s390-flic" +#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; + +} 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 */ From 03cf077ac9c02ccc192b9011ced12cc74f9151bb Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 15 Jul 2013 17:45:03 +0200 Subject: [PATCH 3/9] s390x: add I/O adapter registration Register an I/O adapter interrupt source for when virtio-ccw devices start using adapter interrupts. Reviewed-by: Thomas Huth Reviewed-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- hw/intc/s390_flic.c | 16 ++++++++++++ hw/intc/s390_flic_kvm.c | 29 +++++++++++++++++++++ hw/s390x/css.c | 50 ++++++++++++++++++++++++++++++++++++ hw/s390x/css.h | 4 +++ hw/s390x/virtio-ccw.c | 4 +++ hw/s390x/virtio-ccw.h | 1 + include/hw/s390x/s390_flic.h | 2 ++ 7 files changed, 106 insertions(+) diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 7dc8c7d145..2b568354cd 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -44,10 +44,26 @@ void s390_flic_init(void) } } +static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id, + uint8_t isc, bool swap, + bool is_maskable) +{ + /* nothing to do */ + return 0; +} + +static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) +{ + S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); + + fsc->register_io_adapter = qemu_s390_register_io_adapter; +} + 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 const TypeInfo s390_flic_common_info = { diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index 70c1710ec3..cc4072e5df 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -151,6 +151,33 @@ static int __get_all_irqs(KVMS390FLICState *flic, 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; +} + /** * kvm_flic_save - Save pending floating interrupts * @f: QEMUFile containing migration state @@ -304,10 +331,12 @@ static void kvm_s390_flic_reset(DeviceState *dev) 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; } static const TypeInfo kvm_s390_flic_info = { diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 122cc7e66f..2678e4432c 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -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); diff --git a/hw/s390x/css.h b/hw/s390x/css.h index 220169e7c3..6586106fa7 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -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 diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 1cb4e2c2f8..e3b7120cea 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -522,6 +522,10 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) dev->thinint_isc = thinint->isc; dev->ind_bit = thinint->ind_bit; cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); + ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, + dev->thinint_isc, true, false, + &dev->adapter_id); + assert(ret == 0); sch->thinint_active = ((dev->indicators != 0) && (dev->summary_indicator != 0)); sch->curr_status.scsw.count = ccw.count - len; diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 4393e44814..0b70b91cfe 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -85,6 +85,7 @@ struct VirtioCcwDevice { bool ioeventfd_disabled; uint32_t flags; uint8_t thinint_isc; + uint32_t adapter_id; /* Guest provided values: */ hwaddr indicators; hwaddr indicators2; diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 3bc60fd77f..83913ec097 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -32,6 +32,8 @@ typedef struct S390FLICState { typedef struct S390FLICStateClass { DeviceClass parent_class; + int (*register_io_adapter)(S390FLICState *fs, uint32_t id, uint8_t isc, + bool swap, bool maskable); } S390FLICStateClass; #define TYPE_KVM_S390_FLIC "s390-flic-kvm" From 7bca3892cb951afb5945480b090e3fb93578cd6a Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 1 Aug 2013 17:27:00 +0200 Subject: [PATCH 4/9] s390x/virtio-ccw: reference-counted indicators Make code using the same indicators point to a single allocated structure that is freed when the last user goes away. This will be used by the irqfd code to unmap addresses after the last user is gone. Reviewed-by: Thomas Huth Reviewed-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- hw/s390x/virtio-ccw.c | 80 ++++++++++++++++++++++++++++++++++--------- hw/s390x/virtio-ccw.h | 13 +++++-- 2 files changed, 73 insertions(+), 20 deletions(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index e3b7120cea..d11a78384f 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -27,6 +27,38 @@ #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 void release_indicator(IndAddr *indicator) +{ + assert(indicator->refcnt > 0); + indicator->refcnt--; + if (indicator->refcnt > 0) { + return; + } + QTAILQ_REMOVE(&indicator_addresses, indicator, sibling); + g_free(indicator); +} + static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, VirtioCcwDevice *dev); @@ -445,7 +477,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 +497,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,8 +549,10 @@ 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; cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); @@ -526,8 +560,8 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) dev->thinint_isc, true, false, &dev->adapter_id); assert(ret == 0); - sch->thinint_active = ((dev->indicators != 0) && - (dev->summary_indicator != 0)); + sch->thinint_active = ((dev->indicators != NULL) && + (dev->summary_indicator != NULL)); sch->curr_status.scsw.count = ccw.count - len; ret = 0; } @@ -558,7 +592,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; @@ -697,7 +731,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->indicators); + dev->indicators = NULL; + } return 0; } @@ -954,17 +991,17 @@ 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 + + virtio_set_ind_atomic(sch, dev->indicators->addr + (dev->ind_bit + vector) / 8, 0x80 >> ((dev->ind_bit + vector) % 8)); - if (!virtio_set_ind_atomic(sch, dev->summary_indicator, + 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 { @@ -972,9 +1009,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); } } @@ -995,9 +1032,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->indicators); + dev->indicators = NULL; + } + if (dev->indicators2) { + release_indicator(dev->indicators2); + dev->indicators2 = NULL; + } + if (dev->summary_indicator) { + release_indicator(dev->summary_indicator); + dev->summary_indicator = NULL; + } } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 0b70b91cfe..d340bf4aca 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -75,6 +75,13 @@ 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; + unsigned long refcnt; + int len; + QTAILQ_ENTRY(IndAddr) sibling; +} IndAddr; + struct VirtioCcwDevice { DeviceState parent_obj; SubchDev *sch; @@ -87,9 +94,9 @@ struct VirtioCcwDevice { uint8_t thinint_isc; uint32_t adapter_id; /* Guest provided values: */ - hwaddr indicators; - hwaddr indicators2; - hwaddr summary_indicator; + IndAddr *indicators; + IndAddr *indicators2; + IndAddr *summary_indicator; uint64_t ind_bit; }; From d426d9fba8ea5d728038a9bea6a7c51f11941157 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 15 Jul 2013 17:45:03 +0200 Subject: [PATCH 5/9] s390x/virtio-ccw: wire up irq routing and irqfds Make use of the new s390 adapter irq routing support to enable real in-kernel irqfds for virtio-ccw with adapter interrupts. Note that s390 doesn't provide the common KVM_CAP_IRQCHIP capability, but rather needs KVM_CAP_S390_IRQCHIP to be enabled. This is to ensure backward compatibility. Reviewed-by: Thomas Huth Reviewed-by: Christian Borntraeger Acked-by: Paolo Bonzini Signed-off-by: Cornelia Huck --- hw/intc/s390_flic.c | 21 +++++ hw/intc/s390_flic_kvm.c | 66 ++++++++++++++ hw/s390x/virtio-ccw.c | 165 +++++++++++++++++++++++++++++++---- hw/s390x/virtio-ccw.h | 4 +- include/hw/s390x/adapter.h | 23 +++++ include/hw/s390x/s390_flic.h | 12 +++ include/qemu/typedefs.h | 1 + include/sysemu/kvm.h | 2 + kvm-all.c | 38 +++++++- kvm-stub.c | 5 ++ target-s390x/kvm.c | 10 +++ 11 files changed, 329 insertions(+), 18 deletions(-) create mode 100644 include/hw/s390x/adapter.h diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 2b568354cd..03c5e89f4e 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -52,11 +52,32 @@ static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id, return 0; } +static int qemu_s390_io_adapter_map(S390FLICState *fs, uint32_t id, + uint64_t map_addr, bool do_map) +{ + /* nothing to do */ + return 0; +} + +static int qemu_s390_add_adapter_routes(S390FLICState *fs, + AdapterRoutes *routes) +{ + return -ENOSYS; +} + +static void qemu_s390_release_adapter_routes(S390FLICState *fs, + AdapterRoutes *routes) +{ +} + static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) { S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); 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; } static const TypeInfo qemu_s390_flic_info = { diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index cc4072e5df..46c9e612d1 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -16,6 +16,7 @@ #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() @@ -178,6 +179,68 @@ static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id, 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 @@ -337,6 +400,9 @@ static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) 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 = { diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index d11a78384f..c4f21d3816 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -21,6 +21,8 @@ #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" @@ -48,7 +50,16 @@ static IndAddr *get_indicator(hwaddr ind_addr, int len) return indicator; } -static void release_indicator(IndAddr *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--; @@ -56,9 +67,31 @@ static void release_indicator(IndAddr *indicator) 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); @@ -554,11 +587,12 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) 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); ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, dev->thinint_isc, true, false, - &dev->adapter_id); + &dev->routes.adapter.adapter_id); assert(ret == 0); sch->thinint_active = ((dev->indicators != NULL) && (dev->summary_indicator != NULL)); @@ -732,7 +766,7 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev) g_free(sch); } if (dev->indicators) { - release_indicator(dev->indicators); + release_indicator(&dev->routes.adapter, dev->indicators); dev->indicators = NULL; } return 0; @@ -991,9 +1025,11 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) * ind_bit indicates the start of the indicators in a big * endian notation. */ + uint64_t ind_bit = dev->routes.adapter.ind_offset; + virtio_set_ind_atomic(sch, dev->indicators->addr + - (dev->ind_bit + vector) / 8, - 0x80 >> ((dev->ind_bit + vector) % 8)); + (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); @@ -1033,15 +1069,15 @@ static void virtio_ccw_reset(DeviceState *d) virtio_reset(vdev); css_reset_sch(dev->sch); if (dev->indicators) { - release_indicator(dev->indicators); + release_indicator(&dev->routes.adapter, dev->indicators); dev->indicators = NULL; } if (dev->indicators2) { - release_indicator(dev->indicators2); + release_indicator(&dev->routes.adapter, dev->indicators2); dev->indicators2 = NULL; } if (dev->summary_indicator) { - release_indicator(dev->summary_indicator); + release_indicator(&dev->routes.adapter, dev->summary_indicator); dev->summary_indicator = NULL; } } @@ -1077,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) { @@ -1092,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); @@ -1110,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); } @@ -1121,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; } diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index d340bf4aca..b8b8a8abaa 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -22,6 +22,7 @@ #include #include #include +#include #define VIRTUAL_CSSID 0xfe @@ -77,6 +78,7 @@ typedef struct VirtIOCCWDeviceClass { typedef struct IndAddr { hwaddr addr; + uint64_t map; unsigned long refcnt; int len; QTAILQ_ENTRY(IndAddr) sibling; @@ -92,7 +94,7 @@ struct VirtioCcwDevice { bool ioeventfd_disabled; uint32_t flags; uint8_t thinint_isc; - uint32_t adapter_id; + AdapterRoutes routes; /* Guest provided values: */ IndAddr *indicators; IndAddr *indicators2; diff --git a/include/hw/s390x/adapter.h b/include/hw/s390x/adapter.h new file mode 100644 index 0000000000..7f1703508c --- /dev/null +++ b/include/hw/s390x/adapter.h @@ -0,0 +1,23 @@ +/* + * s390 adapter definitions + * + * Copyright 2013,2014 IBM Corp. + * Author(s): Cornelia Huck + * + * 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 diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 83913ec097..489d73b9b3 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -14,6 +14,14 @@ #define __HW_S390_FLIC_H #include "hw/sysbus.h" +#include "hw/s390x/adapter.h" +#include "hw/virtio/virtio.h" + +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) \ @@ -34,6 +42,10 @@ typedef struct S390FLICStateClass { 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" diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 86bab123a4..5f20b0e263 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.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 */ diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 25c8a2bdfd..e79e92c50e 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -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); diff --git a/kvm-all.c b/kvm-all.c index a343ede4d4..721a3904a9 100644 --- a/kvm-all.c +++ b/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; } diff --git a/kvm-stub.c b/kvm-stub.c index 8acda86ced..ac33d8666d 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -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) { diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 56179afece..bb731a0dcd 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -911,6 +911,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, From 76eb98d51c0a0448c5bc2f70d15fe334e7234964 Mon Sep 17 00:00:00 2001 From: Jens Freimann Date: Mon, 12 May 2014 10:03:46 +0200 Subject: [PATCH 6/9] linux-headers: update Sync linux-headers with kvm/next (87c00572ba05aa8c9db118da75c608f47eb10b9e) Signed-off-by: Jens Freimann Signed-off-by: Cornelia Huck --- linux-headers/asm-s390/kvm.h | 28 ++++++++++++++++++++++++++++ linux-headers/linux/kvm.h | 7 ++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index c003c6a73b..98bedf3c18 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -15,6 +15,7 @@ #include #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) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index b278ab3326..42ddc2cabb 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -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 From 8e4e86afa524588397be2101c3582c897fc408df Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 25 Feb 2014 15:58:45 +0100 Subject: [PATCH 7/9] s390x: remove duplicate definitions of DIAG 501 When restoring the previously saved instruction in kvm_arch_remove_sw_breakpoint(), we only restored one byte. Let's use the sizeof() operator to make sure we restore the entire instruction. While we are at it, let's remove the duplicate definitions of DIAG 501 and replace its size (used when reading/writing the instruction) with a sizeof() operator to make the code self explaining and less error-prone. Signed-off-by: David Hildenbrand Reviewed-by: Cornelia Huck Signed-off-by: Jens Freimann Signed-off-by: Cornelia Huck --- target-s390x/kvm.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index bb731a0dcd..4d12f70021 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -320,12 +320,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,14 +337,14 @@ 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; } From b30f4dfbdaf353e580f8a834aeea6b96ac1c67c9 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 25 Feb 2014 15:53:00 +0100 Subject: [PATCH 8/9] s390x/kvm: software breakpoint support This patch allows to insert and remove sw breakpoints using the QEMU gdbserver on s390 as well as to interrupt execution on a breakpoint hit when running with KVM enabled. Whenever a software breakpoint is inserted, common code calls kvm ioctl KVM_UPDATE_GUEST_DEBUG. As this method's default on s390 is to return an error if not implement, the insertion will fail. Therefore, KVM also has to be updated in order to make use of software breakpoints. Signed-off-by: David Hildenbrand Signed-off-by: Jens Freimann Reviewed-by: Cornelia Huck Signed-off-by: Cornelia Huck --- target-s390x/kvm.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 4d12f70021..6d47637d78 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -583,6 +583,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) @@ -603,7 +619,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); @@ -705,7 +721,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; @@ -732,8 +748,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) @@ -753,7 +772,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 */ From 770a63792b2c9a3f9565b68b0a7ef0be6883f551 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 3 Sep 2012 13:09:10 +0200 Subject: [PATCH 9/9] s390x/kvm: hw debugging support via guest PER facility This patch makes use of the hw debugging support in kvm (provided by the guest's PER facility) on s390. It enables the following features, available using the gdbserver: - single-stepping - hw breakpoints - hw watchpoints Signed-off-by: David Hildenbrand Signed-off-by: Jens Freimann Signed-off-by: Cornelia Huck --- target-s390x/cpu-qom.h | 1 + target-s390x/helper.c | 12 ++++ target-s390x/kvm.c | 146 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 156 insertions(+), 3 deletions(-) diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h index ac0460eb30..f9c96d13a9 100644 --- a/target-s390x/cpu-qom.h +++ b/target-s390x/cpu-qom.h @@ -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); diff --git a/target-s390x/helper.c b/target-s390x/helper.c index 7c76fc149b..3d756cae6c 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -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) { diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 6d47637d78..7a07f9d753 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -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 }; @@ -351,24 +360,126 @@ int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) 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) @@ -853,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)