]> git.proxmox.com Git - qemu.git/blob - hw/scsi-generic.c
target-i386: move tcg initialization into x86_cpu_initfn()
[qemu.git] / hw / scsi-generic.c
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 <Laurent.Vivier@bull.net>
9 *
10 * This code is licensed under the LGPL.
11 *
12 */
13
14 #include "qemu-common.h"
15 #include "qemu-error.h"
16 #include "scsi.h"
17 #include "blockdev.h"
18
19 #ifdef __linux__
20
21 //#define DEBUG_SCSI
22
23 #ifdef DEBUG_SCSI
24 #define DPRINTF(fmt, ...) \
25 do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
26 #else
27 #define DPRINTF(fmt, ...) do {} while(0)
28 #endif
29
30 #define BADF(fmt, ...) \
31 do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
32
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <scsi/sg.h>
38 #include "scsi-defs.h"
39
40 #define SCSI_SENSE_BUF_SIZE 96
41
42 #define SG_ERR_DRIVER_TIMEOUT 0x06
43 #define SG_ERR_DRIVER_SENSE 0x08
44
45 #define SG_ERR_DID_OK 0x00
46 #define SG_ERR_DID_NO_CONNECT 0x01
47 #define SG_ERR_DID_BUS_BUSY 0x02
48 #define SG_ERR_DID_TIME_OUT 0x03
49
50 #ifndef MAX_UINT
51 #define MAX_UINT ((unsigned int)-1)
52 #endif
53
54 typedef struct SCSIGenericReq {
55 SCSIRequest req;
56 uint8_t *buf;
57 int buflen;
58 int len;
59 sg_io_hdr_t io_header;
60 } SCSIGenericReq;
61
62 static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
63 {
64 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
65
66 qemu_put_sbe32s(f, &r->buflen);
67 if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
68 assert(!r->req.sg);
69 qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
70 }
71 }
72
73 static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
74 {
75 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
76
77 qemu_get_sbe32s(f, &r->buflen);
78 if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
79 assert(!r->req.sg);
80 qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
81 }
82 }
83
84 static void scsi_free_request(SCSIRequest *req)
85 {
86 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
87
88 g_free(r->buf);
89 }
90
91 /* Helper function for command completion. */
92 static void scsi_command_complete(void *opaque, int ret)
93 {
94 int status;
95 SCSIGenericReq *r = (SCSIGenericReq *)opaque;
96
97 r->req.aiocb = NULL;
98 if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
99 r->req.sense_len = r->io_header.sb_len_wr;
100 }
101
102 if (ret != 0) {
103 switch (ret) {
104 case -EDOM:
105 status = TASK_SET_FULL;
106 break;
107 case -ENOMEM:
108 status = CHECK_CONDITION;
109 scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE));
110 break;
111 default:
112 status = CHECK_CONDITION;
113 scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR));
114 break;
115 }
116 } else {
117 if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT ||
118 r->io_header.host_status == SG_ERR_DID_BUS_BUSY ||
119 r->io_header.host_status == SG_ERR_DID_TIME_OUT ||
120 (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) {
121 status = BUSY;
122 BADF("Driver Timeout\n");
123 } else if (r->io_header.host_status) {
124 status = CHECK_CONDITION;
125 scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS));
126 } else if (r->io_header.status) {
127 status = r->io_header.status;
128 } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
129 status = CHECK_CONDITION;
130 } else {
131 status = GOOD;
132 }
133 }
134 DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
135 r, r->req.tag, status);
136
137 scsi_req_complete(&r->req, status);
138 if (!r->req.io_canceled) {
139 scsi_req_unref(&r->req);
140 }
141 }
142
143 /* Cancel a pending data transfer. */
144 static void scsi_cancel_io(SCSIRequest *req)
145 {
146 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
147
148 DPRINTF("Cancel tag=0x%x\n", req->tag);
149 if (r->req.aiocb) {
150 bdrv_aio_cancel(r->req.aiocb);
151
152 /* This reference was left in by scsi_*_data. We take ownership of
153 * it independent of whether bdrv_aio_cancel completes the request
154 * or not. */
155 scsi_req_unref(&r->req);
156 }
157 r->req.aiocb = NULL;
158 }
159
160 static int execute_command(BlockDriverState *bdrv,
161 SCSIGenericReq *r, int direction,
162 BlockDriverCompletionFunc *complete)
163 {
164 r->io_header.interface_id = 'S';
165 r->io_header.dxfer_direction = direction;
166 r->io_header.dxferp = r->buf;
167 r->io_header.dxfer_len = r->buflen;
168 r->io_header.cmdp = r->req.cmd.buf;
169 r->io_header.cmd_len = r->req.cmd.len;
170 r->io_header.mx_sb_len = sizeof(r->req.sense);
171 r->io_header.sbp = r->req.sense;
172 r->io_header.timeout = MAX_UINT;
173 r->io_header.usr_ptr = r;
174 r->io_header.flags |= SG_FLAG_DIRECT_IO;
175
176 r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
177
178 return 0;
179 }
180
181 static void scsi_read_complete(void * opaque, int ret)
182 {
183 SCSIGenericReq *r = (SCSIGenericReq *)opaque;
184 SCSIDevice *s = r->req.dev;
185 int len;
186
187 r->req.aiocb = NULL;
188 if (ret) {
189 DPRINTF("IO error ret %d\n", ret);
190 scsi_command_complete(r, ret);
191 return;
192 }
193 len = r->io_header.dxfer_len - r->io_header.resid;
194 DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
195
196 r->len = -1;
197 if (len == 0) {
198 scsi_command_complete(r, 0);
199 } else {
200 /* Snoop READ CAPACITY output to set the blocksize. */
201 if (r->req.cmd.buf[0] == READ_CAPACITY_10) {
202 s->blocksize = ldl_be_p(&r->buf[4]);
203 s->max_lba = ldl_be_p(&r->buf[0]);
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]);
208 }
209 bdrv_set_buffer_alignment(s->conf.bs, s->blocksize);
210
211 scsi_req_data(&r->req, len);
212 if (!r->req.io_canceled) {
213 scsi_req_unref(&r->req);
214 }
215 }
216 }
217
218 /* Read more data from scsi device into buffer. */
219 static void scsi_read_data(SCSIRequest *req)
220 {
221 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
222 SCSIDevice *s = r->req.dev;
223 int ret;
224
225 DPRINTF("scsi_read_data 0x%x\n", req->tag);
226
227 /* The request is used as the AIO opaque value, so add a ref. */
228 scsi_req_ref(&r->req);
229 if (r->len == -1) {
230 scsi_command_complete(r, 0);
231 return;
232 }
233
234 ret = execute_command(s->conf.bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
235 if (ret < 0) {
236 scsi_command_complete(r, ret);
237 }
238 }
239
240 static void scsi_write_complete(void * opaque, int ret)
241 {
242 SCSIGenericReq *r = (SCSIGenericReq *)opaque;
243 SCSIDevice *s = r->req.dev;
244
245 DPRINTF("scsi_write_complete() ret = %d\n", ret);
246 r->req.aiocb = NULL;
247 if (ret) {
248 DPRINTF("IO error\n");
249 scsi_command_complete(r, ret);
250 return;
251 }
252
253 if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
254 s->type == TYPE_TAPE) {
255 s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
256 DPRINTF("block size %d\n", s->blocksize);
257 }
258
259 scsi_command_complete(r, ret);
260 }
261
262 /* Write data to a scsi device. Returns nonzero on failure.
263 The transfer may complete asynchronously. */
264 static void scsi_write_data(SCSIRequest *req)
265 {
266 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
267 SCSIDevice *s = r->req.dev;
268 int ret;
269
270 DPRINTF("scsi_write_data 0x%x\n", req->tag);
271 if (r->len == 0) {
272 r->len = r->buflen;
273 scsi_req_data(&r->req, r->len);
274 return;
275 }
276
277 /* The request is used as the AIO opaque value, so add a ref. */
278 scsi_req_ref(&r->req);
279 ret = execute_command(s->conf.bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
280 if (ret < 0) {
281 scsi_command_complete(r, ret);
282 }
283 }
284
285 /* Return a pointer to the data buffer. */
286 static uint8_t *scsi_get_buf(SCSIRequest *req)
287 {
288 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
289
290 return r->buf;
291 }
292
293 /* Execute a scsi command. Returns the length of the data expected by the
294 command. This will be Positive for data transfers from the device
295 (eg. disk reads), negative for transfers to the device (eg. disk writes),
296 and zero if the command does not transfer any data. */
297
298 static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
299 {
300 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
301 SCSIDevice *s = r->req.dev;
302 int ret;
303
304 DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
305 r->req.cmd.xfer, cmd[0]);
306
307 #ifdef DEBUG_SCSI
308 {
309 int i;
310 for (i = 1; i < r->req.cmd.len; i++) {
311 printf(" 0x%02x", cmd[i]);
312 }
313 printf("\n");
314 }
315 #endif
316
317 if (r->req.cmd.xfer == 0) {
318 if (r->buf != NULL)
319 g_free(r->buf);
320 r->buflen = 0;
321 r->buf = NULL;
322 /* The request is used as the AIO opaque value, so add a ref. */
323 scsi_req_ref(&r->req);
324 ret = execute_command(s->conf.bs, r, SG_DXFER_NONE, scsi_command_complete);
325 if (ret < 0) {
326 scsi_command_complete(r, ret);
327 return 0;
328 }
329 return 0;
330 }
331
332 if (r->buflen != r->req.cmd.xfer) {
333 if (r->buf != NULL)
334 g_free(r->buf);
335 r->buf = g_malloc(r->req.cmd.xfer);
336 r->buflen = r->req.cmd.xfer;
337 }
338
339 memset(r->buf, 0, r->buflen);
340 r->len = r->req.cmd.xfer;
341 if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
342 r->len = 0;
343 return -r->req.cmd.xfer;
344 } else {
345 return r->req.cmd.xfer;
346 }
347 }
348
349 static int get_stream_blocksize(BlockDriverState *bdrv)
350 {
351 uint8_t cmd[6];
352 uint8_t buf[12];
353 uint8_t sensebuf[8];
354 sg_io_hdr_t io_header;
355 int ret;
356
357 memset(cmd, 0, sizeof(cmd));
358 memset(buf, 0, sizeof(buf));
359 cmd[0] = MODE_SENSE;
360 cmd[4] = sizeof(buf);
361
362 memset(&io_header, 0, sizeof(io_header));
363 io_header.interface_id = 'S';
364 io_header.dxfer_direction = SG_DXFER_FROM_DEV;
365 io_header.dxfer_len = sizeof(buf);
366 io_header.dxferp = buf;
367 io_header.cmdp = cmd;
368 io_header.cmd_len = sizeof(cmd);
369 io_header.mx_sb_len = sizeof(sensebuf);
370 io_header.sbp = sensebuf;
371 io_header.timeout = 6000; /* XXX */
372
373 ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
374 if (ret < 0 || io_header.driver_status || io_header.host_status) {
375 return -1;
376 }
377 return (buf[9] << 16) | (buf[10] << 8) | buf[11];
378 }
379
380 static void scsi_generic_reset(DeviceState *dev)
381 {
382 SCSIDevice *s = SCSI_DEVICE(dev);
383
384 scsi_device_purge_requests(s, SENSE_CODE(RESET));
385 }
386
387 static void scsi_destroy(SCSIDevice *s)
388 {
389 scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE));
390 blockdev_mark_auto_del(s->conf.bs);
391 }
392
393 static int scsi_generic_initfn(SCSIDevice *s)
394 {
395 int sg_version;
396 struct sg_scsi_id scsiid;
397
398 if (!s->conf.bs) {
399 error_report("drive property not set");
400 return -1;
401 }
402
403 /* check we are really using a /dev/sg* file */
404 if (!bdrv_is_sg(s->conf.bs)) {
405 error_report("not /dev/sg*");
406 return -1;
407 }
408
409 if (bdrv_get_on_error(s->conf.bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
410 error_report("Device doesn't support drive option werror");
411 return -1;
412 }
413 if (bdrv_get_on_error(s->conf.bs, 1) != BLOCK_ERR_REPORT) {
414 error_report("Device doesn't support drive option rerror");
415 return -1;
416 }
417
418 /* check we are using a driver managing SG_IO (version 3 and after */
419 if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
420 sg_version < 30000) {
421 error_report("scsi generic interface too old");
422 return -1;
423 }
424
425 /* get LUN of the /dev/sg? */
426 if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) {
427 error_report("SG_GET_SCSI_ID ioctl failed");
428 return -1;
429 }
430
431 /* define device state */
432 s->type = scsiid.scsi_type;
433 DPRINTF("device type %d\n", s->type);
434 if (s->type == TYPE_DISK || s->type == TYPE_ROM) {
435 add_boot_device_path(s->conf.bootindex, &s->qdev, NULL);
436 }
437
438 switch (s->type) {
439 case TYPE_TAPE:
440 s->blocksize = get_stream_blocksize(s->conf.bs);
441 if (s->blocksize == -1) {
442 s->blocksize = 0;
443 }
444 break;
445
446 /* Make a guess for block devices, we'll fix it when the guest sends.
447 * READ CAPACITY. If they don't, they likely would assume these sizes
448 * anyway. (TODO: they could also send MODE SENSE).
449 */
450 case TYPE_ROM:
451 case TYPE_WORM:
452 s->blocksize = 2048;
453 break;
454 default:
455 s->blocksize = 512;
456 break;
457 }
458
459 DPRINTF("block size %d\n", s->blocksize);
460 return 0;
461 }
462
463 const SCSIReqOps scsi_generic_req_ops = {
464 .size = sizeof(SCSIGenericReq),
465 .free_req = scsi_free_request,
466 .send_command = scsi_send_command,
467 .read_data = scsi_read_data,
468 .write_data = scsi_write_data,
469 .cancel_io = scsi_cancel_io,
470 .get_buf = scsi_get_buf,
471 .load_request = scsi_generic_load_request,
472 .save_request = scsi_generic_save_request,
473 };
474
475 static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
476 uint8_t *buf, void *hba_private)
477 {
478 SCSIRequest *req;
479
480 req = scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private);
481 return req;
482 }
483
484 static Property scsi_generic_properties[] = {
485 DEFINE_BLOCK_PROPERTIES(SCSIDevice, conf),
486 DEFINE_PROP_END_OF_LIST(),
487 };
488
489 static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
490 {
491 DeviceClass *dc = DEVICE_CLASS(klass);
492 SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
493
494 sc->init = scsi_generic_initfn;
495 sc->destroy = scsi_destroy;
496 sc->alloc_req = scsi_new_request;
497 dc->fw_name = "disk";
498 dc->desc = "pass through generic scsi device (/dev/sg*)";
499 dc->reset = scsi_generic_reset;
500 dc->props = scsi_generic_properties;
501 dc->vmsd = &vmstate_scsi_device;
502 }
503
504 static TypeInfo scsi_generic_info = {
505 .name = "scsi-generic",
506 .parent = TYPE_SCSI_DEVICE,
507 .instance_size = sizeof(SCSIDevice),
508 .class_init = scsi_generic_class_initfn,
509 };
510
511 static void scsi_generic_register_types(void)
512 {
513 type_register_static(&scsi_generic_info);
514 }
515
516 type_init(scsi_generic_register_types)
517
518 #endif /* __linux__ */