qemu/hw/vfio/ccw.c
<<
>>
Prefs
   1/*
   2 * vfio based subchannel assignment support
   3 *
   4 * Copyright 2017 IBM Corp.
   5 * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
   6 *            Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
   7 *            Pierre Morel <pmorel@linux.vnet.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or(at
  10 * your option) any version. See the COPYING file in the top-level
  11 * directory.
  12 */
  13
  14#include <linux/vfio.h>
  15#include <linux/vfio_ccw.h>
  16#include <sys/ioctl.h>
  17
  18#include "qemu/osdep.h"
  19#include "qapi/error.h"
  20#include "hw/sysbus.h"
  21#include "hw/vfio/vfio.h"
  22#include "hw/vfio/vfio-common.h"
  23#include "hw/s390x/s390-ccw.h"
  24#include "hw/s390x/ccw-device.h"
  25#include "qemu/error-report.h"
  26
  27#define TYPE_VFIO_CCW "vfio-ccw"
  28typedef struct VFIOCCWDevice {
  29    S390CCWDevice cdev;
  30    VFIODevice vdev;
  31    uint64_t io_region_size;
  32    uint64_t io_region_offset;
  33    struct ccw_io_region *io_region;
  34    EventNotifier io_notifier;
  35} VFIOCCWDevice;
  36
  37static void vfio_ccw_compute_needs_reset(VFIODevice *vdev)
  38{
  39    vdev->needs_reset = false;
  40}
  41
  42/*
  43 * We don't need vfio_hot_reset_multi and vfio_eoi operations for
  44 * vfio_ccw device now.
  45 */
  46struct VFIODeviceOps vfio_ccw_ops = {
  47    .vfio_compute_needs_reset = vfio_ccw_compute_needs_reset,
  48};
  49
  50static int vfio_ccw_handle_request(ORB *orb, SCSW *scsw, void *data)
  51{
  52    S390CCWDevice *cdev = data;
  53    VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
  54    struct ccw_io_region *region = vcdev->io_region;
  55    int ret;
  56
  57    QEMU_BUILD_BUG_ON(sizeof(region->orb_area) != sizeof(ORB));
  58    QEMU_BUILD_BUG_ON(sizeof(region->scsw_area) != sizeof(SCSW));
  59    QEMU_BUILD_BUG_ON(sizeof(region->irb_area) != sizeof(IRB));
  60
  61    memset(region, 0, sizeof(*region));
  62
  63    memcpy(region->orb_area, orb, sizeof(ORB));
  64    memcpy(region->scsw_area, scsw, sizeof(SCSW));
  65
  66again:
  67    ret = pwrite(vcdev->vdev.fd, region,
  68                 vcdev->io_region_size, vcdev->io_region_offset);
  69    if (ret != vcdev->io_region_size) {
  70        if (errno == EAGAIN) {
  71            goto again;
  72        }
  73        error_report("vfio-ccw: wirte I/O region failed with errno=%d", errno);
  74        return -errno;
  75    }
  76
  77    return region->ret_code;
  78}
  79
  80static void vfio_ccw_reset(DeviceState *dev)
  81{
  82    CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev);
  83    S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev);
  84    VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
  85
  86    ioctl(vcdev->vdev.fd, VFIO_DEVICE_RESET);
  87}
  88
  89static void vfio_ccw_io_notifier_handler(void *opaque)
  90{
  91    VFIOCCWDevice *vcdev = opaque;
  92    struct ccw_io_region *region = vcdev->io_region;
  93    S390CCWDevice *cdev = S390_CCW_DEVICE(vcdev);
  94    CcwDevice *ccw_dev = CCW_DEVICE(cdev);
  95    SubchDev *sch = ccw_dev->sch;
  96    SCSW *s = &sch->curr_status.scsw;
  97    PMCW *p = &sch->curr_status.pmcw;
  98    IRB irb;
  99    int size;
 100
 101    if (!event_notifier_test_and_clear(&vcdev->io_notifier)) {
 102        return;
 103    }
 104
 105    size = pread(vcdev->vdev.fd, region, vcdev->io_region_size,
 106                 vcdev->io_region_offset);
 107    if (size == -1) {
 108        switch (errno) {
 109        case ENODEV:
 110            /* Generate a deferred cc 3 condition. */
 111            s->flags |= SCSW_FLAGS_MASK_CC;
 112            s->ctrl &= ~SCSW_CTRL_MASK_STCTL;
 113            s->ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND);
 114            goto read_err;
 115        case EFAULT:
 116            /* Memory problem, generate channel data check. */
 117            s->ctrl &= ~SCSW_ACTL_START_PEND;
 118            s->cstat = SCSW_CSTAT_DATA_CHECK;
 119            s->ctrl &= ~SCSW_CTRL_MASK_STCTL;
 120            s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
 121                       SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
 122            goto read_err;
 123        default:
 124            /* Error, generate channel program check. */
 125            s->ctrl &= ~SCSW_ACTL_START_PEND;
 126            s->cstat = SCSW_CSTAT_PROG_CHECK;
 127            s->ctrl &= ~SCSW_CTRL_MASK_STCTL;
 128            s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
 129                       SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
 130            goto read_err;
 131        }
 132    } else if (size != vcdev->io_region_size) {
 133        /* Information transfer error, generate channel-control check. */
 134        s->ctrl &= ~SCSW_ACTL_START_PEND;
 135        s->cstat = SCSW_CSTAT_CHN_CTRL_CHK;
 136        s->ctrl &= ~SCSW_CTRL_MASK_STCTL;
 137        s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
 138                   SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
 139        goto read_err;
 140    }
 141
 142    memcpy(&irb, region->irb_area, sizeof(IRB));
 143
 144    /* Update control block via irb. */
 145    copy_scsw_to_guest(s, &irb.scsw);
 146
 147    /* If a uint check is pending, copy sense data. */
 148    if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) &&
 149        (p->chars & PMCW_CHARS_MASK_CSENSE)) {
 150        memcpy(sch->sense_data, irb.ecw, sizeof(irb.ecw));
 151    }
 152
 153read_err:
 154    css_inject_io_interrupt(sch);
 155}
 156
 157static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp)
 158{
 159    VFIODevice *vdev = &vcdev->vdev;
 160    struct vfio_irq_info *irq_info;
 161    struct vfio_irq_set *irq_set;
 162    size_t argsz;
 163    int32_t *pfd;
 164
 165    if (vdev->num_irqs < VFIO_CCW_IO_IRQ_INDEX + 1) {
 166        error_setg(errp, "vfio: unexpected number of io irqs %u",
 167                   vdev->num_irqs);
 168        return;
 169    }
 170
 171    argsz = sizeof(*irq_info);
 172    irq_info = g_malloc0(argsz);
 173    irq_info->index = VFIO_CCW_IO_IRQ_INDEX;
 174    irq_info->argsz = argsz;
 175    if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO,
 176              irq_info) < 0 || irq_info->count < 1) {
 177        error_setg_errno(errp, errno, "vfio: Error getting irq info");
 178        goto out_free_info;
 179    }
 180
 181    if (event_notifier_init(&vcdev->io_notifier, 0)) {
 182        error_setg_errno(errp, errno,
 183                         "vfio: Unable to init event notifier for IO");
 184        goto out_free_info;
 185    }
 186
 187    argsz = sizeof(*irq_set) + sizeof(*pfd);
 188    irq_set = g_malloc0(argsz);
 189    irq_set->argsz = argsz;
 190    irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
 191                     VFIO_IRQ_SET_ACTION_TRIGGER;
 192    irq_set->index = VFIO_CCW_IO_IRQ_INDEX;
 193    irq_set->start = 0;
 194    irq_set->count = 1;
 195    pfd = (int32_t *) &irq_set->data;
 196
 197    *pfd = event_notifier_get_fd(&vcdev->io_notifier);
 198    qemu_set_fd_handler(*pfd, vfio_ccw_io_notifier_handler, NULL, vcdev);
 199    if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
 200        error_setg(errp, "vfio: Failed to set up io notification");
 201        qemu_set_fd_handler(*pfd, NULL, NULL, vcdev);
 202        event_notifier_cleanup(&vcdev->io_notifier);
 203    }
 204
 205    g_free(irq_set);
 206
 207out_free_info:
 208    g_free(irq_info);
 209}
 210
 211static void vfio_ccw_unregister_io_notifier(VFIOCCWDevice *vcdev)
 212{
 213    struct vfio_irq_set *irq_set;
 214    size_t argsz;
 215    int32_t *pfd;
 216
 217    argsz = sizeof(*irq_set) + sizeof(*pfd);
 218    irq_set = g_malloc0(argsz);
 219    irq_set->argsz = argsz;
 220    irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
 221                     VFIO_IRQ_SET_ACTION_TRIGGER;
 222    irq_set->index = VFIO_CCW_IO_IRQ_INDEX;
 223    irq_set->start = 0;
 224    irq_set->count = 1;
 225    pfd = (int32_t *) &irq_set->data;
 226    *pfd = -1;
 227
 228    if (ioctl(vcdev->vdev.fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
 229        error_report("vfio: Failed to de-assign device io fd: %m");
 230    }
 231
 232    qemu_set_fd_handler(event_notifier_get_fd(&vcdev->io_notifier),
 233                        NULL, NULL, vcdev);
 234    event_notifier_cleanup(&vcdev->io_notifier);
 235
 236    g_free(irq_set);
 237}
 238
 239static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
 240{
 241    VFIODevice *vdev = &vcdev->vdev;
 242    struct vfio_region_info *info;
 243    int ret;
 244
 245    /* Sanity check device */
 246    if (!(vdev->flags & VFIO_DEVICE_FLAGS_CCW)) {
 247        error_setg(errp, "vfio: Um, this isn't a vfio-ccw device");
 248        return;
 249    }
 250
 251    if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) {
 252        error_setg(errp, "vfio: Unexpected number of the I/O region %u",
 253                   vdev->num_regions);
 254        return;
 255    }
 256
 257    ret = vfio_get_region_info(vdev, VFIO_CCW_CONFIG_REGION_INDEX, &info);
 258    if (ret) {
 259        error_setg_errno(errp, -ret, "vfio: Error getting config info");
 260        return;
 261    }
 262
 263    vcdev->io_region_size = info->size;
 264    if (sizeof(*vcdev->io_region) != vcdev->io_region_size) {
 265        error_setg(errp, "vfio: Unexpected size of the I/O region");
 266        g_free(info);
 267        return;
 268    }
 269
 270    vcdev->io_region_offset = info->offset;
 271    vcdev->io_region = g_malloc0(info->size);
 272
 273    g_free(info);
 274}
 275
 276static void vfio_ccw_put_region(VFIOCCWDevice *vcdev)
 277{
 278    g_free(vcdev->io_region);
 279}
 280
 281static void vfio_put_device(VFIOCCWDevice *vcdev)
 282{
 283    g_free(vcdev->vdev.name);
 284    vfio_put_base_device(&vcdev->vdev);
 285}
 286
 287static VFIOGroup *vfio_ccw_get_group(S390CCWDevice *cdev, Error **errp)
 288{
 289    char *tmp, group_path[PATH_MAX];
 290    ssize_t len;
 291    int groupid;
 292
 293    tmp = g_strdup_printf("/sys/bus/css/devices/%x.%x.%04x/%s/iommu_group",
 294                          cdev->hostid.cssid, cdev->hostid.ssid,
 295                          cdev->hostid.devid, cdev->mdevid);
 296    len = readlink(tmp, group_path, sizeof(group_path));
 297    g_free(tmp);
 298
 299    if (len <= 0 || len >= sizeof(group_path)) {
 300        error_setg(errp, "vfio: no iommu_group found");
 301        return NULL;
 302    }
 303
 304    group_path[len] = 0;
 305
 306    if (sscanf(basename(group_path), "%d", &groupid) != 1) {
 307        error_setg(errp, "vfio: failed to read %s", group_path);
 308        return NULL;
 309    }
 310
 311    return vfio_get_group(groupid, &address_space_memory, errp);
 312}
 313
 314static void vfio_ccw_realize(DeviceState *dev, Error **errp)
 315{
 316    VFIODevice *vbasedev;
 317    VFIOGroup *group;
 318    CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev);
 319    S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev);
 320    VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
 321    S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev);
 322    Error *err = NULL;
 323
 324    /* Call the class init function for subchannel. */
 325    if (cdc->realize) {
 326        cdc->realize(cdev, vcdev->vdev.sysfsdev, &err);
 327        if (err) {
 328            goto out_err_propagate;
 329        }
 330    }
 331
 332    group = vfio_ccw_get_group(cdev, &err);
 333    if (!group) {
 334        goto out_group_err;
 335    }
 336
 337    vcdev->vdev.ops = &vfio_ccw_ops;
 338    vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW;
 339    vcdev->vdev.name = g_strdup_printf("%x.%x.%04x", cdev->hostid.cssid,
 340                                       cdev->hostid.ssid, cdev->hostid.devid);
 341    vcdev->vdev.dev = dev;
 342    QLIST_FOREACH(vbasedev, &group->device_list, next) {
 343        if (strcmp(vbasedev->name, vcdev->vdev.name) == 0) {
 344            error_setg(&err, "vfio: subchannel %s has already been attached",
 345                       vcdev->vdev.name);
 346            goto out_device_err;
 347        }
 348    }
 349
 350    if (vfio_get_device(group, cdev->mdevid, &vcdev->vdev, &err)) {
 351        goto out_device_err;
 352    }
 353
 354    vfio_ccw_get_region(vcdev, &err);
 355    if (err) {
 356        goto out_region_err;
 357    }
 358
 359    vfio_ccw_register_io_notifier(vcdev, &err);
 360    if (err) {
 361        goto out_notifier_err;
 362    }
 363
 364    return;
 365
 366out_notifier_err:
 367    vfio_ccw_put_region(vcdev);
 368out_region_err:
 369    vfio_put_device(vcdev);
 370out_device_err:
 371    vfio_put_group(group);
 372out_group_err:
 373    if (cdc->unrealize) {
 374        cdc->unrealize(cdev, NULL);
 375    }
 376out_err_propagate:
 377    error_propagate(errp, err);
 378}
 379
 380static void vfio_ccw_unrealize(DeviceState *dev, Error **errp)
 381{
 382    CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev);
 383    S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev);
 384    VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
 385    S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev);
 386    VFIOGroup *group = vcdev->vdev.group;
 387
 388    vfio_ccw_unregister_io_notifier(vcdev);
 389    vfio_ccw_put_region(vcdev);
 390    vfio_put_device(vcdev);
 391    vfio_put_group(group);
 392
 393    if (cdc->unrealize) {
 394        cdc->unrealize(cdev, errp);
 395    }
 396}
 397
 398static Property vfio_ccw_properties[] = {
 399    DEFINE_PROP_STRING("sysfsdev", VFIOCCWDevice, vdev.sysfsdev),
 400    DEFINE_PROP_END_OF_LIST(),
 401};
 402
 403static const VMStateDescription vfio_ccw_vmstate = {
 404    .name = TYPE_VFIO_CCW,
 405    .unmigratable = 1,
 406};
 407
 408static void vfio_ccw_class_init(ObjectClass *klass, void *data)
 409{
 410    DeviceClass *dc = DEVICE_CLASS(klass);
 411    S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass);
 412
 413    dc->props = vfio_ccw_properties;
 414    dc->vmsd = &vfio_ccw_vmstate;
 415    dc->desc = "VFIO-based subchannel assignment";
 416    dc->realize = vfio_ccw_realize;
 417    dc->unrealize = vfio_ccw_unrealize;
 418    dc->reset = vfio_ccw_reset;
 419
 420    cdc->handle_request = vfio_ccw_handle_request;
 421}
 422
 423static const TypeInfo vfio_ccw_info = {
 424    .name = TYPE_VFIO_CCW,
 425    .parent = TYPE_S390_CCW,
 426    .instance_size = sizeof(VFIOCCWDevice),
 427    .class_init = vfio_ccw_class_init,
 428};
 429
 430static void register_vfio_ccw_type(void)
 431{
 432    type_register_static(&vfio_ccw_info);
 433}
 434
 435type_init(register_vfio_ccw_type)
 436