Merge remote-tracking branch 'bonzini/scsi-next' into staging
* bonzini/scsi-next: scsi: Add assertion for use-after-free errors scsi: remove useless debug messages scsi: set VALID bit to 0 in fixed format sense data scsi: do not require a minimum allocation length for REQUEST SENSE scsi: do not require a minimum allocation length for INQUIRY scsi: parse 16-byte tape CDBs scsi: do not report bogus overruns for commands in the 0x00-0x1F range scsi-disk: add dpofua property scsi: change "removable" field to host many features scsi: Specify the xfer direction for UNMAP and ATA_PASSTHROUGH commands scsi: fix WRITE SAME transfer length and direction scsi: fix refcounting for reads scsi: prevent data transfer overflow ISCSI: Add support for thin-provisioning via discard/UNMAP and bigger LUNs
This commit is contained in:
commit
e45bca682c
@ -383,6 +383,65 @@ iscsi_aio_flush(BlockDriverState *bs,
|
|||||||
return &acb->common;
|
return &acb->common;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
|
||||||
|
void *command_data, void *opaque)
|
||||||
|
{
|
||||||
|
IscsiAIOCB *acb = opaque;
|
||||||
|
|
||||||
|
if (acb->canceled != 0) {
|
||||||
|
qemu_aio_release(acb);
|
||||||
|
scsi_free_scsi_task(acb->task);
|
||||||
|
acb->task = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
acb->status = 0;
|
||||||
|
if (status < 0) {
|
||||||
|
error_report("Failed to unmap data on iSCSI lun. %s",
|
||||||
|
iscsi_get_error(iscsi));
|
||||||
|
acb->status = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
|
||||||
|
scsi_free_scsi_task(acb->task);
|
||||||
|
acb->task = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockDriverAIOCB *
|
||||||
|
iscsi_aio_discard(BlockDriverState *bs,
|
||||||
|
int64_t sector_num, int nb_sectors,
|
||||||
|
BlockDriverCompletionFunc *cb, void *opaque)
|
||||||
|
{
|
||||||
|
IscsiLun *iscsilun = bs->opaque;
|
||||||
|
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||||
|
IscsiAIOCB *acb;
|
||||||
|
struct unmap_list list[1];
|
||||||
|
|
||||||
|
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
|
||||||
|
|
||||||
|
acb->iscsilun = iscsilun;
|
||||||
|
acb->canceled = 0;
|
||||||
|
|
||||||
|
list[0].lba = sector_qemu2lun(sector_num, iscsilun);
|
||||||
|
list[0].num = nb_sectors * BDRV_SECTOR_SIZE / iscsilun->block_size;
|
||||||
|
|
||||||
|
acb->task = iscsi_unmap_task(iscsi, iscsilun->lun,
|
||||||
|
0, 0, &list[0], 1,
|
||||||
|
iscsi_unmap_cb,
|
||||||
|
acb);
|
||||||
|
if (acb->task == NULL) {
|
||||||
|
error_report("iSCSI: Failed to send unmap command. %s",
|
||||||
|
iscsi_get_error(iscsi));
|
||||||
|
qemu_aio_release(acb);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
iscsi_set_events(iscsilun);
|
||||||
|
|
||||||
|
return &acb->common;
|
||||||
|
}
|
||||||
|
|
||||||
static int64_t
|
static int64_t
|
||||||
iscsi_getlength(BlockDriverState *bs)
|
iscsi_getlength(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
@ -396,11 +455,11 @@ iscsi_getlength(BlockDriverState *bs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
|
iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
|
||||||
void *command_data, void *opaque)
|
void *command_data, void *opaque)
|
||||||
{
|
{
|
||||||
struct IscsiTask *itask = opaque;
|
struct IscsiTask *itask = opaque;
|
||||||
struct scsi_readcapacity10 *rc10;
|
struct scsi_readcapacity16 *rc16;
|
||||||
struct scsi_task *task = command_data;
|
struct scsi_task *task = command_data;
|
||||||
|
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
@ -412,26 +471,25 @@ iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc10 = scsi_datain_unmarshall(task);
|
rc16 = scsi_datain_unmarshall(task);
|
||||||
if (rc10 == NULL) {
|
if (rc16 == NULL) {
|
||||||
error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
|
error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
|
||||||
itask->status = 1;
|
itask->status = 1;
|
||||||
itask->complete = 1;
|
itask->complete = 1;
|
||||||
scsi_free_scsi_task(task);
|
scsi_free_scsi_task(task);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
itask->iscsilun->block_size = rc10->block_size;
|
itask->iscsilun->block_size = rc16->block_length;
|
||||||
itask->iscsilun->num_blocks = rc10->lba;
|
itask->iscsilun->num_blocks = rc16->returned_lba + 1;
|
||||||
itask->bs->total_sectors = (uint64_t)rc10->lba *
|
itask->bs->total_sectors = itask->iscsilun->num_blocks *
|
||||||
rc10->block_size / BDRV_SECTOR_SIZE ;
|
itask->iscsilun->block_size / BDRV_SECTOR_SIZE ;
|
||||||
|
|
||||||
itask->status = 0;
|
itask->status = 0;
|
||||||
itask->complete = 1;
|
itask->complete = 1;
|
||||||
scsi_free_scsi_task(task);
|
scsi_free_scsi_task(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
@ -445,10 +503,10 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun, 0, 0,
|
task = iscsi_readcapacity16_task(iscsi, itask->iscsilun->lun,
|
||||||
iscsi_readcapacity10_cb, opaque);
|
iscsi_readcapacity16_cb, opaque);
|
||||||
if (task == NULL) {
|
if (task == NULL) {
|
||||||
error_report("iSCSI: failed to send readcapacity command.");
|
error_report("iSCSI: failed to send readcapacity16 command.");
|
||||||
itask->status = 1;
|
itask->status = 1;
|
||||||
itask->complete = 1;
|
itask->complete = 1;
|
||||||
return;
|
return;
|
||||||
@ -700,6 +758,8 @@ static BlockDriver bdrv_iscsi = {
|
|||||||
.bdrv_aio_readv = iscsi_aio_readv,
|
.bdrv_aio_readv = iscsi_aio_readv,
|
||||||
.bdrv_aio_writev = iscsi_aio_writev,
|
.bdrv_aio_writev = iscsi_aio_writev,
|
||||||
.bdrv_aio_flush = iscsi_aio_flush,
|
.bdrv_aio_flush = iscsi_aio_flush,
|
||||||
|
|
||||||
|
.bdrv_aio_discard = iscsi_aio_discard,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void iscsi_block_init(void)
|
static void iscsi_block_init(void)
|
||||||
|
5
configure
vendored
5
configure
vendored
@ -2546,10 +2546,13 @@ fi
|
|||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# Do we have libiscsi
|
# Do we have libiscsi
|
||||||
|
# We check for iscsi_unmap_sync() to make sure we have a
|
||||||
|
# recent enough version of libiscsi.
|
||||||
if test "$libiscsi" != "no" ; then
|
if test "$libiscsi" != "no" ; then
|
||||||
cat > $TMPC << EOF
|
cat > $TMPC << EOF
|
||||||
|
#include <stdio.h>
|
||||||
#include <iscsi/iscsi.h>
|
#include <iscsi/iscsi.h>
|
||||||
int main(void) { iscsi_create_context(""); return 0; }
|
int main(void) { iscsi_unmap_sync(NULL,0,0,0,NULL,0); return 0; }
|
||||||
EOF
|
EOF
|
||||||
if compile_prog "-Werror" "-liscsi" ; then
|
if compile_prog "-Werror" "-liscsi" ; then
|
||||||
libiscsi="yes"
|
libiscsi="yes"
|
||||||
|
@ -239,6 +239,18 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int32_t scsi_invalid_field(SCSIRequest *req, uint8_t *buf)
|
||||||
|
{
|
||||||
|
scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD));
|
||||||
|
scsi_req_complete(req, CHECK_CONDITION);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct SCSIReqOps reqops_invalid_field = {
|
||||||
|
.size = sizeof(SCSIRequest),
|
||||||
|
.send_command = scsi_invalid_field
|
||||||
|
};
|
||||||
|
|
||||||
/* SCSIReqOps implementation for invalid commands. */
|
/* SCSIReqOps implementation for invalid commands. */
|
||||||
|
|
||||||
static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf)
|
static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf)
|
||||||
@ -355,10 +367,6 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
|
|||||||
if (r->req.cmd.buf[1] & 0x1) {
|
if (r->req.cmd.buf[1] & 0x1) {
|
||||||
/* Vital product data */
|
/* Vital product data */
|
||||||
uint8_t page_code = r->req.cmd.buf[2];
|
uint8_t page_code = r->req.cmd.buf[2];
|
||||||
if (r->req.cmd.xfer < 4) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
r->buf[r->len++] = page_code ; /* this page */
|
r->buf[r->len++] = page_code ; /* this page */
|
||||||
r->buf[r->len++] = 0x00;
|
r->buf[r->len++] = 0x00;
|
||||||
|
|
||||||
@ -386,10 +394,6 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* PAGE CODE == 0 */
|
/* PAGE CODE == 0 */
|
||||||
if (r->req.cmd.xfer < 5) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
r->len = MIN(r->req.cmd.xfer, 36);
|
r->len = MIN(r->req.cmd.xfer, 36);
|
||||||
memset(r->buf, 0, r->len);
|
memset(r->buf, 0, r->len);
|
||||||
if (r->req.lun != 0) {
|
if (r->req.lun != 0) {
|
||||||
@ -423,9 +427,6 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REQUEST_SENSE:
|
case REQUEST_SENSE:
|
||||||
if (req->cmd.xfer < 4) {
|
|
||||||
goto illegal_request;
|
|
||||||
}
|
|
||||||
r->len = scsi_device_get_sense(r->req.dev, r->buf,
|
r->len = scsi_device_get_sense(r->req.dev, r->buf,
|
||||||
MIN(req->cmd.xfer, sizeof r->buf),
|
MIN(req->cmd.xfer, sizeof r->buf),
|
||||||
(req->cmd.buf[1] & 1) == 0);
|
(req->cmd.buf[1] & 1) == 0);
|
||||||
@ -517,7 +518,9 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
|
|||||||
cmd.lba);
|
cmd.lba);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((d->unit_attention.key == UNIT_ATTENTION ||
|
if (cmd.xfer > INT32_MAX) {
|
||||||
|
req = scsi_req_alloc(&reqops_invalid_field, d, tag, lun, hba_private);
|
||||||
|
} else if ((d->unit_attention.key == UNIT_ATTENTION ||
|
||||||
bus->unit_attention.key == UNIT_ATTENTION) &&
|
bus->unit_attention.key == UNIT_ATTENTION) &&
|
||||||
(buf[0] != INQUIRY &&
|
(buf[0] != INQUIRY &&
|
||||||
buf[0] != REPORT_LUNS &&
|
buf[0] != REPORT_LUNS &&
|
||||||
@ -533,7 +536,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
|
|||||||
hba_private);
|
hba_private);
|
||||||
} else if (lun != d->lun ||
|
} else if (lun != d->lun ||
|
||||||
buf[0] == REPORT_LUNS ||
|
buf[0] == REPORT_LUNS ||
|
||||||
(buf[0] == REQUEST_SENSE && (d->sense_len || cmd.xfer < 4))) {
|
(buf[0] == REQUEST_SENSE && d->sense_len)) {
|
||||||
req = scsi_req_alloc(&reqops_target_command, d, tag, lun,
|
req = scsi_req_alloc(&reqops_target_command, d, tag, lun,
|
||||||
hba_private);
|
hba_private);
|
||||||
} else {
|
} else {
|
||||||
@ -646,7 +649,7 @@ void scsi_req_build_sense(SCSIRequest *req, SCSISense sense)
|
|||||||
trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag,
|
trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag,
|
||||||
sense.key, sense.asc, sense.ascq);
|
sense.key, sense.asc, sense.ascq);
|
||||||
memset(req->sense, 0, 18);
|
memset(req->sense, 0, 18);
|
||||||
req->sense[0] = 0xf0;
|
req->sense[0] = 0x70;
|
||||||
req->sense[2] = sense.key;
|
req->sense[2] = sense.key;
|
||||||
req->sense[7] = 10;
|
req->sense[7] = 10;
|
||||||
req->sense[12] = sense.asc;
|
req->sense[12] = sense.asc;
|
||||||
@ -721,10 +724,6 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
|
|||||||
case 0:
|
case 0:
|
||||||
cmd->xfer = buf[4];
|
cmd->xfer = buf[4];
|
||||||
cmd->len = 6;
|
cmd->len = 6;
|
||||||
/* length 0 means 256 blocks */
|
|
||||||
if (cmd->xfer == 0) {
|
|
||||||
cmd->xfer = 256;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
@ -777,7 +776,8 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
|
|||||||
case MODE_SENSE:
|
case MODE_SENSE:
|
||||||
break;
|
break;
|
||||||
case WRITE_SAME_10:
|
case WRITE_SAME_10:
|
||||||
cmd->xfer = 1;
|
case WRITE_SAME_16:
|
||||||
|
cmd->xfer = dev->blocksize;
|
||||||
break;
|
break;
|
||||||
case READ_CAPACITY_10:
|
case READ_CAPACITY_10:
|
||||||
cmd->xfer = 8;
|
cmd->xfer = 8;
|
||||||
@ -793,18 +793,26 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
|
|||||||
cmd->xfer = buf[9] | (buf[8] << 8);
|
cmd->xfer = buf[9] | (buf[8] << 8);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case WRITE_6:
|
||||||
|
/* length 0 means 256 blocks */
|
||||||
|
if (cmd->xfer == 0) {
|
||||||
|
cmd->xfer = 256;
|
||||||
|
}
|
||||||
case WRITE_10:
|
case WRITE_10:
|
||||||
case WRITE_VERIFY_10:
|
case WRITE_VERIFY_10:
|
||||||
case WRITE_6:
|
|
||||||
case WRITE_12:
|
case WRITE_12:
|
||||||
case WRITE_VERIFY_12:
|
case WRITE_VERIFY_12:
|
||||||
case WRITE_16:
|
case WRITE_16:
|
||||||
case WRITE_VERIFY_16:
|
case WRITE_VERIFY_16:
|
||||||
cmd->xfer *= dev->blocksize;
|
cmd->xfer *= dev->blocksize;
|
||||||
break;
|
break;
|
||||||
case READ_10:
|
|
||||||
case READ_6:
|
case READ_6:
|
||||||
case READ_REVERSE:
|
case READ_REVERSE:
|
||||||
|
/* length 0 means 256 blocks */
|
||||||
|
if (cmd->xfer == 0) {
|
||||||
|
cmd->xfer = 256;
|
||||||
|
}
|
||||||
|
case READ_10:
|
||||||
case RECOVER_BUFFERED_DATA:
|
case RECOVER_BUFFERED_DATA:
|
||||||
case READ_12:
|
case READ_12:
|
||||||
case READ_16:
|
case READ_16:
|
||||||
@ -872,6 +880,16 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu
|
|||||||
cmd->xfer *= dev->blocksize;
|
cmd->xfer *= dev->blocksize;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case READ_16:
|
||||||
|
case READ_REVERSE_16:
|
||||||
|
case VERIFY_16:
|
||||||
|
case WRITE_16:
|
||||||
|
cmd->len = 16;
|
||||||
|
cmd->xfer = buf[14] | (buf[13] << 8) | (buf[12] << 16);
|
||||||
|
if (buf[1] & 0x01) { /* fixed */
|
||||||
|
cmd->xfer *= dev->blocksize;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case REWIND:
|
case REWIND:
|
||||||
case START_STOP:
|
case START_STOP:
|
||||||
cmd->len = 6;
|
cmd->len = 6;
|
||||||
@ -895,6 +913,10 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu
|
|||||||
|
|
||||||
static void scsi_cmd_xfer_mode(SCSICommand *cmd)
|
static void scsi_cmd_xfer_mode(SCSICommand *cmd)
|
||||||
{
|
{
|
||||||
|
if (!cmd->xfer) {
|
||||||
|
cmd->mode = SCSI_XFER_NONE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (cmd->buf[0]) {
|
switch (cmd->buf[0]) {
|
||||||
case WRITE_6:
|
case WRITE_6:
|
||||||
case WRITE_10:
|
case WRITE_10:
|
||||||
@ -920,6 +942,8 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd)
|
|||||||
case UPDATE_BLOCK:
|
case UPDATE_BLOCK:
|
||||||
case WRITE_LONG_10:
|
case WRITE_LONG_10:
|
||||||
case WRITE_SAME_10:
|
case WRITE_SAME_10:
|
||||||
|
case WRITE_SAME_16:
|
||||||
|
case UNMAP:
|
||||||
case SEARCH_HIGH_12:
|
case SEARCH_HIGH_12:
|
||||||
case SEARCH_EQUAL_12:
|
case SEARCH_EQUAL_12:
|
||||||
case SEARCH_LOW_12:
|
case SEARCH_LOW_12:
|
||||||
@ -929,14 +953,11 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd)
|
|||||||
case SEND_DVD_STRUCTURE:
|
case SEND_DVD_STRUCTURE:
|
||||||
case PERSISTENT_RESERVE_OUT:
|
case PERSISTENT_RESERVE_OUT:
|
||||||
case MAINTENANCE_OUT:
|
case MAINTENANCE_OUT:
|
||||||
|
case ATA_PASSTHROUGH:
|
||||||
cmd->mode = SCSI_XFER_TO_DEV;
|
cmd->mode = SCSI_XFER_TO_DEV;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (cmd->xfer)
|
|
||||||
cmd->mode = SCSI_XFER_FROM_DEV;
|
cmd->mode = SCSI_XFER_FROM_DEV;
|
||||||
else {
|
|
||||||
cmd->mode = SCSI_XFER_NONE;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1127,7 +1148,7 @@ int scsi_build_sense(uint8_t *in_buf, int in_len,
|
|||||||
memset(buf, 0, len);
|
memset(buf, 0, len);
|
||||||
if (fixed) {
|
if (fixed) {
|
||||||
/* Return fixed format sense buffer */
|
/* Return fixed format sense buffer */
|
||||||
buf[0] = 0xf0;
|
buf[0] = 0x70;
|
||||||
buf[2] = sense.key;
|
buf[2] = sense.key;
|
||||||
buf[7] = 10;
|
buf[7] = 10;
|
||||||
buf[12] = sense.asc;
|
buf[12] = sense.asc;
|
||||||
@ -1270,6 +1291,7 @@ SCSIRequest *scsi_req_ref(SCSIRequest *req)
|
|||||||
|
|
||||||
void scsi_req_unref(SCSIRequest *req)
|
void scsi_req_unref(SCSIRequest *req)
|
||||||
{
|
{
|
||||||
|
assert(req->refcount > 0);
|
||||||
if (--req->refcount == 0) {
|
if (--req->refcount == 0) {
|
||||||
if (req->ops->free_req) {
|
if (req->ops->free_req) {
|
||||||
req->ops->free_req(req);
|
req->ops->free_req(req);
|
||||||
|
@ -92,6 +92,7 @@
|
|||||||
#define PERSISTENT_RESERVE_OUT 0x5f
|
#define PERSISTENT_RESERVE_OUT 0x5f
|
||||||
#define VARLENGTH_CDB 0x7f
|
#define VARLENGTH_CDB 0x7f
|
||||||
#define WRITE_FILEMARKS_16 0x80
|
#define WRITE_FILEMARKS_16 0x80
|
||||||
|
#define READ_REVERSE_16 0x81
|
||||||
#define ALLOW_OVERWRITE 0x82
|
#define ALLOW_OVERWRITE 0x82
|
||||||
#define EXTENDED_COPY 0x83
|
#define EXTENDED_COPY 0x83
|
||||||
#define ATA_PASSTHROUGH 0x85
|
#define ATA_PASSTHROUGH 0x85
|
||||||
|
@ -28,9 +28,6 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
|
|||||||
#define DPRINTF(fmt, ...) do {} while(0)
|
#define DPRINTF(fmt, ...) do {} while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BADF(fmt, ...) \
|
|
||||||
do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
|
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu-error.h"
|
#include "qemu-error.h"
|
||||||
#include "scsi.h"
|
#include "scsi.h"
|
||||||
@ -61,10 +58,13 @@ typedef struct SCSIDiskReq {
|
|||||||
BlockAcctCookie acct;
|
BlockAcctCookie acct;
|
||||||
} SCSIDiskReq;
|
} SCSIDiskReq;
|
||||||
|
|
||||||
|
#define SCSI_DISK_F_REMOVABLE 0
|
||||||
|
#define SCSI_DISK_F_DPOFUA 1
|
||||||
|
|
||||||
struct SCSIDiskState
|
struct SCSIDiskState
|
||||||
{
|
{
|
||||||
SCSIDevice qdev;
|
SCSIDevice qdev;
|
||||||
uint32_t removable;
|
uint32_t features;
|
||||||
bool media_changed;
|
bool media_changed;
|
||||||
bool media_event;
|
bool media_event;
|
||||||
bool eject_request;
|
bool eject_request;
|
||||||
@ -296,6 +296,13 @@ static void scsi_do_read(void *opaque, int ret)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (r->req.io_canceled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The request is used as the AIO opaque value, so add a ref. */
|
||||||
|
scsi_req_ref(&r->req);
|
||||||
|
|
||||||
if (r->req.sg) {
|
if (r->req.sg) {
|
||||||
dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
|
dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
|
||||||
r->req.resid -= r->req.sg->size;
|
r->req.resid -= r->req.sg->size;
|
||||||
@ -505,20 +512,9 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
|||||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
||||||
int buflen = 0;
|
int buflen = 0;
|
||||||
|
|
||||||
if (req->cmd.buf[1] & 0x2) {
|
|
||||||
/* Command support data - optional, not implemented */
|
|
||||||
BADF("optional INQUIRY command support request not implemented\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req->cmd.buf[1] & 0x1) {
|
if (req->cmd.buf[1] & 0x1) {
|
||||||
/* Vital product data */
|
/* Vital product data */
|
||||||
uint8_t page_code = req->cmd.buf[2];
|
uint8_t page_code = req->cmd.buf[2];
|
||||||
if (req->cmd.xfer < 4) {
|
|
||||||
BADF("Error: Inquiry (EVPD[%02X]) buffer size %zd is "
|
|
||||||
"less than 4\n", page_code, req->cmd.xfer);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
outbuf[buflen++] = s->qdev.type & 0x1f;
|
outbuf[buflen++] = s->qdev.type & 0x1f;
|
||||||
outbuf[buflen++] = page_code ; // this page
|
outbuf[buflen++] = page_code ; // this page
|
||||||
@ -633,8 +629,6 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
BADF("Error: unsupported Inquiry (EVPD[%02X]) "
|
|
||||||
"buffer size %zd\n", page_code, req->cmd.xfer);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* done with EVPD */
|
/* done with EVPD */
|
||||||
@ -643,18 +637,10 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
|||||||
|
|
||||||
/* Standard INQUIRY data */
|
/* Standard INQUIRY data */
|
||||||
if (req->cmd.buf[2] != 0) {
|
if (req->cmd.buf[2] != 0) {
|
||||||
BADF("Error: Inquiry (STANDARD) page or code "
|
|
||||||
"is non-zero [%02X]\n", req->cmd.buf[2]);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PAGE CODE == 0 */
|
/* PAGE CODE == 0 */
|
||||||
if (req->cmd.xfer < 5) {
|
|
||||||
BADF("Error: Inquiry (STANDARD) buffer size %zd "
|
|
||||||
"is less than 5\n", req->cmd.xfer);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
buflen = req->cmd.xfer;
|
buflen = req->cmd.xfer;
|
||||||
if (buflen > SCSI_MAX_INQUIRY_LEN) {
|
if (buflen > SCSI_MAX_INQUIRY_LEN) {
|
||||||
buflen = SCSI_MAX_INQUIRY_LEN;
|
buflen = SCSI_MAX_INQUIRY_LEN;
|
||||||
@ -662,7 +648,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
|||||||
memset(outbuf, 0, buflen);
|
memset(outbuf, 0, buflen);
|
||||||
|
|
||||||
outbuf[0] = s->qdev.type & 0x1f;
|
outbuf[0] = s->qdev.type & 0x1f;
|
||||||
outbuf[1] = s->removable ? 0x80 : 0;
|
outbuf[1] = (s->features & (1 << SCSI_DISK_F_REMOVABLE)) ? 0x80 : 0;
|
||||||
if (s->qdev.type == TYPE_ROM) {
|
if (s->qdev.type == TYPE_ROM) {
|
||||||
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
|
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
|
||||||
} else {
|
} else {
|
||||||
@ -1094,7 +1080,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
|
|||||||
p = outbuf;
|
p = outbuf;
|
||||||
|
|
||||||
if (s->qdev.type == TYPE_DISK) {
|
if (s->qdev.type == TYPE_DISK) {
|
||||||
dev_specific_param = 0x10; /* DPOFUA */
|
dev_specific_param = s->features & (1 << SCSI_DISK_F_DPOFUA) ? 0x10 : 0;
|
||||||
if (bdrv_is_read_only(s->qdev.conf.bs)) {
|
if (bdrv_is_read_only(s->qdev.conf.bs)) {
|
||||||
dev_specific_param |= 0x80; /* Readonly. */
|
dev_specific_param |= 0x80; /* Readonly. */
|
||||||
}
|
}
|
||||||
@ -1559,8 +1545,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WRITE_SAME_10:
|
case WRITE_SAME_10:
|
||||||
|
len = lduw_be_p(&buf[7]);
|
||||||
|
goto write_same;
|
||||||
case WRITE_SAME_16:
|
case WRITE_SAME_16:
|
||||||
len = r->req.cmd.xfer / s->qdev.blocksize;
|
len = ldl_be_p(&buf[10]) & 0xffffffffULL;
|
||||||
|
write_same:
|
||||||
|
|
||||||
DPRINTF("WRITE SAME() (sector %" PRId64 ", count %d)\n",
|
DPRINTF("WRITE SAME() (sector %" PRId64 ", count %d)\n",
|
||||||
r->req.cmd.lba, len);
|
r->req.cmd.lba, len);
|
||||||
@ -1700,7 +1689,8 @@ static int scsi_initfn(SCSIDevice *dev)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s->removable && !bdrv_is_inserted(s->qdev.conf.bs)) {
|
if (!(s->features & (1 << SCSI_DISK_F_REMOVABLE)) &&
|
||||||
|
!bdrv_is_inserted(s->qdev.conf.bs)) {
|
||||||
error_report("Device needs media, but drive is empty");
|
error_report("Device needs media, but drive is empty");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -1722,7 +1712,7 @@ static int scsi_initfn(SCSIDevice *dev)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->removable) {
|
if (s->features & (1 << SCSI_DISK_F_REMOVABLE)) {
|
||||||
bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_cd_block_ops, s);
|
bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_cd_block_ops, s);
|
||||||
}
|
}
|
||||||
bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
|
bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
|
||||||
@ -1745,7 +1735,7 @@ static int scsi_cd_initfn(SCSIDevice *dev)
|
|||||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
|
||||||
s->qdev.blocksize = 2048;
|
s->qdev.blocksize = 2048;
|
||||||
s->qdev.type = TYPE_ROM;
|
s->qdev.type = TYPE_ROM;
|
||||||
s->removable = true;
|
s->features |= 1 << SCSI_DISK_F_REMOVABLE;
|
||||||
return scsi_initfn(&s->qdev);
|
return scsi_initfn(&s->qdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1818,7 +1808,9 @@ static int get_device_type(SCSIDiskState *s)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
s->qdev.type = buf[0];
|
s->qdev.type = buf[0];
|
||||||
s->removable = (buf[1] & 0x80) != 0;
|
if (buf[1] & 0x80) {
|
||||||
|
s->features |= 1 << SCSI_DISK_F_REMOVABLE;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1918,7 +1910,10 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
|
|||||||
|
|
||||||
static Property scsi_hd_properties[] = {
|
static Property scsi_hd_properties[] = {
|
||||||
DEFINE_SCSI_DISK_PROPERTIES(),
|
DEFINE_SCSI_DISK_PROPERTIES(),
|
||||||
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
|
DEFINE_PROP_BIT("removable", SCSIDiskState, features,
|
||||||
|
SCSI_DISK_F_REMOVABLE, false),
|
||||||
|
DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
|
||||||
|
SCSI_DISK_F_DPOFUA, false),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2020,7 +2015,10 @@ static TypeInfo scsi_block_info = {
|
|||||||
|
|
||||||
static Property scsi_disk_properties[] = {
|
static Property scsi_disk_properties[] = {
|
||||||
DEFINE_SCSI_DISK_PROPERTIES(),
|
DEFINE_SCSI_DISK_PROPERTIES(),
|
||||||
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
|
DEFINE_PROP_BIT("removable", SCSIDiskState, features,
|
||||||
|
SCSI_DISK_F_REMOVABLE, false),
|
||||||
|
DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
|
||||||
|
SCSI_DISK_F_DPOFUA, false),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user