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