#define SCSI_REQ_STATUS_RETRY_TYPE_MASK 0x06
#define SCSI_REQ_STATUS_RETRY_READ 0x00
#define SCSI_REQ_STATUS_RETRY_WRITE 0x02
+#define SCSI_REQ_STATUS_RETRY_FLUSH 0x04
typedef struct SCSIDiskState SCSIDiskState;
+typedef struct SCSISense {
+ uint8_t key;
+} SCSISense;
+
typedef struct SCSIDiskReq {
SCSIRequest req;
/* ??? We should probably keep track of whether the data transfer is
uint32_t status;
} SCSIDiskReq;
+typedef enum { SCSI_HD, SCSI_CD } SCSIDriveKind;
+
struct SCSIDiskState
{
SCSIDevice qdev;
/* The qemu block layer uses a fixed 512 byte sector size.
This is the number of 512 byte blocks in a single scsi sector. */
int cluster_size;
+ uint32_t removable;
uint64_t max_lba;
QEMUBH *bh;
char *version;
char *serial;
+ SCSISense sense;
+ SCSIDriveKind drive_kind;
};
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type);
+static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf);
-static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag,
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag,
uint32_t lun)
{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *req;
SCSIDiskReq *r;
req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun);
r = DO_UPCAST(SCSIDiskReq, req, req);
r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE);
- return r;
+ return req;
}
-static void scsi_remove_request(SCSIDiskReq *r)
+static void scsi_free_request(SCSIRequest *req)
{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
qemu_vfree(r->iov.iov_base);
- scsi_req_free(&r->req);
}
-static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag)
+static void scsi_disk_clear_sense(SCSIDiskState *s)
{
- return DO_UPCAST(SCSIDiskReq, req, scsi_req_find(&s->qdev, tag));
+ memset(&s->sense, 0, sizeof(s->sense));
}
-static void scsi_req_set_status(SCSIRequest *req, int status, int sense_code)
+static void scsi_disk_set_sense(SCSIDiskState *s, uint8_t key)
{
- req->status = status;
- scsi_dev_set_sense(req->dev, sense_code);
+ s->sense.key = key;
+}
+
+static void scsi_req_set_status(SCSIDiskReq *r, int status, int sense_code)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ r->req.status = status;
+ scsi_disk_set_sense(s, sense_code);
}
/* Helper function for command completion. */
{
DPRINTF("Command complete tag=0x%x status=%d sense=%d\n",
r->req.tag, status, sense);
- scsi_req_set_status(&r->req, status, sense);
+ scsi_req_set_status(r, status, sense);
scsi_req_complete(&r->req);
- scsi_remove_request(r);
}
/* Cancel a pending data transfer. */
-static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
+static void scsi_cancel_io(SCSIRequest *req)
{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
- SCSIDiskReq *r;
- DPRINTF("Cancel tag=0x%x\n", tag);
- r = scsi_find_request(s, tag);
- if (r) {
- if (r->req.aiocb)
- bdrv_aio_cancel(r->req.aiocb);
- r->req.aiocb = NULL;
- scsi_remove_request(r);
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+ DPRINTF("Cancel tag=0x%x\n", req->tag);
+ if (r->req.aiocb) {
+ bdrv_aio_cancel(r->req.aiocb);
}
+ r->req.aiocb = NULL;
}
static void scsi_read_complete(void * opaque, int ret)
n = r->iov.iov_len / 512;
r->sector += n;
r->sector_count -= n;
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
+ scsi_req_data(&r->req, r->iov.iov_len);
}
-static void scsi_read_request(SCSIDiskReq *r)
+/* Read more data from scsi device into buffer. */
+static void scsi_read_data(SCSIRequest *req)
{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
if (r->sector_count == (uint32_t)-1) {
DPRINTF("Read buf_len=%zd\n", r->iov.iov_len);
r->sector_count = 0;
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
+ scsi_req_data(&r->req, r->iov.iov_len);
return;
}
DPRINTF("Read sector_count=%d\n", r->sector_count);
return;
}
+ /* No data transfer may already be in progress */
+ assert(r->req.aiocb == NULL);
+
n = r->sector_count;
if (n > SCSI_DMA_BUF_SIZE / 512)
n = SCSI_DMA_BUF_SIZE / 512;
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
r->req.aiocb = bdrv_aio_readv(s->bs, r->sector, &r->qiov, n,
scsi_read_complete, r);
- if (r->req.aiocb == NULL)
- scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
-}
-
-/* Read more data from scsi device into buffer. */
-static void scsi_read_data(SCSIDevice *d, uint32_t tag)
-{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
- SCSIDiskReq *r;
-
- r = scsi_find_request(s, tag);
- if (!r) {
- BADF("Bad read tag 0x%x\n", tag);
- /* ??? This is the wrong error. */
- scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
- return;
+ if (r->req.aiocb == NULL) {
+ scsi_read_complete(r, -EIO);
}
-
- /* No data transfer may already be in progress */
- assert(r->req.aiocb == NULL);
-
- scsi_read_request(r);
}
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
r->status |= SCSI_REQ_STATUS_RETRY | type;
bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
- vm_stop(0);
+ vm_stop(VMSTOP_DISKFULL);
} else {
if (type == SCSI_REQ_STATUS_RETRY_READ) {
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0);
+ scsi_req_data(&r->req, 0);
}
scsi_command_complete(r, CHECK_CONDITION,
HARDWARE_ERROR);
}
r->iov.iov_len = len;
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, len);
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len);
+ scsi_req_data(&r->req, len);
}
}
-static void scsi_write_request(SCSIDiskReq *r)
+static int scsi_write_data(SCSIRequest *req)
{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
+ /* No data transfer may already be in progress */
+ assert(r->req.aiocb == NULL);
+
n = r->iov.iov_len / 512;
if (n) {
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n,
scsi_write_complete, r);
- if (r->req.aiocb == NULL)
- scsi_command_complete(r, CHECK_CONDITION,
- HARDWARE_ERROR);
+ if (r->req.aiocb == NULL) {
+ scsi_write_complete(r, -EIO);
+ }
} else {
/* Invoke completion routine to fetch data from host. */
scsi_write_complete(r, 0);
}
-}
-
-/* Write data to a scsi device. Returns nonzero on failure.
- The transfer may complete asynchronously. */
-static int scsi_write_data(SCSIDevice *d, uint32_t tag)
-{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
- SCSIDiskReq *r;
-
- DPRINTF("Write data tag=0x%x\n", tag);
- r = scsi_find_request(s, tag);
- if (!r) {
- BADF("Bad write tag 0x%x\n", tag);
- scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
- return 1;
- }
-
- /* No data transfer may already be in progress */
- assert(r->req.aiocb == NULL);
-
- scsi_write_request(r);
return 0;
}
r = DO_UPCAST(SCSIDiskReq, req, req);
if (r->status & SCSI_REQ_STATUS_RETRY) {
int status = r->status;
+ int ret;
+
r->status &=
~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK);
switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) {
case SCSI_REQ_STATUS_RETRY_READ:
- scsi_read_request(r);
+ scsi_read_data(&r->req);
break;
case SCSI_REQ_STATUS_RETRY_WRITE:
- scsi_write_request(r);
+ scsi_write_data(&r->req);
break;
+ case SCSI_REQ_STATUS_RETRY_FLUSH:
+ ret = scsi_disk_emulate_command(r, r->iov.iov_base);
+ if (ret == 0) {
+ scsi_command_complete(r, GOOD, NO_SENSE);
+ }
}
}
}
}
/* Return a pointer to the data buffer. */
-static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
+static uint8_t *scsi_get_buf(SCSIRequest *req)
{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
- SCSIDiskReq *r;
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
- r = scsi_find_request(s, tag);
- if (!r) {
- BADF("Bad buffer tag 0x%x\n", tag);
- return NULL;
- }
return (uint8_t *)r->iov.iov_base;
}
return -1;
}
- if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ if (s->drive_kind == SCSI_CD) {
outbuf[buflen++] = 5;
} else {
outbuf[buflen++] = 0;
switch (page_code) {
case 0x00: /* Supported page codes, mandatory */
+ {
+ int pages;
DPRINTF("Inquiry EVPD[Supported pages] "
"buffer size %zd\n", req->cmd.xfer);
- outbuf[buflen++] = 4; // number of pages
+ pages = buflen++;
outbuf[buflen++] = 0x00; // list of supported pages (this page)
outbuf[buflen++] = 0x80; // unit serial number
outbuf[buflen++] = 0x83; // device identification
- outbuf[buflen++] = 0xb0; // block device characteristics
+ if (s->drive_kind == SCSI_HD) {
+ outbuf[buflen++] = 0xb0; // block limits
+ outbuf[buflen++] = 0xb2; // thin provisioning
+ }
+ outbuf[pages] = buflen - pages - 1; // number of pages
break;
-
+ }
case 0x80: /* Device serial number, optional */
{
int l = strlen(s->serial);
DPRINTF("Inquiry EVPD[Device identification] "
"buffer size %zd\n", req->cmd.xfer);
- outbuf[buflen++] = 3 + id_len;
+ outbuf[buflen++] = 4 + id_len;
outbuf[buflen++] = 0x2; // ASCII
outbuf[buflen++] = 0; // not officially assigned
outbuf[buflen++] = 0; // reserved
buflen += id_len;
break;
}
- case 0xb0: /* block device characteristics */
+ case 0xb0: /* block limits */
{
+ unsigned int unmap_sectors =
+ s->qdev.conf.discard_granularity / s->qdev.blocksize;
unsigned int min_io_size =
s->qdev.conf.min_io_size / s->qdev.blocksize;
unsigned int opt_io_size =
s->qdev.conf.opt_io_size / s->qdev.blocksize;
+ if (s->drive_kind == SCSI_CD) {
+ DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
+ page_code);
+ return -1;
+ }
/* required VPD size with unmap support */
outbuf[3] = buflen = 0x3c;
outbuf[13] = (opt_io_size >> 16) & 0xff;
outbuf[14] = (opt_io_size >> 8) & 0xff;
outbuf[15] = opt_io_size & 0xff;
+
+ /* optimal unmap granularity */
+ outbuf[28] = (unmap_sectors >> 24) & 0xff;
+ outbuf[29] = (unmap_sectors >> 16) & 0xff;
+ outbuf[30] = (unmap_sectors >> 8) & 0xff;
+ outbuf[31] = unmap_sectors & 0xff;
+ break;
+ }
+ case 0xb2: /* thin provisioning */
+ {
+ outbuf[3] = buflen = 8;
+ outbuf[4] = 0;
+ outbuf[5] = 0x40; /* write same with unmap supported */
+ outbuf[6] = 0;
+ outbuf[7] = 0;
break;
}
default:
return buflen;
}
- if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ if (s->drive_kind == SCSI_CD) {
outbuf[0] = 5;
outbuf[1] = 0x80;
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
} else {
outbuf[0] = 0;
+ outbuf[1] = s->removable ? 0x80 : 0;
memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
}
memcpy(&outbuf[8], "QEMU ", 8);
return p[1] + 2;
case 0x2a: /* CD Capabilities and Mechanical Status page. */
- if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM)
+ if (s->drive_kind != SCSI_CD)
return 0;
p[0] = 0x2a;
p[1] = 0x14;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
uint64_t nb_sectors;
int buflen = 0;
+ int ret;
switch (req->cmd.buf[0]) {
case TEST_UNIT_READY:
goto illegal_request;
memset(outbuf, 0, 4);
buflen = 4;
- if (req->dev->sense.key == NOT_READY && req->cmd.xfer >= 18) {
+ if (s->sense.key == NOT_READY && req->cmd.xfer >= 18) {
memset(outbuf, 0, 18);
buflen = 18;
outbuf[7] = 10;
}
outbuf[0] = 0xf0;
outbuf[1] = 0;
- outbuf[2] = req->dev->sense.key;
- scsi_dev_clear_sense(req->dev);
+ outbuf[2] = s->sense.key;
+ scsi_disk_clear_sense(s);
break;
case INQUIRY:
buflen = scsi_disk_emulate_inquiry(req, outbuf);
goto illegal_request;
break;
case START_STOP:
- if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM && (req->cmd.buf[4] & 2)) {
+ if (s->drive_kind == SCSI_CD && (req->cmd.buf[4] & 2)) {
/* load/eject medium */
bdrv_eject(s->bs, !(req->cmd.buf[4] & 1));
}
buflen = 8;
break;
case SYNCHRONIZE_CACHE:
- bdrv_flush(s->bs);
+ ret = bdrv_flush(s->bs);
+ if (ret < 0) {
+ if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_FLUSH)) {
+ return -1;
+ }
+ }
break;
case GET_CONFIGURATION:
memset(outbuf, 0, 8);
outbuf[11] = 0;
outbuf[12] = 0;
outbuf[13] = get_physical_block_exp(&s->qdev.conf);
+
+ /* set TPE bit if the format supports discard */
+ if (s->qdev.conf.discard_granularity) {
+ outbuf[14] = 0x80;
+ }
+
/* Protection, exponent and lowest lba field left blank. */
buflen = req->cmd.xfer;
break;
default:
goto illegal_request;
}
- scsi_req_set_status(req, GOOD, NO_SENSE);
+ scsi_req_set_status(r, GOOD, NO_SENSE);
return buflen;
not_ready:
(eg. disk reads), negative for transfers to the device (eg. disk writes),
and zero if the command does not transfer any data. */
-static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
- uint8_t *buf, int lun)
+static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
- uint64_t lba;
- uint32_t len;
- int cmdlen;
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+ int32_t len;
int is_write;
uint8_t command;
uint8_t *outbuf;
- SCSIDiskReq *r;
int rc;
+ scsi_req_enqueue(req);
command = buf[0];
- r = scsi_find_request(s, tag);
- if (r) {
- BADF("Tag 0x%x already in use\n", tag);
- scsi_cancel_io(d, tag);
- }
- /* ??? Tags are not unique for different luns. We only implement a
- single lun, so this should not matter. */
- r = scsi_new_request(s, tag, lun);
outbuf = (uint8_t *)r->iov.iov_base;
is_write = 0;
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
- switch (command >> 5) {
- case 0:
- lba = (uint64_t) buf[3] | ((uint64_t) buf[2] << 8) |
- (((uint64_t) buf[1] & 0x1f) << 16);
- len = buf[4];
- cmdlen = 6;
- break;
- case 1:
- case 2:
- lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) |
- ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24);
- len = buf[8] | (buf[7] << 8);
- cmdlen = 10;
- break;
- case 4:
- lba = (uint64_t) buf[9] | ((uint64_t) buf[8] << 8) |
- ((uint64_t) buf[7] << 16) | ((uint64_t) buf[6] << 24) |
- ((uint64_t) buf[5] << 32) | ((uint64_t) buf[4] << 40) |
- ((uint64_t) buf[3] << 48) | ((uint64_t) buf[2] << 56);
- len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
- cmdlen = 16;
- break;
- case 5:
- lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) |
- ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24);
- len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24);
- cmdlen = 12;
- break;
- default:
+
+ if (scsi_req_parse(&r->req, buf) != 0) {
BADF("Unsupported command length, command %x\n", command);
goto fail;
}
#ifdef DEBUG_SCSI
{
int i;
- for (i = 1; i < cmdlen; i++) {
+ for (i = 1; i < r->req.cmd.len; i++) {
printf(" 0x%02x", buf[i]);
}
printf("\n");
}
#endif
- if (scsi_req_parse(&r->req, buf) != 0) {
- BADF("Unsupported command length, command %x\n", command);
- goto fail;
- }
- assert(r->req.cmd.len == cmdlen);
- assert(r->req.cmd.lba == lba);
-
- if (lun || buf[1] >> 5) {
+ if (req->lun || buf[1] >> 5) {
/* Only LUN 0 supported. */
- DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
+ DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : buf[1] >> 5);
if (command != REQUEST_SENSE && command != INQUIRY)
goto fail;
}
case READ_10:
case READ_12:
case READ_16:
- DPRINTF("Read (sector %" PRId64 ", count %d)\n", lba, len);
- if (lba > s->max_lba)
+ len = r->req.cmd.xfer / s->qdev.blocksize;
+ DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len);
+ if (r->req.cmd.lba > s->max_lba)
goto illegal_lba;
- r->sector = lba * s->cluster_size;
+ r->sector = r->req.cmd.lba * s->cluster_size;
r->sector_count = len * s->cluster_size;
break;
case WRITE_6:
case WRITE_VERIFY:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
+ len = r->req.cmd.xfer / s->qdev.blocksize;
DPRINTF("Write %s(sector %" PRId64 ", count %d)\n",
- (command & 0xe) == 0xe ? "And Verify " : "", lba, len);
- if (lba > s->max_lba)
+ (command & 0xe) == 0xe ? "And Verify " : "",
+ r->req.cmd.lba, len);
+ if (r->req.cmd.lba > s->max_lba)
goto illegal_lba;
- r->sector = lba * s->cluster_size;
+ r->sector = r->req.cmd.lba * s->cluster_size;
r->sector_count = len * s->cluster_size;
is_write = 1;
break;
case MODE_SELECT:
- DPRINTF("Mode Select(6) (len %d)\n", len);
+ DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer);
/* We don't support mode parameter changes.
Allow the mode parameter header + block descriptors only. */
- if (len > 12) {
+ if (r->req.cmd.xfer > 12) {
goto fail;
}
break;
case MODE_SELECT_10:
- DPRINTF("Mode Select(10) (len %d)\n", len);
+ DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer);
/* We don't support mode parameter changes.
Allow the mode parameter header + block descriptors only. */
- if (len > 16) {
+ if (r->req.cmd.xfer > 16) {
goto fail;
}
break;
case SEEK_6:
case SEEK_10:
- DPRINTF("Seek(%d) (sector %" PRId64 ")\n", command == SEEK_6 ? 6 : 10, lba);
- if (lba > s->max_lba) {
+ DPRINTF("Seek(%d) (sector %" PRId64 ")\n", command == SEEK_6 ? 6 : 10,
+ r->req.cmd.lba);
+ if (r->req.cmd.lba > s->max_lba) {
+ goto illegal_lba;
+ }
+ break;
+ case WRITE_SAME_16:
+ len = r->req.cmd.xfer / s->qdev.blocksize;
+
+ DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n",
+ r->req.cmd.lba, len);
+
+ if (r->req.cmd.lba > s->max_lba) {
goto illegal_lba;
}
+
+ /*
+ * We only support WRITE SAME with the unmap bit set for now.
+ */
+ if (!(buf[1] & 0x8)) {
+ goto fail;
+ }
+
+ rc = bdrv_discard(s->bs, r->req.cmd.lba * s->cluster_size,
+ len * s->cluster_size);
+ if (rc < 0) {
+ /* XXX: better error code ?*/
+ goto fail;
+ }
+
break;
default:
- DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
+ DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
fail:
scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
- return 0;
+ return 0;
illegal_lba:
scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
return 0;
}
len = r->sector_count * 512 + r->iov.iov_len;
if (is_write) {
- return -len;
+ len = -len;
} else {
if (!r->sector_count)
r->sector_count = -1;
- return len;
- }
-}
-
-static void scsi_disk_purge_requests(SCSIDiskState *s)
-{
- SCSIDiskReq *r;
-
- while (!QTAILQ_EMPTY(&s->qdev.requests)) {
- r = DO_UPCAST(SCSIDiskReq, req, QTAILQ_FIRST(&s->qdev.requests));
- if (r->req.aiocb) {
- bdrv_aio_cancel(r->req.aiocb);
- }
- scsi_remove_request(r);
}
+ return len;
}
static void scsi_disk_reset(DeviceState *dev)
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev);
uint64_t nb_sectors;
- scsi_disk_purge_requests(s);
+ scsi_device_purge_requests(&s->qdev);
bdrv_get_geometry(s->bs, &nb_sectors);
nb_sectors /= s->cluster_size;
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
- scsi_disk_purge_requests(s);
+ scsi_device_purge_requests(&s->qdev);
blockdev_mark_auto_del(s->qdev.conf.bs);
}
-static int scsi_disk_initfn(SCSIDevice *dev)
+static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
- int is_cd;
DriveInfo *dinfo;
if (!s->qdev.conf.bs) {
return -1;
}
s->bs = s->qdev.conf.bs;
- is_cd = bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM;
+ s->drive_kind = kind;
- if (!is_cd && !bdrv_is_inserted(s->bs)) {
+ if (kind == SCSI_HD && !bdrv_is_inserted(s->bs)) {
error_report("Device needs media, but drive is empty");
return -1;
}
return -1;
}
- if (is_cd) {
+ if (kind == SCSI_CD) {
s->qdev.blocksize = 2048;
} else {
s->qdev.blocksize = s->qdev.conf.logical_block_size;
s->qdev.type = TYPE_DISK;
qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
- bdrv_set_removable(s->bs, is_cd);
+ bdrv_set_removable(s->bs, kind == SCSI_CD);
+ add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0");
return 0;
}
-static SCSIDeviceInfo scsi_disk_info = {
- .qdev.name = "scsi-disk",
- .qdev.desc = "virtual scsi disk or cdrom",
- .qdev.size = sizeof(SCSIDiskState),
- .qdev.reset = scsi_disk_reset,
- .init = scsi_disk_initfn,
- .destroy = scsi_destroy,
- .send_command = scsi_send_command,
- .read_data = scsi_read_data,
- .write_data = scsi_write_data,
- .cancel_io = scsi_cancel_io,
- .get_buf = scsi_get_buf,
- .qdev.props = (Property[]) {
- DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf),
- DEFINE_PROP_STRING("ver", SCSIDiskState, version),
- DEFINE_PROP_STRING("serial", SCSIDiskState, serial),
- DEFINE_PROP_END_OF_LIST(),
- },
+static int scsi_hd_initfn(SCSIDevice *dev)
+{
+ return scsi_initfn(dev, SCSI_HD);
+}
+
+static int scsi_cd_initfn(SCSIDevice *dev)
+{
+ return scsi_initfn(dev, SCSI_CD);
+}
+
+static int scsi_disk_initfn(SCSIDevice *dev)
+{
+ SCSIDriveKind kind;
+ DriveInfo *dinfo;
+
+ if (!dev->conf.bs) {
+ kind = SCSI_HD; /* will die in scsi_initfn() */
+ } else {
+ dinfo = drive_get_by_blockdev(dev->conf.bs);
+ kind = dinfo->media_cd ? SCSI_CD : SCSI_HD;
+ }
+
+ return scsi_initfn(dev, kind);
+}
+
+#define DEFINE_SCSI_DISK_PROPERTIES() \
+ DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \
+ DEFINE_PROP_STRING("ver", SCSIDiskState, version), \
+ DEFINE_PROP_STRING("serial", SCSIDiskState, serial)
+
+static SCSIDeviceInfo scsi_disk_info[] = {
+ {
+ .qdev.name = "scsi-hd",
+ .qdev.fw_name = "disk",
+ .qdev.desc = "virtual SCSI disk",
+ .qdev.size = sizeof(SCSIDiskState),
+ .qdev.reset = scsi_disk_reset,
+ .init = scsi_hd_initfn,
+ .destroy = scsi_destroy,
+ .alloc_req = scsi_new_request,
+ .free_req = scsi_free_request,
+ .send_command = scsi_send_command,
+ .read_data = scsi_read_data,
+ .write_data = scsi_write_data,
+ .cancel_io = scsi_cancel_io,
+ .get_buf = scsi_get_buf,
+ .qdev.props = (Property[]) {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+ },{
+ .qdev.name = "scsi-cd",
+ .qdev.fw_name = "disk",
+ .qdev.desc = "virtual SCSI CD-ROM",
+ .qdev.size = sizeof(SCSIDiskState),
+ .qdev.reset = scsi_disk_reset,
+ .init = scsi_cd_initfn,
+ .destroy = scsi_destroy,
+ .alloc_req = scsi_new_request,
+ .free_req = scsi_free_request,
+ .send_command = scsi_send_command,
+ .read_data = scsi_read_data,
+ .write_data = scsi_write_data,
+ .cancel_io = scsi_cancel_io,
+ .get_buf = scsi_get_buf,
+ .qdev.props = (Property[]) {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+ },{
+ .qdev.name = "scsi-disk", /* legacy -device scsi-disk */
+ .qdev.fw_name = "disk",
+ .qdev.desc = "virtual SCSI disk or CD-ROM (legacy)",
+ .qdev.size = sizeof(SCSIDiskState),
+ .qdev.reset = scsi_disk_reset,
+ .init = scsi_disk_initfn,
+ .destroy = scsi_destroy,
+ .alloc_req = scsi_new_request,
+ .free_req = scsi_free_request,
+ .send_command = scsi_send_command,
+ .read_data = scsi_read_data,
+ .write_data = scsi_write_data,
+ .cancel_io = scsi_cancel_io,
+ .get_buf = scsi_get_buf,
+ .qdev.props = (Property[]) {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+ }
};
static void scsi_disk_register_devices(void)
{
- scsi_qdev_register(&scsi_disk_info);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(scsi_disk_info); i++) {
+ scsi_qdev_register(&scsi_disk_info[i]);
+ }
}
device_init(scsi_disk_register_devices)