qemu/pc-bios/s390-ccw/virtio-blkdev.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
  16static int virtio_blk_read_many(VDev *vdev, ulong sector, void *load_addr,
  17                                int sec_num)
  18{
  19    VirtioBlkOuthdr out_hdr;
  20    u8 status;
  21    VRing *vr = &vdev->vrings[vdev->cmd_vr_idx];
  22
  23    /* Tell the host we want to read */
  24    out_hdr.type = VIRTIO_BLK_T_IN;
  25    out_hdr.ioprio = 99;
  26    out_hdr.sector = virtio_sector_adjust(sector);
  27
  28    vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT);
  29
  30    /* This is where we want to receive data */
  31    vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num,
  32                   VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
  33                   VRING_DESC_F_NEXT);
  34
  35    /* status field */
  36    vring_send_buf(vr, &status, sizeof(u8),
  37                   VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN);
  38
  39    /* Now we can tell the host to read */
  40    vring_wait_reply();
  41
  42    if (drain_irqs(vr->schid)) {
  43        /* Well, whatever status is supposed to contain... */
  44        status = 1;
  45    }
  46    return status;
  47}
  48
  49int virtio_read_many(ulong sector, void *load_addr, int sec_num)
  50{
  51    VDev *vdev = virtio_get_device();
  52
  53    switch (vdev->senseid.cu_model) {
  54    case VIRTIO_ID_BLOCK:
  55        return virtio_blk_read_many(vdev, sector, load_addr, sec_num);
  56    case VIRTIO_ID_SCSI:
  57        return virtio_scsi_read_many(vdev, sector, load_addr, sec_num);
  58    }
  59    panic("\n! No readable IPL device !\n");
  60    return -1;
  61}
  62
  63unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
  64                                 ulong subchan_id, void *load_addr)
  65{
  66    u8 status;
  67    int sec = rec_list1;
  68    int sec_num = ((rec_list2 >> 32) & 0xffff) + 1;
  69    int sec_len = rec_list2 >> 48;
  70    ulong addr = (ulong)load_addr;
  71
  72    if (sec_len != virtio_get_block_size()) {
  73        return -1;
  74    }
  75
  76    sclp_print(".");
  77    status = virtio_read_many(sec, (void *)addr, sec_num);
  78    if (status) {
  79        panic("I/O Error");
  80    }
  81    addr += sec_num * virtio_get_block_size();
  82
  83    return addr;
  84}
  85
  86int virtio_read(ulong sector, void *load_addr)
  87{
  88    return virtio_read_many(sector, load_addr, 1);
  89}
  90
  91/*
  92 * Other supported value pairs, if any, would need to be added here.
  93 * Note: head count is always 15.
  94 */
  95static inline u8 virtio_eckd_sectors_for_block_size(int size)
  96{
  97    switch (size) {
  98    case 512:
  99        return 49;
 100    case 1024:
 101        return 33;
 102    case 2048:
 103        return 21;
 104    case 4096:
 105        return 12;
 106    }
 107    return 0;
 108}
 109
 110VirtioGDN virtio_guessed_disk_nature(void)
 111{
 112    return virtio_get_device()->guessed_disk_nature;
 113}
 114
 115void virtio_assume_scsi(void)
 116{
 117    VDev *vdev = virtio_get_device();
 118
 119    switch (vdev->senseid.cu_model) {
 120    case VIRTIO_ID_BLOCK:
 121        vdev->guessed_disk_nature = VIRTIO_GDN_SCSI;
 122        vdev->config.blk.blk_size = VIRTIO_SCSI_BLOCK_SIZE;
 123        vdev->config.blk.physical_block_exp = 0;
 124        vdev->blk_factor = 1;
 125        break;
 126    case VIRTIO_ID_SCSI:
 127        vdev->scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE;
 128        break;
 129    }
 130}
 131
 132void virtio_assume_iso9660(void)
 133{
 134    VDev *vdev = virtio_get_device();
 135
 136    switch (vdev->senseid.cu_model) {
 137    case VIRTIO_ID_BLOCK:
 138        vdev->guessed_disk_nature = VIRTIO_GDN_SCSI;
 139        vdev->config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE;
 140        vdev->config.blk.physical_block_exp = 0;
 141        vdev->blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE;
 142        break;
 143    case VIRTIO_ID_SCSI:
 144        vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE;
 145        break;
 146    }
 147}
 148
 149void virtio_assume_eckd(void)
 150{
 151    VDev *vdev = virtio_get_device();
 152
 153    vdev->guessed_disk_nature = VIRTIO_GDN_DASD;
 154    vdev->blk_factor = 1;
 155    vdev->config.blk.physical_block_exp = 0;
 156    switch (vdev->senseid.cu_model) {
 157    case VIRTIO_ID_BLOCK:
 158        vdev->config.blk.blk_size = 4096;
 159        break;
 160    case VIRTIO_ID_SCSI:
 161        vdev->config.blk.blk_size = vdev->scsi_block_size;
 162        break;
 163    }
 164    vdev->config.blk.geometry.heads = 15;
 165    vdev->config.blk.geometry.sectors =
 166        virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size);
 167}
 168
 169bool virtio_disk_is_scsi(void)
 170{
 171    VDev *vdev = virtio_get_device();
 172
 173    if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI) {
 174        return true;
 175    }
 176    switch (vdev->senseid.cu_model) {
 177    case VIRTIO_ID_BLOCK:
 178        return (vdev->config.blk.geometry.heads == 255)
 179            && (vdev->config.blk.geometry.sectors == 63)
 180            && (virtio_get_block_size()  == VIRTIO_SCSI_BLOCK_SIZE);
 181    case VIRTIO_ID_SCSI:
 182        return true;
 183    }
 184    return false;
 185}
 186
 187bool virtio_disk_is_eckd(void)
 188{
 189    VDev *vdev = virtio_get_device();
 190    const int block_size = virtio_get_block_size();
 191
 192    if (vdev->guessed_disk_nature == VIRTIO_GDN_DASD) {
 193        return true;
 194    }
 195    switch (vdev->senseid.cu_model) {
 196    case VIRTIO_ID_BLOCK:
 197        return (vdev->config.blk.geometry.heads == 15)
 198            && (vdev->config.blk.geometry.sectors ==
 199                virtio_eckd_sectors_for_block_size(block_size));
 200    case VIRTIO_ID_SCSI:
 201        return false;
 202    }
 203    return false;
 204}
 205
 206bool virtio_ipl_disk_is_valid(void)
 207{
 208    return virtio_disk_is_scsi() || virtio_disk_is_eckd();
 209}
 210
 211int virtio_get_block_size(void)
 212{
 213    VDev *vdev = virtio_get_device();
 214
 215    switch (vdev->senseid.cu_model) {
 216    case VIRTIO_ID_BLOCK:
 217        return vdev->config.blk.blk_size << vdev->config.blk.physical_block_exp;
 218    case VIRTIO_ID_SCSI:
 219        return vdev->scsi_block_size;
 220    }
 221    return 0;
 222}
 223
 224uint8_t virtio_get_heads(void)
 225{
 226    VDev *vdev = virtio_get_device();
 227
 228    switch (vdev->senseid.cu_model) {
 229    case VIRTIO_ID_BLOCK:
 230        return vdev->config.blk.geometry.heads;
 231    case VIRTIO_ID_SCSI:
 232        return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
 233               ? vdev->config.blk.geometry.heads : 255;
 234    }
 235    return 0;
 236}
 237
 238uint8_t virtio_get_sectors(void)
 239{
 240    VDev *vdev = virtio_get_device();
 241
 242    switch (vdev->senseid.cu_model) {
 243    case VIRTIO_ID_BLOCK:
 244        return vdev->config.blk.geometry.sectors;
 245    case VIRTIO_ID_SCSI:
 246        return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
 247               ? vdev->config.blk.geometry.sectors : 63;
 248    }
 249    return 0;
 250}
 251
 252uint64_t virtio_get_blocks(void)
 253{
 254    VDev *vdev = virtio_get_device();
 255    const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE;
 256
 257    switch (vdev->senseid.cu_model) {
 258    case VIRTIO_ID_BLOCK:
 259        return vdev->config.blk.capacity / factor;
 260    case VIRTIO_ID_SCSI:
 261        return vdev->scsi_last_block / factor;
 262    }
 263    return 0;
 264}
 265
 266int virtio_blk_setup_device(SubChannelId schid)
 267{
 268    VDev *vdev = virtio_get_device();
 269    int ret = 0;
 270
 271    vdev->schid = schid;
 272    virtio_setup_ccw(vdev);
 273
 274    switch (vdev->senseid.cu_model) {
 275    case VIRTIO_ID_BLOCK:
 276        sclp_print("Using virtio-blk.\n");
 277        if (!virtio_ipl_disk_is_valid()) {
 278            /* make sure all getters but blocksize return 0 for
 279             * invalid IPL disk
 280             */
 281            memset(&vdev->config.blk, 0, sizeof(vdev->config.blk));
 282            virtio_assume_scsi();
 283        }
 284        break;
 285    case VIRTIO_ID_SCSI:
 286        IPL_assert(vdev->config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE,
 287            "Config: sense size mismatch");
 288        IPL_assert(vdev->config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE,
 289            "Config: CDB size mismatch");
 290
 291        sclp_print("Using virtio-scsi.\n");
 292        ret = virtio_scsi_setup(vdev);
 293        break;
 294    default:
 295        panic("\n! No IPL device available !\n");
 296    }
 297
 298    return ret;
 299}
 300