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