cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

vhost-vsock-common.c (8316B)


      1/*
      2 * Parent class for vhost-vsock devices
      3 *
      4 * Copyright 2015-2020 Red Hat, Inc.
      5 *
      6 * This work is licensed under the terms of the GNU GPL, version 2 or
      7 * (at your option) any later version.  See the COPYING file in the
      8 * top-level directory.
      9 */
     10
     11#include "qemu/osdep.h"
     12#include "standard-headers/linux/virtio_vsock.h"
     13#include "qapi/error.h"
     14#include "hw/virtio/virtio-access.h"
     15#include "qemu/error-report.h"
     16#include "hw/qdev-properties.h"
     17#include "hw/virtio/vhost-vsock.h"
     18#include "qemu/iov.h"
     19#include "monitor/monitor.h"
     20
     21const int feature_bits[] = {
     22    VIRTIO_VSOCK_F_SEQPACKET,
     23    VHOST_INVALID_FEATURE_BIT
     24};
     25
     26uint64_t vhost_vsock_common_get_features(VirtIODevice *vdev, uint64_t features,
     27                                         Error **errp)
     28{
     29    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
     30
     31    if (vvc->seqpacket != ON_OFF_AUTO_OFF) {
     32        virtio_add_feature(&features, VIRTIO_VSOCK_F_SEQPACKET);
     33    }
     34
     35    features = vhost_get_features(&vvc->vhost_dev, feature_bits, features);
     36
     37    if (vvc->seqpacket == ON_OFF_AUTO_ON &&
     38        !virtio_has_feature(features, VIRTIO_VSOCK_F_SEQPACKET)) {
     39        error_setg(errp, "vhost-vsock backend doesn't support seqpacket");
     40    }
     41
     42    return features;
     43}
     44
     45int vhost_vsock_common_start(VirtIODevice *vdev)
     46{
     47    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
     48    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
     49    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
     50    int ret;
     51    int i;
     52
     53    if (!k->set_guest_notifiers) {
     54        error_report("binding does not support guest notifiers");
     55        return -ENOSYS;
     56    }
     57
     58    ret = vhost_dev_enable_notifiers(&vvc->vhost_dev, vdev);
     59    if (ret < 0) {
     60        error_report("Error enabling host notifiers: %d", -ret);
     61        return ret;
     62    }
     63
     64    ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, true);
     65    if (ret < 0) {
     66        error_report("Error binding guest notifier: %d", -ret);
     67        goto err_host_notifiers;
     68    }
     69
     70    vvc->vhost_dev.acked_features = vdev->guest_features;
     71    ret = vhost_dev_start(&vvc->vhost_dev, vdev);
     72    if (ret < 0) {
     73        error_report("Error starting vhost: %d", -ret);
     74        goto err_guest_notifiers;
     75    }
     76
     77    /*
     78     * guest_notifier_mask/pending not used yet, so just unmask
     79     * everything here.  virtio-pci will do the right thing by
     80     * enabling/disabling irqfd.
     81     */
     82    for (i = 0; i < vvc->vhost_dev.nvqs; i++) {
     83        vhost_virtqueue_mask(&vvc->vhost_dev, vdev, i, false);
     84    }
     85
     86    return 0;
     87
     88err_guest_notifiers:
     89    k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false);
     90err_host_notifiers:
     91    vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev);
     92    return ret;
     93}
     94
     95void vhost_vsock_common_stop(VirtIODevice *vdev)
     96{
     97    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
     98    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
     99    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
    100    int ret;
    101
    102    if (!k->set_guest_notifiers) {
    103        return;
    104    }
    105
    106    vhost_dev_stop(&vvc->vhost_dev, vdev);
    107
    108    ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false);
    109    if (ret < 0) {
    110        error_report("vhost guest notifier cleanup failed: %d", ret);
    111        return;
    112    }
    113
    114    vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev);
    115}
    116
    117
    118static void vhost_vsock_common_handle_output(VirtIODevice *vdev, VirtQueue *vq)
    119{
    120    /* Do nothing */
    121}
    122
    123static void vhost_vsock_common_guest_notifier_mask(VirtIODevice *vdev, int idx,
    124                                            bool mask)
    125{
    126    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
    127
    128    vhost_virtqueue_mask(&vvc->vhost_dev, vdev, idx, mask);
    129}
    130
    131static bool vhost_vsock_common_guest_notifier_pending(VirtIODevice *vdev,
    132                                               int idx)
    133{
    134    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
    135
    136    return vhost_virtqueue_pending(&vvc->vhost_dev, idx);
    137}
    138
    139static void vhost_vsock_common_send_transport_reset(VHostVSockCommon *vvc)
    140{
    141    VirtQueueElement *elem;
    142    VirtQueue *vq = vvc->event_vq;
    143    struct virtio_vsock_event event = {
    144        .id = cpu_to_le32(VIRTIO_VSOCK_EVENT_TRANSPORT_RESET),
    145    };
    146
    147    elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
    148    if (!elem) {
    149        error_report("vhost-vsock missed transport reset event");
    150        return;
    151    }
    152
    153    if (elem->out_num) {
    154        error_report("invalid vhost-vsock event virtqueue element with "
    155                     "out buffers");
    156        goto out;
    157    }
    158
    159    if (iov_from_buf(elem->in_sg, elem->in_num, 0,
    160                     &event, sizeof(event)) != sizeof(event)) {
    161        error_report("vhost-vsock event virtqueue element is too short");
    162        goto out;
    163    }
    164
    165    virtqueue_push(vq, elem, sizeof(event));
    166    virtio_notify(VIRTIO_DEVICE(vvc), vq);
    167
    168out:
    169    g_free(elem);
    170}
    171
    172static void vhost_vsock_common_post_load_timer_cleanup(VHostVSockCommon *vvc)
    173{
    174    if (!vvc->post_load_timer) {
    175        return;
    176    }
    177
    178    timer_free(vvc->post_load_timer);
    179    vvc->post_load_timer = NULL;
    180}
    181
    182static void vhost_vsock_common_post_load_timer_cb(void *opaque)
    183{
    184    VHostVSockCommon *vvc = opaque;
    185
    186    vhost_vsock_common_post_load_timer_cleanup(vvc);
    187    vhost_vsock_common_send_transport_reset(vvc);
    188}
    189
    190int vhost_vsock_common_pre_save(void *opaque)
    191{
    192    VHostVSockCommon *vvc = opaque;
    193
    194    /*
    195     * At this point, backend must be stopped, otherwise
    196     * it might keep writing to memory.
    197     */
    198    assert(!vvc->vhost_dev.started);
    199
    200    return 0;
    201}
    202
    203int vhost_vsock_common_post_load(void *opaque, int version_id)
    204{
    205    VHostVSockCommon *vvc = opaque;
    206    VirtIODevice *vdev = VIRTIO_DEVICE(vvc);
    207
    208    if (virtio_queue_get_addr(vdev, 2)) {
    209        /*
    210         * Defer transport reset event to a vm clock timer so that virtqueue
    211         * changes happen after migration has completed.
    212         */
    213        assert(!vvc->post_load_timer);
    214        vvc->post_load_timer =
    215            timer_new_ns(QEMU_CLOCK_VIRTUAL,
    216                         vhost_vsock_common_post_load_timer_cb,
    217                         vvc);
    218        timer_mod(vvc->post_load_timer, 1);
    219    }
    220    return 0;
    221}
    222
    223void vhost_vsock_common_realize(VirtIODevice *vdev, const char *name)
    224{
    225    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
    226
    227    virtio_init(vdev, name, VIRTIO_ID_VSOCK,
    228                sizeof(struct virtio_vsock_config));
    229
    230    /* Receive and transmit queues belong to vhost */
    231    vvc->recv_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
    232                                      vhost_vsock_common_handle_output);
    233    vvc->trans_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
    234                                       vhost_vsock_common_handle_output);
    235
    236    /* The event queue belongs to QEMU */
    237    vvc->event_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
    238                                       vhost_vsock_common_handle_output);
    239
    240    vvc->vhost_dev.nvqs = ARRAY_SIZE(vvc->vhost_vqs);
    241    vvc->vhost_dev.vqs = vvc->vhost_vqs;
    242
    243    vvc->post_load_timer = NULL;
    244}
    245
    246void vhost_vsock_common_unrealize(VirtIODevice *vdev)
    247{
    248    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
    249
    250    vhost_vsock_common_post_load_timer_cleanup(vvc);
    251
    252    virtio_delete_queue(vvc->recv_vq);
    253    virtio_delete_queue(vvc->trans_vq);
    254    virtio_delete_queue(vvc->event_vq);
    255    virtio_cleanup(vdev);
    256}
    257
    258static Property vhost_vsock_common_properties[] = {
    259    DEFINE_PROP_ON_OFF_AUTO("seqpacket", VHostVSockCommon, seqpacket,
    260                            ON_OFF_AUTO_AUTO),
    261    DEFINE_PROP_END_OF_LIST(),
    262};
    263
    264static void vhost_vsock_common_class_init(ObjectClass *klass, void *data)
    265{
    266    DeviceClass *dc = DEVICE_CLASS(klass);
    267    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
    268
    269    device_class_set_props(dc, vhost_vsock_common_properties);
    270    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
    271    vdc->guest_notifier_mask = vhost_vsock_common_guest_notifier_mask;
    272    vdc->guest_notifier_pending = vhost_vsock_common_guest_notifier_pending;
    273}
    274
    275static const TypeInfo vhost_vsock_common_info = {
    276    .name = TYPE_VHOST_VSOCK_COMMON,
    277    .parent = TYPE_VIRTIO_DEVICE,
    278    .instance_size = sizeof(VHostVSockCommon),
    279    .class_init = vhost_vsock_common_class_init,
    280    .abstract = true,
    281};
    282
    283static void vhost_vsock_common_register_types(void)
    284{
    285    type_register_static(&vhost_vsock_common_info);
    286}
    287
    288type_init(vhost_vsock_common_register_types)