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 119f427883SJared Rossi #include <stdio.h> 12867e039aSThomas Huth #include "s390-ccw.h" 13867e039aSThomas Huth #include "virtio.h" 14867e039aSThomas Huth #include "virtio-scsi.h" 15867e039aSThomas Huth 169125a314SThomas Huth #define VIRTIO_BLK_F_GEOMETRY (1 << 4) 179125a314SThomas Huth #define VIRTIO_BLK_F_BLK_SIZE (1 << 6) 189125a314SThomas Huth 19f7f2f96fSJuan Quintela static int virtio_blk_read_many(VDev *vdev, unsigned long sector, void *load_addr, 20867e039aSThomas Huth int sec_num) 21867e039aSThomas Huth { 22867e039aSThomas Huth VirtioBlkOuthdr out_hdr; 23867e039aSThomas Huth u8 status; 24867e039aSThomas Huth VRing *vr = &vdev->vrings[vdev->cmd_vr_idx]; 25867e039aSThomas Huth 26867e039aSThomas Huth /* Tell the host we want to read */ 27867e039aSThomas Huth out_hdr.type = VIRTIO_BLK_T_IN; 28867e039aSThomas Huth out_hdr.ioprio = 99; 29867e039aSThomas Huth out_hdr.sector = virtio_sector_adjust(sector); 30867e039aSThomas Huth 31867e039aSThomas Huth vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT); 32867e039aSThomas Huth 33867e039aSThomas Huth /* This is where we want to receive data */ 34867e039aSThomas Huth vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num, 35867e039aSThomas Huth VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN | 36867e039aSThomas Huth VRING_DESC_F_NEXT); 37867e039aSThomas Huth 38867e039aSThomas Huth /* status field */ 39867e039aSThomas Huth vring_send_buf(vr, &status, sizeof(u8), 40867e039aSThomas Huth VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN); 41867e039aSThomas Huth 42867e039aSThomas Huth /* Now we can tell the host to read */ 43867e039aSThomas Huth vring_wait_reply(); 44867e039aSThomas Huth 45867e039aSThomas Huth if (drain_irqs(vr->schid)) { 46867e039aSThomas Huth /* Well, whatever status is supposed to contain... */ 47867e039aSThomas Huth status = 1; 48867e039aSThomas Huth } 49867e039aSThomas Huth return status; 50867e039aSThomas Huth } 51867e039aSThomas Huth 52f7f2f96fSJuan Quintela int virtio_read_many(unsigned long sector, void *load_addr, int sec_num) 53867e039aSThomas Huth { 54867e039aSThomas Huth VDev *vdev = virtio_get_device(); 55867e039aSThomas Huth 56867e039aSThomas Huth switch (vdev->senseid.cu_model) { 57867e039aSThomas Huth case VIRTIO_ID_BLOCK: 58867e039aSThomas Huth return virtio_blk_read_many(vdev, sector, load_addr, sec_num); 59867e039aSThomas Huth case VIRTIO_ID_SCSI: 60867e039aSThomas Huth return virtio_scsi_read_many(vdev, sector, load_addr, sec_num); 61867e039aSThomas Huth } 62867e039aSThomas Huth panic("\n! No readable IPL device !\n"); 63867e039aSThomas Huth return -1; 64867e039aSThomas Huth } 65867e039aSThomas Huth 66f7f2f96fSJuan Quintela unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2, 67f7f2f96fSJuan Quintela unsigned long subchan_id, void *load_addr) 68867e039aSThomas Huth { 69867e039aSThomas Huth u8 status; 70867e039aSThomas Huth int sec = rec_list1; 71867e039aSThomas Huth int sec_num = ((rec_list2 >> 32) & 0xffff) + 1; 72867e039aSThomas Huth int sec_len = rec_list2 >> 48; 73f7f2f96fSJuan Quintela unsigned long addr = (unsigned long)load_addr; 74867e039aSThomas Huth 75867e039aSThomas Huth if (sec_len != virtio_get_block_size()) { 76*facd91acSJared Rossi return 0; 77867e039aSThomas Huth } 78867e039aSThomas Huth 799f427883SJared Rossi printf("."); 80867e039aSThomas Huth status = virtio_read_many(sec, (void *)addr, sec_num); 81867e039aSThomas Huth if (status) { 82*facd91acSJared Rossi return 0; 83867e039aSThomas Huth } 84867e039aSThomas Huth addr += sec_num * virtio_get_block_size(); 85867e039aSThomas Huth 86867e039aSThomas Huth return addr; 87867e039aSThomas Huth } 88867e039aSThomas Huth 89f7f2f96fSJuan Quintela int virtio_read(unsigned long sector, void *load_addr) 90867e039aSThomas Huth { 91867e039aSThomas Huth return virtio_read_many(sector, load_addr, 1); 92867e039aSThomas Huth } 93867e039aSThomas Huth 94867e039aSThomas Huth /* 95867e039aSThomas Huth * Other supported value pairs, if any, would need to be added here. 96867e039aSThomas Huth * Note: head count is always 15. 97867e039aSThomas Huth */ 98867e039aSThomas Huth static inline u8 virtio_eckd_sectors_for_block_size(int size) 99867e039aSThomas Huth { 100867e039aSThomas Huth switch (size) { 101867e039aSThomas Huth case 512: 102867e039aSThomas Huth return 49; 103867e039aSThomas Huth case 1024: 104867e039aSThomas Huth return 33; 105867e039aSThomas Huth case 2048: 106867e039aSThomas Huth return 21; 107867e039aSThomas Huth case 4096: 108867e039aSThomas Huth return 12; 109867e039aSThomas Huth } 110867e039aSThomas Huth return 0; 111867e039aSThomas Huth } 112867e039aSThomas Huth 113867e039aSThomas Huth VirtioGDN virtio_guessed_disk_nature(void) 114867e039aSThomas Huth { 115867e039aSThomas Huth return virtio_get_device()->guessed_disk_nature; 116867e039aSThomas Huth } 117867e039aSThomas Huth 118867e039aSThomas Huth void virtio_assume_iso9660(void) 119867e039aSThomas Huth { 120867e039aSThomas Huth VDev *vdev = virtio_get_device(); 121867e039aSThomas Huth 122867e039aSThomas Huth switch (vdev->senseid.cu_model) { 123867e039aSThomas Huth case VIRTIO_ID_BLOCK: 124867e039aSThomas Huth vdev->guessed_disk_nature = VIRTIO_GDN_SCSI; 125867e039aSThomas Huth vdev->config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE; 126867e039aSThomas Huth vdev->config.blk.physical_block_exp = 0; 127867e039aSThomas Huth vdev->blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE; 128867e039aSThomas Huth break; 129867e039aSThomas Huth case VIRTIO_ID_SCSI: 130867e039aSThomas Huth vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; 131867e039aSThomas Huth break; 132867e039aSThomas Huth } 133867e039aSThomas Huth } 134867e039aSThomas Huth 135867e039aSThomas Huth void virtio_assume_eckd(void) 136867e039aSThomas Huth { 137867e039aSThomas Huth VDev *vdev = virtio_get_device(); 138867e039aSThomas Huth 139867e039aSThomas Huth vdev->guessed_disk_nature = VIRTIO_GDN_DASD; 140867e039aSThomas Huth vdev->blk_factor = 1; 141867e039aSThomas Huth vdev->config.blk.physical_block_exp = 0; 142867e039aSThomas Huth switch (vdev->senseid.cu_model) { 143867e039aSThomas Huth case VIRTIO_ID_BLOCK: 1441f2c2ee4SThomas Huth vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE; 145867e039aSThomas Huth break; 146867e039aSThomas Huth case VIRTIO_ID_SCSI: 147867e039aSThomas Huth vdev->config.blk.blk_size = vdev->scsi_block_size; 148867e039aSThomas Huth break; 149867e039aSThomas Huth } 150867e039aSThomas Huth vdev->config.blk.geometry.heads = 15; 151867e039aSThomas Huth vdev->config.blk.geometry.sectors = 152867e039aSThomas Huth virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size); 153867e039aSThomas Huth } 154867e039aSThomas Huth 155867e039aSThomas Huth bool virtio_ipl_disk_is_valid(void) 156867e039aSThomas Huth { 157bbf615f7SThomas Huth int blksize = virtio_get_block_size(); 158bbf615f7SThomas Huth VDev *vdev = virtio_get_device(); 159bbf615f7SThomas Huth 160bbf615f7SThomas Huth if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI || 161bbf615f7SThomas Huth vdev->guessed_disk_nature == VIRTIO_GDN_DASD) { 162bbf615f7SThomas Huth return true; 163bbf615f7SThomas Huth } 164bbf615f7SThomas Huth 165bbf615f7SThomas Huth return (vdev->senseid.cu_model == VIRTIO_ID_BLOCK || 166bbf615f7SThomas Huth vdev->senseid.cu_model == VIRTIO_ID_SCSI) && 167bbf615f7SThomas Huth blksize >= 512 && blksize <= 4096; 168867e039aSThomas Huth } 169867e039aSThomas Huth 170867e039aSThomas Huth int virtio_get_block_size(void) 171867e039aSThomas Huth { 172867e039aSThomas Huth VDev *vdev = virtio_get_device(); 173867e039aSThomas Huth 174867e039aSThomas Huth switch (vdev->senseid.cu_model) { 175867e039aSThomas Huth case VIRTIO_ID_BLOCK: 176393296deSThomas Huth return vdev->config.blk.blk_size; 177867e039aSThomas Huth case VIRTIO_ID_SCSI: 178867e039aSThomas Huth return vdev->scsi_block_size; 179867e039aSThomas Huth } 180867e039aSThomas Huth return 0; 181867e039aSThomas Huth } 182867e039aSThomas Huth 183867e039aSThomas Huth uint8_t virtio_get_heads(void) 184867e039aSThomas Huth { 185867e039aSThomas Huth VDev *vdev = virtio_get_device(); 186867e039aSThomas Huth 187867e039aSThomas Huth switch (vdev->senseid.cu_model) { 188867e039aSThomas Huth case VIRTIO_ID_BLOCK: 189867e039aSThomas Huth return vdev->config.blk.geometry.heads; 190867e039aSThomas Huth case VIRTIO_ID_SCSI: 191867e039aSThomas Huth return vdev->guessed_disk_nature == VIRTIO_GDN_DASD 192867e039aSThomas Huth ? vdev->config.blk.geometry.heads : 255; 193867e039aSThomas Huth } 194867e039aSThomas Huth return 0; 195867e039aSThomas Huth } 196867e039aSThomas Huth 197867e039aSThomas Huth uint8_t virtio_get_sectors(void) 198867e039aSThomas Huth { 199867e039aSThomas Huth VDev *vdev = virtio_get_device(); 200867e039aSThomas Huth 201867e039aSThomas Huth switch (vdev->senseid.cu_model) { 202867e039aSThomas Huth case VIRTIO_ID_BLOCK: 203867e039aSThomas Huth return vdev->config.blk.geometry.sectors; 204867e039aSThomas Huth case VIRTIO_ID_SCSI: 205867e039aSThomas Huth return vdev->guessed_disk_nature == VIRTIO_GDN_DASD 206867e039aSThomas Huth ? vdev->config.blk.geometry.sectors : 63; 207867e039aSThomas Huth } 208867e039aSThomas Huth return 0; 209867e039aSThomas Huth } 210867e039aSThomas Huth 211867e039aSThomas Huth uint64_t virtio_get_blocks(void) 212867e039aSThomas Huth { 213867e039aSThomas Huth VDev *vdev = virtio_get_device(); 214867e039aSThomas Huth const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE; 215867e039aSThomas Huth 216867e039aSThomas Huth switch (vdev->senseid.cu_model) { 217867e039aSThomas Huth case VIRTIO_ID_BLOCK: 218867e039aSThomas Huth return vdev->config.blk.capacity / factor; 219867e039aSThomas Huth case VIRTIO_ID_SCSI: 220867e039aSThomas Huth return vdev->scsi_last_block / factor; 221867e039aSThomas Huth } 222867e039aSThomas Huth return 0; 223867e039aSThomas Huth } 224867e039aSThomas Huth 225605751b5SThomas Huth int virtio_blk_setup_device(SubChannelId schid) 226867e039aSThomas Huth { 227867e039aSThomas Huth VDev *vdev = virtio_get_device(); 228867e039aSThomas Huth 2299125a314SThomas Huth vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE; 230867e039aSThomas Huth vdev->schid = schid; 231867e039aSThomas Huth virtio_setup_ccw(vdev); 232867e039aSThomas Huth 2339f427883SJared Rossi puts("Using virtio-blk."); 234867e039aSThomas Huth 235cf30b7c4SThomas Huth return 0; 236867e039aSThomas Huth } 237