qemu/pc-bios/s390-ccw/virtio.c
<<
>>
Prefs
   1/*
   2 * Virtio driver bits
   3 *
   4 * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or (at
   7 * your option) any later version. See the COPYING file in the top-level
   8 * directory.
   9 */
  10
  11#include "libc.h"
  12#include "s390-ccw.h"
  13#include "cio.h"
  14#include "virtio.h"
  15#include "virtio-scsi.h"
  16#include "bswap.h"
  17#include "helper.h"
  18
  19#define VRING_WAIT_REPLY_TIMEOUT 30
  20
  21static VRing block[VIRTIO_MAX_VQS];
  22static char ring_area[VIRTIO_RING_SIZE * VIRTIO_MAX_VQS]
  23                     __attribute__((__aligned__(PAGE_SIZE)));
  24
  25static VDev vdev = {
  26    .nr_vqs = 1,
  27    .vrings = block,
  28    .cmd_vr_idx = 0,
  29    .ring_area = ring_area,
  30    .wait_reply_timeout = VRING_WAIT_REPLY_TIMEOUT,
  31    .schid = { .one = 1 },
  32    .scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE,
  33    .blk_factor = 1,
  34};
  35
  36VDev *virtio_get_device(void)
  37{
  38    return &vdev;
  39}
  40
  41VirtioDevType virtio_get_device_type(void)
  42{
  43    return vdev.senseid.cu_model;
  44}
  45
  46/* virtio spec v1.0 para 4.3.3.2 */
  47static long kvm_hypercall(unsigned long nr, unsigned long param1,
  48                          unsigned long param2, unsigned long param3)
  49{
  50    register ulong r_nr asm("1") = nr;
  51    register ulong r_param1 asm("2") = param1;
  52    register ulong r_param2 asm("3") = param2;
  53    register ulong r_param3 asm("4") = param3;
  54    register long retval asm("2");
  55
  56    asm volatile ("diag 2,4,0x500"
  57                  : "=d" (retval)
  58                  : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
  59                  : "memory", "cc");
  60
  61    return retval;
  62}
  63
  64static long virtio_notify(SubChannelId schid, int vq_idx, long cookie)
  65{
  66    return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid,
  67                         vq_idx, cookie);
  68}
  69
  70/***********************************************
  71 *             Virtio functions                *
  72 ***********************************************/
  73
  74int drain_irqs(SubChannelId schid)
  75{
  76    Irb irb = {};
  77    int r = 0;
  78
  79    while (1) {
  80        /* FIXME: make use of TPI, for that enable subchannel and isc */
  81        if (tsch(schid, &irb)) {
  82            /* Might want to differentiate error codes later on. */
  83            if (irb.scsw.cstat) {
  84                r = -EIO;
  85            } else if (irb.scsw.dstat != 0xc) {
  86                r = -EIO;
  87            }
  88            return r;
  89        }
  90    }
  91}
  92
  93static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
  94{
  95    Ccw1 ccw = {};
  96
  97    ccw.cmd_code = cmd;
  98    ccw.cda = (long)ptr;
  99    ccw.count = len;
 100
 101    if (sli) {
 102        ccw.flags |= CCW_FLAG_SLI;
 103    }
 104
 105    return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw), CCW_FMT1);
 106}
 107
 108static void vring_init(VRing *vr, VqInfo *info)
 109{
 110    void *p = (void *) info->queue;
 111
 112    debug_print_addr("init p", p);
 113    vr->id = info->index;
 114    vr->num = info->num;
 115    vr->desc = p;
 116    vr->avail = p + info->num * sizeof(VRingDesc);
 117    vr->used = (void *)(((unsigned long)&vr->avail->ring[info->num]
 118               + info->align - 1) & ~(info->align - 1));
 119
 120    /* Zero out all relevant field */
 121    vr->avail->flags = 0;
 122    vr->avail->idx = 0;
 123
 124    /* We're running with interrupts off anyways, so don't bother */
 125    vr->used->flags = VRING_USED_F_NO_NOTIFY;
 126    vr->used->idx = 0;
 127    vr->used_idx = 0;
 128    vr->next_idx = 0;
 129    vr->cookie = 0;
 130
 131    debug_print_addr("init vr", vr);
 132}
 133
 134bool vring_notify(VRing *vr)
 135{
 136    vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie);
 137    return vr->cookie >= 0;
 138}
 139
 140void vring_send_buf(VRing *vr, void *p, int len, int flags)
 141{
 142    /* For follow-up chains we need to keep the first entry point */
 143    if (!(flags & VRING_HIDDEN_IS_CHAIN)) {
 144        vr->avail->ring[vr->avail->idx % vr->num] = vr->next_idx;
 145    }
 146
 147    vr->desc[vr->next_idx].addr = (ulong)p;
 148    vr->desc[vr->next_idx].len = len;
 149    vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN;
 150    vr->desc[vr->next_idx].next = vr->next_idx;
 151    vr->desc[vr->next_idx].next++;
 152    vr->next_idx++;
 153
 154    /* Chains only have a single ID */
 155    if (!(flags & VRING_DESC_F_NEXT)) {
 156        vr->avail->idx++;
 157    }
 158}
 159
 160u64 get_clock(void)
 161{
 162    u64 r;
 163
 164    asm volatile("stck %0" : "=Q" (r) : : "cc");
 165    return r;
 166}
 167
 168ulong get_second(void)
 169{
 170    return (get_clock() >> 12) / 1000000;
 171}
 172
 173int vr_poll(VRing *vr)
 174{
 175    if (vr->used->idx == vr->used_idx) {
 176        vring_notify(vr);
 177        yield();
 178        return 0;
 179    }
 180
 181    vr->used_idx = vr->used->idx;
 182    vr->next_idx = 0;
 183    vr->desc[0].len = 0;
 184    vr->desc[0].flags = 0;
 185    return 1; /* vr has been updated */
 186}
 187
 188/*
 189 * Wait for the host to reply.
 190 *
 191 * timeout is in seconds if > 0.
 192 *
 193 * Returns 0 on success, 1 on timeout.
 194 */
 195int vring_wait_reply(void)
 196{
 197    ulong target_second = get_second() + vdev.wait_reply_timeout;
 198
 199    /* Wait for any queue to be updated by the host */
 200    do {
 201        int i, r = 0;
 202
 203        for (i = 0; i < vdev.nr_vqs; i++) {
 204            r += vr_poll(&vdev.vrings[i]);
 205        }
 206        yield();
 207        if (r) {
 208            return 0;
 209        }
 210    } while (!vdev.wait_reply_timeout || (get_second() < target_second));
 211
 212    return 1;
 213}
 214
 215int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
 216{
 217    VRing *vr = &vdev->vrings[vqid];
 218    int i = 0;
 219
 220    do {
 221        vring_send_buf(vr, cmd[i].data, cmd[i].size,
 222                       cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
 223    } while (cmd[i++].flags & VRING_DESC_F_NEXT);
 224
 225    vring_wait_reply();
 226    if (drain_irqs(vr->schid)) {
 227        return -1;
 228    }
 229    return 0;
 230}
 231
 232void virtio_setup_ccw(VDev *vdev)
 233{
 234    int i, rc, cfg_size = 0;
 235    unsigned char status = VIRTIO_CONFIG_S_DRIVER_OK;
 236    struct VirtioFeatureDesc {
 237        uint32_t features;
 238        uint8_t index;
 239    } __attribute__((packed)) feats;
 240
 241    IPL_assert(virtio_is_supported(vdev->schid), "PE");
 242    /* device ID has been established now */
 243
 244    vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
 245    vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
 246
 247    run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
 248
 249    switch (vdev->senseid.cu_model) {
 250    case VIRTIO_ID_NET:
 251        vdev->nr_vqs = 2;
 252        vdev->cmd_vr_idx = 0;
 253        cfg_size = sizeof(vdev->config.net);
 254        break;
 255    case VIRTIO_ID_BLOCK:
 256        vdev->nr_vqs = 1;
 257        vdev->cmd_vr_idx = 0;
 258        cfg_size = sizeof(vdev->config.blk);
 259        break;
 260    case VIRTIO_ID_SCSI:
 261        vdev->nr_vqs = 3;
 262        vdev->cmd_vr_idx = VR_REQUEST;
 263        cfg_size = sizeof(vdev->config.scsi);
 264        break;
 265    default:
 266        panic("Unsupported virtio device\n");
 267    }
 268    IPL_assert(
 269        run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false) == 0,
 270       "Could not get block device configuration");
 271
 272    /* Feature negotiation */
 273    for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
 274        feats.features = 0;
 275        feats.index = i;
 276        rc = run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false);
 277        IPL_assert(rc == 0, "Could not get features bits");
 278        vdev->guest_features[i] &= bswap32(feats.features);
 279        feats.features = bswap32(vdev->guest_features[i]);
 280        rc = run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false);
 281        IPL_assert(rc == 0, "Could not set features bits");
 282    }
 283
 284    for (i = 0; i < vdev->nr_vqs; i++) {
 285        VqInfo info = {
 286            .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE),
 287            .align = KVM_S390_VIRTIO_RING_ALIGN,
 288            .index = i,
 289            .num = 0,
 290        };
 291        VqConfig config = {
 292            .index = i,
 293            .num = 0,
 294        };
 295
 296        IPL_assert(
 297            run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), false) == 0,
 298            "Could not get block device VQ configuration");
 299        info.num = config.num;
 300        vring_init(&vdev->vrings[i], &info);
 301        vdev->vrings[i].schid = vdev->schid;
 302        IPL_assert(
 303            run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false) == 0,
 304            "Cannot set VQ info");
 305    }
 306    IPL_assert(
 307        run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false) == 0,
 308        "Could not write status to host");
 309}
 310
 311bool virtio_is_supported(SubChannelId schid)
 312{
 313    vdev.schid = schid;
 314    memset(&vdev.senseid, 0, sizeof(vdev.senseid));
 315
 316    /*
 317     * Run sense id command.
 318     * The size of the senseid data differs between devices (notably,
 319     * between virtio devices and dasds), so specify the largest possible
 320     * size and suppress the incorrect length indication for smaller sizes.
 321     */
 322    if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid),
 323                true)) {
 324        return false;
 325    }
 326    if (vdev.senseid.cu_type == 0x3832) {
 327        switch (vdev.senseid.cu_model) {
 328        case VIRTIO_ID_BLOCK:
 329        case VIRTIO_ID_SCSI:
 330        case VIRTIO_ID_NET:
 331            return true;
 332        }
 333    }
 334    return false;
 335}
 336