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-scsi.c (9798B)


      1/*
      2 * vhost_scsi host device
      3 *
      4 * Copyright IBM, Corp. 2011
      5 *
      6 * Authors:
      7 *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com>
      8 *
      9 * Changes for QEMU mainline + tcm_vhost kernel upstream:
     10 *  Nicholas Bellinger <nab@risingtidesystems.com>
     11 *
     12 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
     13 * See the COPYING.LIB file in the top-level directory.
     14 *
     15 */
     16
     17#include "qemu/osdep.h"
     18#include <linux/vhost.h>
     19#include <sys/ioctl.h>
     20#include "qapi/error.h"
     21#include "qemu/error-report.h"
     22#include "qemu/module.h"
     23#include "monitor/monitor.h"
     24#include "migration/blocker.h"
     25#include "hw/virtio/vhost-scsi.h"
     26#include "hw/virtio/vhost.h"
     27#include "hw/virtio/virtio-scsi.h"
     28#include "hw/virtio/virtio-bus.h"
     29#include "hw/virtio/virtio-access.h"
     30#include "hw/fw-path-provider.h"
     31#include "hw/qdev-properties.h"
     32#include "qemu/cutils.h"
     33#include "sysemu/sysemu.h"
     34
     35/* Features supported by host kernel. */
     36static const int kernel_feature_bits[] = {
     37    VIRTIO_F_NOTIFY_ON_EMPTY,
     38    VIRTIO_RING_F_INDIRECT_DESC,
     39    VIRTIO_RING_F_EVENT_IDX,
     40    VIRTIO_SCSI_F_HOTPLUG,
     41    VHOST_INVALID_FEATURE_BIT
     42};
     43
     44static int vhost_scsi_set_endpoint(VHostSCSI *s)
     45{
     46    VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
     47    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
     48    const VhostOps *vhost_ops = vsc->dev.vhost_ops;
     49    struct vhost_scsi_target backend;
     50    int ret;
     51
     52    memset(&backend, 0, sizeof(backend));
     53    pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
     54    ret = vhost_ops->vhost_scsi_set_endpoint(&vsc->dev, &backend);
     55    if (ret < 0) {
     56        return -errno;
     57    }
     58    return 0;
     59}
     60
     61static void vhost_scsi_clear_endpoint(VHostSCSI *s)
     62{
     63    VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
     64    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
     65    struct vhost_scsi_target backend;
     66    const VhostOps *vhost_ops = vsc->dev.vhost_ops;
     67
     68    memset(&backend, 0, sizeof(backend));
     69    pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
     70    vhost_ops->vhost_scsi_clear_endpoint(&vsc->dev, &backend);
     71}
     72
     73static int vhost_scsi_start(VHostSCSI *s)
     74{
     75    int ret, abi_version;
     76    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
     77    const VhostOps *vhost_ops = vsc->dev.vhost_ops;
     78
     79    ret = vhost_ops->vhost_scsi_get_abi_version(&vsc->dev, &abi_version);
     80    if (ret < 0) {
     81        return -errno;
     82    }
     83    if (abi_version > VHOST_SCSI_ABI_VERSION) {
     84        error_report("vhost-scsi: The running tcm_vhost kernel abi_version:"
     85                     " %d is greater than vhost_scsi userspace supports: %d,"
     86                     " please upgrade your version of QEMU", abi_version,
     87                     VHOST_SCSI_ABI_VERSION);
     88        return -ENOSYS;
     89    }
     90
     91    ret = vhost_scsi_common_start(vsc);
     92    if (ret < 0) {
     93        return ret;
     94    }
     95
     96    ret = vhost_scsi_set_endpoint(s);
     97    if (ret < 0) {
     98        error_report("Error setting vhost-scsi endpoint");
     99        vhost_scsi_common_stop(vsc);
    100    }
    101
    102    return ret;
    103}
    104
    105static void vhost_scsi_stop(VHostSCSI *s)
    106{
    107    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
    108
    109    vhost_scsi_clear_endpoint(s);
    110    vhost_scsi_common_stop(vsc);
    111}
    112
    113static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val)
    114{
    115    VHostSCSI *s = VHOST_SCSI(vdev);
    116    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
    117    bool start = (val & VIRTIO_CONFIG_S_DRIVER_OK);
    118
    119    if (!vdev->vm_running) {
    120        start = false;
    121    }
    122
    123    if (vsc->dev.started == start) {
    124        return;
    125    }
    126
    127    if (start) {
    128        int ret;
    129
    130        ret = vhost_scsi_start(s);
    131        if (ret < 0) {
    132            error_report("unable to start vhost-scsi: %s", strerror(-ret));
    133            exit(1);
    134        }
    135    } else {
    136        vhost_scsi_stop(s);
    137    }
    138}
    139
    140static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq)
    141{
    142}
    143
    144static int vhost_scsi_pre_save(void *opaque)
    145{
    146    VHostSCSICommon *vsc = opaque;
    147
    148    /* At this point, backend must be stopped, otherwise
    149     * it might keep writing to memory. */
    150    assert(!vsc->dev.started);
    151
    152    return 0;
    153}
    154
    155static const VMStateDescription vmstate_virtio_vhost_scsi = {
    156    .name = "virtio-vhost_scsi",
    157    .minimum_version_id = 1,
    158    .version_id = 1,
    159    .fields = (VMStateField[]) {
    160        VMSTATE_VIRTIO_DEVICE,
    161        VMSTATE_END_OF_LIST()
    162    },
    163    .pre_save = vhost_scsi_pre_save,
    164};
    165
    166static void vhost_scsi_realize(DeviceState *dev, Error **errp)
    167{
    168    VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
    169    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev);
    170    Error *err = NULL;
    171    int vhostfd = -1;
    172    int ret;
    173
    174    if (!vs->conf.wwpn) {
    175        error_setg(errp, "vhost-scsi: missing wwpn");
    176        return;
    177    }
    178
    179    if (vs->conf.vhostfd) {
    180        vhostfd = monitor_fd_param(monitor_cur(), vs->conf.vhostfd, errp);
    181        if (vhostfd == -1) {
    182            error_prepend(errp, "vhost-scsi: unable to parse vhostfd: ");
    183            return;
    184        }
    185    } else {
    186        vhostfd = open("/dev/vhost-scsi", O_RDWR);
    187        if (vhostfd < 0) {
    188            error_setg(errp, "vhost-scsi: open vhost char device failed: %s",
    189                       strerror(errno));
    190            return;
    191        }
    192    }
    193
    194    virtio_scsi_common_realize(dev,
    195                               vhost_dummy_handle_output,
    196                               vhost_dummy_handle_output,
    197                               vhost_dummy_handle_output,
    198                               &err);
    199    if (err != NULL) {
    200        error_propagate(errp, err);
    201        goto close_fd;
    202    }
    203
    204    if (!vsc->migratable) {
    205        error_setg(&vsc->migration_blocker,
    206                "vhost-scsi does not support migration in all cases. "
    207                "When external environment supports it (Orchestrator migrates "
    208                "target SCSI device state or use shared storage over network), "
    209                "set 'migratable' property to true to enable migration.");
    210        if (migrate_add_blocker(vsc->migration_blocker, errp) < 0) {
    211            goto free_virtio;
    212        }
    213    }
    214
    215    vsc->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues;
    216    vsc->dev.vqs = g_new0(struct vhost_virtqueue, vsc->dev.nvqs);
    217    vsc->dev.vq_index = 0;
    218    vsc->dev.backend_features = 0;
    219
    220    ret = vhost_dev_init(&vsc->dev, (void *)(uintptr_t)vhostfd,
    221                         VHOST_BACKEND_TYPE_KERNEL, 0, errp);
    222    if (ret < 0) {
    223        goto free_vqs;
    224    }
    225
    226    /* At present, channel and lun both are 0 for bootable vhost-scsi disk */
    227    vsc->channel = 0;
    228    vsc->lun = 0;
    229    /* Note: we can also get the minimum tpgt from kernel */
    230    vsc->target = vs->conf.boot_tpgt;
    231
    232    return;
    233
    234 free_vqs:
    235    g_free(vsc->dev.vqs);
    236    if (!vsc->migratable) {
    237        migrate_del_blocker(vsc->migration_blocker);
    238    }
    239 free_virtio:
    240    error_free(vsc->migration_blocker);
    241    virtio_scsi_common_unrealize(dev);
    242 close_fd:
    243    close(vhostfd);
    244    return;
    245}
    246
    247static void vhost_scsi_unrealize(DeviceState *dev)
    248{
    249    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
    250    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev);
    251    struct vhost_virtqueue *vqs = vsc->dev.vqs;
    252
    253    if (!vsc->migratable) {
    254        migrate_del_blocker(vsc->migration_blocker);
    255        error_free(vsc->migration_blocker);
    256    }
    257
    258    /* This will stop vhost backend. */
    259    vhost_scsi_set_status(vdev, 0);
    260
    261    vhost_dev_cleanup(&vsc->dev);
    262    g_free(vqs);
    263
    264    virtio_scsi_common_unrealize(dev);
    265}
    266
    267static Property vhost_scsi_properties[] = {
    268    DEFINE_PROP_STRING("vhostfd", VirtIOSCSICommon, conf.vhostfd),
    269    DEFINE_PROP_STRING("wwpn", VirtIOSCSICommon, conf.wwpn),
    270    DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0),
    271    DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues,
    272                       VIRTIO_SCSI_AUTO_NUM_QUEUES),
    273    DEFINE_PROP_UINT32("virtqueue_size", VirtIOSCSICommon, conf.virtqueue_size,
    274                       128),
    275    DEFINE_PROP_BOOL("seg_max_adjust", VirtIOSCSICommon, conf.seg_max_adjust,
    276                      true),
    277    DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors,
    278                       0xFFFF),
    279    DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128),
    280    DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features,
    281                                                 VIRTIO_SCSI_F_T10_PI,
    282                                                 false),
    283    DEFINE_PROP_BOOL("migratable", VHostSCSICommon, migratable, false),
    284    DEFINE_PROP_END_OF_LIST(),
    285};
    286
    287static void vhost_scsi_class_init(ObjectClass *klass, void *data)
    288{
    289    DeviceClass *dc = DEVICE_CLASS(klass);
    290    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
    291    FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass);
    292
    293    device_class_set_props(dc, vhost_scsi_properties);
    294    dc->vmsd = &vmstate_virtio_vhost_scsi;
    295    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
    296    vdc->realize = vhost_scsi_realize;
    297    vdc->unrealize = vhost_scsi_unrealize;
    298    vdc->get_features = vhost_scsi_common_get_features;
    299    vdc->set_config = vhost_scsi_common_set_config;
    300    vdc->set_status = vhost_scsi_set_status;
    301    fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path;
    302}
    303
    304static void vhost_scsi_instance_init(Object *obj)
    305{
    306    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj);
    307
    308    vsc->feature_bits = kernel_feature_bits;
    309
    310    device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL,
    311                                  DEVICE(vsc));
    312}
    313
    314static const TypeInfo vhost_scsi_info = {
    315    .name = TYPE_VHOST_SCSI,
    316    .parent = TYPE_VHOST_SCSI_COMMON,
    317    .instance_size = sizeof(VHostSCSI),
    318    .class_init = vhost_scsi_class_init,
    319    .instance_init = vhost_scsi_instance_init,
    320    .interfaces = (InterfaceInfo[]) {
    321        { TYPE_FW_PATH_PROVIDER },
    322        { }
    323    },
    324};
    325
    326static void virtio_register_types(void)
    327{
    328    type_register_static(&vhost_scsi_info);
    329}
    330
    331type_init(virtio_register_types)