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 "libc.h"
12 #include "s390-ccw.h"
13 #include "virtio.h"
14 #include "virtio-scsi.h"
15 
16 static int virtio_blk_read_many(VDev *vdev, ulong sector, void *load_addr,
17                                 int sec_num)
18 {
19     VirtioBlkOuthdr out_hdr;
20     u8 status;
21     VRing *vr = &vdev->vrings[vdev->cmd_vr_idx];
22 
23     /* Tell the host we want to read */
24     out_hdr.type = VIRTIO_BLK_T_IN;
25     out_hdr.ioprio = 99;
26     out_hdr.sector = virtio_sector_adjust(sector);
27 
28     vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT);
29 
30     /* This is where we want to receive data */
31     vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num,
32                    VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
33                    VRING_DESC_F_NEXT);
34 
35     /* status field */
36     vring_send_buf(vr, &status, sizeof(u8),
37                    VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN);
38 
39     /* Now we can tell the host to read */
40     vring_wait_reply();
41 
42     if (drain_irqs(vr->schid)) {
43         /* Well, whatever status is supposed to contain... */
44         status = 1;
45     }
46     return status;
47 }
48 
49 int virtio_read_many(ulong sector, void *load_addr, int sec_num)
50 {
51     VDev *vdev = virtio_get_device();
52 
53     switch (vdev->senseid.cu_model) {
54     case VIRTIO_ID_BLOCK:
55         return virtio_blk_read_many(vdev, sector, load_addr, sec_num);
56     case VIRTIO_ID_SCSI:
57         return virtio_scsi_read_many(vdev, sector, load_addr, sec_num);
58     }
59     panic("\n! No readable IPL device !\n");
60     return -1;
61 }
62 
63 unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
64                                  ulong subchan_id, void *load_addr)
65 {
66     u8 status;
67     int sec = rec_list1;
68     int sec_num = ((rec_list2 >> 32) & 0xffff) + 1;
69     int sec_len = rec_list2 >> 48;
70     ulong addr = (ulong)load_addr;
71 
72     if (sec_len != virtio_get_block_size()) {
73         return -1;
74     }
75 
76     sclp_print(".");
77     status = virtio_read_many(sec, (void *)addr, sec_num);
78     if (status) {
79         panic("I/O Error");
80     }
81     addr += sec_num * virtio_get_block_size();
82 
83     return addr;
84 }
85 
86 int virtio_read(ulong sector, void *load_addr)
87 {
88     return virtio_read_many(sector, load_addr, 1);
89 }
90 
91 /*
92  * Other supported value pairs, if any, would need to be added here.
93  * Note: head count is always 15.
94  */
95 static inline u8 virtio_eckd_sectors_for_block_size(int size)
96 {
97     switch (size) {
98     case 512:
99         return 49;
100     case 1024:
101         return 33;
102     case 2048:
103         return 21;
104     case 4096:
105         return 12;
106     }
107     return 0;
108 }
109 
110 VirtioGDN virtio_guessed_disk_nature(void)
111 {
112     return virtio_get_device()->guessed_disk_nature;
113 }
114 
115 void virtio_assume_scsi(void)
116 {
117     VDev *vdev = virtio_get_device();
118 
119     switch (vdev->senseid.cu_model) {
120     case VIRTIO_ID_BLOCK:
121         vdev->guessed_disk_nature = VIRTIO_GDN_SCSI;
122         vdev->config.blk.blk_size = VIRTIO_SCSI_BLOCK_SIZE;
123         vdev->config.blk.physical_block_exp = 0;
124         vdev->blk_factor = 1;
125         break;
126     case VIRTIO_ID_SCSI:
127         vdev->scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE;
128         break;
129     }
130 }
131 
132 void virtio_assume_iso9660(void)
133 {
134     VDev *vdev = virtio_get_device();
135 
136     switch (vdev->senseid.cu_model) {
137     case VIRTIO_ID_BLOCK:
138         vdev->guessed_disk_nature = VIRTIO_GDN_SCSI;
139         vdev->config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE;
140         vdev->config.blk.physical_block_exp = 0;
141         vdev->blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE;
142         break;
143     case VIRTIO_ID_SCSI:
144         vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE;
145         break;
146     }
147 }
148 
149 void virtio_assume_eckd(void)
150 {
151     VDev *vdev = virtio_get_device();
152 
153     vdev->guessed_disk_nature = VIRTIO_GDN_DASD;
154     vdev->blk_factor = 1;
155     vdev->config.blk.physical_block_exp = 0;
156     switch (vdev->senseid.cu_model) {
157     case VIRTIO_ID_BLOCK:
158         vdev->config.blk.blk_size = 4096;
159         break;
160     case VIRTIO_ID_SCSI:
161         vdev->config.blk.blk_size = vdev->scsi_block_size;
162         break;
163     }
164     vdev->config.blk.geometry.heads = 15;
165     vdev->config.blk.geometry.sectors =
166         virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size);
167 }
168 
169 bool virtio_disk_is_scsi(void)
170 {
171     VDev *vdev = virtio_get_device();
172 
173     if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI) {
174         return true;
175     }
176     switch (vdev->senseid.cu_model) {
177     case VIRTIO_ID_BLOCK:
178         return (vdev->config.blk.geometry.heads == 255)
179             && (vdev->config.blk.geometry.sectors == 63)
180             && (virtio_get_block_size()  == VIRTIO_SCSI_BLOCK_SIZE);
181     case VIRTIO_ID_SCSI:
182         return true;
183     }
184     return false;
185 }
186 
187 bool virtio_disk_is_eckd(void)
188 {
189     VDev *vdev = virtio_get_device();
190     const int block_size = virtio_get_block_size();
191 
192     if (vdev->guessed_disk_nature == VIRTIO_GDN_DASD) {
193         return true;
194     }
195     switch (vdev->senseid.cu_model) {
196     case VIRTIO_ID_BLOCK:
197         return (vdev->config.blk.geometry.heads == 15)
198             && (vdev->config.blk.geometry.sectors ==
199                 virtio_eckd_sectors_for_block_size(block_size));
200     case VIRTIO_ID_SCSI:
201         return false;
202     }
203     return false;
204 }
205 
206 bool virtio_ipl_disk_is_valid(void)
207 {
208     return virtio_disk_is_scsi() || virtio_disk_is_eckd();
209 }
210 
211 int virtio_get_block_size(void)
212 {
213     VDev *vdev = virtio_get_device();
214 
215     switch (vdev->senseid.cu_model) {
216     case VIRTIO_ID_BLOCK:
217         return vdev->config.blk.blk_size << vdev->config.blk.physical_block_exp;
218     case VIRTIO_ID_SCSI:
219         return vdev->scsi_block_size;
220     }
221     return 0;
222 }
223 
224 uint8_t virtio_get_heads(void)
225 {
226     VDev *vdev = virtio_get_device();
227 
228     switch (vdev->senseid.cu_model) {
229     case VIRTIO_ID_BLOCK:
230         return vdev->config.blk.geometry.heads;
231     case VIRTIO_ID_SCSI:
232         return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
233                ? vdev->config.blk.geometry.heads : 255;
234     }
235     return 0;
236 }
237 
238 uint8_t virtio_get_sectors(void)
239 {
240     VDev *vdev = virtio_get_device();
241 
242     switch (vdev->senseid.cu_model) {
243     case VIRTIO_ID_BLOCK:
244         return vdev->config.blk.geometry.sectors;
245     case VIRTIO_ID_SCSI:
246         return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
247                ? vdev->config.blk.geometry.sectors : 63;
248     }
249     return 0;
250 }
251 
252 uint64_t virtio_get_blocks(void)
253 {
254     VDev *vdev = virtio_get_device();
255     const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE;
256 
257     switch (vdev->senseid.cu_model) {
258     case VIRTIO_ID_BLOCK:
259         return vdev->config.blk.capacity / factor;
260     case VIRTIO_ID_SCSI:
261         return vdev->scsi_last_block / factor;
262     }
263     return 0;
264 }
265 
266 int virtio_blk_setup_device(SubChannelId schid)
267 {
268     VDev *vdev = virtio_get_device();
269     int ret = 0;
270 
271     vdev->schid = schid;
272     virtio_setup_ccw(vdev);
273 
274     switch (vdev->senseid.cu_model) {
275     case VIRTIO_ID_BLOCK:
276         sclp_print("Using virtio-blk.\n");
277         if (!virtio_ipl_disk_is_valid()) {
278             /* make sure all getters but blocksize return 0 for
279              * invalid IPL disk
280              */
281             memset(&vdev->config.blk, 0, sizeof(vdev->config.blk));
282             virtio_assume_scsi();
283         }
284         break;
285     case VIRTIO_ID_SCSI:
286         IPL_assert(vdev->config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE,
287             "Config: sense size mismatch");
288         IPL_assert(vdev->config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE,
289             "Config: CDB size mismatch");
290 
291         sclp_print("Using virtio-scsi.\n");
292         ret = virtio_scsi_setup(vdev);
293         break;
294     default:
295         panic("\n! No IPL device available !\n");
296     }
297 
298     return ret;
299 }
300