* the host adapter emulator.
*/
+#include <qemu-common.h>
+#include <sysemu.h>
//#define DEBUG_SCSI
#ifdef DEBUG_SCSI
-#define DPRINTF(fmt, args...) \
-do { printf("scsi-disk: " fmt , ##args); } while (0)
+#define DPRINTF(fmt, ...) \
+do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#else
-#define DPRINTF(fmt, args...) do {} while(0)
+#define DPRINTF(fmt, ...) do {} while(0)
#endif
-#define BADF(fmt, args...) \
-do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include "qemu-common.h"
#include "block.h"
#define SENSE_HARDWARE_ERROR 4
#define SENSE_ILLEGAL_REQUEST 5
-#define SCSI_DMA_BUF_SIZE 65536
+#define STATUS_GOOD 0
+#define STATUS_CHECK_CONDITION 2
+
+#define SCSI_DMA_BUF_SIZE 131072
+#define SCSI_MAX_INQUIRY_LEN 256
+
+#define SCSI_REQ_STATUS_RETRY 0x01
+
+typedef struct SCSIDiskState SCSIDiskState;
typedef struct SCSIRequest {
- SCSIDeviceState *dev;
+ SCSIBus *bus;
+ SCSIDiskState *dev;
uint32_t tag;
- /* ??? We should probably keep track of whether the data trasfer is
+ /* ??? We should probably keep track of whether the data transfer is
a read or a write. Currently we rely on the host getting it right. */
/* Both sector and sector_count are in terms of qemu 512 byte blocks. */
- int sector;
- int sector_count;
- /* The amounnt of data in the buffer. */
- int buf_len;
- uint8_t *dma_buf;
+ uint64_t sector;
+ uint32_t sector_count;
+ struct iovec iov;
+ QEMUIOVector qiov;
BlockDriverAIOCB *aiocb;
struct SCSIRequest *next;
+ uint32_t status;
} SCSIRequest;
-struct SCSIDeviceState
+struct SCSIDiskState
{
- BlockDriverState *bdrv;
+ SCSIDevice qdev;
+ DriveInfo *dinfo;
SCSIRequest *requests;
/* 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;
+ uint64_t max_lba;
int sense;
- int tcq;
- /* Completion functions may be called from either scsi_{read,write}_data
- or from the AIO completion routines. */
- scsi_completionfn completion;
- void *opaque;
+ char drive_serial_str[21];
+ QEMUBH *bh;
};
/* Global pool of SCSIRequest structures. */
static SCSIRequest *free_requests = NULL;
-static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag)
{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r;
if (free_requests) {
free_requests = r->next;
} else {
r = qemu_malloc(sizeof(SCSIRequest));
- r->dma_buf = qemu_memalign(512, SCSI_DMA_BUF_SIZE);
+ r->iov.iov_base = qemu_memalign(512, SCSI_DMA_BUF_SIZE);
}
+ r->bus = scsi_bus_from_device(d);
r->dev = s;
r->tag = tag;
r->sector_count = 0;
- r->buf_len = 0;
+ r->iov.iov_len = 0;
r->aiocb = NULL;
+ r->status = 0;
r->next = s->requests;
s->requests = r;
static void scsi_remove_request(SCSIRequest *r)
{
SCSIRequest *last;
- SCSIDeviceState *s = r->dev;
+ SCSIDiskState *s = r->dev;
if (s->requests == r) {
s->requests = r->next;
free_requests = r;
}
-static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
+static SCSIRequest *scsi_find_request(SCSIDiskState *s, uint32_t tag)
{
SCSIRequest *r;
}
/* Helper function for command completion. */
-static void scsi_command_complete(SCSIRequest *r, int sense)
+static void scsi_command_complete(SCSIRequest *r, int status, int sense)
{
- SCSIDeviceState *s = r->dev;
+ SCSIDiskState *s = r->dev;
uint32_t tag;
- DPRINTF("Command complete tag=0x%x sense=%d\n", r->tag, sense);
+ DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", r->tag, status, sense);
s->sense = sense;
tag = r->tag;
scsi_remove_request(r);
- s->completion(s->opaque, SCSI_REASON_DONE, tag, sense);
+ r->bus->complete(r->bus, SCSI_REASON_DONE, tag, status);
}
/* Cancel a pending data transfer. */
static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
{
- SCSIDeviceState *s = d->state;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r;
DPRINTF("Cancel tag=0x%x\n", tag);
r = scsi_find_request(s, tag);
static void scsi_read_complete(void * opaque, int ret)
{
SCSIRequest *r = (SCSIRequest *)opaque;
- SCSIDeviceState *s = r->dev;
if (ret) {
DPRINTF("IO error\n");
- scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+ r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, 0);
+ scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NO_SENSE);
return;
}
- DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, r->buf_len);
+ DPRINTF("Data ready tag=0x%x len=%" PRId64 "\n", r->tag, r->iov.iov_len);
- s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->buf_len);
+ r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
}
/* Read more data from scsi device into buffer. */
static void scsi_read_data(SCSIDevice *d, uint32_t tag)
{
- SCSIDeviceState *s = d->state;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r;
uint32_t n;
if (!r) {
BADF("Bad read tag 0x%x\n", tag);
/* ??? This is the wrong error. */
- scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+ scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
return;
}
if (r->sector_count == (uint32_t)-1) {
- DPRINTF("Read buf_len=%d\n", r->buf_len);
+ DPRINTF("Read buf_len=%" PRId64 "\n", r->iov.iov_len);
r->sector_count = 0;
- s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->buf_len);
+ r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
return;
}
DPRINTF("Read sector_count=%d\n", r->sector_count);
if (r->sector_count == 0) {
- scsi_command_complete(r, SENSE_NO_SENSE);
+ scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE);
return;
}
if (n > SCSI_DMA_BUF_SIZE / 512)
n = SCSI_DMA_BUF_SIZE / 512;
- r->buf_len = n * 512;
- r->aiocb = bdrv_aio_read(s->bdrv, r->sector, r->dma_buf, n,
- scsi_read_complete, r);
+ r->iov.iov_len = n * 512;
+ qemu_iovec_init_external(&r->qiov, &r->iov, 1);
+ r->aiocb = bdrv_aio_readv(s->dinfo->bdrv, r->sector, &r->qiov, n,
+ scsi_read_complete, r);
if (r->aiocb == NULL)
- scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+ scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
r->sector += n;
r->sector_count -= n;
}
+static int scsi_handle_write_error(SCSIRequest *r, int error)
+{
+ BlockInterfaceErrorAction action = drive_get_onerror(r->dev->dinfo->bdrv);
+
+ if (action == BLOCK_ERR_IGNORE)
+ return 0;
+
+ if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
+ || action == BLOCK_ERR_STOP_ANY) {
+ r->status |= SCSI_REQ_STATUS_RETRY;
+ vm_stop(0);
+ } else {
+ scsi_command_complete(r, STATUS_CHECK_CONDITION,
+ SENSE_HARDWARE_ERROR);
+ }
+
+ return 1;
+}
+
static void scsi_write_complete(void * opaque, int ret)
{
SCSIRequest *r = (SCSIRequest *)opaque;
- SCSIDeviceState *s = r->dev;
uint32_t len;
+ uint32_t n;
+
+ r->aiocb = NULL;
if (ret) {
- fprintf(stderr, "scsi-disc: IO write error\n");
- exit(1);
+ if (scsi_handle_write_error(r, -ret))
+ return;
}
- r->aiocb = NULL;
+ n = r->iov.iov_len / 512;
+ r->sector += n;
+ r->sector_count -= n;
if (r->sector_count == 0) {
- scsi_command_complete(r, SENSE_NO_SENSE);
+ scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE);
} else {
len = r->sector_count * 512;
if (len > SCSI_DMA_BUF_SIZE) {
len = SCSI_DMA_BUF_SIZE;
}
- r->buf_len = len;
+ r->iov.iov_len = len;
DPRINTF("Write complete tag=0x%x more=%d\n", r->tag, len);
- s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
+ r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, len);
+ }
+}
+
+static void scsi_write_request(SCSIRequest *r)
+{
+ SCSIDiskState *s = r->dev;
+ uint32_t n;
+
+ n = r->iov.iov_len / 512;
+ if (n) {
+ qemu_iovec_init_external(&r->qiov, &r->iov, 1);
+ r->aiocb = bdrv_aio_writev(s->dinfo->bdrv, r->sector, &r->qiov, n,
+ scsi_write_complete, r);
+ if (r->aiocb == NULL)
+ scsi_command_complete(r, STATUS_CHECK_CONDITION,
+ SENSE_HARDWARE_ERROR);
+ } else {
+ /* Invoke completion routine to fetch data from host. */
+ scsi_write_complete(r, 0);
}
}
The transfer may complete asynchronously. */
static int scsi_write_data(SCSIDevice *d, uint32_t tag)
{
- SCSIDeviceState *s = d->state;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r;
- uint32_t n;
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, SENSE_HARDWARE_ERROR);
+ scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
return 1;
}
+
if (r->aiocb)
BADF("Data transfer already in progress\n");
- n = r->buf_len / 512;
- if (n) {
- r->aiocb = bdrv_aio_write(s->bdrv, r->sector, r->dma_buf, n,
- scsi_write_complete, r);
- if (r->aiocb == NULL)
- scsi_command_complete(r, SENSE_HARDWARE_ERROR);
- r->sector += n;
- r->sector_count -= n;
- } else {
- /* Invoke completion routine to fetch data from host. */
- scsi_write_complete(r, 0);
- }
+
+ scsi_write_request(r);
return 0;
}
+static void scsi_dma_restart_bh(void *opaque)
+{
+ SCSIDiskState *s = opaque;
+ SCSIRequest *r = s->requests;
+
+ qemu_bh_delete(s->bh);
+ s->bh = NULL;
+
+ while (r) {
+ if (r->status & SCSI_REQ_STATUS_RETRY) {
+ r->status &= ~SCSI_REQ_STATUS_RETRY;
+ scsi_write_request(r);
+ }
+ r = r->next;
+ }
+}
+
+static void scsi_dma_restart_cb(void *opaque, int running, int reason)
+{
+ SCSIDiskState *s = opaque;
+
+ if (!running)
+ return;
+
+ if (!s->bh) {
+ s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
+ qemu_bh_schedule(s->bh);
+ }
+}
+
/* Return a pointer to the data buffer. */
static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
{
- SCSIDeviceState *s = d->state;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r;
r = scsi_find_request(s, tag);
BADF("Bad buffer tag 0x%x\n", tag);
return NULL;
}
- return r->dma_buf;
+ return (uint8_t *)r->iov.iov_base;
}
/* Execute a scsi command. Returns the length of the data expected by the
static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
uint8_t *buf, int lun)
{
- SCSIDeviceState *s = d->state;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
uint64_t nb_sectors;
- uint32_t lba;
+ uint64_t lba;
uint32_t len;
int cmdlen;
int is_write;
}
/* ??? Tags are not unique for different luns. We only implement a
single lun, so this should not matter. */
- r = scsi_new_request(s, tag);
- outbuf = r->dma_buf;
+ r = scsi_new_request(d, tag);
+ 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 = buf[3] | (buf[2] << 8) | ((buf[1] & 0x1f) << 16);
+ 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 = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+ 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 = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+ 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 = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+ 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;
if (lun || buf[1] >> 5) {
/* Only LUN 0 supported. */
DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
- goto fail;
+ if (command != 0x03 && command != 0x12) /* REQUEST SENSE and INQUIRY */
+ goto fail;
}
switch (command) {
case 0x0:
DPRINTF("Test Unit Ready\n");
+ if (!bdrv_is_inserted(s->dinfo->bdrv))
+ goto notready;
break;
case 0x03:
DPRINTF("Request Sense (len %d)\n", len);
if (len < 4)
goto fail;
memset(outbuf, 0, 4);
+ r->iov.iov_len = 4;
+ if (s->sense == SENSE_NOT_READY && len >= 18) {
+ memset(outbuf, 0, 18);
+ r->iov.iov_len = 18;
+ outbuf[7] = 10;
+ /* asc 0x3a, ascq 0: Medium not present */
+ outbuf[12] = 0x3a;
+ outbuf[13] = 0;
+ }
outbuf[0] = 0xf0;
outbuf[1] = 0;
outbuf[2] = s->sense;
- r->buf_len = 4;
break;
case 0x12:
DPRINTF("Inquiry (len %d)\n", len);
DPRINTF("Inquiry EVPD[Supported pages] "
"buffer size %d\n", len);
- r->buf_len = 0;
+ r->iov.iov_len = 0;
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
- outbuf[r->buf_len++] = 5;
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
+ outbuf[r->iov.iov_len++] = 5;
} else {
- outbuf[r->buf_len++] = 0;
+ outbuf[r->iov.iov_len++] = 0;
}
- outbuf[r->buf_len++] = 0x00; // this page
- outbuf[r->buf_len++] = 0x00;
- outbuf[r->buf_len++] = 3; // number of pages
- outbuf[r->buf_len++] = 0x00; // list of supported pages (this page)
- outbuf[r->buf_len++] = 0x80; // unit serial number
- outbuf[r->buf_len++] = 0x83; // device identification
+ outbuf[r->iov.iov_len++] = 0x00; // this page
+ outbuf[r->iov.iov_len++] = 0x00;
+ outbuf[r->iov.iov_len++] = 3; // number of pages
+ outbuf[r->iov.iov_len++] = 0x00; // list of supported pages (this page)
+ outbuf[r->iov.iov_len++] = 0x80; // unit serial number
+ outbuf[r->iov.iov_len++] = 0x83; // device identification
}
break;
case 0x80:
{
+ int l;
+
/* Device serial number, optional */
if (len < 4) {
BADF("Error: EVPD[Serial number] Inquiry buffer "
}
DPRINTF("Inquiry EVPD[Serial number] buffer size %d\n", len);
+ l = MIN(len, strlen(s->drive_serial_str));
- r->buf_len = 0;
+ r->iov.iov_len = 0;
/* Supported page codes */
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
- outbuf[r->buf_len++] = 5;
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
+ outbuf[r->iov.iov_len++] = 5;
} else {
- outbuf[r->buf_len++] = 0;
+ outbuf[r->iov.iov_len++] = 0;
}
- outbuf[r->buf_len++] = 0x80; // this page
- outbuf[r->buf_len++] = 0x00;
- outbuf[r->buf_len++] = 0x01; // 1 byte data follow
-
- outbuf[r->buf_len++] = '0'; // 1 byte data follow
+ outbuf[r->iov.iov_len++] = 0x80; // this page
+ outbuf[r->iov.iov_len++] = 0x00;
+ outbuf[r->iov.iov_len++] = l;
+ memcpy(&outbuf[r->iov.iov_len], s->drive_serial_str, l);
+ r->iov.iov_len += l;
}
break;
{
/* Device identification page, mandatory */
int max_len = 255 - 8;
- int id_len = strlen(bdrv_get_device_name(s->bdrv));
+ int id_len = strlen(bdrv_get_device_name(s->dinfo->bdrv));
if (id_len > max_len)
id_len = max_len;
DPRINTF("Inquiry EVPD[Device identification] "
"buffer size %d\n", len);
- r->buf_len = 0;
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
- outbuf[r->buf_len++] = 5;
+ r->iov.iov_len = 0;
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
+ outbuf[r->iov.iov_len++] = 5;
} else {
- outbuf[r->buf_len++] = 0;
+ outbuf[r->iov.iov_len++] = 0;
}
- outbuf[r->buf_len++] = 0x83; // this page
- outbuf[r->buf_len++] = 0x00;
- outbuf[r->buf_len++] = 3 + id_len;
+ outbuf[r->iov.iov_len++] = 0x83; // this page
+ outbuf[r->iov.iov_len++] = 0x00;
+ outbuf[r->iov.iov_len++] = 3 + id_len;
- outbuf[r->buf_len++] = 0x2; // ASCII
- outbuf[r->buf_len++] = 0; // not officially assigned
- outbuf[r->buf_len++] = 0; // reserved
- outbuf[r->buf_len++] = id_len; // length of data following
+ outbuf[r->iov.iov_len++] = 0x2; // ASCII
+ outbuf[r->iov.iov_len++] = 0; // not officially assigned
+ outbuf[r->iov.iov_len++] = 0; // reserved
+ outbuf[r->iov.iov_len++] = id_len; // length of data following
- memcpy(&outbuf[r->buf_len],
- bdrv_get_device_name(s->bdrv), id_len);
- r->buf_len += id_len;
+ memcpy(&outbuf[r->iov.iov_len],
+ bdrv_get_device_name(s->dinfo->bdrv), id_len);
+ r->iov.iov_len += id_len;
}
break;
default:
"is less than 36 (TODO: only 5 required)\n", len);
}
}
- memset(outbuf, 0, 36);
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+
+ if(len > SCSI_MAX_INQUIRY_LEN)
+ len = SCSI_MAX_INQUIRY_LEN;
+
+ memset(outbuf, 0, len);
+
+ if (lun || buf[1] >> 5) {
+ outbuf[0] = 0x7f; /* LUN not supported */
+ } else if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
outbuf[0] = 5;
outbuf[1] = 0x80;
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
Some later commands are also implemented. */
outbuf[2] = 3;
outbuf[3] = 2; /* Format 2 */
- outbuf[4] = 31;
+ outbuf[4] = len - 5; /* Additional Length = (Len - 1) - 4 */
/* Sync data transfer and TCQ. */
- outbuf[7] = 0x10 | (s->tcq ? 0x02 : 0);
- r->buf_len = 36;
+ outbuf[7] = 0x10 | (r->bus->tcq ? 0x02 : 0);
+ r->iov.iov_len = len;
break;
case 0x16:
DPRINTF("Reserve(6)\n");
{
uint8_t *p;
int page;
-
+ int dbd;
+
+ dbd = buf[1] & 0x8;
page = buf[2] & 0x3f;
DPRINTF("Mode Sense (page %d, len %d)\n", page, len);
p = outbuf;
memset(p, 0, 4);
outbuf[1] = 0; /* Default media type. */
outbuf[3] = 0; /* Block descriptor length. */
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
outbuf[2] = 0x80; /* Readonly. */
}
p += 4;
- if ((page == 8 || page == 0x3f)) {
+ bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
+ if ((~dbd) & nb_sectors) {
+ nb_sectors /= s->cluster_size;
+ nb_sectors--;
+ if (nb_sectors > 0xffffff)
+ nb_sectors = 0xffffff;
+ outbuf[3] = 8; /* Block descriptor length */
+ p[0] = 0; /* media density code */
+ p[1] = (nb_sectors >> 16) & 0xff;
+ p[2] = (nb_sectors >> 8) & 0xff;
+ p[3] = nb_sectors & 0xff;
+ p[4] = 0; /* reserved */
+ p[5] = 0; /* bytes 5-7 are the sector size in bytes */
+ p[6] = s->cluster_size * 2;
+ p[7] = 0;
+ p += 8;
+ }
+
+ if (page == 4) {
+ int cylinders, heads, secs;
+
+ /* Rigid disk device geometry page. */
+ p[0] = 4;
+ p[1] = 0x16;
+ /* if a geometry hint is available, use it */
+ bdrv_get_geometry_hint(s->dinfo->bdrv, &cylinders, &heads, &secs);
+ p[2] = (cylinders >> 16) & 0xff;
+ p[3] = (cylinders >> 8) & 0xff;
+ p[4] = cylinders & 0xff;
+ p[5] = heads & 0xff;
+ /* Write precomp start cylinder, disabled */
+ p[6] = (cylinders >> 16) & 0xff;
+ p[7] = (cylinders >> 8) & 0xff;
+ p[8] = cylinders & 0xff;
+ /* Reduced current start cylinder, disabled */
+ p[9] = (cylinders >> 16) & 0xff;
+ p[10] = (cylinders >> 8) & 0xff;
+ p[11] = cylinders & 0xff;
+ /* Device step rate [ns], 200ns */
+ p[12] = 0;
+ p[13] = 200;
+ /* Landing zone cylinder */
+ p[14] = 0xff;
+ p[15] = 0xff;
+ p[16] = 0xff;
+ /* Medium rotation rate [rpm], 5400 rpm */
+ p[20] = (5400 >> 8) & 0xff;
+ p[21] = 5400 & 0xff;
+ p += 0x16;
+ } else if (page == 5) {
+ int cylinders, heads, secs;
+
+ /* Flexible disk device geometry page. */
+ p[0] = 5;
+ p[1] = 0x1e;
+ /* Transfer rate [kbit/s], 5Mbit/s */
+ p[2] = 5000 >> 8;
+ p[3] = 5000 & 0xff;
+ /* if a geometry hint is available, use it */
+ bdrv_get_geometry_hint(s->dinfo->bdrv, &cylinders, &heads, &secs);
+ p[4] = heads & 0xff;
+ p[5] = secs & 0xff;
+ p[6] = s->cluster_size * 2;
+ p[8] = (cylinders >> 8) & 0xff;
+ p[9] = cylinders & 0xff;
+ /* Write precomp start cylinder, disabled */
+ p[10] = (cylinders >> 8) & 0xff;
+ p[11] = cylinders & 0xff;
+ /* Reduced current start cylinder, disabled */
+ p[12] = (cylinders >> 8) & 0xff;
+ p[13] = cylinders & 0xff;
+ /* Device step rate [100us], 100us */
+ p[14] = 0;
+ p[15] = 1;
+ /* Device step pulse width [us], 1us */
+ p[16] = 1;
+ /* Device head settle delay [100us], 100us */
+ p[17] = 0;
+ p[18] = 1;
+ /* Motor on delay [0.1s], 0.1s */
+ p[19] = 1;
+ /* Motor off delay [0.1s], 0.1s */
+ p[20] = 1;
+ /* Medium rotation rate [rpm], 5400 rpm */
+ p[28] = (5400 >> 8) & 0xff;
+ p[29] = 5400 & 0xff;
+ p += 0x1e;
+ } else if ((page == 8 || page == 0x3f)) {
/* Caching page. */
memset(p,0,20);
p[0] = 8;
p[1] = 0x12;
- p[2] = 4; /* WCE */
+ if (bdrv_enable_write_cache(s->dinfo->bdrv)) {
+ p[2] = 4; /* WCE */
+ }
p += 20;
}
if ((page == 0x3f || page == 0x2a)
- && (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) {
+ && (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM)) {
/* CD Capabilities and Mechanical Status page. */
p[0] = 0x2a;
p[1] = 0x14;
p[5] = 0xff; /* CD DA, DA accurate, RW supported,
RW corrected, C2 errors, ISRC,
UPC, Bar code */
- p[6] = 0x2d | (bdrv_is_locked(s->bdrv)? 2 : 0);
+ p[6] = 0x2d | (bdrv_is_locked(s->dinfo->bdrv)? 2 : 0);
/* Locking supported, jumper present, eject, tray */
p[7] = 0; /* no volume & mute control, no
changer */
p[21] = (16 * 176) & 0xff;
p += 22;
}
- r->buf_len = p - outbuf;
- outbuf[0] = r->buf_len - 4;
- if (r->buf_len > len)
- r->buf_len = len;
+ r->iov.iov_len = p - outbuf;
+ outbuf[0] = r->iov.iov_len - 4;
+ if (r->iov.iov_len > len)
+ r->iov.iov_len = len;
}
break;
case 0x1b:
DPRINTF("Start Stop Unit\n");
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM &&
+ (buf[4] & 2))
+ /* load/eject medium */
+ bdrv_eject(s->dinfo->bdrv, !(buf[4] & 1));
break;
case 0x1e:
DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3);
- bdrv_set_locked(s->bdrv, buf[4] & 1);
+ bdrv_set_locked(s->dinfo->bdrv, buf[4] & 1);
break;
case 0x25:
DPRINTF("Read Capacity\n");
/* The normal LEN field for this command is zero. */
memset(outbuf, 0, 8);
- bdrv_get_geometry(s->bdrv, &nb_sectors);
+ bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
+ nb_sectors /= s->cluster_size;
/* Returned value is the address of the last sector. */
if (nb_sectors) {
nb_sectors--;
+ /* Remember the new size for read/write sanity checking. */
+ s->max_lba = nb_sectors;
+ /* Clip to 2TB, instead of returning capacity modulo 2TB. */
+ if (nb_sectors > UINT32_MAX)
+ nb_sectors = UINT32_MAX;
outbuf[0] = (nb_sectors >> 24) & 0xff;
outbuf[1] = (nb_sectors >> 16) & 0xff;
outbuf[2] = (nb_sectors >> 8) & 0xff;
outbuf[5] = 0;
outbuf[6] = s->cluster_size * 2;
outbuf[7] = 0;
- r->buf_len = 8;
+ r->iov.iov_len = 8;
} else {
- scsi_command_complete(r, SENSE_NOT_READY);
+ notready:
+ scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NOT_READY);
return 0;
}
break;
case 0x08:
case 0x28:
- DPRINTF("Read (sector %d, count %d)\n", lba, len);
+ case 0x88:
+ DPRINTF("Read (sector %" PRId64 ", count %d)\n", lba, len);
+ if (lba > s->max_lba)
+ goto illegal_lba;
r->sector = lba * s->cluster_size;
r->sector_count = len * s->cluster_size;
break;
case 0x0a:
case 0x2a:
- DPRINTF("Write (sector %d, count %d)\n", lba, len);
+ case 0x8a:
+ DPRINTF("Write (sector %" PRId64 ", count %d)\n", lba, len);
+ if (lba > s->max_lba)
+ goto illegal_lba;
r->sector = lba * s->cluster_size;
r->sector_count = len * s->cluster_size;
is_write = 1;
break;
case 0x35:
- DPRINTF("Synchronise cache (sector %d, count %d)\n", lba, len);
- bdrv_flush(s->bdrv);
+ DPRINTF("Synchronise cache (sector %" PRId64 ", count %d)\n", lba, len);
+ bdrv_flush(s->dinfo->bdrv);
break;
case 0x43:
{
msf = buf[1] & 2;
format = buf[2] & 0xf;
start_track = buf[6];
- bdrv_get_geometry(s->bdrv, &nb_sectors);
+ bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
+ nb_sectors /= s->cluster_size;
switch(format) {
case 0:
toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track);
if (toclen > 0) {
if (len > toclen)
len = toclen;
- r->buf_len = len;
+ r->iov.iov_len = len;
break;
}
error_cmd:
case 0x46:
DPRINTF("Get Configuration (rt %d, maxlen %d)\n", buf[1] & 3, len);
memset(outbuf, 0, 8);
- /* ??? This shoud probably return much more information. For now
+ /* ??? This should probably return much more information. For now
just return the basic header indicating the CD-ROM profile. */
outbuf[7] = 8; // CD-ROM
- r->buf_len = 8;
+ r->iov.iov_len = 8;
break;
case 0x56:
DPRINTF("Reserve(10)\n");
if (buf[1] & 3)
goto fail;
break;
+ case 0x9e:
+ /* Service Action In subcommands. */
+ if ((buf[1] & 31) == 0x10) {
+ DPRINTF("SAI READ CAPACITY(16)\n");
+ memset(outbuf, 0, len);
+ bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
+ nb_sectors /= s->cluster_size;
+ /* Returned value is the address of the last sector. */
+ if (nb_sectors) {
+ nb_sectors--;
+ /* Remember the new size for read/write sanity checking. */
+ s->max_lba = nb_sectors;
+ outbuf[0] = (nb_sectors >> 56) & 0xff;
+ outbuf[1] = (nb_sectors >> 48) & 0xff;
+ outbuf[2] = (nb_sectors >> 40) & 0xff;
+ outbuf[3] = (nb_sectors >> 32) & 0xff;
+ outbuf[4] = (nb_sectors >> 24) & 0xff;
+ outbuf[5] = (nb_sectors >> 16) & 0xff;
+ outbuf[6] = (nb_sectors >> 8) & 0xff;
+ outbuf[7] = nb_sectors & 0xff;
+ outbuf[8] = 0;
+ outbuf[9] = 0;
+ outbuf[10] = s->cluster_size * 2;
+ outbuf[11] = 0;
+ /* Protection, exponent and lowest lba field left blank. */
+ r->iov.iov_len = len;
+ } else {
+ scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NOT_READY);
+ return 0;
+ }
+ break;
+ }
+ DPRINTF("Unsupported Service Action In\n");
+ goto fail;
case 0xa0:
DPRINTF("Report LUNs (len %d)\n", len);
if (len < 16)
goto fail;
memset(outbuf, 0, 16);
outbuf[3] = 8;
- r->buf_len = 16;
+ r->iov.iov_len = 16;
+ break;
+ case 0x2f:
+ DPRINTF("Verify (sector %" PRId64 ", count %d)\n", lba, len);
break;
default:
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
fail:
- scsi_command_complete(r, SENSE_ILLEGAL_REQUEST);
+ scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_ILLEGAL_REQUEST);
return 0;
+ illegal_lba:
+ scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
+ return 0;
}
- if (r->sector_count == 0 && r->buf_len == 0) {
- scsi_command_complete(r, SENSE_NO_SENSE);
+ if (r->sector_count == 0 && r->iov.iov_len == 0) {
+ scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE);
}
- len = r->sector_count * 512 + r->buf_len;
+ len = r->sector_count * 512 + r->iov.iov_len;
if (is_write) {
return -len;
} else {
}
}
-static void scsi_destroy(SCSIDevice *d)
+static void scsi_destroy(SCSIDevice *dev)
{
- qemu_free(d->state);
- qemu_free(d);
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+
+ drive_uninit(s->dinfo);
}
-SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
- scsi_completionfn completion, void *opaque)
+static int scsi_disk_initfn(SCSIDevice *dev)
{
- SCSIDevice *d;
- SCSIDeviceState *s;
-
- s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState));
- s->bdrv = bdrv;
- s->tcq = tcq;
- s->completion = completion;
- s->opaque = opaque;
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+ uint64_t nb_sectors;
+
+ if (!s->dinfo || !s->dinfo->bdrv) {
+ qemu_error("scsi-disk: drive property not set\n");
+ return -1;
+ }
+
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
s->cluster_size = 4;
} else {
s->cluster_size = 1;
}
+ bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
+ nb_sectors /= s->cluster_size;
+ if (nb_sectors)
+ nb_sectors--;
+ s->max_lba = nb_sectors;
+ strncpy(s->drive_serial_str, drive_get_serial(s->dinfo->bdrv),
+ sizeof(s->drive_serial_str));
+ if (strlen(s->drive_serial_str) == 0)
+ pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), "0");
+ qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
+ return 0;
+}
- d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
- d->state = s;
- d->destroy = scsi_destroy;
- d->send_command = scsi_send_command;
- d->read_data = scsi_read_data;
- d->write_data = scsi_write_data;
- d->cancel_io = scsi_cancel_io;
- d->get_buf = scsi_get_buf;
+static SCSIDeviceInfo scsi_disk_info = {
+ .qdev.name = "scsi-disk",
+ .qdev.desc = "virtual scsi disk or cdrom",
+ .qdev.size = sizeof(SCSIDiskState),
+ .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_PROP_DRIVE("drive", SCSIDiskState, dinfo),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
- return d;
+static void scsi_disk_register_devices(void)
+{
+ scsi_qdev_register(&scsi_disk_info);
}
+device_init(scsi_disk_register_devices)