Commit 2cc977e27d2e6a1212b449ab7f58beb2e294ff83

Authored by ths
1 parent 985a03b0

scsi-generic implemnentation, missing in last commit.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3852 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 653 additions and 0 deletions
hw/scsi-generic.c 0 → 100644
  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 licenced under the LGPL.
  11 + *
  12 + */
  13 +
  14 +#include "qemu-common.h"
  15 +#include "block.h"
  16 +#include "scsi-disk.h"
  17 +
  18 +#ifndef __linux__
  19 +
  20 +SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
  21 + scsi_completionfn completion, void *opaque)
  22 +{
  23 + return NULL;
  24 +}
  25 +
  26 +#else /* __linux__ */
  27 +
  28 +//#define DEBUG_SCSI
  29 +
  30 +#ifdef DEBUG_SCSI
  31 +#define DPRINTF(fmt, args...) \
  32 +do { printf("scsi-generic: " fmt , ##args); } while (0)
  33 +#else
  34 +#define DPRINTF(fmt, args...) do {} while(0)
  35 +#endif
  36 +
  37 +#define BADF(fmt, args...) \
  38 +do { fprintf(stderr, "scsi-generic: " fmt , ##args); } while (0)
  39 +
  40 +#include <stdio.h>
  41 +#include <sys/types.h>
  42 +#include <sys/stat.h>
  43 +#include <unistd.h>
  44 +#include <scsi/sg.h>
  45 +#include <scsi/scsi.h>
  46 +
  47 +#define LOAD_UNLOAD 0xa6
  48 +#define SET_CD_SPEED 0xbb
  49 +#define BLANK 0xa1
  50 +
  51 +#define SCSI_CMD_BUF_SIZE 16
  52 +#define SCSI_SENSE_BUF_SIZE 32
  53 +
  54 +#define SG_ERR_DRIVER_TIMEOUT 0x06
  55 +#define SG_ERR_DRIVER_SENSE 0x08
  56 +
  57 +#ifndef MAX_UINT
  58 +#define MAX_UINT ((unsigned int)-1)
  59 +#endif
  60 +
  61 +typedef struct SCSIRequest {
  62 + BlockDriverAIOCB *aiocb;
  63 + struct SCSIRequest *next;
  64 + SCSIDeviceState *dev;
  65 + uint32_t tag;
  66 + uint8_t cmd[SCSI_CMD_BUF_SIZE];
  67 + int cmdlen;
  68 + uint8_t *buf;
  69 + int buflen;
  70 + int len;
  71 + sg_io_hdr_t io_header;
  72 +} SCSIRequest;
  73 +
  74 +struct SCSIDeviceState
  75 +{
  76 + SCSIRequest *requests;
  77 + BlockDriverState *bdrv;
  78 + int blocksize;
  79 + int lun;
  80 + scsi_completionfn completion;
  81 + void *opaque;
  82 + int driver_status;
  83 + uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
  84 +};
  85 +
  86 +/* Global pool of SCSIRequest structures. */
  87 +static SCSIRequest *free_requests = NULL;
  88 +
  89 +static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
  90 +{
  91 + SCSIRequest *r;
  92 +
  93 + if (free_requests) {
  94 + r = free_requests;
  95 + free_requests = r->next;
  96 + } else {
  97 + r = qemu_malloc(sizeof(SCSIRequest));
  98 + r->buf = NULL;
  99 + r->buflen = 0;
  100 + }
  101 + r->dev = s;
  102 + r->tag = tag;
  103 + memset(r->cmd, 0, sizeof(r->cmd));
  104 + memset(&r->io_header, 0, sizeof(r->io_header));
  105 + r->cmdlen = 0;
  106 + r->len = 0;
  107 + r->aiocb = NULL;
  108 +
  109 + /* link */
  110 +
  111 + r->next = s->requests;
  112 + s->requests = r;
  113 + return r;
  114 +}
  115 +
  116 +static void scsi_remove_request(SCSIRequest *r)
  117 +{
  118 + SCSIRequest *last;
  119 + SCSIDeviceState *s = r->dev;
  120 +
  121 + if (s->requests == r) {
  122 + s->requests = r->next;
  123 + } else {
  124 + last = s->requests;
  125 + while (last && last->next != r)
  126 + last = last->next;
  127 + if (last) {
  128 + last->next = r->next;
  129 + } else {
  130 + BADF("Orphaned request\n");
  131 + }
  132 + }
  133 + r->next = free_requests;
  134 + free_requests = r;
  135 +}
  136 +
  137 +static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
  138 +{
  139 + SCSIRequest *r;
  140 +
  141 + r = s->requests;
  142 + while (r && r->tag != tag)
  143 + r = r->next;
  144 +
  145 + return r;
  146 +}
  147 +
  148 +/* Helper function for command completion. */
  149 +static void scsi_command_complete(void *opaque, int ret)
  150 +{
  151 + SCSIRequest *r = (SCSIRequest *)opaque;
  152 + SCSIDeviceState *s = r->dev;
  153 + uint32_t tag;
  154 + int sense;
  155 +
  156 + s->driver_status = r->io_header.driver_status;
  157 + if (ret != 0)
  158 + sense = HARDWARE_ERROR;
  159 + else {
  160 + if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
  161 + sense = HARDWARE_ERROR;
  162 + BADF("Driver Timeout\n");
  163 + } else if ((s->driver_status & SG_ERR_DRIVER_SENSE) == 0)
  164 + sense = NO_SENSE;
  165 + else
  166 + sense = s->sensebuf[2] & 0x0f;
  167 + }
  168 +
  169 + DPRINTF("Command complete 0x%p tag=0x%x sense=%d\n", r, r->tag, sense);
  170 + tag = r->tag;
  171 + scsi_remove_request(r);
  172 + s->completion(s->opaque, SCSI_REASON_DONE, tag, sense);
  173 +}
  174 +
  175 +/* Cancel a pending data transfer. */
  176 +static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
  177 +{
  178 + DPRINTF("scsi_cancel_io 0x%x\n", tag);
  179 + SCSIDeviceState *s = d->state;
  180 + SCSIRequest *r;
  181 + DPRINTF("Cancel tag=0x%x\n", tag);
  182 + r = scsi_find_request(s, tag);
  183 + if (r) {
  184 + if (r->aiocb)
  185 + bdrv_aio_cancel(r->aiocb);
  186 + r->aiocb = NULL;
  187 + scsi_remove_request(r);
  188 + }
  189 +}
  190 +
  191 +static int execute_command(BlockDriverState *bdrv,
  192 + SCSIRequest *r, int direction,
  193 + BlockDriverCompletionFunc *complete)
  194 +{
  195 +
  196 + r->io_header.interface_id = 'S';
  197 + r->io_header.dxfer_direction = direction;
  198 + r->io_header.dxferp = r->buf;
  199 + r->io_header.dxfer_len = r->buflen;
  200 + r->io_header.cmdp = r->cmd;
  201 + r->io_header.cmd_len = r->cmdlen;
  202 + r->io_header.mx_sb_len = sizeof(r->dev->sensebuf);
  203 + r->io_header.sbp = r->dev->sensebuf;
  204 + r->io_header.timeout = MAX_UINT;
  205 + r->io_header.usr_ptr = r;
  206 + r->io_header.flags |= SG_FLAG_DIRECT_IO;
  207 +
  208 + if (bdrv_pwrite(bdrv, -1, &r->io_header, sizeof(r->io_header)) == -1) {
  209 + BADF("execute_command: write failed ! (%d)\n", errno);
  210 + return -1;
  211 + }
  212 + if (complete == NULL) {
  213 + int ret;
  214 + r->aiocb = NULL;
  215 + while ((ret = bdrv_pread(bdrv, -1, &r->io_header,
  216 + sizeof(r->io_header))) == -1 &&
  217 + errno == EINTR);
  218 + if (ret == -1) {
  219 + BADF("execute_command: read failed !\n");
  220 + return -1;
  221 + }
  222 + return 0;
  223 + }
  224 +
  225 + r->aiocb = bdrv_aio_read(bdrv, 0, (uint8_t*)&r->io_header,
  226 + -(int64_t)sizeof(r->io_header), complete, r);
  227 + if (r->aiocb == NULL) {
  228 + BADF("execute_command: read failed !\n");
  229 + return -1;
  230 + }
  231 +
  232 + return 0;
  233 +}
  234 +
  235 +static void scsi_read_complete(void * opaque, int ret)
  236 +{
  237 + SCSIRequest *r = (SCSIRequest *)opaque;
  238 + SCSIDeviceState *s = r->dev;
  239 + int len;
  240 +
  241 + if (ret) {
  242 + DPRINTF("IO error\n");
  243 + scsi_command_complete(r, ret);
  244 + return;
  245 + }
  246 + len = r->io_header.dxfer_len - r->io_header.resid;
  247 + DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, len);
  248 +
  249 + r->len = -1;
  250 + s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
  251 +}
  252 +
  253 +/* Read more data from scsi device into buffer. */
  254 +static void scsi_read_data(SCSIDevice *d, uint32_t tag)
  255 +{
  256 + SCSIDeviceState *s = d->state;
  257 + SCSIRequest *r;
  258 + int ret;
  259 +
  260 + DPRINTF("scsi_read_data 0x%x\n", tag);
  261 + r = scsi_find_request(s, tag);
  262 + if (!r) {
  263 + BADF("Bad read tag 0x%x\n", tag);
  264 + /* ??? This is the wrong error. */
  265 + scsi_command_complete(r, -EINVAL);
  266 + return;
  267 + }
  268 +
  269 + if (r->len == -1) {
  270 + scsi_command_complete(r, 0);
  271 + return;
  272 + }
  273 +
  274 + if (r->cmd[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE)
  275 + {
  276 + memcpy(r->buf, s->sensebuf, 16);
  277 + r->io_header.driver_status = 0;
  278 + r->len = -1;
  279 + s->completion(s->opaque, SCSI_REASON_DATA, r->tag, 16);
  280 + return;
  281 + }
  282 +
  283 + ret = execute_command(s->bdrv, r, SG_DXFER_FROM_DEV, scsi_read_complete);
  284 + if (ret == -1) {
  285 + scsi_command_complete(r, -EINVAL);
  286 + return;
  287 + }
  288 +}
  289 +
  290 +static void scsi_write_complete(void * opaque, int ret)
  291 +{
  292 + SCSIRequest *r = (SCSIRequest *)opaque;
  293 +
  294 + DPRINTF("scsi_write_complete() ret = %d\n", ret);
  295 + if (ret) {
  296 + DPRINTF("IO error\n");
  297 + scsi_command_complete(r, ret);
  298 + return;
  299 + }
  300 +
  301 + scsi_command_complete(r, ret);
  302 +}
  303 +
  304 +/* Write data to a scsi device. Returns nonzero on failure.
  305 + The transfer may complete asynchronously. */
  306 +static int scsi_write_data(SCSIDevice *d, uint32_t tag)
  307 +{
  308 + SCSIDeviceState *s = d->state;
  309 + SCSIRequest *r;
  310 + int ret;
  311 +
  312 + DPRINTF("scsi_write_data 0x%x\n", tag);
  313 + r = scsi_find_request(s, tag);
  314 + if (!r) {
  315 + BADF("Bad write tag 0x%x\n", tag);
  316 + /* ??? This is the wrong error. */
  317 + scsi_command_complete(r, -EINVAL);
  318 + return 0;
  319 + }
  320 +
  321 + if (r->len == 0) {
  322 + r->len = r->buflen;
  323 + s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->len);
  324 + return 0;
  325 + }
  326 +
  327 + ret = execute_command(s->bdrv, r, SG_DXFER_TO_DEV, scsi_write_complete);
  328 + if (ret == -1) {
  329 + scsi_command_complete(r, -EINVAL);
  330 + return 1;
  331 + }
  332 +
  333 + return 0;
  334 +}
  335 +
  336 +/* Return a pointer to the data buffer. */
  337 +static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
  338 +{
  339 + SCSIDeviceState *s = d->state;
  340 + SCSIRequest *r;
  341 + r = scsi_find_request(s, tag);
  342 + if (!r) {
  343 + BADF("Bad buffer tag 0x%x\n", tag);
  344 + return NULL;
  345 + }
  346 + return r->buf;
  347 +}
  348 +
  349 +static int scsi_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len)
  350 +{
  351 + switch (cmd[0] >> 5) {
  352 + case 0:
  353 + *len = cmd[4];
  354 + *cmdlen = 6;
  355 + break;
  356 + case 1:
  357 + case 2:
  358 + *len = cmd[8] | (cmd[7] << 8);
  359 + *cmdlen = 10;
  360 + break;
  361 + case 4:
  362 + *len = cmd[13] | (cmd[12] << 8) | (cmd[11] << 16) | (cmd[10] << 24);
  363 + *cmdlen = 16;
  364 + break;
  365 + case 5:
  366 + *len = cmd[9] | (cmd[8] << 8) | (cmd[7] << 16) | (cmd[6] << 24);
  367 + *cmdlen = 12;
  368 + break;
  369 + default:
  370 + return -1;
  371 + }
  372 +
  373 + switch(cmd[0]) {
  374 + case TEST_UNIT_READY:
  375 + case REZERO_UNIT:
  376 + case START_STOP:
  377 + case SEEK_6:
  378 + case WRITE_FILEMARKS:
  379 + case SPACE:
  380 + case ERASE:
  381 + case ALLOW_MEDIUM_REMOVAL:
  382 + case VERIFY:
  383 + case SEEK_10:
  384 + case SYNCHRONIZE_CACHE:
  385 + case LOCK_UNLOCK_CACHE:
  386 + case LOAD_UNLOAD:
  387 + case SET_CD_SPEED:
  388 + case SET_LIMITS:
  389 + case WRITE_LONG:
  390 + case MOVE_MEDIUM:
  391 + case UPDATE_BLOCK:
  392 + *len = 0;
  393 + break;
  394 + case MODE_SENSE:
  395 + break;
  396 + case WRITE_SAME:
  397 + *len = 1;
  398 + break;
  399 + case READ_CAPACITY:
  400 + *len = 8;
  401 + break;
  402 + case READ_BLOCK_LIMITS:
  403 + *len = 6;
  404 + break;
  405 + case READ_POSITION:
  406 + *len = 20;
  407 + break;
  408 + case SEND_VOLUME_TAG:
  409 + *len *= 40;
  410 + break;
  411 + case MEDIUM_SCAN:
  412 + *len *= 8;
  413 + break;
  414 + case WRITE_10:
  415 + cmd[1] &= ~0x08; /* disable FUA */
  416 + case WRITE_VERIFY:
  417 + case WRITE_6:
  418 + case WRITE_12:
  419 + case WRITE_VERIFY_12:
  420 + *len *= blocksize;
  421 + break;
  422 + case READ_10:
  423 + cmd[1] &= ~0x08; /* disable FUA */
  424 + case READ_6:
  425 + case READ_REVERSE:
  426 + case RECOVER_BUFFERED_DATA:
  427 + case READ_12:
  428 + *len *= blocksize;
  429 + break;
  430 + }
  431 + return 0;
  432 +}
  433 +
  434 +static int is_write(int command)
  435 +{
  436 + switch (command) {
  437 + case COPY:
  438 + case COPY_VERIFY:
  439 + case COMPARE:
  440 + case CHANGE_DEFINITION:
  441 + case LOG_SELECT:
  442 + case MODE_SELECT:
  443 + case MODE_SELECT_10:
  444 + case SEND_DIAGNOSTIC:
  445 + case WRITE_BUFFER:
  446 + case FORMAT_UNIT:
  447 + case REASSIGN_BLOCKS:
  448 + case RESERVE:
  449 + case SEARCH_EQUAL:
  450 + case SEARCH_HIGH:
  451 + case SEARCH_LOW:
  452 + case WRITE_6:
  453 + case WRITE_10:
  454 + case WRITE_VERIFY:
  455 + case UPDATE_BLOCK:
  456 + case WRITE_LONG:
  457 + case WRITE_SAME:
  458 + case SEARCH_HIGH_12:
  459 + case SEARCH_EQUAL_12:
  460 + case SEARCH_LOW_12:
  461 + case WRITE_12:
  462 + case WRITE_VERIFY_12:
  463 + case SET_WINDOW:
  464 + case MEDIUM_SCAN:
  465 + case SEND_VOLUME_TAG:
  466 + case WRITE_LONG_2:
  467 + return 1;
  468 + }
  469 + return 0;
  470 +}
  471 +
  472 +/* Execute a scsi command. Returns the length of the data expected by the
  473 + command. This will be Positive for data transfers from the device
  474 + (eg. disk reads), negative for transfers to the device (eg. disk writes),
  475 + and zero if the command does not transfer any data. */
  476 +
  477 +static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
  478 + uint8_t *cmd, int lun)
  479 +{
  480 + SCSIDeviceState *s = d->state;
  481 + uint32_t len;
  482 + int cmdlen;
  483 + SCSIRequest *r;
  484 + int ret;
  485 +
  486 + /* ??? Tags are not unique for different luns. We only implement a
  487 + single lun, so this should not matter. */
  488 +
  489 + if (lun != s->lun || (cmd[1] >> 5) != s->lun) {
  490 + DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5);
  491 + s->completion(s->opaque, SCSI_REASON_DONE, tag, ILLEGAL_REQUEST);
  492 + return 0;
  493 + }
  494 +
  495 + if (scsi_length(cmd, s->blocksize, &cmdlen, &len) == -1) {
  496 + BADF("Unsupported command length, command %x\n", cmd[0]);
  497 + return 0;
  498 + }
  499 +
  500 + DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag,
  501 + cmd[0], len);
  502 +
  503 + r = scsi_find_request(s, tag);
  504 + if (r) {
  505 + BADF("Tag 0x%x already in use %p\n", tag, r);
  506 + scsi_cancel_io(d, tag);
  507 + }
  508 + r = scsi_new_request(s, tag);
  509 +
  510 + memcpy(r->cmd, cmd, cmdlen);
  511 + r->cmdlen = cmdlen;
  512 +
  513 + if (len == 0) {
  514 + if (r->buf != NULL)
  515 + free(r->buf);
  516 + r->buflen = 0;
  517 + r->buf = NULL;
  518 + ret = execute_command(s->bdrv, r, SG_DXFER_NONE, scsi_command_complete);
  519 + if (ret == -1) {
  520 + scsi_command_complete(r, -EINVAL);
  521 + return 0;
  522 + }
  523 + return 0;
  524 + }
  525 +
  526 + if (r->buflen != len) {
  527 + if (r->buf != NULL)
  528 + free(r->buf);
  529 + r->buf = qemu_malloc(len);
  530 + r->buflen = len;
  531 + }
  532 +
  533 + memset(r->buf, 0, r->buflen);
  534 + r->len = len;
  535 + if (is_write(cmd[0])) {
  536 + r->len = 0;
  537 + return -len;
  538 + }
  539 +
  540 + return len;
  541 +}
  542 +
  543 +static int get_blocksize(BlockDriverState *bdrv)
  544 +{
  545 + uint8_t cmd[10];
  546 + uint8_t buf[8];
  547 + uint8_t sensebuf[8];
  548 + sg_io_hdr_t io_header;
  549 + int ret;
  550 +
  551 + memset(cmd, sizeof(cmd), 0);
  552 + memset(buf, sizeof(buf), 0);
  553 + cmd[0] = READ_CAPACITY;
  554 +
  555 + memset(&io_header, 0, sizeof(io_header));
  556 + io_header.interface_id = 'S';
  557 + io_header.dxfer_direction = SG_DXFER_FROM_DEV;
  558 + io_header.dxfer_len = sizeof(buf);
  559 + io_header.dxferp = buf;
  560 + io_header.cmdp = cmd;
  561 + io_header.cmd_len = sizeof(cmd);
  562 + io_header.mx_sb_len = sizeof(sensebuf);
  563 + io_header.sbp = sensebuf;
  564 + io_header.timeout = 6000; /* XXX */
  565 +
  566 + ret = bdrv_pwrite(bdrv, -1, &io_header, sizeof(io_header));
  567 + if (ret == -1)
  568 + return -1;
  569 +
  570 + while ((ret = bdrv_pread(bdrv, -1, &io_header, sizeof(io_header))) == -1 &&
  571 + errno == EINTR);
  572 +
  573 + if (ret == -1)
  574 + return -1;
  575 +
  576 + return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
  577 +}
  578 +
  579 +static void scsi_destroy(SCSIDevice *d)
  580 +{
  581 + SCSIRequest *r, *n;
  582 +
  583 + r = d->state->requests;
  584 + while (r) {
  585 + n = r->next;
  586 + qemu_free(r);
  587 + r = n;
  588 + }
  589 +
  590 + r = free_requests;
  591 + while (r) {
  592 + n = r->next;
  593 + qemu_free(r);
  594 + r = n;
  595 + }
  596 +
  597 + qemu_free(d->state);
  598 + qemu_free(d);
  599 +}
  600 +
  601 +SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
  602 + scsi_completionfn completion, void *opaque)
  603 +{
  604 + int sg_version;
  605 + SCSIDevice *d;
  606 + SCSIDeviceState *s;
  607 + struct sg_scsi_id scsiid;
  608 +
  609 + /* check we are really using a /dev/sg* file */
  610 +
  611 + if (!bdrv_is_sg(bdrv))
  612 + return NULL;
  613 +
  614 + /* check we are using a driver managing SG_IO (version 3 and after */
  615 +
  616 + if (bdrv_ioctl(bdrv, SG_GET_VERSION_NUM, &sg_version) < 0 ||
  617 + sg_version < 30000)
  618 + return NULL;
  619 +
  620 + /* get LUN of the /dev/sg? */
  621 +
  622 + if (bdrv_ioctl(bdrv, SG_GET_SCSI_ID, &scsiid))
  623 + return NULL;
  624 +
  625 + /* define device state */
  626 +
  627 + s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState));
  628 + s->bdrv = bdrv;
  629 + s->requests = NULL;
  630 + s->completion = completion;
  631 + s->opaque = opaque;
  632 + s->lun = scsiid.lun;
  633 + s->blocksize = get_blocksize(s->bdrv);
  634 + s->driver_status = 0;
  635 + memset(s->sensebuf, 0, sizeof(s->sensebuf));
  636 + /* removable media returns 0 if not present */
  637 + if (s->blocksize <= 0)
  638 + s->blocksize = 2048;
  639 +
  640 + /* define function to manage device */
  641 +
  642 + d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
  643 + d->state = s;
  644 + d->destroy = scsi_destroy;
  645 + d->send_command = scsi_send_command;
  646 + d->read_data = scsi_read_data;
  647 + d->write_data = scsi_write_data;
  648 + d->cancel_io = scsi_cancel_io;
  649 + d->get_buf = scsi_get_buf;
  650 +
  651 + return d;
  652 +}
  653 +#endif /* __linux__ */
... ...