1867e039aSThomas Huth /* 2867e039aSThomas Huth * Virtio driver bits 3867e039aSThomas Huth * 4867e039aSThomas Huth * Copyright (c) 2013 Alexander Graf <agraf@suse.de> 5867e039aSThomas Huth * 6867e039aSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or (at 7867e039aSThomas Huth * your option) any later version. See the COPYING file in the top-level 8867e039aSThomas Huth * directory. 9867e039aSThomas Huth */ 10867e039aSThomas Huth 11867e039aSThomas Huth #include "libc.h" 12867e039aSThomas Huth #include "s390-ccw.h" 13867e039aSThomas Huth #include "virtio.h" 14867e039aSThomas Huth #include "virtio-scsi.h" 15867e039aSThomas Huth 16867e039aSThomas Huth static int virtio_blk_read_many(VDev *vdev, ulong sector, void *load_addr, 17867e039aSThomas Huth int sec_num) 18867e039aSThomas Huth { 19867e039aSThomas Huth VirtioBlkOuthdr out_hdr; 20867e039aSThomas Huth u8 status; 21867e039aSThomas Huth VRing *vr = &vdev->vrings[vdev->cmd_vr_idx]; 22867e039aSThomas Huth 23867e039aSThomas Huth /* Tell the host we want to read */ 24867e039aSThomas Huth out_hdr.type = VIRTIO_BLK_T_IN; 25867e039aSThomas Huth out_hdr.ioprio = 99; 26867e039aSThomas Huth out_hdr.sector = virtio_sector_adjust(sector); 27867e039aSThomas Huth 28867e039aSThomas Huth vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT); 29867e039aSThomas Huth 30867e039aSThomas Huth /* This is where we want to receive data */ 31867e039aSThomas Huth vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num, 32867e039aSThomas Huth VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN | 33867e039aSThomas Huth VRING_DESC_F_NEXT); 34867e039aSThomas Huth 35867e039aSThomas Huth /* status field */ 36867e039aSThomas Huth vring_send_buf(vr, &status, sizeof(u8), 37867e039aSThomas Huth VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN); 38867e039aSThomas Huth 39867e039aSThomas Huth /* Now we can tell the host to read */ 40867e039aSThomas Huth vring_wait_reply(); 41867e039aSThomas Huth 42867e039aSThomas Huth if (drain_irqs(vr->schid)) { 43867e039aSThomas Huth /* Well, whatever status is supposed to contain... */ 44867e039aSThomas Huth status = 1; 45867e039aSThomas Huth } 46867e039aSThomas Huth return status; 47867e039aSThomas Huth } 48867e039aSThomas Huth 49867e039aSThomas Huth int virtio_read_many(ulong sector, void *load_addr, int sec_num) 50867e039aSThomas Huth { 51867e039aSThomas Huth VDev *vdev = virtio_get_device(); 52867e039aSThomas Huth 53867e039aSThomas Huth switch (vdev->senseid.cu_model) { 54867e039aSThomas Huth case VIRTIO_ID_BLOCK: 55867e039aSThomas Huth return virtio_blk_read_many(vdev, sector, load_addr, sec_num); 56867e039aSThomas Huth case VIRTIO_ID_SCSI: 57867e039aSThomas Huth return virtio_scsi_read_many(vdev, sector, load_addr, sec_num); 58867e039aSThomas Huth } 59867e039aSThomas Huth panic("\n! No readable IPL device !\n"); 60867e039aSThomas Huth return -1; 61867e039aSThomas Huth } 62867e039aSThomas Huth 63867e039aSThomas Huth unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, 64867e039aSThomas Huth ulong subchan_id, void *load_addr) 65867e039aSThomas Huth { 66867e039aSThomas Huth u8 status; 67867e039aSThomas Huth int sec = rec_list1; 68867e039aSThomas Huth int sec_num = ((rec_list2 >> 32) & 0xffff) + 1; 69867e039aSThomas Huth int sec_len = rec_list2 >> 48; 70867e039aSThomas Huth ulong addr = (ulong)load_addr; 71867e039aSThomas Huth 72867e039aSThomas Huth if (sec_len != virtio_get_block_size()) { 73867e039aSThomas Huth return -1; 74867e039aSThomas Huth } 75867e039aSThomas Huth 76867e039aSThomas Huth sclp_print("."); 77867e039aSThomas Huth status = virtio_read_many(sec, (void *)addr, sec_num); 78867e039aSThomas Huth if (status) { 79867e039aSThomas Huth panic("I/O Error"); 80867e039aSThomas Huth } 81867e039aSThomas Huth addr += sec_num * virtio_get_block_size(); 82867e039aSThomas Huth 83867e039aSThomas Huth return addr; 84867e039aSThomas Huth } 85867e039aSThomas Huth 86867e039aSThomas Huth int virtio_read(ulong sector, void *load_addr) 87867e039aSThomas Huth { 88867e039aSThomas Huth return virtio_read_many(sector, load_addr, 1); 89867e039aSThomas Huth } 90867e039aSThomas Huth 91867e039aSThomas Huth /* 92867e039aSThomas Huth * Other supported value pairs, if any, would need to be added here. 93867e039aSThomas Huth * Note: head count is always 15. 94867e039aSThomas Huth */ 95867e039aSThomas Huth static inline u8 virtio_eckd_sectors_for_block_size(int size) 96867e039aSThomas Huth { 97867e039aSThomas Huth switch (size) { 98867e039aSThomas Huth case 512: 99867e039aSThomas Huth return 49; 100867e039aSThomas Huth case 1024: 101867e039aSThomas Huth return 33; 102867e039aSThomas Huth case 2048: 103867e039aSThomas Huth return 21; 104867e039aSThomas Huth case 4096: 105867e039aSThomas Huth return 12; 106867e039aSThomas Huth } 107867e039aSThomas Huth return 0; 108867e039aSThomas Huth } 109867e039aSThomas Huth 110867e039aSThomas Huth VirtioGDN virtio_guessed_disk_nature(void) 111867e039aSThomas Huth { 112867e039aSThomas Huth return virtio_get_device()->guessed_disk_nature; 113867e039aSThomas Huth } 114867e039aSThomas Huth 115867e039aSThomas Huth void virtio_assume_scsi(void) 116867e039aSThomas Huth { 117867e039aSThomas Huth VDev *vdev = virtio_get_device(); 118867e039aSThomas Huth 119867e039aSThomas Huth switch (vdev->senseid.cu_model) { 120867e039aSThomas Huth case VIRTIO_ID_BLOCK: 121867e039aSThomas Huth vdev->guessed_disk_nature = VIRTIO_GDN_SCSI; 122867e039aSThomas Huth vdev->config.blk.blk_size = VIRTIO_SCSI_BLOCK_SIZE; 123867e039aSThomas Huth vdev->config.blk.physical_block_exp = 0; 124867e039aSThomas Huth vdev->blk_factor = 1; 125867e039aSThomas Huth break; 126867e039aSThomas Huth case VIRTIO_ID_SCSI: 127867e039aSThomas Huth vdev->scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE; 128867e039aSThomas Huth break; 129867e039aSThomas Huth } 130867e039aSThomas Huth } 131867e039aSThomas Huth 132867e039aSThomas Huth void virtio_assume_iso9660(void) 133867e039aSThomas Huth { 134867e039aSThomas Huth VDev *vdev = virtio_get_device(); 135867e039aSThomas Huth 136867e039aSThomas Huth switch (vdev->senseid.cu_model) { 137867e039aSThomas Huth case VIRTIO_ID_BLOCK: 138867e039aSThomas Huth vdev->guessed_disk_nature = VIRTIO_GDN_SCSI; 139867e039aSThomas Huth vdev->config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE; 140867e039aSThomas Huth vdev->config.blk.physical_block_exp = 0; 141867e039aSThomas Huth vdev->blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE; 142867e039aSThomas Huth break; 143867e039aSThomas Huth case VIRTIO_ID_SCSI: 144867e039aSThomas Huth vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; 145867e039aSThomas Huth break; 146867e039aSThomas Huth } 147867e039aSThomas Huth } 148867e039aSThomas Huth 149867e039aSThomas Huth void virtio_assume_eckd(void) 150867e039aSThomas Huth { 151867e039aSThomas Huth VDev *vdev = virtio_get_device(); 152867e039aSThomas Huth 153867e039aSThomas Huth vdev->guessed_disk_nature = VIRTIO_GDN_DASD; 154867e039aSThomas Huth vdev->blk_factor = 1; 155867e039aSThomas Huth vdev->config.blk.physical_block_exp = 0; 156867e039aSThomas Huth switch (vdev->senseid.cu_model) { 157867e039aSThomas Huth case VIRTIO_ID_BLOCK: 158*1f2c2ee4SThomas Huth vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE; 159867e039aSThomas Huth break; 160867e039aSThomas Huth case VIRTIO_ID_SCSI: 161867e039aSThomas Huth vdev->config.blk.blk_size = vdev->scsi_block_size; 162867e039aSThomas Huth break; 163867e039aSThomas Huth } 164867e039aSThomas Huth vdev->config.blk.geometry.heads = 15; 165867e039aSThomas Huth vdev->config.blk.geometry.sectors = 166867e039aSThomas Huth virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size); 167867e039aSThomas Huth } 168867e039aSThomas Huth 169867e039aSThomas Huth bool virtio_disk_is_scsi(void) 170867e039aSThomas Huth { 171867e039aSThomas Huth VDev *vdev = virtio_get_device(); 172867e039aSThomas Huth 173867e039aSThomas Huth if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI) { 174867e039aSThomas Huth return true; 175867e039aSThomas Huth } 176867e039aSThomas Huth switch (vdev->senseid.cu_model) { 177867e039aSThomas Huth case VIRTIO_ID_BLOCK: 178867e039aSThomas Huth return (vdev->config.blk.geometry.heads == 255) 179867e039aSThomas Huth && (vdev->config.blk.geometry.sectors == 63) 180867e039aSThomas Huth && (virtio_get_block_size() == VIRTIO_SCSI_BLOCK_SIZE); 181867e039aSThomas Huth case VIRTIO_ID_SCSI: 182867e039aSThomas Huth return true; 183867e039aSThomas Huth } 184867e039aSThomas Huth return false; 185867e039aSThomas Huth } 186867e039aSThomas Huth 187867e039aSThomas Huth bool virtio_disk_is_eckd(void) 188867e039aSThomas Huth { 189867e039aSThomas Huth VDev *vdev = virtio_get_device(); 190867e039aSThomas Huth const int block_size = virtio_get_block_size(); 191867e039aSThomas Huth 192867e039aSThomas Huth if (vdev->guessed_disk_nature == VIRTIO_GDN_DASD) { 193867e039aSThomas Huth return true; 194867e039aSThomas Huth } 195867e039aSThomas Huth switch (vdev->senseid.cu_model) { 196867e039aSThomas Huth case VIRTIO_ID_BLOCK: 197867e039aSThomas Huth return (vdev->config.blk.geometry.heads == 15) 198867e039aSThomas Huth && (vdev->config.blk.geometry.sectors == 199867e039aSThomas Huth virtio_eckd_sectors_for_block_size(block_size)); 200867e039aSThomas Huth case VIRTIO_ID_SCSI: 201867e039aSThomas Huth return false; 202867e039aSThomas Huth } 203867e039aSThomas Huth return false; 204867e039aSThomas Huth } 205867e039aSThomas Huth 206867e039aSThomas Huth bool virtio_ipl_disk_is_valid(void) 207867e039aSThomas Huth { 208867e039aSThomas Huth return virtio_disk_is_scsi() || virtio_disk_is_eckd(); 209867e039aSThomas Huth } 210867e039aSThomas Huth 211867e039aSThomas Huth int virtio_get_block_size(void) 212867e039aSThomas Huth { 213867e039aSThomas Huth VDev *vdev = virtio_get_device(); 214867e039aSThomas Huth 215867e039aSThomas Huth switch (vdev->senseid.cu_model) { 216867e039aSThomas Huth case VIRTIO_ID_BLOCK: 217867e039aSThomas Huth return vdev->config.blk.blk_size << vdev->config.blk.physical_block_exp; 218867e039aSThomas Huth case VIRTIO_ID_SCSI: 219867e039aSThomas Huth return vdev->scsi_block_size; 220867e039aSThomas Huth } 221867e039aSThomas Huth return 0; 222867e039aSThomas Huth } 223867e039aSThomas Huth 224867e039aSThomas Huth uint8_t virtio_get_heads(void) 225867e039aSThomas Huth { 226867e039aSThomas Huth VDev *vdev = virtio_get_device(); 227867e039aSThomas Huth 228867e039aSThomas Huth switch (vdev->senseid.cu_model) { 229867e039aSThomas Huth case VIRTIO_ID_BLOCK: 230867e039aSThomas Huth return vdev->config.blk.geometry.heads; 231867e039aSThomas Huth case VIRTIO_ID_SCSI: 232867e039aSThomas Huth return vdev->guessed_disk_nature == VIRTIO_GDN_DASD 233867e039aSThomas Huth ? vdev->config.blk.geometry.heads : 255; 234867e039aSThomas Huth } 235867e039aSThomas Huth return 0; 236867e039aSThomas Huth } 237867e039aSThomas Huth 238867e039aSThomas Huth uint8_t virtio_get_sectors(void) 239867e039aSThomas Huth { 240867e039aSThomas Huth VDev *vdev = virtio_get_device(); 241867e039aSThomas Huth 242867e039aSThomas Huth switch (vdev->senseid.cu_model) { 243867e039aSThomas Huth case VIRTIO_ID_BLOCK: 244867e039aSThomas Huth return vdev->config.blk.geometry.sectors; 245867e039aSThomas Huth case VIRTIO_ID_SCSI: 246867e039aSThomas Huth return vdev->guessed_disk_nature == VIRTIO_GDN_DASD 247867e039aSThomas Huth ? vdev->config.blk.geometry.sectors : 63; 248867e039aSThomas Huth } 249867e039aSThomas Huth return 0; 250867e039aSThomas Huth } 251867e039aSThomas Huth 252867e039aSThomas Huth uint64_t virtio_get_blocks(void) 253867e039aSThomas Huth { 254867e039aSThomas Huth VDev *vdev = virtio_get_device(); 255867e039aSThomas Huth const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE; 256867e039aSThomas Huth 257867e039aSThomas Huth switch (vdev->senseid.cu_model) { 258867e039aSThomas Huth case VIRTIO_ID_BLOCK: 259867e039aSThomas Huth return vdev->config.blk.capacity / factor; 260867e039aSThomas Huth case VIRTIO_ID_SCSI: 261867e039aSThomas Huth return vdev->scsi_last_block / factor; 262867e039aSThomas Huth } 263867e039aSThomas Huth return 0; 264867e039aSThomas Huth } 265867e039aSThomas Huth 266605751b5SThomas Huth int virtio_blk_setup_device(SubChannelId schid) 267867e039aSThomas Huth { 268867e039aSThomas Huth VDev *vdev = virtio_get_device(); 269605751b5SThomas Huth int ret = 0; 270867e039aSThomas Huth 271867e039aSThomas Huth vdev->schid = schid; 272867e039aSThomas Huth virtio_setup_ccw(vdev); 273867e039aSThomas Huth 274867e039aSThomas Huth switch (vdev->senseid.cu_model) { 275867e039aSThomas Huth case VIRTIO_ID_BLOCK: 276867e039aSThomas Huth sclp_print("Using virtio-blk.\n"); 277867e039aSThomas Huth if (!virtio_ipl_disk_is_valid()) { 278867e039aSThomas Huth /* make sure all getters but blocksize return 0 for 279867e039aSThomas Huth * invalid IPL disk 280867e039aSThomas Huth */ 281867e039aSThomas Huth memset(&vdev->config.blk, 0, sizeof(vdev->config.blk)); 282867e039aSThomas Huth virtio_assume_scsi(); 283867e039aSThomas Huth } 284867e039aSThomas Huth break; 285867e039aSThomas Huth case VIRTIO_ID_SCSI: 286867e039aSThomas Huth IPL_assert(vdev->config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE, 287867e039aSThomas Huth "Config: sense size mismatch"); 288867e039aSThomas Huth IPL_assert(vdev->config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE, 289867e039aSThomas Huth "Config: CDB size mismatch"); 290867e039aSThomas Huth 291867e039aSThomas Huth sclp_print("Using virtio-scsi.\n"); 292605751b5SThomas Huth ret = virtio_scsi_setup(vdev); 293867e039aSThomas Huth break; 294867e039aSThomas Huth default: 295867e039aSThomas Huth panic("\n! No IPL device available !\n"); 296867e039aSThomas Huth } 297605751b5SThomas Huth 298605751b5SThomas Huth return ret; 299867e039aSThomas Huth } 300