]>
Commit | Line | Data |
---|---|---|
86aec22d ED |
1 | /* |
2 | * Virtio-SCSI implementation for s390 machine loader for qemu | |
3 | * | |
4 | * Copyright 2015 IBM Corp. | |
5 | * Author: Eugene "jno" Dvurechenski <[email protected]> | |
6 | * | |
7 | * This work is licensed under the terms of the GNU GPL, version 2 or (at | |
8 | * your option) any later version. See the COPYING file in the top-level | |
9 | * directory. | |
10 | */ | |
11 | ||
90806fec | 12 | #include "libc.h" |
86aec22d ED |
13 | #include "s390-ccw.h" |
14 | #include "virtio.h" | |
15 | #include "scsi.h" | |
16 | #include "virtio-scsi.h" | |
17 | ||
18 | static ScsiDevice default_scsi_device; | |
19 | static VirtioScsiCmdReq req; | |
20 | static VirtioScsiCmdResp resp; | |
21 | ||
22 | static uint8_t scsi_inquiry_std_response[256]; | |
8edfe85b | 23 | static ScsiInquiryEvpdPages scsi_inquiry_evpd_pages_response; |
fe921fc8 | 24 | static ScsiInquiryEvpdBl scsi_inquiry_evpd_bl_response; |
86aec22d ED |
25 | |
26 | static inline void vs_assert(bool term, const char **msgs) | |
27 | { | |
28 | if (!term) { | |
29 | int i = 0; | |
30 | ||
31 | sclp_print("\n! "); | |
32 | while (msgs[i]) { | |
33 | sclp_print(msgs[i++]); | |
34 | } | |
35 | panic(" !\n"); | |
36 | } | |
37 | } | |
38 | ||
39 | static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp, | |
40 | const char *title) | |
41 | { | |
42 | const char *mr[] = { | |
43 | title, ": response ", virtio_scsi_response_msg(resp), 0 | |
44 | }; | |
45 | const char *ms[] = { | |
46 | title, | |
47 | CDB_STATUS_VALID(resp->status) ? ": " : ": invalid ", | |
48 | scsi_cdb_status_msg(resp->status), | |
49 | resp->status == CDB_STATUS_CHECK_CONDITION ? " " : 0, | |
50 | resp->sense_len ? scsi_cdb_asc_msg(resp->sense) | |
51 | : "no sense data", | |
52 | scsi_sense_response(resp->sense) == 0x70 ? ", sure" : "?", | |
53 | 0 | |
54 | }; | |
55 | ||
56 | vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr); | |
57 | vs_assert(resp->status == CDB_STATUS_GOOD, ms); | |
58 | } | |
59 | ||
60 | static void prepare_request(VDev *vdev, const void *cdb, int cdb_size, | |
61 | void *data, uint32_t data_size) | |
62 | { | |
63 | const ScsiDevice *sdev = vdev->scsi_device; | |
64 | ||
65 | memset(&req, 0, sizeof(req)); | |
66 | req.lun = make_lun(sdev->channel, sdev->target, sdev->lun); | |
67 | memcpy(&req.cdb, cdb, cdb_size); | |
68 | ||
69 | memset(&resp, 0, sizeof(resp)); | |
70 | resp.status = 0xff; /* set invalid */ | |
71 | resp.response = 0xff; /* */ | |
72 | ||
73 | if (data && data_size) { | |
74 | memset(data, 0, data_size); | |
75 | } | |
76 | } | |
77 | ||
78 | static inline void vs_io_assert(bool term, const char *msg) | |
79 | { | |
80 | if (!term) { | |
81 | virtio_scsi_verify_response(&resp, msg); | |
82 | } | |
83 | } | |
84 | ||
85 | static void vs_run(const char *title, VirtioCmd *cmd, VDev *vdev, | |
86 | const void *cdb, int cdb_size, | |
87 | void *data, uint32_t data_size) | |
88 | { | |
89 | prepare_request(vdev, cdb, cdb_size, data, data_size); | |
90 | vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title); | |
91 | } | |
92 | ||
93 | /* SCSI protocol implementation routines */ | |
94 | ||
9c12359c EF |
95 | static bool scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page, |
96 | void *data, uint32_t data_size) | |
86aec22d ED |
97 | { |
98 | ScsiCdbInquiry cdb = { | |
99 | .command = 0x12, | |
9c12359c EF |
100 | .b1 = evpd, |
101 | .b2 = page, | |
86aec22d ED |
102 | .alloc_len = data_size < 65535 ? data_size : 65535, |
103 | }; | |
104 | VirtioCmd inquiry[] = { | |
105 | { &req, sizeof(req), VRING_DESC_F_NEXT }, | |
106 | { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, | |
107 | { data, data_size, VRING_DESC_F_WRITE }, | |
108 | }; | |
109 | ||
110 | vs_run("inquiry", inquiry, vdev, &cdb, sizeof(cdb), data, data_size); | |
111 | ||
112 | return virtio_scsi_response_ok(&resp); | |
113 | } | |
114 | ||
115 | static bool scsi_test_unit_ready(VDev *vdev) | |
116 | { | |
117 | ScsiCdbTestUnitReady cdb = { | |
118 | .command = 0x00, | |
119 | }; | |
120 | VirtioCmd test_unit_ready[] = { | |
121 | { &req, sizeof(req), VRING_DESC_F_NEXT }, | |
122 | { &resp, sizeof(resp), VRING_DESC_F_WRITE }, | |
123 | }; | |
124 | ||
125 | prepare_request(vdev, &cdb, sizeof(cdb), 0, 0); | |
126 | virtio_run(vdev, VR_REQUEST, test_unit_ready); /* ignore errors here */ | |
127 | ||
128 | return virtio_scsi_response_ok(&resp); | |
129 | } | |
130 | ||
131 | static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) | |
132 | { | |
133 | ScsiCdbReportLuns cdb = { | |
134 | .command = 0xa0, | |
135 | .select_report = 0x02, /* REPORT ALL */ | |
136 | .alloc_len = data_size, | |
137 | }; | |
138 | VirtioCmd report_luns[] = { | |
139 | { &req, sizeof(req), VRING_DESC_F_NEXT }, | |
140 | { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, | |
141 | { data, data_size, VRING_DESC_F_WRITE }, | |
142 | }; | |
143 | ||
144 | vs_run("report luns", report_luns, | |
145 | vdev, &cdb, sizeof(cdb), data, data_size); | |
146 | ||
147 | return virtio_scsi_response_ok(&resp); | |
148 | } | |
149 | ||
150 | static bool scsi_read_10(VDev *vdev, | |
98d3c524 EF |
151 | ulong sector, int sectors, void *data, |
152 | unsigned int data_size) | |
86aec22d | 153 | { |
86aec22d ED |
154 | ScsiCdbRead10 cdb = { |
155 | .command = 0x28, | |
98d3c524 EF |
156 | .lba = sector, |
157 | .xfer_length = sectors, | |
86aec22d ED |
158 | }; |
159 | VirtioCmd read_10[] = { | |
160 | { &req, sizeof(req), VRING_DESC_F_NEXT }, | |
161 | { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, | |
77c76392 | 162 | { data, data_size, VRING_DESC_F_WRITE }, |
86aec22d ED |
163 | }; |
164 | ||
165 | debug_print_int("read_10 sector", sector); | |
166 | debug_print_int("read_10 sectors", sectors); | |
167 | ||
168 | vs_run("read(10)", read_10, vdev, &cdb, sizeof(cdb), data, data_size); | |
169 | ||
170 | return virtio_scsi_response_ok(&resp); | |
171 | } | |
172 | ||
173 | static bool scsi_read_capacity(VDev *vdev, | |
174 | void *data, uint32_t data_size) | |
175 | { | |
176 | ScsiCdbReadCapacity16 cdb = { | |
177 | .command = 0x9e, /* SERVICE_ACTION_IN_16 */ | |
178 | .service_action = 0x10, /* SA_READ_CAPACITY */ | |
179 | .alloc_len = data_size, | |
180 | }; | |
181 | VirtioCmd read_capacity_16[] = { | |
182 | { &req, sizeof(req), VRING_DESC_F_NEXT }, | |
183 | { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, | |
184 | { data, data_size, VRING_DESC_F_WRITE }, | |
185 | }; | |
186 | ||
187 | vs_run("read capacity", read_capacity_16, | |
188 | vdev, &cdb, sizeof(cdb), data, data_size); | |
189 | ||
190 | return virtio_scsi_response_ok(&resp); | |
191 | } | |
192 | ||
193 | /* virtio-scsi routines */ | |
194 | ||
195 | static void virtio_scsi_locate_device(VDev *vdev) | |
196 | { | |
197 | const uint16_t channel = 0; /* again, it's what QEMU does */ | |
198 | uint16_t target; | |
199 | static uint8_t data[16 + 8 * 63]; | |
200 | ScsiLunReport *r = (void *) data; | |
201 | ScsiDevice *sdev = vdev->scsi_device; | |
202 | int i, luns; | |
203 | ||
204 | /* QEMU has hardcoded channel #0 in many places. | |
205 | * If this hardcoded value is ever changed, we'll need to add code for | |
206 | * vdev->config.scsi.max_channel != 0 here. | |
207 | */ | |
208 | debug_print_int("config.scsi.max_channel", vdev->config.scsi.max_channel); | |
209 | debug_print_int("config.scsi.max_target ", vdev->config.scsi.max_target); | |
210 | debug_print_int("config.scsi.max_lun ", vdev->config.scsi.max_lun); | |
5ffd4a3c | 211 | debug_print_int("config.scsi.max_sectors", vdev->config.scsi.max_sectors); |
86aec22d | 212 | |
b39b7718 ED |
213 | if (vdev->scsi_device_selected) { |
214 | sdev->channel = vdev->selected_scsi_device.channel; | |
215 | sdev->target = vdev->selected_scsi_device.target; | |
216 | sdev->lun = vdev->selected_scsi_device.lun; | |
217 | ||
218 | IPL_check(sdev->channel == 0, "non-zero channel requested"); | |
219 | IPL_check(sdev->target <= vdev->config.scsi.max_target, "target# high"); | |
220 | IPL_check(sdev->lun <= vdev->config.scsi.max_lun, "LUN# high"); | |
221 | return; | |
222 | } | |
223 | ||
86aec22d ED |
224 | for (target = 0; target <= vdev->config.scsi.max_target; target++) { |
225 | sdev->channel = channel; | |
8775d91a TH |
226 | sdev->target = target; |
227 | sdev->lun = 0; /* LUN has to be 0 for REPORT LUNS */ | |
86aec22d ED |
228 | if (!scsi_report_luns(vdev, data, sizeof(data))) { |
229 | if (resp.response == VIRTIO_SCSI_S_BAD_TARGET) { | |
230 | continue; | |
231 | } | |
232 | print_int("target", target); | |
233 | virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs"); | |
234 | } | |
235 | if (r->lun_list_len == 0) { | |
236 | print_int("no LUNs for target", target); | |
237 | continue; | |
238 | } | |
239 | luns = r->lun_list_len / 8; | |
240 | debug_print_int("LUNs reported", luns); | |
241 | if (luns == 1) { | |
242 | /* There is no ",lun=#" arg for -device or ",lun=0" given. | |
243 | * Hence, the only LUN reported. | |
244 | * Usually, it's 0. | |
245 | */ | |
246 | sdev->lun = r->lun[0].v16[0]; /* it's returned this way */ | |
247 | debug_print_int("Have to use LUN", sdev->lun); | |
248 | return; /* we have to use this device */ | |
249 | } | |
250 | for (i = 0; i < luns; i++) { | |
251 | if (r->lun[i].v64) { | |
252 | /* Look for non-zero LUN - we have where to choose from */ | |
253 | sdev->lun = r->lun[i].v16[0]; | |
254 | debug_print_int("Will use LUN", sdev->lun); | |
255 | return; /* we have found a device */ | |
256 | } | |
257 | } | |
258 | } | |
259 | panic("\n! Cannot locate virtio-scsi device !\n"); | |
260 | } | |
261 | ||
262 | int virtio_scsi_read_many(VDev *vdev, | |
263 | ulong sector, void *load_addr, int sec_num) | |
264 | { | |
5ffd4a3c | 265 | int sector_count; |
98d3c524 | 266 | int f = vdev->blk_factor; |
5ffd4a3c | 267 | unsigned int data_size; |
fe921fc8 EF |
268 | unsigned int max_transfer = MIN_NON_ZERO(vdev->config.scsi.max_sectors, |
269 | vdev->max_transfer); | |
5ffd4a3c EF |
270 | |
271 | do { | |
fe921fc8 | 272 | sector_count = MIN_NON_ZERO(sec_num, max_transfer); |
5ffd4a3c EF |
273 | data_size = sector_count * virtio_get_block_size() * f; |
274 | if (!scsi_read_10(vdev, sector * f, sector_count * f, load_addr, | |
275 | data_size)) { | |
276 | virtio_scsi_verify_response(&resp, "virtio-scsi:read_many"); | |
277 | } | |
278 | load_addr += data_size; | |
279 | sector += sector_count; | |
280 | sec_num -= sector_count; | |
281 | } while (sec_num > 0); | |
86aec22d ED |
282 | |
283 | return 0; | |
284 | } | |
285 | ||
286 | static bool virtio_scsi_inquiry_response_is_cdrom(void *data) | |
287 | { | |
288 | const ScsiInquiryStd *response = data; | |
289 | const int resp_data_fmt = response->b3 & 0x0f; | |
290 | int i; | |
291 | ||
292 | IPL_check(resp_data_fmt == 2, "Wrong INQUIRY response format"); | |
293 | if (resp_data_fmt != 2) { | |
294 | return false; /* cannot decode */ | |
295 | } | |
296 | ||
297 | if ((response->peripheral_qdt & 0x1f) == SCSI_INQ_RDT_CDROM) { | |
298 | return true; | |
299 | } | |
300 | ||
301 | for (i = 0; i < sizeof(response->prod_id); i++) { | |
302 | if (response->prod_id[i] != QEMU_CDROM_SIGNATURE[i]) { | |
303 | return false; | |
304 | } | |
305 | } | |
306 | return true; | |
307 | } | |
308 | ||
309 | static void scsi_parse_capacity_report(void *data, | |
310 | uint64_t *last_lba, uint32_t *lb_len) | |
311 | { | |
312 | ScsiReadCapacity16Data *p = data; | |
313 | ||
314 | if (last_lba) { | |
315 | *last_lba = p->ret_lba; | |
316 | } | |
317 | ||
318 | if (lb_len) { | |
319 | *lb_len = p->lb_len; | |
320 | } | |
321 | } | |
322 | ||
323 | void virtio_scsi_setup(VDev *vdev) | |
324 | { | |
325 | int retry_test_unit_ready = 3; | |
326 | uint8_t data[256]; | |
327 | uint32_t data_size = sizeof(data); | |
8edfe85b | 328 | ScsiInquiryEvpdPages *evpd = &scsi_inquiry_evpd_pages_response; |
fe921fc8 | 329 | ScsiInquiryEvpdBl *evpd_bl = &scsi_inquiry_evpd_bl_response; |
8edfe85b | 330 | int i; |
86aec22d ED |
331 | |
332 | vdev->scsi_device = &default_scsi_device; | |
333 | virtio_scsi_locate_device(vdev); | |
334 | ||
335 | /* We have to "ping" the device before it becomes readable */ | |
336 | while (!scsi_test_unit_ready(vdev)) { | |
337 | ||
338 | if (!virtio_scsi_response_ok(&resp)) { | |
339 | uint8_t code = resp.sense[0] & SCSI_SENSE_CODE_MASK; | |
340 | uint8_t sense_key = resp.sense[2] & SCSI_SENSE_KEY_MASK; | |
341 | ||
342 | IPL_assert(resp.sense_len != 0, "virtio-scsi:setup: no SENSE data"); | |
343 | ||
344 | IPL_assert(retry_test_unit_ready && code == 0x70 && | |
345 | sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION, | |
346 | "virtio-scsi:setup: cannot retry"); | |
347 | ||
348 | /* retry on CHECK_CONDITION/UNIT_ATTENTION as it | |
349 | * may not designate a real error, but it may be | |
350 | * a result of device reset, etc. | |
351 | */ | |
352 | retry_test_unit_ready--; | |
353 | sleep(1); | |
354 | continue; | |
355 | } | |
356 | ||
357 | virtio_scsi_verify_response(&resp, "virtio-scsi:setup"); | |
358 | } | |
359 | ||
360 | /* read and cache SCSI INQUIRY response */ | |
9c12359c EF |
361 | if (!scsi_inquiry(vdev, |
362 | SCSI_INQUIRY_STANDARD, | |
363 | SCSI_INQUIRY_STANDARD_NONE, | |
364 | scsi_inquiry_std_response, | |
86aec22d ED |
365 | sizeof(scsi_inquiry_std_response))) { |
366 | virtio_scsi_verify_response(&resp, "virtio-scsi:setup:inquiry"); | |
367 | } | |
368 | ||
369 | if (virtio_scsi_inquiry_response_is_cdrom(scsi_inquiry_std_response)) { | |
370 | sclp_print("SCSI CD-ROM detected.\n"); | |
371 | vdev->is_cdrom = true; | |
372 | vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; | |
373 | } | |
374 | ||
8edfe85b EF |
375 | if (!scsi_inquiry(vdev, |
376 | SCSI_INQUIRY_EVPD, | |
377 | SCSI_INQUIRY_EVPD_SUPPORTED_PAGES, | |
378 | evpd, | |
379 | sizeof(*evpd))) { | |
380 | virtio_scsi_verify_response(&resp, "virtio-scsi:setup:supported_pages"); | |
381 | } | |
382 | ||
383 | debug_print_int("EVPD length", evpd->page_length); | |
384 | ||
385 | for (i = 0; i <= evpd->page_length; i++) { | |
386 | debug_print_int("supported EVPD page", evpd->byte[i]); | |
fe921fc8 EF |
387 | |
388 | if (evpd->byte[i] != SCSI_INQUIRY_EVPD_BLOCK_LIMITS) { | |
389 | continue; | |
390 | } | |
391 | ||
392 | if (!scsi_inquiry(vdev, | |
393 | SCSI_INQUIRY_EVPD, | |
394 | SCSI_INQUIRY_EVPD_BLOCK_LIMITS, | |
395 | evpd_bl, | |
396 | sizeof(*evpd_bl))) { | |
397 | virtio_scsi_verify_response(&resp, "virtio-scsi:setup:blocklimits"); | |
398 | } | |
399 | ||
400 | debug_print_int("max transfer", evpd_bl->max_transfer); | |
401 | vdev->max_transfer = evpd_bl->max_transfer; | |
8edfe85b EF |
402 | } |
403 | ||
de4e3ae4 EF |
404 | /* |
405 | * The host sg driver will often be unhappy with particularly large | |
406 | * I/Os that exceed the block iovec limits. Let's enforce something | |
407 | * reasonable, despite what the device configuration tells us. | |
408 | */ | |
409 | ||
410 | vdev->max_transfer = MIN_NON_ZERO(VIRTIO_SCSI_MAX_SECTORS, | |
411 | vdev->max_transfer); | |
412 | ||
86aec22d ED |
413 | if (!scsi_read_capacity(vdev, data, data_size)) { |
414 | virtio_scsi_verify_response(&resp, "virtio-scsi:setup:read_capacity"); | |
415 | } | |
416 | scsi_parse_capacity_report(data, &vdev->scsi_last_block, | |
417 | (uint32_t *) &vdev->scsi_block_size); | |
418 | } |