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
  16#define VIRTIO_BLK_F_GEOMETRY   (1 << 4)
  17#define VIRTIO_BLK_F_BLK_SIZE   (1 << 6)
  18
  19static int virtio_blk_read_many(VDev *vdev, ulong sector, void *load_addr,
  20                                int sec_num)
  21{
  22    VirtioBlkOuthdr out_hdr;
  23    u8 status;
  24    VRing *vr = &vdev->vrings[vdev->cmd_vr_idx];
  25
  26    /* Tell the host we want to read */
  27    out_hdr.type = VIRTIO_BLK_T_IN;
  28    out_hdr.ioprio = 99;
  29    out_hdr.sector = virtio_sector_adjust(sector);
  30
  31    vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT);
  32
  33    /* This is where we want to receive data */
  34    vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num,
  35                   VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
  36                   VRING_DESC_F_NEXT);
  37
  38    /* status field */
  39    vring_send_buf(vr, &status, sizeof(u8),
  40                   VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN);
  41
  42    /* Now we can tell the host to read */
  43    vring_wait_reply();
  44
  45    if (drain_irqs(vr->schid)) {
  46        /* Well, whatever status is supposed to contain... */
  47        status = 1;
  48    }
  49    return status;
  50}
  51
  52int virtio_read_many(ulong sector, void *load_addr, int sec_num)
  53{
  54    VDev *vdev = virtio_get_device();
  55
  56    switch (vdev->senseid.cu_model) {
  57    case VIRTIO_ID_BLOCK:
  58        return virtio_blk_read_many(vdev, sector, load_addr, sec_num);
  59    case VIRTIO_ID_SCSI:
  60        return virtio_scsi_read_many(vdev, sector, load_addr, sec_num);
  61    }
  62    panic("\n! No readable IPL device !\n");
  63    return -1;
  64}
  65
  66unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
  67                                 ulong subchan_id, void *load_addr)
  68{
  69    u8 status;
  70    int sec = rec_list1;
  71    int sec_num = ((rec_list2 >> 32) & 0xffff) + 1;
  72    int sec_len = rec_list2 >> 48;
  73    ulong addr = (ulong)load_addr;
  74
  75    if (sec_len != virtio_get_block_size()) {
  76        return -1;
  77    }
  78
  79    sclp_print(".");
  80    status = virtio_read_many(sec, (void *)addr, sec_num);
  81    if (status) {
  82        panic("I/O Error");
  83    }
  84    addr += sec_num * virtio_get_block_size();
  85
  86    return addr;
  87}
  88
  89int virtio_read(ulong sector, void *load_addr)
  90{
  91    return virtio_read_many(sector, load_addr, 1);
  92}
  93
  94/*
  95 * Other supported value pairs, if any, would need to be added here.
  96 * Note: head count is always 15.
  97 */
  98static inline u8 virtio_eckd_sectors_for_block_size(int size)
  99{
 100    switch (size) {
 101    case 512:
 102        return 49;
 103    case 1024:
 104        return 33;
 105    case 2048:
 106        return 21;
 107    case 4096:
 108        return 12;
 109    }
 110    return 0;
 111}
 112
 113VirtioGDN virtio_guessed_disk_nature(void)
 114{
 115    return virtio_get_device()->guessed_disk_nature;
 116}
 117
 118void virtio_assume_iso9660(void)
 119{
 120    VDev *vdev = virtio_get_device();
 121
 122    switch (vdev->senseid.cu_model) {
 123    case VIRTIO_ID_BLOCK:
 124        vdev->guessed_disk_nature = VIRTIO_GDN_SCSI;
 125        vdev->config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE;
 126        vdev->config.blk.physical_block_exp = 0;
 127        vdev->blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE;
 128        break;
 129    case VIRTIO_ID_SCSI:
 130        vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE;
 131        break;
 132    }
 133}
 134
 135void virtio_assume_eckd(void)
 136{
 137    VDev *vdev = virtio_get_device();
 138
 139    vdev->guessed_disk_nature = VIRTIO_GDN_DASD;
 140    vdev->blk_factor = 1;
 141    vdev->config.blk.physical_block_exp = 0;
 142    switch (vdev->senseid.cu_model) {
 143    case VIRTIO_ID_BLOCK:
 144        vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE;
 145        break;
 146    case VIRTIO_ID_SCSI:
 147        vdev->config.blk.blk_size = vdev->scsi_block_size;
 148        break;
 149    }
 150    vdev->config.blk.geometry.heads = 15;
 151    vdev->config.blk.geometry.sectors =
 152        virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size);
 153}
 154
 155bool virtio_ipl_disk_is_valid(void)
 156{
 157    int blksize = virtio_get_block_size();
 158    VDev *vdev = virtio_get_device();
 159
 160    if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI ||
 161        vdev->guessed_disk_nature == VIRTIO_GDN_DASD) {
 162        return true;
 163    }
 164
 165    return (vdev->senseid.cu_model == VIRTIO_ID_BLOCK ||
 166            vdev->senseid.cu_model == VIRTIO_ID_SCSI) &&
 167           blksize >= 512 && blksize <= 4096;
 168}
 169
 170int virtio_get_block_size(void)
 171{
 172    VDev *vdev = virtio_get_device();
 173
 174    switch (vdev->senseid.cu_model) {
 175    case VIRTIO_ID_BLOCK:
 176        return vdev->config.blk.blk_size;
 177    case VIRTIO_ID_SCSI:
 178        return vdev->scsi_block_size;
 179    }
 180    return 0;
 181}
 182
 183uint8_t virtio_get_heads(void)
 184{
 185    VDev *vdev = virtio_get_device();
 186
 187    switch (vdev->senseid.cu_model) {
 188    case VIRTIO_ID_BLOCK:
 189        return vdev->config.blk.geometry.heads;
 190    case VIRTIO_ID_SCSI:
 191        return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
 192               ? vdev->config.blk.geometry.heads : 255;
 193    }
 194    return 0;
 195}
 196
 197uint8_t virtio_get_sectors(void)
 198{
 199    VDev *vdev = virtio_get_device();
 200
 201    switch (vdev->senseid.cu_model) {
 202    case VIRTIO_ID_BLOCK:
 203        return vdev->config.blk.geometry.sectors;
 204    case VIRTIO_ID_SCSI:
 205        return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
 206               ? vdev->config.blk.geometry.sectors : 63;
 207    }
 208    return 0;
 209}
 210
 211uint64_t virtio_get_blocks(void)
 212{
 213    VDev *vdev = virtio_get_device();
 214    const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE;
 215
 216    switch (vdev->senseid.cu_model) {
 217    case VIRTIO_ID_BLOCK:
 218        return vdev->config.blk.capacity / factor;
 219    case VIRTIO_ID_SCSI:
 220        return vdev->scsi_last_block / factor;
 221    }
 222    return 0;
 223}
 224
 225int virtio_blk_setup_device(SubChannelId schid)
 226{
 227    VDev *vdev = virtio_get_device();
 228
 229    vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE;
 230    vdev->schid = schid;
 231    virtio_setup_ccw(vdev);
 232
 233    sclp_print("Using virtio-blk.\n");
 234
 235    return 0;
 236}
 237