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 "virtio.h"
  14#include "virtio-scsi.h"
  15#include "bswap.h"
  16
  17#define VRING_WAIT_REPLY_TIMEOUT 3
  18
  19static VRing block[VIRTIO_MAX_VQS];
  20static char ring_area[VIRTIO_RING_SIZE * VIRTIO_MAX_VQS]
  21                     __attribute__((__aligned__(PAGE_SIZE)));
  22
  23static char chsc_page[PAGE_SIZE] __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)
  94{
  95    Ccw1 ccw = {};
  96    CmdOrb orb = {};
  97    Schib schib;
  98    int r;
  99
 100    /* start command processing */
 101    stsch_err(vdev->schid, &schib);
 102    /* enable the subchannel for IPL device */
 103    schib.pmcw.ena = 1;
 104    msch(vdev->schid, &schib);
 105
 106    /* start subchannel command */
 107    orb.fmt = 1;
 108    orb.cpa = (u32)(long)&ccw;
 109    orb.lpm = 0x80;
 110
 111    ccw.cmd_code = cmd;
 112    ccw.cda = (long)ptr;
 113    ccw.count = len;
 114
 115    r = ssch(vdev->schid, &orb);
 116    /*
 117     * XXX Wait until device is done processing the CCW. For now we can
 118     *     assume that a simple tsch will have finished the CCW processing,
 119     *     but the architecture allows for asynchronous operation
 120     */
 121    if (!r) {
 122        r = drain_irqs(vdev->schid);
 123    }
 124    return r;
 125}
 126
 127static void vring_init(VRing *vr, VqInfo *info)
 128{
 129    void *p = (void *) info->queue;
 130
 131    debug_print_addr("init p", p);
 132    vr->id = info->index;
 133    vr->num = info->num;
 134    vr->desc = p;
 135    vr->avail = p + info->num * sizeof(VRingDesc);
 136    vr->used = (void *)(((unsigned long)&vr->avail->ring[info->num]
 137               + info->align - 1) & ~(info->align - 1));
 138
 139    /* Zero out all relevant field */
 140    vr->avail->flags = 0;
 141    vr->avail->idx = 0;
 142
 143    /* We're running with interrupts off anyways, so don't bother */
 144    vr->used->flags = VRING_USED_F_NO_NOTIFY;
 145    vr->used->idx = 0;
 146    vr->used_idx = 0;
 147    vr->next_idx = 0;
 148    vr->cookie = 0;
 149
 150    debug_print_addr("init vr", vr);
 151}
 152
 153bool vring_notify(VRing *vr)
 154{
 155    vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie);
 156    return vr->cookie >= 0;
 157}
 158
 159void vring_send_buf(VRing *vr, void *p, int len, int flags)
 160{
 161    /* For follow-up chains we need to keep the first entry point */
 162    if (!(flags & VRING_HIDDEN_IS_CHAIN)) {
 163        vr->avail->ring[vr->avail->idx % vr->num] = vr->next_idx;
 164    }
 165
 166    vr->desc[vr->next_idx].addr = (ulong)p;
 167    vr->desc[vr->next_idx].len = len;
 168    vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN;
 169    vr->desc[vr->next_idx].next = vr->next_idx;
 170    vr->desc[vr->next_idx].next++;
 171    vr->next_idx++;
 172
 173    /* Chains only have a single ID */
 174    if (!(flags & VRING_DESC_F_NEXT)) {
 175        vr->avail->idx++;
 176    }
 177}
 178
 179static u64 get_clock(void)
 180{
 181    u64 r;
 182
 183    asm volatile("stck %0" : "=Q" (r) : : "cc");
 184    return r;
 185}
 186
 187ulong get_second(void)
 188{
 189    return (get_clock() >> 12) / 1000000;
 190}
 191
 192int vr_poll(VRing *vr)
 193{
 194    if (vr->used->idx == vr->used_idx) {
 195        vring_notify(vr);
 196        yield();
 197        return 0;
 198    }
 199
 200    vr->used_idx = vr->used->idx;
 201    vr->next_idx = 0;
 202    vr->desc[0].len = 0;
 203    vr->desc[0].flags = 0;
 204    return 1; /* vr has been updated */
 205}
 206
 207/*
 208 * Wait for the host to reply.
 209 *
 210 * timeout is in seconds if > 0.
 211 *
 212 * Returns 0 on success, 1 on timeout.
 213 */
 214int vring_wait_reply(void)
 215{
 216    ulong target_second = get_second() + vdev.wait_reply_timeout;
 217
 218    /* Wait for any queue to be updated by the host */
 219    do {
 220        int i, r = 0;
 221
 222        for (i = 0; i < vdev.nr_vqs; i++) {
 223            r += vr_poll(&vdev.vrings[i]);
 224        }
 225        yield();
 226        if (r) {
 227            return 0;
 228        }
 229    } while (!vdev.wait_reply_timeout || (get_second() < target_second));
 230
 231    return 1;
 232}
 233
 234int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
 235{
 236    VRing *vr = &vdev->vrings[vqid];
 237    int i = 0;
 238
 239    do {
 240        vring_send_buf(vr, cmd[i].data, cmd[i].size,
 241                       cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
 242    } while (cmd[i++].flags & VRING_DESC_F_NEXT);
 243
 244    vring_wait_reply();
 245    if (drain_irqs(vr->schid)) {
 246        return -1;
 247    }
 248    return 0;
 249}
 250
 251void virtio_setup_ccw(VDev *vdev)
 252{
 253    int i, rc, cfg_size = 0;
 254    unsigned char status = VIRTIO_CONFIG_S_DRIVER_OK;
 255    struct VirtioFeatureDesc {
 256        uint32_t features;
 257        uint8_t index;
 258    } __attribute__((packed)) feats;
 259
 260    IPL_assert(virtio_is_supported(vdev->schid), "PE");
 261    /* device ID has been established now */
 262
 263    vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
 264    vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
 265
 266    run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0);
 267
 268    switch (vdev->senseid.cu_model) {
 269    case VIRTIO_ID_NET:
 270        vdev->nr_vqs = 2;
 271        vdev->cmd_vr_idx = 0;
 272        cfg_size = sizeof(vdev->config.net);
 273        break;
 274    case VIRTIO_ID_BLOCK:
 275        vdev->nr_vqs = 1;
 276        vdev->cmd_vr_idx = 0;
 277        cfg_size = sizeof(vdev->config.blk);
 278        break;
 279    case VIRTIO_ID_SCSI:
 280        vdev->nr_vqs = 3;
 281        vdev->cmd_vr_idx = VR_REQUEST;
 282        cfg_size = sizeof(vdev->config.scsi);
 283        break;
 284    default:
 285        panic("Unsupported virtio device\n");
 286    }
 287    IPL_assert(run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size) == 0,
 288               "Could not get block device configuration");
 289
 290    /* Feature negotiation */
 291    for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
 292        feats.features = 0;
 293        feats.index = i;
 294        rc = run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats));
 295        IPL_assert(rc == 0, "Could not get features bits");
 296        vdev->guest_features[i] &= bswap32(feats.features);
 297        feats.features = bswap32(vdev->guest_features[i]);
 298        rc = run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats));
 299        IPL_assert(rc == 0, "Could not set features bits");
 300    }
 301
 302    for (i = 0; i < vdev->nr_vqs; i++) {
 303        VqInfo info = {
 304            .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE),
 305            .align = KVM_S390_VIRTIO_RING_ALIGN,
 306            .index = i,
 307            .num = 0,
 308        };
 309        VqConfig config = {
 310            .index = i,
 311            .num = 0,
 312        };
 313
 314        IPL_assert(
 315            run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config)) == 0,
 316            "Could not get block device VQ configuration");
 317        info.num = config.num;
 318        vring_init(&vdev->vrings[i], &info);
 319        vdev->vrings[i].schid = vdev->schid;
 320        IPL_assert(run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info)) == 0,
 321                   "Cannot set VQ info");
 322    }
 323    IPL_assert(
 324        run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status)) == 0,
 325        "Could not write status to host");
 326}
 327
 328bool virtio_is_supported(SubChannelId schid)
 329{
 330    vdev.schid = schid;
 331    memset(&vdev.senseid, 0, sizeof(vdev.senseid));
 332    /* run sense id command */
 333    if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid))) {
 334        return false;
 335    }
 336    if (vdev.senseid.cu_type == 0x3832) {
 337        switch (vdev.senseid.cu_model) {
 338        case VIRTIO_ID_BLOCK:
 339        case VIRTIO_ID_SCSI:
 340        case VIRTIO_ID_NET:
 341            return true;
 342        }
 343    }
 344    return false;
 345}
 346
 347int enable_mss_facility(void)
 348{
 349    int ret;
 350    ChscAreaSda *sda_area = (ChscAreaSda *) chsc_page;
 351
 352    memset(sda_area, 0, PAGE_SIZE);
 353    sda_area->request.length = 0x0400;
 354    sda_area->request.code = 0x0031;
 355    sda_area->operation_code = 0x2;
 356
 357    ret = chsc(sda_area);
 358    if ((ret == 0) && (sda_area->response.code == 0x0001)) {
 359        return 0;
 360    }
 361    return -EIO;
 362}
 363