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 * @size: ignored
 284 *
 285 * Note: Pass buf and len to kernel. Start with one page and
 286 * increase until buffer is sufficient or maxium size is
 287 * reached
 288 */
 289static void kvm_flic_save(QEMUFile *f, void *opaque, size_t size)
 290{
 291    KVMS390FLICState *flic = opaque;
 292    int len = FLIC_SAVE_INITIAL_SIZE;
 293    void *buf;
 294    int count;
 295
 296    flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
 297
 298    buf = g_try_malloc0(len);
 299    if (!buf) {
 300        /* Storing FLIC_FAILED into the count field here will cause the
 301         * target system to fail when attempting to load irqs from the
 302         * migration state */
 303        error_report("flic: couldn't allocate memory");
 304        qemu_put_be64(f, FLIC_FAILED);
 305        return;
 306    }
 307
 308    count = __get_all_irqs(flic, &buf, len);
 309    if (count < 0) {
 310        error_report("flic: couldn't retrieve irqs from kernel, rc %d",
 311                     count);
 312        /* Storing FLIC_FAILED into the count field here will cause the
 313         * target system to fail when attempting to load irqs from the
 314         * migration state */
 315        qemu_put_be64(f, FLIC_FAILED);
 316    } else {
 317        qemu_put_be64(f, count);
 318        qemu_put_buffer(f, (uint8_t *) buf,
 319                        count * sizeof(struct kvm_s390_irq));
 320    }
 321    g_free(buf);
 322}
 323
 324/**
 325 * kvm_flic_load - Load pending floating interrupts
 326 * @f: QEMUFile containing migration state
 327 * @opaque: pointer to flic device state
 328 * @size: ignored
 329 *
 330 * Returns: value of flic_enqueue_irqs, -EINVAL on error
 331 * Note: Do nothing when no interrupts where stored
 332 * in QEMUFile
 333 */
 334static int kvm_flic_load(QEMUFile *f, void *opaque, size_t size)
 335{
 336    uint64_t len = 0;
 337    uint64_t count = 0;
 338    void *buf = NULL;
 339    int r = 0;
 340
 341    flic_enable_pfault((struct KVMS390FLICState *) opaque);
 342
 343    count = qemu_get_be64(f);
 344    len = count * sizeof(struct kvm_s390_irq);
 345    if (count == FLIC_FAILED) {
 346        r = -EINVAL;
 347        goto out;
 348    }
 349    if (count == 0) {
 350        r = 0;
 351        goto out;
 352    }
 353    buf = g_try_malloc0(len);
 354    if (!buf) {
 355        r = -ENOMEM;
 356        goto out;
 357    }
 358
 359    if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
 360        r = -EINVAL;
 361        goto out_free;
 362    }
 363    r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
 364
 365out_free:
 366    g_free(buf);
 367out:
 368    return r;
 369}
 370
 371static const VMStateDescription kvm_s390_flic_vmstate = {
 372    .name = "s390-flic",
 373    .version_id = FLIC_SAVEVM_VERSION,
 374    .minimum_version_id = FLIC_SAVEVM_VERSION,
 375    .fields = (VMStateField[]) {
 376        {
 377            .name = "irqs",
 378            .info = &(const VMStateInfo) {
 379                .name = "irqs",
 380                .get = kvm_flic_load,
 381                .put = kvm_flic_save,
 382            },
 383            .flags = VMS_SINGLE,
 384        },
 385        VMSTATE_END_OF_LIST()
 386    }
 387};
 388
 389static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
 390{
 391    KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
 392    struct kvm_create_device cd = {0};
 393    struct kvm_device_attr test_attr = {0};
 394    int ret;
 395
 396    flic_state->fd = -1;
 397    if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
 398        trace_flic_no_device_api(errno);
 399        return;
 400    }
 401
 402    cd.type = KVM_DEV_TYPE_FLIC;
 403    ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
 404    if (ret < 0) {
 405        trace_flic_create_device(errno);
 406        return;
 407    }
 408    flic_state->fd = cd.fd;
 409
 410    /* Check clear_io_irq support */
 411    test_attr.group = KVM_DEV_FLIC_CLEAR_IO_IRQ;
 412    flic_state->clear_io_supported = !ioctl(flic_state->fd,
 413                                            KVM_HAS_DEVICE_ATTR, test_attr);
 414
 415}
 416
 417static void kvm_s390_flic_reset(DeviceState *dev)
 418{
 419    KVMS390FLICState *flic = KVM_S390_FLIC(dev);
 420    struct kvm_device_attr attr = {
 421        .group = KVM_DEV_FLIC_CLEAR_IRQS,
 422    };
 423    int rc = 0;
 424
 425    if (flic->fd == -1) {
 426        return;
 427    }
 428
 429    flic_disable_wait_pfault(flic);
 430
 431    rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
 432    if (rc) {
 433        trace_flic_reset_failed(errno);
 434    }
 435
 436    flic_enable_pfault(flic);
 437}
 438
 439static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
 440{
 441    DeviceClass *dc = DEVICE_CLASS(oc);
 442    S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
 443
 444    dc->realize = kvm_s390_flic_realize;
 445    dc->vmsd = &kvm_s390_flic_vmstate;
 446    dc->reset = kvm_s390_flic_reset;
 447    fsc->register_io_adapter = kvm_s390_register_io_adapter;
 448    fsc->io_adapter_map = kvm_s390_io_adapter_map;
 449    fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
 450    fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
 451    fsc->clear_io_irq = kvm_s390_clear_io_flic;
 452}
 453
 454static const TypeInfo kvm_s390_flic_info = {
 455    .name          = TYPE_KVM_S390_FLIC,
 456    .parent        = TYPE_S390_FLIC_COMMON,
 457    .instance_size = sizeof(KVMS390FLICState),
 458    .class_init    = kvm_s390_flic_class_init,
 459};
 460
 461static void kvm_s390_flic_register_types(void)
 462{
 463    type_register_static(&kvm_s390_flic_info);
 464}
 465
 466type_init(kvm_s390_flic_register_types)
 467