]>
Commit | Line | Data |
---|---|---|
2cc977e2 TS |
1 | /* |
2 | * Generic SCSI Device support | |
3 | * | |
4 | * Copyright (c) 2007 Bull S.A.S. | |
5 | * Based on code by Paul Brook | |
6 | * Based on code by Fabrice Bellard | |
7 | * | |
8 | * Written by Laurent Vivier <[email protected]> | |
9 | * | |
8e31bf38 | 10 | * This code is licensed under the LGPL. |
2cc977e2 TS |
11 | * |
12 | */ | |
13 | ||
a4ab4792 | 14 | #include "qemu/osdep.h" |
da34e65c | 15 | #include "qapi/error.h" |
2cc977e2 | 16 | #include "qemu-common.h" |
1de7afc9 | 17 | #include "qemu/error-report.h" |
0d09e41a | 18 | #include "hw/scsi/scsi.h" |
4be74634 | 19 | #include "sysemu/block-backend.h" |
9c17d615 | 20 | #include "sysemu/blockdev.h" |
2cc977e2 | 21 | |
d52affa7 | 22 | #ifdef __linux__ |
2cc977e2 TS |
23 | |
24 | //#define DEBUG_SCSI | |
25 | ||
26 | #ifdef DEBUG_SCSI | |
001faf32 BS |
27 | #define DPRINTF(fmt, ...) \ |
28 | do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0) | |
2cc977e2 | 29 | #else |
001faf32 | 30 | #define DPRINTF(fmt, ...) do {} while(0) |
2cc977e2 TS |
31 | #endif |
32 | ||
001faf32 BS |
33 | #define BADF(fmt, ...) \ |
34 | do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) | |
2cc977e2 | 35 | |
2cc977e2 | 36 | #include <scsi/sg.h> |
0d09e41a | 37 | #include "block/scsi.h" |
2cc977e2 | 38 | |
a3b16e71 PB |
39 | #define SG_ERR_DRIVER_TIMEOUT 0x06 |
40 | #define SG_ERR_DRIVER_SENSE 0x08 | |
41 | ||
42 | #define SG_ERR_DID_OK 0x00 | |
43 | #define SG_ERR_DID_NO_CONNECT 0x01 | |
44 | #define SG_ERR_DID_BUS_BUSY 0x02 | |
45 | #define SG_ERR_DID_TIME_OUT 0x03 | |
2cc977e2 TS |
46 | |
47 | #ifndef MAX_UINT | |
48 | #define MAX_UINT ((unsigned int)-1) | |
49 | #endif | |
50 | ||
4c41d2ef GH |
51 | typedef struct SCSIGenericReq { |
52 | SCSIRequest req; | |
2cc977e2 TS |
53 | uint8_t *buf; |
54 | int buflen; | |
55 | int len; | |
56 | sg_io_hdr_t io_header; | |
4c41d2ef | 57 | } SCSIGenericReq; |
2cc977e2 | 58 | |
56b1fc48 PB |
59 | static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req) |
60 | { | |
61 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); | |
62 | ||
63 | qemu_put_sbe32s(f, &r->buflen); | |
64 | if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { | |
65 | assert(!r->req.sg); | |
66 | qemu_put_buffer(f, r->buf, r->req.cmd.xfer); | |
67 | } | |
68 | } | |
69 | ||
70 | static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req) | |
71 | { | |
72 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); | |
73 | ||
74 | qemu_get_sbe32s(f, &r->buflen); | |
75 | if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { | |
76 | assert(!r->req.sg); | |
77 | qemu_get_buffer(f, r->buf, r->req.cmd.xfer); | |
78 | } | |
79 | } | |
80 | ||
ad2d30f7 | 81 | static void scsi_free_request(SCSIRequest *req) |
2cc977e2 | 82 | { |
ad2d30f7 PB |
83 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
84 | ||
7267c094 | 85 | g_free(r->buf); |
2cc977e2 TS |
86 | } |
87 | ||
2cc977e2 | 88 | /* Helper function for command completion. */ |
fa0d653b | 89 | static void scsi_command_complete_noio(SCSIGenericReq *r, int ret) |
2cc977e2 | 90 | { |
682a9b21 | 91 | int status; |
2cc977e2 | 92 | |
fa0d653b PB |
93 | assert(r->req.aiocb == NULL); |
94 | ||
6c25fa6c | 95 | if (r->req.io_canceled) { |
d5776465 | 96 | scsi_req_cancel_complete(&r->req); |
6c25fa6c FZ |
97 | goto done; |
98 | } | |
a3b16e71 | 99 | if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { |
b45ef674 | 100 | r->req.sense_len = r->io_header.sb_len_wr; |
a3b16e71 | 101 | } |
89c0f643 | 102 | |
a1f0cce2 HR |
103 | if (ret != 0) { |
104 | switch (ret) { | |
2e7cc4d6 | 105 | case -EDOM: |
682a9b21 | 106 | status = TASK_SET_FULL; |
2e7cc4d6 | 107 | break; |
a1f0cce2 | 108 | case -ENOMEM: |
682a9b21 | 109 | status = CHECK_CONDITION; |
b45ef674 | 110 | scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE)); |
a1f0cce2 HR |
111 | break; |
112 | default: | |
682a9b21 | 113 | status = CHECK_CONDITION; |
b45ef674 | 114 | scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR)); |
a1f0cce2 HR |
115 | break; |
116 | } | |
117 | } else { | |
a3b16e71 PB |
118 | if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT || |
119 | r->io_header.host_status == SG_ERR_DID_BUS_BUSY || | |
120 | r->io_header.host_status == SG_ERR_DID_TIME_OUT || | |
121 | (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) { | |
682a9b21 | 122 | status = BUSY; |
2cc977e2 | 123 | BADF("Driver Timeout\n"); |
a3b16e71 PB |
124 | } else if (r->io_header.host_status) { |
125 | status = CHECK_CONDITION; | |
126 | scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS)); | |
682a9b21 PB |
127 | } else if (r->io_header.status) { |
128 | status = r->io_header.status; | |
b45ef674 | 129 | } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { |
682a9b21 PB |
130 | status = CHECK_CONDITION; |
131 | } else { | |
132 | status = GOOD; | |
133 | } | |
2cc977e2 | 134 | } |
89c0f643 | 135 | DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", |
682a9b21 | 136 | r, r->req.tag, status); |
ed3a34a3 | 137 | |
682a9b21 | 138 | scsi_req_complete(&r->req, status); |
6c25fa6c | 139 | done: |
3df9caf8 | 140 | scsi_req_unref(&r->req); |
2cc977e2 TS |
141 | } |
142 | ||
fa0d653b PB |
143 | static void scsi_command_complete(void *opaque, int ret) |
144 | { | |
145 | SCSIGenericReq *r = (SCSIGenericReq *)opaque; | |
146 | ||
147 | assert(r->req.aiocb != NULL); | |
148 | r->req.aiocb = NULL; | |
149 | scsi_command_complete_noio(r, ret); | |
150 | } | |
151 | ||
4be74634 | 152 | static int execute_command(BlockBackend *blk, |
4c41d2ef | 153 | SCSIGenericReq *r, int direction, |
097310b5 | 154 | BlockCompletionFunc *complete) |
2cc977e2 | 155 | { |
2cc977e2 TS |
156 | r->io_header.interface_id = 'S'; |
157 | r->io_header.dxfer_direction = direction; | |
158 | r->io_header.dxferp = r->buf; | |
159 | r->io_header.dxfer_len = r->buflen; | |
29362ebe GH |
160 | r->io_header.cmdp = r->req.cmd.buf; |
161 | r->io_header.cmd_len = r->req.cmd.len; | |
b45ef674 PB |
162 | r->io_header.mx_sb_len = sizeof(r->req.sense); |
163 | r->io_header.sbp = r->req.sense; | |
2cc977e2 TS |
164 | r->io_header.timeout = MAX_UINT; |
165 | r->io_header.usr_ptr = r; | |
166 | r->io_header.flags |= SG_FLAG_DIRECT_IO; | |
167 | ||
4be74634 | 168 | r->req.aiocb = blk_aio_ioctl(blk, SG_IO, &r->io_header, complete, r); |
d836f8d3 PH |
169 | if (r->req.aiocb == NULL) { |
170 | return -EIO; | |
171 | } | |
2cc977e2 TS |
172 | |
173 | return 0; | |
174 | } | |
175 | ||
176 | static void scsi_read_complete(void * opaque, int ret) | |
177 | { | |
4c41d2ef | 178 | SCSIGenericReq *r = (SCSIGenericReq *)opaque; |
9b6eef8a | 179 | SCSIDevice *s = r->req.dev; |
2cc977e2 TS |
180 | int len; |
181 | ||
fa0d653b | 182 | assert(r->req.aiocb != NULL); |
d33e0ce2 | 183 | r->req.aiocb = NULL; |
fa0d653b | 184 | |
6c25fa6c | 185 | if (ret || r->req.io_canceled) { |
fa0d653b | 186 | scsi_command_complete_noio(r, ret); |
2cc977e2 TS |
187 | return; |
188 | } | |
fa0d653b | 189 | |
2cc977e2 | 190 | len = r->io_header.dxfer_len - r->io_header.resid; |
4c41d2ef | 191 | DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); |
2cc977e2 TS |
192 | |
193 | r->len = -1; | |
40f16dd1 | 194 | if (len == 0) { |
fa0d653b PB |
195 | scsi_command_complete_noio(r, 0); |
196 | return; | |
197 | } | |
9b6eef8a | 198 | |
fa0d653b PB |
199 | /* Snoop READ CAPACITY output to set the blocksize. */ |
200 | if (r->req.cmd.buf[0] == READ_CAPACITY_10 && | |
201 | (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) { | |
202 | s->blocksize = ldl_be_p(&r->buf[4]); | |
203 | s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL; | |
204 | } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 && | |
205 | (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { | |
206 | s->blocksize = ldl_be_p(&r->buf[8]); | |
207 | s->max_lba = ldq_be_p(&r->buf[0]); | |
40f16dd1 | 208 | } |
fa0d653b PB |
209 | blk_set_guest_block_size(s->conf.blk, s->blocksize); |
210 | ||
0eb2baeb PB |
211 | /* Patch MODE SENSE device specific parameters if the BDS is opened |
212 | * readonly. | |
213 | */ | |
214 | if ((s->type == TYPE_DISK || s->type == TYPE_TAPE) && | |
215 | blk_is_read_only(s->conf.blk) && | |
216 | (r->req.cmd.buf[0] == MODE_SENSE || | |
217 | r->req.cmd.buf[0] == MODE_SENSE_10) && | |
218 | (r->req.cmd.buf[1] & 0x8) == 0) { | |
219 | if (r->req.cmd.buf[0] == MODE_SENSE) { | |
220 | r->buf[2] |= 0x80; | |
221 | } else { | |
222 | r->buf[3] |= 0x80; | |
223 | } | |
224 | } | |
063143d5 FZ |
225 | if (s->type == TYPE_DISK && |
226 | r->req.cmd.buf[0] == INQUIRY && | |
227 | r->req.cmd.buf[2] == 0xb0) { | |
5def6b80 EB |
228 | uint32_t max_transfer = |
229 | blk_get_max_transfer(s->conf.blk) / s->blocksize; | |
24ce9a20 | 230 | |
5def6b80 EB |
231 | assert(max_transfer); |
232 | stl_be_p(&r->buf[8], max_transfer); | |
24ce9a20 | 233 | /* Also take care of the opt xfer len. */ |
5def6b80 EB |
234 | if (ldl_be_p(&r->buf[12]) > max_transfer) { |
235 | stl_be_p(&r->buf[12], max_transfer); | |
063143d5 FZ |
236 | } |
237 | } | |
fa0d653b PB |
238 | scsi_req_data(&r->req, len); |
239 | scsi_req_unref(&r->req); | |
2cc977e2 TS |
240 | } |
241 | ||
242 | /* Read more data from scsi device into buffer. */ | |
5c6c0e51 | 243 | static void scsi_read_data(SCSIRequest *req) |
2cc977e2 | 244 | { |
5c6c0e51 | 245 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
8869e103 | 246 | SCSIDevice *s = r->req.dev; |
2cc977e2 TS |
247 | int ret; |
248 | ||
5c6c0e51 | 249 | DPRINTF("scsi_read_data 0x%x\n", req->tag); |
c9501c95 PB |
250 | |
251 | /* The request is used as the AIO opaque value, so add a ref. */ | |
252 | scsi_req_ref(&r->req); | |
2cc977e2 | 253 | if (r->len == -1) { |
fa0d653b | 254 | scsi_command_complete_noio(r, 0); |
2cc977e2 TS |
255 | return; |
256 | } | |
257 | ||
4be74634 MA |
258 | ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV, |
259 | scsi_read_complete); | |
a1f0cce2 | 260 | if (ret < 0) { |
fa0d653b | 261 | scsi_command_complete_noio(r, ret); |
2cc977e2 TS |
262 | } |
263 | } | |
264 | ||
265 | static void scsi_write_complete(void * opaque, int ret) | |
266 | { | |
4c41d2ef | 267 | SCSIGenericReq *r = (SCSIGenericReq *)opaque; |
8869e103 | 268 | SCSIDevice *s = r->req.dev; |
2cc977e2 TS |
269 | |
270 | DPRINTF("scsi_write_complete() ret = %d\n", ret); | |
fa0d653b PB |
271 | |
272 | assert(r->req.aiocb != NULL); | |
d33e0ce2 | 273 | r->req.aiocb = NULL; |
fa0d653b | 274 | |
6c25fa6c | 275 | if (ret || r->req.io_canceled) { |
fa0d653b | 276 | scsi_command_complete_noio(r, ret); |
2cc977e2 TS |
277 | return; |
278 | } | |
279 | ||
29362ebe | 280 | if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && |
8869e103 PB |
281 | s->type == TYPE_TAPE) { |
282 | s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; | |
283 | DPRINTF("block size %d\n", s->blocksize); | |
89c0f643 AJ |
284 | } |
285 | ||
fa0d653b | 286 | scsi_command_complete_noio(r, ret); |
2cc977e2 TS |
287 | } |
288 | ||
289 | /* Write data to a scsi device. Returns nonzero on failure. | |
290 | The transfer may complete asynchronously. */ | |
42741212 | 291 | static void scsi_write_data(SCSIRequest *req) |
2cc977e2 | 292 | { |
5c6c0e51 | 293 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
8869e103 | 294 | SCSIDevice *s = r->req.dev; |
2cc977e2 TS |
295 | int ret; |
296 | ||
5c6c0e51 | 297 | DPRINTF("scsi_write_data 0x%x\n", req->tag); |
2cc977e2 TS |
298 | if (r->len == 0) { |
299 | r->len = r->buflen; | |
ab9adc88 | 300 | scsi_req_data(&r->req, r->len); |
42741212 | 301 | return; |
2cc977e2 TS |
302 | } |
303 | ||
c9501c95 PB |
304 | /* The request is used as the AIO opaque value, so add a ref. */ |
305 | scsi_req_ref(&r->req); | |
4be74634 | 306 | ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete); |
a1f0cce2 | 307 | if (ret < 0) { |
fa0d653b | 308 | scsi_command_complete_noio(r, ret); |
2cc977e2 | 309 | } |
2cc977e2 TS |
310 | } |
311 | ||
312 | /* Return a pointer to the data buffer. */ | |
5c6c0e51 | 313 | static uint8_t *scsi_get_buf(SCSIRequest *req) |
2cc977e2 | 314 | { |
5c6c0e51 HR |
315 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
316 | ||
2cc977e2 TS |
317 | return r->buf; |
318 | } | |
319 | ||
2cc977e2 TS |
320 | /* Execute a scsi command. Returns the length of the data expected by the |
321 | command. This will be Positive for data transfers from the device | |
322 | (eg. disk reads), negative for transfers to the device (eg. disk writes), | |
323 | and zero if the command does not transfer any data. */ | |
324 | ||
5c6c0e51 | 325 | static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) |
2cc977e2 | 326 | { |
5c6c0e51 | 327 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
8869e103 | 328 | SCSIDevice *s = r->req.dev; |
2cc977e2 TS |
329 | int ret; |
330 | ||
aa2b1e89 BK |
331 | #ifdef DEBUG_SCSI |
332 | { | |
333 | int i; | |
334 | for (i = 1; i < r->req.cmd.len; i++) { | |
335 | printf(" 0x%02x", cmd[i]); | |
336 | } | |
337 | printf("\n"); | |
338 | } | |
339 | #endif | |
2cc977e2 | 340 | |
2ec749cb | 341 | if (r->req.cmd.xfer == 0) { |
1c3381af | 342 | g_free(r->buf); |
2cc977e2 TS |
343 | r->buflen = 0; |
344 | r->buf = NULL; | |
c9501c95 PB |
345 | /* The request is used as the AIO opaque value, so add a ref. */ |
346 | scsi_req_ref(&r->req); | |
4be74634 MA |
347 | ret = execute_command(s->conf.blk, r, SG_DXFER_NONE, |
348 | scsi_command_complete); | |
a1f0cce2 | 349 | if (ret < 0) { |
fa0d653b | 350 | scsi_command_complete_noio(r, ret); |
a1f0cce2 | 351 | return 0; |
2cc977e2 TS |
352 | } |
353 | return 0; | |
354 | } | |
355 | ||
2ec749cb | 356 | if (r->buflen != r->req.cmd.xfer) { |
1c3381af | 357 | g_free(r->buf); |
7267c094 | 358 | r->buf = g_malloc(r->req.cmd.xfer); |
2ec749cb | 359 | r->buflen = r->req.cmd.xfer; |
2cc977e2 TS |
360 | } |
361 | ||
362 | memset(r->buf, 0, r->buflen); | |
2ec749cb | 363 | r->len = r->req.cmd.xfer; |
97a06435 | 364 | if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { |
2cc977e2 | 365 | r->len = 0; |
5c6c0e51 | 366 | return -r->req.cmd.xfer; |
ad2d30f7 | 367 | } else { |
5c6c0e51 | 368 | return r->req.cmd.xfer; |
2cc977e2 | 369 | } |
2cc977e2 TS |
370 | } |
371 | ||
9fd7e859 PB |
372 | static int read_naa_id(const uint8_t *p, uint64_t *p_wwn) |
373 | { | |
374 | int i; | |
375 | ||
376 | if ((p[1] & 0xF) == 3) { | |
377 | /* NAA designator type */ | |
378 | if (p[3] != 8) { | |
379 | return -EINVAL; | |
380 | } | |
381 | *p_wwn = ldq_be_p(p + 4); | |
382 | return 0; | |
383 | } | |
384 | ||
385 | if ((p[1] & 0xF) == 8) { | |
386 | /* SCSI name string designator type */ | |
387 | if (p[3] < 20 || memcmp(&p[4], "naa.", 4)) { | |
388 | return -EINVAL; | |
389 | } | |
390 | if (p[3] > 20 && p[24] != ',') { | |
391 | return -EINVAL; | |
392 | } | |
393 | *p_wwn = 0; | |
394 | for (i = 8; i < 24; i++) { | |
395 | char c = toupper(p[i]); | |
396 | c -= (c >= '0' && c <= '9' ? '0' : 'A' - 10); | |
397 | *p_wwn = (*p_wwn << 4) | c; | |
398 | } | |
399 | return 0; | |
400 | } | |
401 | ||
402 | return -EINVAL; | |
403 | } | |
404 | ||
405 | void scsi_generic_read_device_identification(SCSIDevice *s) | |
406 | { | |
407 | uint8_t cmd[6]; | |
408 | uint8_t buf[250]; | |
409 | uint8_t sensebuf[8]; | |
410 | sg_io_hdr_t io_header; | |
411 | int ret; | |
412 | int i, len; | |
413 | ||
414 | memset(cmd, 0, sizeof(cmd)); | |
415 | memset(buf, 0, sizeof(buf)); | |
416 | cmd[0] = INQUIRY; | |
417 | cmd[1] = 1; | |
418 | cmd[2] = 0x83; | |
419 | cmd[4] = sizeof(buf); | |
420 | ||
421 | memset(&io_header, 0, sizeof(io_header)); | |
422 | io_header.interface_id = 'S'; | |
423 | io_header.dxfer_direction = SG_DXFER_FROM_DEV; | |
424 | io_header.dxfer_len = sizeof(buf); | |
425 | io_header.dxferp = buf; | |
426 | io_header.cmdp = cmd; | |
427 | io_header.cmd_len = sizeof(cmd); | |
428 | io_header.mx_sb_len = sizeof(sensebuf); | |
429 | io_header.sbp = sensebuf; | |
430 | io_header.timeout = 6000; /* XXX */ | |
431 | ||
432 | ret = blk_ioctl(s->conf.blk, SG_IO, &io_header); | |
433 | if (ret < 0 || io_header.driver_status || io_header.host_status) { | |
434 | return; | |
435 | } | |
436 | ||
437 | len = MIN((buf[2] << 8) | buf[3], sizeof(buf) - 4); | |
438 | for (i = 0; i + 3 <= len; ) { | |
439 | const uint8_t *p = &buf[i + 4]; | |
440 | uint64_t wwn; | |
441 | ||
442 | if (i + (p[3] + 4) > len) { | |
443 | break; | |
444 | } | |
445 | ||
446 | if ((p[1] & 0x10) == 0) { | |
447 | /* Associated with the logical unit */ | |
448 | if (read_naa_id(p, &wwn) == 0) { | |
449 | s->wwn = wwn; | |
450 | } | |
451 | } else if ((p[1] & 0x10) == 0x10) { | |
452 | /* Associated with the target port */ | |
453 | if (read_naa_id(p, &wwn) == 0) { | |
454 | s->port_wwn = wwn; | |
455 | } | |
456 | } | |
457 | ||
458 | i += p[3] + 4; | |
459 | } | |
460 | } | |
461 | ||
4be74634 | 462 | static int get_stream_blocksize(BlockBackend *blk) |
89c0f643 AJ |
463 | { |
464 | uint8_t cmd[6]; | |
465 | uint8_t buf[12]; | |
466 | uint8_t sensebuf[8]; | |
467 | sg_io_hdr_t io_header; | |
468 | int ret; | |
469 | ||
470 | memset(cmd, 0, sizeof(cmd)); | |
471 | memset(buf, 0, sizeof(buf)); | |
472 | cmd[0] = MODE_SENSE; | |
473 | cmd[4] = sizeof(buf); | |
474 | ||
475 | memset(&io_header, 0, sizeof(io_header)); | |
476 | io_header.interface_id = 'S'; | |
477 | io_header.dxfer_direction = SG_DXFER_FROM_DEV; | |
478 | io_header.dxfer_len = sizeof(buf); | |
479 | io_header.dxferp = buf; | |
480 | io_header.cmdp = cmd; | |
481 | io_header.cmd_len = sizeof(cmd); | |
482 | io_header.mx_sb_len = sizeof(sensebuf); | |
483 | io_header.sbp = sensebuf; | |
484 | io_header.timeout = 6000; /* XXX */ | |
485 | ||
4be74634 | 486 | ret = blk_ioctl(blk, SG_IO, &io_header); |
fe0ed712 | 487 | if (ret < 0 || io_header.driver_status || io_header.host_status) { |
89c0f643 | 488 | return -1; |
fe0ed712 | 489 | } |
89c0f643 AJ |
490 | return (buf[9] << 16) | (buf[10] << 8) | buf[11]; |
491 | } | |
492 | ||
f8b6d672 BK |
493 | static void scsi_generic_reset(DeviceState *dev) |
494 | { | |
b9eea3e6 | 495 | SCSIDevice *s = SCSI_DEVICE(dev); |
f8b6d672 | 496 | |
8869e103 | 497 | scsi_device_purge_requests(s, SENSE_CODE(RESET)); |
f8b6d672 BK |
498 | } |
499 | ||
a818a4b6 | 500 | static void scsi_generic_realize(SCSIDevice *s, Error **errp) |
2cc977e2 | 501 | { |
6ee143a0 | 502 | int rc; |
2cc977e2 | 503 | int sg_version; |
2cc977e2 TS |
504 | struct sg_scsi_id scsiid; |
505 | ||
4be74634 | 506 | if (!s->conf.blk) { |
a818a4b6 FZ |
507 | error_setg(errp, "drive property not set"); |
508 | return; | |
d52affa7 | 509 | } |
2cc977e2 | 510 | |
4be74634 | 511 | if (blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) { |
a818a4b6 FZ |
512 | error_setg(errp, "Device doesn't support drive option werror"); |
513 | return; | |
620f862e | 514 | } |
4be74634 | 515 | if (blk_get_on_error(s->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { |
a818a4b6 FZ |
516 | error_setg(errp, "Device doesn't support drive option rerror"); |
517 | return; | |
620f862e MA |
518 | } |
519 | ||
2cc977e2 | 520 | /* check we are using a driver managing SG_IO (version 3 and after */ |
4be74634 | 521 | rc = blk_ioctl(s->conf.blk, SG_GET_VERSION_NUM, &sg_version); |
6ee143a0 | 522 | if (rc < 0) { |
a818a4b6 FZ |
523 | error_setg(errp, "cannot get SG_IO version number: %s. " |
524 | "Is this a SCSI device?", | |
525 | strerror(-rc)); | |
526 | return; | |
98392453 RS |
527 | } |
528 | if (sg_version < 30000) { | |
a818a4b6 FZ |
529 | error_setg(errp, "scsi generic interface too old"); |
530 | return; | |
d52affa7 | 531 | } |
2cc977e2 TS |
532 | |
533 | /* get LUN of the /dev/sg? */ | |
4be74634 | 534 | if (blk_ioctl(s->conf.blk, SG_GET_SCSI_ID, &scsiid)) { |
a818a4b6 FZ |
535 | error_setg(errp, "SG_GET_SCSI_ID ioctl failed"); |
536 | return; | |
d52affa7 | 537 | } |
2cc977e2 TS |
538 | |
539 | /* define device state */ | |
8869e103 PB |
540 | s->type = scsiid.scsi_type; |
541 | DPRINTF("device type %d\n", s->type); | |
28b77657 | 542 | |
9b6eef8a PB |
543 | switch (s->type) { |
544 | case TYPE_TAPE: | |
4be74634 | 545 | s->blocksize = get_stream_blocksize(s->conf.blk); |
8869e103 PB |
546 | if (s->blocksize == -1) { |
547 | s->blocksize = 0; | |
548 | } | |
9b6eef8a PB |
549 | break; |
550 | ||
551 | /* Make a guess for block devices, we'll fix it when the guest sends. | |
552 | * READ CAPACITY. If they don't, they likely would assume these sizes | |
553 | * anyway. (TODO: they could also send MODE SENSE). | |
554 | */ | |
555 | case TYPE_ROM: | |
556 | case TYPE_WORM: | |
557 | s->blocksize = 2048; | |
558 | break; | |
559 | default: | |
560 | s->blocksize = 512; | |
561 | break; | |
89c0f643 | 562 | } |
8869e103 PB |
563 | |
564 | DPRINTF("block size %d\n", s->blocksize); | |
9fd7e859 PB |
565 | |
566 | scsi_generic_read_device_identification(s); | |
d52affa7 | 567 | } |
2cc977e2 | 568 | |
765d1525 | 569 | const SCSIReqOps scsi_generic_req_ops = { |
8dbd4574 | 570 | .size = sizeof(SCSIGenericReq), |
12010e7b PB |
571 | .free_req = scsi_free_request, |
572 | .send_command = scsi_send_command, | |
573 | .read_data = scsi_read_data, | |
574 | .write_data = scsi_write_data, | |
12010e7b | 575 | .get_buf = scsi_get_buf, |
56b1fc48 PB |
576 | .load_request = scsi_generic_load_request, |
577 | .save_request = scsi_generic_save_request, | |
8dbd4574 PB |
578 | }; |
579 | ||
580 | static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, | |
63db0f0e | 581 | uint8_t *buf, void *hba_private) |
8dbd4574 | 582 | { |
9be38598 | 583 | return scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private); |
8dbd4574 PB |
584 | } |
585 | ||
39bffca2 | 586 | static Property scsi_generic_properties[] = { |
4be74634 | 587 | DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk), |
39bffca2 AL |
588 | DEFINE_PROP_END_OF_LIST(), |
589 | }; | |
590 | ||
3e7e180a PB |
591 | static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, |
592 | uint8_t *buf, void *hba_private) | |
593 | { | |
594 | return scsi_bus_parse_cdb(dev, cmd, buf, hba_private); | |
595 | } | |
596 | ||
b9eea3e6 AL |
597 | static void scsi_generic_class_initfn(ObjectClass *klass, void *data) |
598 | { | |
39bffca2 | 599 | DeviceClass *dc = DEVICE_CLASS(klass); |
b9eea3e6 AL |
600 | SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); |
601 | ||
a818a4b6 | 602 | sc->realize = scsi_generic_realize; |
b9eea3e6 | 603 | sc->alloc_req = scsi_new_request; |
3e7e180a | 604 | sc->parse_cdb = scsi_generic_parse_cdb; |
39bffca2 AL |
605 | dc->fw_name = "disk"; |
606 | dc->desc = "pass through generic scsi device (/dev/sg*)"; | |
607 | dc->reset = scsi_generic_reset; | |
608 | dc->props = scsi_generic_properties; | |
56b1fc48 | 609 | dc->vmsd = &vmstate_scsi_device; |
b9eea3e6 AL |
610 | } |
611 | ||
8c43a6f0 | 612 | static const TypeInfo scsi_generic_info = { |
39bffca2 AL |
613 | .name = "scsi-generic", |
614 | .parent = TYPE_SCSI_DEVICE, | |
615 | .instance_size = sizeof(SCSIDevice), | |
616 | .class_init = scsi_generic_class_initfn, | |
d52affa7 | 617 | }; |
2cc977e2 | 618 | |
83f7d43a | 619 | static void scsi_generic_register_types(void) |
d52affa7 | 620 | { |
39bffca2 | 621 | type_register_static(&scsi_generic_info); |
2cc977e2 | 622 | } |
83f7d43a AF |
623 | |
624 | type_init(scsi_generic_register_types) | |
d52affa7 | 625 | |
2cc977e2 | 626 | #endif /* __linux__ */ |