aboutsummaryrefslogtreecommitdiff
path: root/hw/scsi-bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/scsi-bus.c')
-rw-r--r--hw/scsi-bus.c128
1 files changed, 108 insertions, 20 deletions
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index e6ebbd594e..3a2a7bb72c 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -9,8 +9,6 @@
static char *scsibus_get_fw_dev_path(DeviceState *dev);
static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf);
static void scsi_req_dequeue(SCSIRequest *req);
-static int scsi_build_sense(uint8_t *in_buf, int in_len,
- uint8_t *buf, int len, bool fixed);
static struct BusInfo scsi_bus_info = {
.name = "SCSI",
@@ -502,7 +500,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
hba_private);
} else if (lun != d->lun ||
buf[0] == REPORT_LUNS ||
- buf[0] == REQUEST_SENSE) {
+ (buf[0] == REQUEST_SENSE && (d->sense_len || cmd.xfer < 4))) {
req = scsi_req_alloc(&reqops_target_command, d, tag, lun,
hba_private);
} else {
@@ -649,6 +647,31 @@ static void scsi_req_dequeue(SCSIRequest *req)
}
}
+static int scsi_get_performance_length(int num_desc, int type, int data_type)
+{
+ /* MMC-6, paragraph 6.7. */
+ switch (type) {
+ case 0:
+ if ((data_type & 3) == 0) {
+ /* Each descriptor is as in Table 295 - Nominal performance. */
+ return 16 * num_desc + 8;
+ } else {
+ /* Each descriptor is as in Table 296 - Exceptions. */
+ return 6 * num_desc + 8;
+ }
+ case 1:
+ case 4:
+ case 5:
+ return 8 * num_desc + 8;
+ case 2:
+ return 2048 * num_desc + 8;
+ case 3:
+ return 16 * num_desc + 8;
+ default:
+ return 8;
+ }
+}
+
static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
{
switch (buf[0] >> 5) {
@@ -666,11 +689,11 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
cmd->len = 10;
break;
case 4:
- cmd->xfer = ldl_be_p(&buf[10]);
+ cmd->xfer = ldl_be_p(&buf[10]) & 0xffffffffULL;
cmd->len = 16;
break;
case 5:
- cmd->xfer = ldl_be_p(&buf[6]);
+ cmd->xfer = ldl_be_p(&buf[6]) & 0xffffffffULL;
cmd->len = 12;
break;
default:
@@ -681,8 +704,9 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case TEST_UNIT_READY:
case REWIND:
case START_STOP:
- case SEEK_6:
+ case SET_CAPACITY:
case WRITE_FILEMARKS:
+ case WRITE_FILEMARKS_16:
case SPACE:
case RESERVE:
case RELEASE:
@@ -691,6 +715,8 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case VERIFY_10:
case SEEK_10:
case SYNCHRONIZE_CACHE:
+ case SYNCHRONIZE_CACHE_16:
+ case LOCATE_16:
case LOCK_UNLOCK_CACHE:
case LOAD_UNLOAD:
case SET_CD_SPEED:
@@ -698,6 +724,11 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case WRITE_LONG_10:
case MOVE_MEDIUM:
case UPDATE_BLOCK:
+ case RESERVE_TRACK:
+ case SET_READ_AHEAD:
+ case PRE_FETCH:
+ case PRE_FETCH_16:
+ case ALLOW_OVERWRITE:
cmd->xfer = 0;
break;
case MODE_SENSE:
@@ -711,14 +742,13 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case READ_BLOCK_LIMITS:
cmd->xfer = 6;
break;
- case READ_POSITION:
- cmd->xfer = 20;
- break;
case SEND_VOLUME_TAG:
- cmd->xfer *= 40;
- break;
- case MEDIUM_SCAN:
- cmd->xfer *= 8;
+ /* GPCMD_SET_STREAMING from multimedia commands. */
+ if (dev->type == TYPE_ROM) {
+ cmd->xfer = buf[10] | (buf[9] << 8);
+ } else {
+ cmd->xfer = buf[9] | (buf[8] << 8);
+ }
break;
case WRITE_10:
case WRITE_VERIFY_10:
@@ -737,9 +767,39 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case READ_16:
cmd->xfer *= dev->blocksize;
break;
+ case FORMAT_UNIT:
+ /* MMC mandates the parameter list to be 12-bytes long. Parameters
+ * for block devices are restricted to the header right now. */
+ if (dev->type == TYPE_ROM && (buf[1] & 16)) {
+ cmd->xfer = 12;
+ } else {
+ cmd->xfer = (buf[1] & 16) == 0 ? 0 : (buf[1] & 32 ? 8 : 4);
+ }
+ break;
case INQUIRY:
+ case RECEIVE_DIAGNOSTIC:
+ case SEND_DIAGNOSTIC:
cmd->xfer = buf[4] | (buf[3] << 8);
break;
+ case READ_CD:
+ case READ_BUFFER:
+ case WRITE_BUFFER:
+ case SEND_CUE_SHEET:
+ cmd->xfer = buf[8] | (buf[7] << 8) | (buf[6] << 16);
+ break;
+ case PERSISTENT_RESERVE_OUT:
+ cmd->xfer = ldl_be_p(&buf[5]) & 0xffffffffULL;
+ break;
+ case ERASE_12:
+ if (dev->type == TYPE_ROM) {
+ /* MMC command GET PERFORMANCE. */
+ cmd->xfer = scsi_get_performance_length(buf[9] | (buf[8] << 8),
+ buf[10], buf[1] & 0x1f);
+ }
+ break;
+ case MECHANISM_STATUS:
+ case READ_DVD_STRUCTURE:
+ case SEND_DVD_STRUCTURE:
case MAINTENANCE_OUT:
case MAINTENANCE_IN:
if (dev->type == TYPE_ROM) {
@@ -755,6 +815,10 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu
{
switch (buf[0]) {
/* stream commands */
+ case ERASE_12:
+ case ERASE_16:
+ cmd->xfer = 0;
+ break;
case READ_6:
case READ_REVERSE:
case RECOVER_BUFFERED_DATA:
@@ -770,6 +834,15 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu
cmd->len = 6;
cmd->xfer = 0;
break;
+ case SPACE_16:
+ cmd->xfer = buf[13] | (buf[12] << 8);
+ break;
+ case READ_POSITION:
+ cmd->xfer = buf[8] | (buf[7] << 8);
+ break;
+ case FORMAT_UNIT:
+ cmd->xfer = buf[4] | (buf[3] << 8);
+ break;
/* generic commands */
default:
return scsi_req_length(cmd, dev, buf);
@@ -809,6 +882,8 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd)
case SEARCH_LOW_12:
case MEDIUM_SCAN:
case SEND_VOLUME_TAG:
+ case SEND_CUE_SHEET:
+ case SEND_DVD_STRUCTURE:
case PERSISTENT_RESERVE_OUT:
case MAINTENANCE_OUT:
cmd->mode = SCSI_XFER_TO_DEV;
@@ -835,7 +910,7 @@ static uint64_t scsi_cmd_lba(SCSICommand *cmd)
case 1:
case 2:
case 5:
- lba = ldl_be_p(&buf[2]);
+ lba = ldl_be_p(&buf[2]) & 0xffffffffULL;
break;
case 4:
lba = ldq_be_p(&buf[2]);
@@ -1036,7 +1111,7 @@ static const char *scsi_command_name(uint8_t cmd)
[ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS",
[ READ_6 ] = "READ_6",
[ WRITE_6 ] = "WRITE_6",
- [ SEEK_6 ] = "SEEK_6",
+ [ SET_CAPACITY ] = "SET_CAPACITY",
[ READ_REVERSE ] = "READ_REVERSE",
[ WRITE_FILEMARKS ] = "WRITE_FILEMARKS",
[ SPACE ] = "SPACE",
@@ -1064,7 +1139,7 @@ static const char *scsi_command_name(uint8_t cmd)
[ SEARCH_EQUAL ] = "SEARCH_EQUAL",
[ SEARCH_LOW ] = "SEARCH_LOW",
[ SET_LIMITS ] = "SET_LIMITS",
- [ PRE_FETCH ] = "PRE_FETCH",
+ [ PRE_FETCH ] = "PRE_FETCH/READ_POSITION",
/* READ_POSITION and PRE_FETCH use the same operation code */
[ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE",
[ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE",
@@ -1101,9 +1176,11 @@ static const char *scsi_command_name(uint8_t cmd)
[ WRITE_16 ] = "WRITE_16",
[ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16",
[ VERIFY_16 ] = "VERIFY_16",
- [ SYNCHRONIZE_CACHE_16 ] = "SYNCHRONIZE_CACHE_16",
+ [ PRE_FETCH_16 ] = "PRE_FETCH_16",
+ [ SYNCHRONIZE_CACHE_16 ] = "SPACE_16/SYNCHRONIZE_CACHE_16",
+ /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */
[ LOCATE_16 ] = "LOCATE_16",
- [ WRITE_SAME_16 ] = "WRITE_SAME_16",
+ [ WRITE_SAME_16 ] = "ERASE_16/WRITE_SAME_16",
/* ERASE_16 and WRITE_SAME_16 use the same operation code */
[ SERVICE_ACTION_IN_16 ] = "SERVICE_ACTION_IN_16",
[ WRITE_LONG_16 ] = "WRITE_LONG_16",
@@ -1113,6 +1190,8 @@ static const char *scsi_command_name(uint8_t cmd)
[ LOAD_UNLOAD ] = "LOAD_UNLOAD",
[ READ_12 ] = "READ_12",
[ WRITE_12 ] = "WRITE_12",
+ [ ERASE_12 ] = "ERASE_12/GET_PERFORMANCE",
+ /* ERASE_12 and GET_PERFORMANCE use the same operation code */
[ SERVICE_ACTION_IN_12 ] = "SERVICE_ACTION_IN_12",
[ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12",
[ VERIFY_12 ] = "VERIFY_12",
@@ -1120,9 +1199,18 @@ static const char *scsi_command_name(uint8_t cmd)
[ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12",
[ SEARCH_LOW_12 ] = "SEARCH_LOW_12",
[ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS",
- [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG",
+ [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG/SET_STREAMING",
+ /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */
+ [ READ_CD ] = "READ_CD",
[ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12",
+ [ READ_DVD_STRUCTURE ] = "READ_DVD_STRUCTURE",
+ [ RESERVE_TRACK ] = "RESERVE_TRACK",
+ [ SEND_CUE_SHEET ] = "SEND_CUE_SHEET",
+ [ SEND_DVD_STRUCTURE ] = "SEND_DVD_STRUCTURE",
[ SET_CD_SPEED ] = "SET_CD_SPEED",
+ [ SET_READ_AHEAD ] = "SET_READ_AHEAD",
+ [ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE",
+ [ MECHANISM_STATUS ] = "MECHANISM_STATUS",
};
if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL)
@@ -1279,7 +1367,7 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev)
SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
char path[100];
- snprintf(path, sizeof(path), "%s@%d:%d:%d", qdev_fw_name(dev),
+ snprintf(path, sizeof(path), "%s@%d,%d,%d", qdev_fw_name(dev),
d->channel, d->id, d->lun);
return strdup(path);