Merge tag 'linux-user-for-7.1-pull-request' of https://gitlab.com/laurent_vivier...
[qemu.git] / pc-bios / s390-ccw / virtio-blkdev.c
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 }