qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

virtio-blkdev.c (6069B)


      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 
     19 static 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 
     52 int 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 
     66 unsigned 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 
     89 int 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  */
     98 static 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 
    113 VirtioGDN virtio_guessed_disk_nature(void)
    114 {
    115     return virtio_get_device()->guessed_disk_nature;
    116 }
    117 
    118 void 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 
    135 void 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 
    155 bool 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 
    170 int 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 
    183 uint8_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 
    197 uint8_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 
    211 uint64_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 
    225 int 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 }