qemu/hw/intc/s390_flic_kvm.c
<<
>>
Prefs
   1/*
   2 * QEMU S390x KVM floating interrupt controller (flic)
   3 *
   4 * Copyright 2014 IBM Corp.
   5 * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
   6 *            Cornelia Huck <cornelia.huck@de.ibm.com>
   7 *
   8 * This work is licensed under the terms of the GNU GPL, version 2 or (at
   9 * your option) any later version. See the COPYING file in the top-level
  10 * directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include <sys/ioctl.h>
  15#include "qemu/error-report.h"
  16#include "hw/sysbus.h"
  17#include "sysemu/kvm.h"
  18#include "migration/qemu-file.h"
  19#include "hw/s390x/s390_flic.h"
  20#include "hw/s390x/adapter.h"
  21#include "trace.h"
  22
  23#define FLIC_SAVE_INITIAL_SIZE getpagesize()
  24#define FLIC_FAILED (-1UL)
  25#define FLIC_SAVEVM_VERSION 1
  26
  27typedef struct KVMS390FLICState {
  28    S390FLICState parent_obj;
  29
  30    uint32_t fd;
  31} KVMS390FLICState;
  32
  33DeviceState *s390_flic_kvm_create(void)
  34{
  35    DeviceState *dev = NULL;
  36
  37    if (kvm_enabled()) {
  38        dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
  39        object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
  40                                  OBJECT(dev), NULL);
  41    }
  42    return dev;
  43}
  44
  45/**
  46 * flic_get_all_irqs - store all pending irqs in buffer
  47 * @buf: pointer to buffer which is passed to kernel
  48 * @len: length of buffer
  49 * @flic: pointer to flic device state
  50 *
  51 * Returns: -ENOMEM if buffer is too small,
  52 * -EINVAL if attr.group is invalid,
  53 * -EFAULT if copying to userspace failed,
  54 * on success return number of stored interrupts
  55 */
  56static int flic_get_all_irqs(KVMS390FLICState *flic,
  57                             void *buf, int len)
  58{
  59    struct kvm_device_attr attr = {
  60        .group = KVM_DEV_FLIC_GET_ALL_IRQS,
  61        .addr = (uint64_t) buf,
  62        .attr = len,
  63    };
  64    int rc;
  65
  66    rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
  67
  68    return rc == -1 ? -errno : rc;
  69}
  70
  71static void flic_enable_pfault(KVMS390FLICState *flic)
  72{
  73    struct kvm_device_attr attr = {
  74        .group = KVM_DEV_FLIC_APF_ENABLE,
  75    };
  76    int rc;
  77
  78    rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
  79
  80    if (rc) {
  81        fprintf(stderr, "flic: couldn't enable pfault\n");
  82    }
  83}
  84
  85static void flic_disable_wait_pfault(KVMS390FLICState *flic)
  86{
  87    struct kvm_device_attr attr = {
  88        .group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
  89    };
  90    int rc;
  91
  92    rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
  93
  94    if (rc) {
  95        fprintf(stderr, "flic: couldn't disable pfault\n");
  96    }
  97}
  98
  99/** flic_enqueue_irqs - returns 0 on success
 100 * @buf: pointer to buffer which is passed to kernel
 101 * @len: length of buffer
 102 * @flic: pointer to flic device state
 103 *
 104 * Returns: -EINVAL if attr.group is unknown
 105 */
 106static int flic_enqueue_irqs(void *buf, uint64_t len,
 107                            KVMS390FLICState *flic)
 108{
 109    int rc;
 110    struct kvm_device_attr attr = {
 111        .group = KVM_DEV_FLIC_ENQUEUE,
 112        .addr = (uint64_t) buf,
 113        .attr = len,
 114    };
 115
 116    rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
 117
 118    return rc ? -errno : 0;
 119}
 120
 121int kvm_s390_inject_flic(struct kvm_s390_irq *irq)
 122{
 123    static KVMS390FLICState *flic;
 124
 125    if (unlikely(!flic)) {
 126        flic = KVM_S390_FLIC(s390_get_flic());
 127    }
 128    return flic_enqueue_irqs(irq, sizeof(*irq), flic);
 129}
 130
 131/**
 132 * __get_all_irqs - store all pending irqs in buffer
 133 * @flic: pointer to flic device state
 134 * @buf: pointer to pointer to a buffer
 135 * @len: length of buffer
 136 *
 137 * Returns: return value of flic_get_all_irqs
 138 * Note: Retry and increase buffer size until flic_get_all_irqs
 139 * either returns a value >= 0 or a negative error code.
 140 * -ENOMEM is an exception, which means the buffer is too small
 141 * and we should try again. Other negative error codes can be
 142 * -EFAULT and -EINVAL which we ignore at this point
 143 */
 144static int __get_all_irqs(KVMS390FLICState *flic,
 145                          void **buf, int len)
 146{
 147    int r;
 148
 149    do {
 150        /* returns -ENOMEM if buffer is too small and number
 151         * of queued interrupts on success */
 152        r = flic_get_all_irqs(flic, *buf, len);
 153        if (r >= 0) {
 154            break;
 155        }
 156        len *= 2;
 157        *buf = g_try_realloc(*buf, len);
 158        if (!buf) {
 159            return -ENOMEM;
 160        }
 161    } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
 162
 163    return r;
 164}
 165
 166static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
 167                                        uint8_t isc, bool swap,
 168                                        bool is_maskable)
 169{
 170    struct kvm_s390_io_adapter adapter = {
 171        .id = id,
 172        .isc = isc,
 173        .maskable = is_maskable,
 174        .swap = swap,
 175    };
 176    KVMS390FLICState *flic = KVM_S390_FLIC(fs);
 177    int r, ret;
 178    struct kvm_device_attr attr = {
 179        .group = KVM_DEV_FLIC_ADAPTER_REGISTER,
 180        .addr = (uint64_t)&adapter,
 181    };
 182
 183    if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
 184        /* nothing to do */
 185        return 0;
 186    }
 187
 188    r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
 189
 190    ret = r ? -errno : 0;
 191    return ret;
 192}
 193
 194static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
 195                                   uint64_t map_addr, bool do_map)
 196{
 197    struct kvm_s390_io_adapter_req req = {
 198        .id = id,
 199        .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
 200        .addr = map_addr,
 201    };
 202    struct kvm_device_attr attr = {
 203        .group = KVM_DEV_FLIC_ADAPTER_MODIFY,
 204        .addr = (uint64_t)&req,
 205    };
 206    KVMS390FLICState *flic = KVM_S390_FLIC(fs);
 207    int r;
 208
 209    if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
 210        /* nothing to do */
 211        return 0;
 212    }
 213
 214    r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
 215    return r ? -errno : 0;
 216}
 217
 218static int kvm_s390_add_adapter_routes(S390FLICState *fs,
 219                                       AdapterRoutes *routes)
 220{
 221    int ret, i;
 222    uint64_t ind_offset = routes->adapter.ind_offset;
 223
 224    for (i = 0; i < routes->num_routes; i++) {
 225        ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
 226        if (ret < 0) {
 227            goto out_undo;
 228        }
 229        routes->gsi[i] = ret;
 230        routes->adapter.ind_offset++;
 231    }
 232    kvm_irqchip_commit_routes(kvm_state);
 233
 234    /* Restore passed-in structure to original state. */
 235    routes->adapter.ind_offset = ind_offset;
 236    return 0;
 237out_undo:
 238    while (--i >= 0) {
 239        kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
 240        routes->gsi[i] = -1;
 241    }
 242    routes->adapter.ind_offset = ind_offset;
 243    return ret;
 244}
 245
 246static void kvm_s390_release_adapter_routes(S390FLICState *fs,
 247                                            AdapterRoutes *routes)
 248{
 249    int i;
 250
 251    for (i = 0; i < routes->num_routes; i++) {
 252        if (routes->gsi[i] >= 0) {
 253            kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
 254            routes->gsi[i] = -1;
 255        }
 256    }
 257}
 258
 259/**
 260 * kvm_flic_save - Save pending floating interrupts
 261 * @f: QEMUFile containing migration state
 262 * @opaque: pointer to flic device state
 263 *
 264 * Note: Pass buf and len to kernel. Start with one page and
 265 * increase until buffer is sufficient or maxium size is
 266 * reached
 267 */
 268static void kvm_flic_save(QEMUFile *f, void *opaque)
 269{
 270    KVMS390FLICState *flic = opaque;
 271    int len = FLIC_SAVE_INITIAL_SIZE;
 272    void *buf;
 273    int count;
 274
 275    flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
 276
 277    buf = g_try_malloc0(len);
 278    if (!buf) {
 279        /* Storing FLIC_FAILED into the count field here will cause the
 280         * target system to fail when attempting to load irqs from the
 281         * migration state */
 282        error_report("flic: couldn't allocate memory");
 283        qemu_put_be64(f, FLIC_FAILED);
 284        return;
 285    }
 286
 287    count = __get_all_irqs(flic, &buf, len);
 288    if (count < 0) {
 289        error_report("flic: couldn't retrieve irqs from kernel, rc %d",
 290                     count);
 291        /* Storing FLIC_FAILED into the count field here will cause the
 292         * target system to fail when attempting to load irqs from the
 293         * migration state */
 294        qemu_put_be64(f, FLIC_FAILED);
 295    } else {
 296        qemu_put_be64(f, count);
 297        qemu_put_buffer(f, (uint8_t *) buf,
 298                        count * sizeof(struct kvm_s390_irq));
 299    }
 300    g_free(buf);
 301}
 302
 303/**
 304 * kvm_flic_load - Load pending floating interrupts
 305 * @f: QEMUFile containing migration state
 306 * @opaque: pointer to flic device state
 307 * @version_id: version id for migration
 308 *
 309 * Returns: value of flic_enqueue_irqs, -EINVAL on error
 310 * Note: Do nothing when no interrupts where stored
 311 * in QEMUFile
 312 */
 313static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
 314{
 315    uint64_t len = 0;
 316    uint64_t count = 0;
 317    void *buf = NULL;
 318    int r = 0;
 319
 320    if (version_id != FLIC_SAVEVM_VERSION) {
 321        r = -EINVAL;
 322        goto out;
 323    }
 324
 325    flic_enable_pfault((struct KVMS390FLICState *) opaque);
 326
 327    count = qemu_get_be64(f);
 328    len = count * sizeof(struct kvm_s390_irq);
 329    if (count == FLIC_FAILED) {
 330        r = -EINVAL;
 331        goto out;
 332    }
 333    if (count == 0) {
 334        r = 0;
 335        goto out;
 336    }
 337    buf = g_try_malloc0(len);
 338    if (!buf) {
 339        r = -ENOMEM;
 340        goto out;
 341    }
 342
 343    if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
 344        r = -EINVAL;
 345        goto out_free;
 346    }
 347    r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
 348
 349out_free:
 350    g_free(buf);
 351out:
 352    return r;
 353}
 354
 355static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
 356{
 357    KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
 358    struct kvm_create_device cd = {0};
 359    int ret;
 360
 361    flic_state->fd = -1;
 362    if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
 363        trace_flic_no_device_api(errno);
 364        return;
 365    }
 366
 367    cd.type = KVM_DEV_TYPE_FLIC;
 368    ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
 369    if (ret < 0) {
 370        trace_flic_create_device(errno);
 371        return;
 372    }
 373    flic_state->fd = cd.fd;
 374
 375    /* Register savevm handler for floating interrupts */
 376    register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
 377                    kvm_flic_load, (void *) flic_state);
 378}
 379
 380static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
 381{
 382    KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
 383
 384    unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
 385}
 386
 387static void kvm_s390_flic_reset(DeviceState *dev)
 388{
 389    KVMS390FLICState *flic = KVM_S390_FLIC(dev);
 390    struct kvm_device_attr attr = {
 391        .group = KVM_DEV_FLIC_CLEAR_IRQS,
 392    };
 393    int rc = 0;
 394
 395    if (flic->fd == -1) {
 396        return;
 397    }
 398
 399    flic_disable_wait_pfault(flic);
 400
 401    rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
 402    if (rc) {
 403        trace_flic_reset_failed(errno);
 404    }
 405
 406    flic_enable_pfault(flic);
 407}
 408
 409static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
 410{
 411    DeviceClass *dc = DEVICE_CLASS(oc);
 412    S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
 413
 414    dc->realize = kvm_s390_flic_realize;
 415    dc->unrealize = kvm_s390_flic_unrealize;
 416    dc->reset = kvm_s390_flic_reset;
 417    fsc->register_io_adapter = kvm_s390_register_io_adapter;
 418    fsc->io_adapter_map = kvm_s390_io_adapter_map;
 419    fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
 420    fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
 421}
 422
 423static const TypeInfo kvm_s390_flic_info = {
 424    .name          = TYPE_KVM_S390_FLIC,
 425    .parent        = TYPE_S390_FLIC_COMMON,
 426    .instance_size = sizeof(KVMS390FLICState),
 427    .class_init    = kvm_s390_flic_class_init,
 428};
 429
 430static void kvm_s390_flic_register_types(void)
 431{
 432    type_register_static(&kvm_s390_flic_info);
 433}
 434
 435type_init(kvm_s390_flic_register_types)
 436