xref: /qemu/pc-bios/s390-ccw/virtio-blkdev.c (revision 1f2c2ee48e87ea743f8e23cc7569dd26c4cf9623)
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