xref: /qemu/pc-bios/s390-ccw/virtio-blkdev.c (revision e17e57e862faf6e1f372385c18dcf6d3fd31158e)
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 
virtio_blk_read_many(VDev * vdev,unsigned long sector,void * load_addr,int sec_num)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 
virtio_read_many(unsigned long sector,void * load_addr,int sec_num)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     }
62*0181e237SJared Rossi 
63867e039aSThomas Huth     return -1;
64867e039aSThomas Huth }
65867e039aSThomas Huth 
virtio_load_direct(unsigned long rec_list1,unsigned long rec_list2,unsigned long subchan_id,void * load_addr)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()) {
76facd91acSJared 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) {
82facd91acSJared Rossi         return 0;
83867e039aSThomas Huth     }
84867e039aSThomas Huth     addr += sec_num * virtio_get_block_size();
85867e039aSThomas Huth 
86867e039aSThomas Huth     return addr;
87867e039aSThomas Huth }
88867e039aSThomas Huth 
virtio_read(unsigned long sector,void * load_addr)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  */
virtio_eckd_sectors_for_block_size(int size)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 
virtio_guessed_disk_nature(void)113867e039aSThomas Huth VirtioGDN virtio_guessed_disk_nature(void)
114867e039aSThomas Huth {
115867e039aSThomas Huth     return virtio_get_device()->guessed_disk_nature;
116867e039aSThomas Huth }
117867e039aSThomas Huth 
virtio_assume_iso9660(void)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 
virtio_assume_eckd(void)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 
virtio_ipl_disk_is_valid(void)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 
virtio_get_block_size(void)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 
virtio_get_heads(void)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 
virtio_get_sectors(void)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 
virtio_get_blocks(void)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 
virtio_blk_setup_device(SubChannelId schid)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