qemu/hw/virtio/virtio-bus.c
<<
>>
Prefs
   1/*
   2 * VirtioBus
   3 *
   4 *  Copyright (C) 2012 : GreenSocs Ltd
   5 *      http://www.greensocs.com/ , email: info@greensocs.com
   6 *
   7 *  Developed by :
   8 *  Frederic Konrad   <fred.konrad@greensocs.com>
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License as published by
  12 * the Free Software Foundation, either version 2 of the License, or
  13 * (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License along
  21 * with this program; if not, see <http://www.gnu.org/licenses/>.
  22 *
  23 */
  24
  25#include "qemu/osdep.h"
  26#include "qemu/error-report.h"
  27#include "qemu/module.h"
  28#include "qapi/error.h"
  29#include "hw/virtio/virtio-bus.h"
  30#include "hw/virtio/virtio.h"
  31#include "exec/address-spaces.h"
  32
  33/* #define DEBUG_VIRTIO_BUS */
  34
  35#ifdef DEBUG_VIRTIO_BUS
  36#define DPRINTF(fmt, ...) \
  37do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0)
  38#else
  39#define DPRINTF(fmt, ...) do { } while (0)
  40#endif
  41
  42/* A VirtIODevice is being plugged */
  43void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp)
  44{
  45    DeviceState *qdev = DEVICE(vdev);
  46    BusState *qbus = BUS(qdev_get_parent_bus(qdev));
  47    VirtioBusState *bus = VIRTIO_BUS(qbus);
  48    VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
  49    VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
  50    bool has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM);
  51    Error *local_err = NULL;
  52
  53    DPRINTF("%s: plug device.\n", qbus->name);
  54
  55    if (klass->pre_plugged != NULL) {
  56        klass->pre_plugged(qbus->parent, &local_err);
  57        if (local_err) {
  58            error_propagate(errp, local_err);
  59            return;
  60        }
  61    }
  62
  63    /* Get the features of the plugged device. */
  64    assert(vdc->get_features != NULL);
  65    vdev->host_features = vdc->get_features(vdev, vdev->host_features,
  66                                            &local_err);
  67    if (local_err) {
  68        error_propagate(errp, local_err);
  69        return;
  70    }
  71
  72    if (has_iommu && !virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) {
  73        error_setg(errp, "iommu_platform=true is not supported by the device");
  74        return;
  75    }
  76
  77    if (klass->device_plugged != NULL) {
  78        klass->device_plugged(qbus->parent, &local_err);
  79    }
  80    if (local_err) {
  81        error_propagate(errp, local_err);
  82        return;
  83    }
  84
  85    if (klass->get_dma_as != NULL && has_iommu) {
  86        virtio_add_feature(&vdev->host_features, VIRTIO_F_IOMMU_PLATFORM);
  87        vdev->dma_as = klass->get_dma_as(qbus->parent);
  88    } else {
  89        vdev->dma_as = &address_space_memory;
  90    }
  91}
  92
  93/* Reset the virtio_bus */
  94void virtio_bus_reset(VirtioBusState *bus)
  95{
  96    VirtIODevice *vdev = virtio_bus_get_device(bus);
  97
  98    DPRINTF("%s: reset device.\n", BUS(bus)->name);
  99    if (vdev != NULL) {
 100        virtio_reset(vdev);
 101    }
 102}
 103
 104/* A VirtIODevice is being unplugged */
 105void virtio_bus_device_unplugged(VirtIODevice *vdev)
 106{
 107    DeviceState *qdev = DEVICE(vdev);
 108    BusState *qbus = BUS(qdev_get_parent_bus(qdev));
 109    VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(qbus);
 110
 111    DPRINTF("%s: remove device.\n", qbus->name);
 112
 113    if (vdev != NULL) {
 114        if (klass->device_unplugged != NULL) {
 115            klass->device_unplugged(qbus->parent);
 116        }
 117    }
 118}
 119
 120/* Get the device id of the plugged device. */
 121uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus)
 122{
 123    VirtIODevice *vdev = virtio_bus_get_device(bus);
 124    assert(vdev != NULL);
 125    return vdev->device_id;
 126}
 127
 128/* Get the config_len field of the plugged device. */
 129size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus)
 130{
 131    VirtIODevice *vdev = virtio_bus_get_device(bus);
 132    assert(vdev != NULL);
 133    return vdev->config_len;
 134}
 135
 136/* Get bad features of the plugged device. */
 137uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus)
 138{
 139    VirtIODevice *vdev = virtio_bus_get_device(bus);
 140    VirtioDeviceClass *k;
 141
 142    assert(vdev != NULL);
 143    k = VIRTIO_DEVICE_GET_CLASS(vdev);
 144    if (k->bad_features != NULL) {
 145        return k->bad_features(vdev);
 146    } else {
 147        return 0;
 148    }
 149}
 150
 151/* Get config of the plugged device. */
 152void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config)
 153{
 154    VirtIODevice *vdev = virtio_bus_get_device(bus);
 155    VirtioDeviceClass *k;
 156
 157    assert(vdev != NULL);
 158    k = VIRTIO_DEVICE_GET_CLASS(vdev);
 159    if (k->get_config != NULL) {
 160        k->get_config(vdev, config);
 161    }
 162}
 163
 164/* Set config of the plugged device. */
 165void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config)
 166{
 167    VirtIODevice *vdev = virtio_bus_get_device(bus);
 168    VirtioDeviceClass *k;
 169
 170    assert(vdev != NULL);
 171    k = VIRTIO_DEVICE_GET_CLASS(vdev);
 172    if (k->set_config != NULL) {
 173        k->set_config(vdev, config);
 174    }
 175}
 176
 177/* On success, ioeventfd ownership belongs to the caller.  */
 178int virtio_bus_grab_ioeventfd(VirtioBusState *bus)
 179{
 180    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
 181
 182    /* vhost can be used even if ioeventfd=off in the proxy device,
 183     * so do not check k->ioeventfd_enabled.
 184     */
 185    if (!k->ioeventfd_assign) {
 186        return -ENOSYS;
 187    }
 188
 189    if (bus->ioeventfd_grabbed == 0 && bus->ioeventfd_started) {
 190        virtio_bus_stop_ioeventfd(bus);
 191        /* Remember that we need to restart ioeventfd
 192         * when ioeventfd_grabbed becomes zero.
 193         */
 194        bus->ioeventfd_started = true;
 195    }
 196    bus->ioeventfd_grabbed++;
 197    return 0;
 198}
 199
 200void virtio_bus_release_ioeventfd(VirtioBusState *bus)
 201{
 202    assert(bus->ioeventfd_grabbed != 0);
 203    if (--bus->ioeventfd_grabbed == 0 && bus->ioeventfd_started) {
 204        /* Force virtio_bus_start_ioeventfd to act.  */
 205        bus->ioeventfd_started = false;
 206        virtio_bus_start_ioeventfd(bus);
 207    }
 208}
 209
 210int virtio_bus_start_ioeventfd(VirtioBusState *bus)
 211{
 212    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
 213    DeviceState *proxy = DEVICE(BUS(bus)->parent);
 214    VirtIODevice *vdev = virtio_bus_get_device(bus);
 215    VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
 216    int r;
 217
 218    if (!k->ioeventfd_assign || !k->ioeventfd_enabled(proxy)) {
 219        return -ENOSYS;
 220    }
 221    if (bus->ioeventfd_started) {
 222        return 0;
 223    }
 224
 225    /* Only set our notifier if we have ownership.  */
 226    if (!bus->ioeventfd_grabbed) {
 227        r = vdc->start_ioeventfd(vdev);
 228        if (r < 0) {
 229            error_report("%s: failed. Fallback to userspace (slower).", __func__);
 230            return r;
 231        }
 232    }
 233    bus->ioeventfd_started = true;
 234    return 0;
 235}
 236
 237void virtio_bus_stop_ioeventfd(VirtioBusState *bus)
 238{
 239    VirtIODevice *vdev;
 240    VirtioDeviceClass *vdc;
 241
 242    if (!bus->ioeventfd_started) {
 243        return;
 244    }
 245
 246    /* Only remove our notifier if we have ownership.  */
 247    if (!bus->ioeventfd_grabbed) {
 248        vdev = virtio_bus_get_device(bus);
 249        vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
 250        vdc->stop_ioeventfd(vdev);
 251    }
 252    bus->ioeventfd_started = false;
 253}
 254
 255bool virtio_bus_ioeventfd_enabled(VirtioBusState *bus)
 256{
 257    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
 258    DeviceState *proxy = DEVICE(BUS(bus)->parent);
 259
 260    return k->ioeventfd_assign && k->ioeventfd_enabled(proxy);
 261}
 262
 263/*
 264 * This function switches ioeventfd on/off in the device.
 265 * The caller must set or clear the handlers for the EventNotifier.
 266 */
 267int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign)
 268{
 269    VirtIODevice *vdev = virtio_bus_get_device(bus);
 270    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
 271    DeviceState *proxy = DEVICE(BUS(bus)->parent);
 272    VirtQueue *vq = virtio_get_queue(vdev, n);
 273    EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
 274    int r = 0;
 275
 276    if (!k->ioeventfd_assign) {
 277        return -ENOSYS;
 278    }
 279
 280    if (assign) {
 281        r = event_notifier_init(notifier, 1);
 282        if (r < 0) {
 283            error_report("%s: unable to init event notifier: %s (%d)",
 284                         __func__, strerror(-r), r);
 285            return r;
 286        }
 287        r = k->ioeventfd_assign(proxy, notifier, n, true);
 288        if (r < 0) {
 289            error_report("%s: unable to assign ioeventfd: %d", __func__, r);
 290            virtio_bus_cleanup_host_notifier(bus, n);
 291        }
 292    } else {
 293        k->ioeventfd_assign(proxy, notifier, n, false);
 294    }
 295
 296    if (r == 0) {
 297        virtio_queue_set_host_notifier_enabled(vq, assign);
 298    }
 299
 300    return r;
 301}
 302
 303void virtio_bus_cleanup_host_notifier(VirtioBusState *bus, int n)
 304{
 305    VirtIODevice *vdev = virtio_bus_get_device(bus);
 306    VirtQueue *vq = virtio_get_queue(vdev, n);
 307    EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
 308
 309    /* Test and clear notifier after disabling event,
 310     * in case poll callback didn't have time to run.
 311     */
 312    virtio_queue_host_notifier_read(notifier);
 313    event_notifier_cleanup(notifier);
 314}
 315
 316static char *virtio_bus_get_dev_path(DeviceState *dev)
 317{
 318    BusState *bus = qdev_get_parent_bus(dev);
 319    DeviceState *proxy = DEVICE(bus->parent);
 320    return qdev_get_dev_path(proxy);
 321}
 322
 323static char *virtio_bus_get_fw_dev_path(DeviceState *dev)
 324{
 325    return NULL;
 326}
 327
 328bool virtio_bus_device_iommu_enabled(VirtIODevice *vdev)
 329{
 330    DeviceState *qdev = DEVICE(vdev);
 331    BusState *qbus = BUS(qdev_get_parent_bus(qdev));
 332    VirtioBusState *bus = VIRTIO_BUS(qbus);
 333    VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
 334
 335    if (!klass->iommu_enabled) {
 336        return false;
 337    }
 338
 339    return klass->iommu_enabled(qbus->parent);
 340}
 341
 342static void virtio_bus_class_init(ObjectClass *klass, void *data)
 343{
 344    BusClass *bus_class = BUS_CLASS(klass);
 345    bus_class->get_dev_path = virtio_bus_get_dev_path;
 346    bus_class->get_fw_dev_path = virtio_bus_get_fw_dev_path;
 347}
 348
 349static const TypeInfo virtio_bus_info = {
 350    .name = TYPE_VIRTIO_BUS,
 351    .parent = TYPE_BUS,
 352    .instance_size = sizeof(VirtioBusState),
 353    .abstract = true,
 354    .class_size = sizeof(VirtioBusClass),
 355    .class_init = virtio_bus_class_init
 356};
 357
 358static void virtio_register_types(void)
 359{
 360    type_register_static(&virtio_bus_info);
 361}
 362
 363type_init(virtio_register_types)
 364