]>
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 <jno@linux.vnet.ibm.com> | |
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 | ||
12 | #include "s390-ccw.h" | |
13 | #include "virtio.h" | |
14 | #include "scsi.h" | |
15 | #include "virtio-scsi.h" | |
16 | ||
17 | static ScsiDevice default_scsi_device; | |
18 | static VirtioScsiCmdReq req; | |
19 | static VirtioScsiCmdResp resp; | |
20 | ||
21 | static uint8_t scsi_inquiry_std_response[256]; | |
22 | ||
23 | static inline void vs_assert(bool term, const char **msgs) | |
24 | { | |
25 | if (!term) { | |
26 | int i = 0; | |
27 | ||
28 | sclp_print("\n! "); | |
29 | while (msgs[i]) { | |
30 | sclp_print(msgs[i++]); | |
31 | } | |
32 | panic(" !\n"); | |
33 | } | |
34 | } | |
35 | ||
36 | static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp, | |
37 | const char *title) | |
38 | { | |
39 | const char *mr[] = { | |
40 | title, ": response ", virtio_scsi_response_msg(resp), 0 | |
41 | }; | |
42 | const char *ms[] = { | |
43 | title, | |
44 | CDB_STATUS_VALID(resp->status) ? ": " : ": invalid ", | |
45 | scsi_cdb_status_msg(resp->status), | |
46 | resp->status == CDB_STATUS_CHECK_CONDITION ? " " : 0, | |
47 | resp->sense_len ? scsi_cdb_asc_msg(resp->sense) | |
48 | : "no sense data", | |
49 | scsi_sense_response(resp->sense) == 0x70 ? ", sure" : "?", | |
50 | 0 | |
51 | }; | |
52 | ||
53 | vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr); | |
54 | vs_assert(resp->status == CDB_STATUS_GOOD, ms); | |
55 | } | |
56 | ||
57 | static void prepare_request(VDev *vdev, const void *cdb, int cdb_size, | |
58 | void *data, uint32_t data_size) | |
59 | { | |
60 | const ScsiDevice *sdev = vdev->scsi_device; | |
61 | ||
62 | memset(&req, 0, sizeof(req)); | |
63 | req.lun = make_lun(sdev->channel, sdev->target, sdev->lun); | |
64 | memcpy(&req.cdb, cdb, cdb_size); | |
65 | ||
66 | memset(&resp, 0, sizeof(resp)); | |
67 | resp.status = 0xff; /* set invalid */ | |
68 | resp.response = 0xff; /* */ | |
69 | ||
70 | if (data && data_size) { | |
71 | memset(data, 0, data_size); | |
72 | } | |
73 | } | |
74 | ||
75 | static inline void vs_io_assert(bool term, const char *msg) | |
76 | { | |
77 | if (!term) { | |
78 | virtio_scsi_verify_response(&resp, msg); | |
79 | } | |
80 | } | |
81 | ||
82 | static void vs_run(const char *title, VirtioCmd *cmd, VDev *vdev, | |
83 | const void *cdb, int cdb_size, | |
84 | void *data, uint32_t data_size) | |
85 | { | |
86 | prepare_request(vdev, cdb, cdb_size, data, data_size); | |
87 | vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title); | |
88 | } | |
89 | ||
90 | /* SCSI protocol implementation routines */ | |
91 | ||
92 | static bool scsi_inquiry(VDev *vdev, void *data, uint32_t data_size) | |
93 | { | |
94 | ScsiCdbInquiry cdb = { | |
95 | .command = 0x12, | |
96 | .alloc_len = data_size < 65535 ? data_size : 65535, | |
97 | }; | |
98 | VirtioCmd inquiry[] = { | |
99 | { &req, sizeof(req), VRING_DESC_F_NEXT }, | |
100 | { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, | |
101 | { data, data_size, VRING_DESC_F_WRITE }, | |
102 | }; | |
103 | ||
104 | vs_run("inquiry", inquiry, vdev, &cdb, sizeof(cdb), data, data_size); | |
105 | ||
106 | return virtio_scsi_response_ok(&resp); | |
107 | } | |
108 | ||
109 | static bool scsi_test_unit_ready(VDev *vdev) | |
110 | { | |
111 | ScsiCdbTestUnitReady cdb = { | |
112 | .command = 0x00, | |
113 | }; | |
114 | VirtioCmd test_unit_ready[] = { | |
115 | { &req, sizeof(req), VRING_DESC_F_NEXT }, | |
116 | { &resp, sizeof(resp), VRING_DESC_F_WRITE }, | |
117 | }; | |
118 | ||
119 | prepare_request(vdev, &cdb, sizeof(cdb), 0, 0); | |
120 | virtio_run(vdev, VR_REQUEST, test_unit_ready); /* ignore errors here */ | |
121 | ||
122 | return virtio_scsi_response_ok(&resp); | |
123 | } | |
124 | ||
125 | static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) | |
126 | { | |
127 | ScsiCdbReportLuns cdb = { | |
128 | .command = 0xa0, | |
129 | .select_report = 0x02, /* REPORT ALL */ | |
130 | .alloc_len = data_size, | |
131 | }; | |
132 | VirtioCmd report_luns[] = { | |
133 | { &req, sizeof(req), VRING_DESC_F_NEXT }, | |
134 | { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, | |
135 | { data, data_size, VRING_DESC_F_WRITE }, | |
136 | }; | |
137 | ||
138 | vs_run("report luns", report_luns, | |
139 | vdev, &cdb, sizeof(cdb), data, data_size); | |
140 | ||
141 | return virtio_scsi_response_ok(&resp); | |
142 | } | |
143 | ||
144 | static bool scsi_read_10(VDev *vdev, | |
145 | ulong sector, int sectors, void *data) | |
146 | { | |
147 | int f = vdev->blk_factor; | |
148 | unsigned int data_size = sectors * virtio_get_block_size() * f; | |
149 | ScsiCdbRead10 cdb = { | |
150 | .command = 0x28, | |
151 | .lba = sector * f, | |
152 | .xfer_length = sectors * f, | |
153 | }; | |
154 | VirtioCmd read_10[] = { | |
155 | { &req, sizeof(req), VRING_DESC_F_NEXT }, | |
156 | { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, | |
157 | { data, data_size * f, VRING_DESC_F_WRITE }, | |
158 | }; | |
159 | ||
160 | debug_print_int("read_10 sector", sector); | |
161 | debug_print_int("read_10 sectors", sectors); | |
162 | ||
163 | vs_run("read(10)", read_10, vdev, &cdb, sizeof(cdb), data, data_size); | |
164 | ||
165 | return virtio_scsi_response_ok(&resp); | |
166 | } | |
167 | ||
168 | static bool scsi_read_capacity(VDev *vdev, | |
169 | void *data, uint32_t data_size) | |
170 | { | |
171 | ScsiCdbReadCapacity16 cdb = { | |
172 | .command = 0x9e, /* SERVICE_ACTION_IN_16 */ | |
173 | .service_action = 0x10, /* SA_READ_CAPACITY */ | |
174 | .alloc_len = data_size, | |
175 | }; | |
176 | VirtioCmd read_capacity_16[] = { | |
177 | { &req, sizeof(req), VRING_DESC_F_NEXT }, | |
178 | { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, | |
179 | { data, data_size, VRING_DESC_F_WRITE }, | |
180 | }; | |
181 | ||
182 | vs_run("read capacity", read_capacity_16, | |
183 | vdev, &cdb, sizeof(cdb), data, data_size); | |
184 | ||
185 | return virtio_scsi_response_ok(&resp); | |
186 | } | |
187 | ||
188 | /* virtio-scsi routines */ | |
189 | ||
190 | static void virtio_scsi_locate_device(VDev *vdev) | |
191 | { | |
192 | const uint16_t channel = 0; /* again, it's what QEMU does */ | |
193 | uint16_t target; | |
194 | static uint8_t data[16 + 8 * 63]; | |
195 | ScsiLunReport *r = (void *) data; | |
196 | ScsiDevice *sdev = vdev->scsi_device; | |
197 | int i, luns; | |
198 | ||
199 | /* QEMU has hardcoded channel #0 in many places. | |
200 | * If this hardcoded value is ever changed, we'll need to add code for | |
201 | * vdev->config.scsi.max_channel != 0 here. | |
202 | */ | |
203 | debug_print_int("config.scsi.max_channel", vdev->config.scsi.max_channel); | |
204 | debug_print_int("config.scsi.max_target ", vdev->config.scsi.max_target); | |
205 | debug_print_int("config.scsi.max_lun ", vdev->config.scsi.max_lun); | |
206 | ||
b39b7718 ED |
207 | if (vdev->scsi_device_selected) { |
208 | sdev->channel = vdev->selected_scsi_device.channel; | |
209 | sdev->target = vdev->selected_scsi_device.target; | |
210 | sdev->lun = vdev->selected_scsi_device.lun; | |
211 | ||
212 | IPL_check(sdev->channel == 0, "non-zero channel requested"); | |
213 | IPL_check(sdev->target <= vdev->config.scsi.max_target, "target# high"); | |
214 | IPL_check(sdev->lun <= vdev->config.scsi.max_lun, "LUN# high"); | |
215 | return; | |
216 | } | |
217 | ||
86aec22d ED |
218 | for (target = 0; target <= vdev->config.scsi.max_target; target++) { |
219 | sdev->channel = channel; | |
220 | sdev->target = target; /* sdev->lun will be 0 here */ | |
221 | if (!scsi_report_luns(vdev, data, sizeof(data))) { | |
222 | if (resp.response == VIRTIO_SCSI_S_BAD_TARGET) { | |
223 | continue; | |
224 | } | |
225 | print_int("target", target); | |
226 | virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs"); | |
227 | } | |
228 | if (r->lun_list_len == 0) { | |
229 | print_int("no LUNs for target", target); | |
230 | continue; | |
231 | } | |
232 | luns = r->lun_list_len / 8; | |
233 | debug_print_int("LUNs reported", luns); | |
234 | if (luns == 1) { | |
235 | /* There is no ",lun=#" arg for -device or ",lun=0" given. | |
236 | * Hence, the only LUN reported. | |
237 | * Usually, it's 0. | |
238 | */ | |
239 | sdev->lun = r->lun[0].v16[0]; /* it's returned this way */ | |
240 | debug_print_int("Have to use LUN", sdev->lun); | |
241 | return; /* we have to use this device */ | |
242 | } | |
243 | for (i = 0; i < luns; i++) { | |
244 | if (r->lun[i].v64) { | |
245 | /* Look for non-zero LUN - we have where to choose from */ | |
246 | sdev->lun = r->lun[i].v16[0]; | |
247 | debug_print_int("Will use LUN", sdev->lun); | |
248 | return; /* we have found a device */ | |
249 | } | |
250 | } | |
251 | } | |
252 | panic("\n! Cannot locate virtio-scsi device !\n"); | |
253 | } | |
254 | ||
255 | int virtio_scsi_read_many(VDev *vdev, | |
256 | ulong sector, void *load_addr, int sec_num) | |
257 | { | |
258 | if (!scsi_read_10(vdev, sector, sec_num, load_addr)) { | |
259 | virtio_scsi_verify_response(&resp, "virtio-scsi:read_many"); | |
260 | } | |
261 | ||
262 | return 0; | |
263 | } | |
264 | ||
265 | static bool virtio_scsi_inquiry_response_is_cdrom(void *data) | |
266 | { | |
267 | const ScsiInquiryStd *response = data; | |
268 | const int resp_data_fmt = response->b3 & 0x0f; | |
269 | int i; | |
270 | ||
271 | IPL_check(resp_data_fmt == 2, "Wrong INQUIRY response format"); | |
272 | if (resp_data_fmt != 2) { | |
273 | return false; /* cannot decode */ | |
274 | } | |
275 | ||
276 | if ((response->peripheral_qdt & 0x1f) == SCSI_INQ_RDT_CDROM) { | |
277 | return true; | |
278 | } | |
279 | ||
280 | for (i = 0; i < sizeof(response->prod_id); i++) { | |
281 | if (response->prod_id[i] != QEMU_CDROM_SIGNATURE[i]) { | |
282 | return false; | |
283 | } | |
284 | } | |
285 | return true; | |
286 | } | |
287 | ||
288 | static void scsi_parse_capacity_report(void *data, | |
289 | uint64_t *last_lba, uint32_t *lb_len) | |
290 | { | |
291 | ScsiReadCapacity16Data *p = data; | |
292 | ||
293 | if (last_lba) { | |
294 | *last_lba = p->ret_lba; | |
295 | } | |
296 | ||
297 | if (lb_len) { | |
298 | *lb_len = p->lb_len; | |
299 | } | |
300 | } | |
301 | ||
302 | void virtio_scsi_setup(VDev *vdev) | |
303 | { | |
304 | int retry_test_unit_ready = 3; | |
305 | uint8_t data[256]; | |
306 | uint32_t data_size = sizeof(data); | |
307 | ||
308 | vdev->scsi_device = &default_scsi_device; | |
309 | virtio_scsi_locate_device(vdev); | |
310 | ||
311 | /* We have to "ping" the device before it becomes readable */ | |
312 | while (!scsi_test_unit_ready(vdev)) { | |
313 | ||
314 | if (!virtio_scsi_response_ok(&resp)) { | |
315 | uint8_t code = resp.sense[0] & SCSI_SENSE_CODE_MASK; | |
316 | uint8_t sense_key = resp.sense[2] & SCSI_SENSE_KEY_MASK; | |
317 | ||
318 | IPL_assert(resp.sense_len != 0, "virtio-scsi:setup: no SENSE data"); | |
319 | ||
320 | IPL_assert(retry_test_unit_ready && code == 0x70 && | |
321 | sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION, | |
322 | "virtio-scsi:setup: cannot retry"); | |
323 | ||
324 | /* retry on CHECK_CONDITION/UNIT_ATTENTION as it | |
325 | * may not designate a real error, but it may be | |
326 | * a result of device reset, etc. | |
327 | */ | |
328 | retry_test_unit_ready--; | |
329 | sleep(1); | |
330 | continue; | |
331 | } | |
332 | ||
333 | virtio_scsi_verify_response(&resp, "virtio-scsi:setup"); | |
334 | } | |
335 | ||
336 | /* read and cache SCSI INQUIRY response */ | |
337 | if (!scsi_inquiry(vdev, scsi_inquiry_std_response, | |
338 | sizeof(scsi_inquiry_std_response))) { | |
339 | virtio_scsi_verify_response(&resp, "virtio-scsi:setup:inquiry"); | |
340 | } | |
341 | ||
342 | if (virtio_scsi_inquiry_response_is_cdrom(scsi_inquiry_std_response)) { | |
343 | sclp_print("SCSI CD-ROM detected.\n"); | |
344 | vdev->is_cdrom = true; | |
345 | vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; | |
346 | } | |
347 | ||
348 | if (!scsi_read_capacity(vdev, data, data_size)) { | |
349 | virtio_scsi_verify_response(&resp, "virtio-scsi:setup:read_capacity"); | |
350 | } | |
351 | scsi_parse_capacity_report(data, &vdev->scsi_last_block, | |
352 | (uint32_t *) &vdev->scsi_block_size); | |
353 | } |