mirror of https://gitee.com/openkylin/qemu.git
scsi-disk: restruct emulation: INQUIRY
Move INQUIRY emulation from scsi_send_command() to scsi_disk_emulate_command(). Also split the longish INQUITY emulation code into the new scsi_disk_emulate_inquiry() function. Serial number handling is slightly changed, we don't copy it any more but look it up directly in DriveInfo which we have at hand anyway. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
parent
51ad87c963
commit
0b06c05979
305
hw/scsi-disk.c
305
hw/scsi-disk.c
|
@ -58,7 +58,6 @@ struct SCSIDiskState
|
||||||
This is the number of 512 byte blocks in a single scsi sector. */
|
This is the number of 512 byte blocks in a single scsi sector. */
|
||||||
int cluster_size;
|
int cluster_size;
|
||||||
uint64_t max_lba;
|
uint64_t max_lba;
|
||||||
char drive_serial_str[21];
|
|
||||||
QEMUBH *bh;
|
QEMUBH *bh;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -306,6 +305,141 @@ static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
|
||||||
return (uint8_t *)r->iov.iov_base;
|
return (uint8_t *)r->iov.iov_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
||||||
|
{
|
||||||
|
BlockDriverState *bdrv = req->dev->dinfo->bdrv;
|
||||||
|
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) {
|
||||||
|
/* Vital product data */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bdrv_get_type_hint(bdrv) == BDRV_TYPE_CDROM) {
|
||||||
|
outbuf[buflen++] = 5;
|
||||||
|
} else {
|
||||||
|
outbuf[buflen++] = 0;
|
||||||
|
}
|
||||||
|
outbuf[buflen++] = page_code ; // this page
|
||||||
|
outbuf[buflen++] = 0x00;
|
||||||
|
|
||||||
|
switch (page_code) {
|
||||||
|
case 0x00: /* Supported page codes, mandatory */
|
||||||
|
DPRINTF("Inquiry EVPD[Supported pages] "
|
||||||
|
"buffer size %zd\n", req->cmd.xfer);
|
||||||
|
outbuf[buflen++] = 3; // number of pages
|
||||||
|
outbuf[buflen++] = 0x00; // list of supported pages (this page)
|
||||||
|
outbuf[buflen++] = 0x80; // unit serial number
|
||||||
|
outbuf[buflen++] = 0x83; // device identification
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x80: /* Device serial number, optional */
|
||||||
|
{
|
||||||
|
const char *serial = req->dev->dinfo->serial ?: "0";
|
||||||
|
int l = strlen(serial);
|
||||||
|
|
||||||
|
if (l > req->cmd.xfer)
|
||||||
|
l = req->cmd.xfer;
|
||||||
|
if (l > 20)
|
||||||
|
l = 20;
|
||||||
|
|
||||||
|
DPRINTF("Inquiry EVPD[Serial number] "
|
||||||
|
"buffer size %zd\n", req->cmd.xfer);
|
||||||
|
outbuf[buflen++] = l;
|
||||||
|
memcpy(outbuf+buflen, serial, l);
|
||||||
|
buflen += l;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x83: /* Device identification page, mandatory */
|
||||||
|
{
|
||||||
|
int max_len = 255 - 8;
|
||||||
|
int id_len = strlen(bdrv_get_device_name(bdrv));
|
||||||
|
|
||||||
|
if (id_len > max_len)
|
||||||
|
id_len = max_len;
|
||||||
|
DPRINTF("Inquiry EVPD[Device identification] "
|
||||||
|
"buffer size %zd\n", req->cmd.xfer);
|
||||||
|
|
||||||
|
outbuf[buflen++] = 3 + id_len;
|
||||||
|
outbuf[buflen++] = 0x2; // ASCII
|
||||||
|
outbuf[buflen++] = 0; // not officially assigned
|
||||||
|
outbuf[buflen++] = 0; // reserved
|
||||||
|
outbuf[buflen++] = id_len; // length of data following
|
||||||
|
|
||||||
|
memcpy(outbuf+buflen, bdrv_get_device_name(bdrv), id_len);
|
||||||
|
buflen += id_len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
BADF("Error: unsupported Inquiry (EVPD[%02X]) "
|
||||||
|
"buffer size %zd\n", page_code, req->cmd.xfer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* done with EVPD */
|
||||||
|
return buflen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Standard INQUIRY data */
|
||||||
|
if (req->cmd.buf[2] != 0) {
|
||||||
|
BADF("Error: Inquiry (STANDARD) page or code "
|
||||||
|
"is non-zero [%02X]\n", req->cmd.buf[2]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->cmd.xfer < 36) {
|
||||||
|
BADF("Error: Inquiry (STANDARD) buffer size %zd "
|
||||||
|
"is less than 36 (TODO: only 5 required)\n", req->cmd.xfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
buflen = req->cmd.xfer;
|
||||||
|
if (buflen > SCSI_MAX_INQUIRY_LEN)
|
||||||
|
buflen = SCSI_MAX_INQUIRY_LEN;
|
||||||
|
|
||||||
|
memset(outbuf, 0, buflen);
|
||||||
|
|
||||||
|
if (req->lun || req->cmd.buf[1] >> 5) {
|
||||||
|
outbuf[0] = 0x7f; /* LUN not supported */
|
||||||
|
return buflen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bdrv_get_type_hint(bdrv) == BDRV_TYPE_CDROM) {
|
||||||
|
outbuf[0] = 5;
|
||||||
|
outbuf[1] = 0x80;
|
||||||
|
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
|
||||||
|
} else {
|
||||||
|
outbuf[0] = 0;
|
||||||
|
memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
|
||||||
|
}
|
||||||
|
memcpy(&outbuf[8], "QEMU ", 8);
|
||||||
|
memcpy(&outbuf[32], QEMU_VERSION, 4);
|
||||||
|
/* Identify device as SCSI-3 rev 1.
|
||||||
|
Some later commands are also implemented. */
|
||||||
|
outbuf[2] = 3;
|
||||||
|
outbuf[3] = 2; /* Format 2 */
|
||||||
|
outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */
|
||||||
|
/* Sync data transfer and TCQ. */
|
||||||
|
outbuf[7] = 0x10 | (req->bus->tcq ? 0x02 : 0);
|
||||||
|
return buflen;
|
||||||
|
}
|
||||||
|
|
||||||
static int scsi_disk_emulate_command(SCSIRequest *req, uint8_t *outbuf)
|
static int scsi_disk_emulate_command(SCSIRequest *req, uint8_t *outbuf)
|
||||||
{
|
{
|
||||||
BlockDriverState *bdrv = req->dev->dinfo->bdrv;
|
BlockDriverState *bdrv = req->dev->dinfo->bdrv;
|
||||||
|
@ -334,6 +468,11 @@ static int scsi_disk_emulate_command(SCSIRequest *req, uint8_t *outbuf)
|
||||||
outbuf[2] = req->dev->sense.key;
|
outbuf[2] = req->dev->sense.key;
|
||||||
scsi_dev_clear_sense(req->dev);
|
scsi_dev_clear_sense(req->dev);
|
||||||
break;
|
break;
|
||||||
|
case INQUIRY:
|
||||||
|
buflen = scsi_disk_emulate_inquiry(req, outbuf);
|
||||||
|
if (buflen < 0)
|
||||||
|
goto illegal_request;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
goto illegal_request;
|
goto illegal_request;
|
||||||
}
|
}
|
||||||
|
@ -438,170 +577,16 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case TEST_UNIT_READY:
|
case TEST_UNIT_READY:
|
||||||
case REQUEST_SENSE:
|
case REQUEST_SENSE:
|
||||||
|
case INQUIRY:
|
||||||
rc = scsi_disk_emulate_command(&r->req, outbuf);
|
rc = scsi_disk_emulate_command(&r->req, outbuf);
|
||||||
if (rc > 0) {
|
if (rc > 0) {
|
||||||
r->iov.iov_len = rc;
|
r->iov.iov_len = rc;
|
||||||
} else {
|
} else {
|
||||||
scsi_req_complete(&r->req);
|
scsi_req_complete(&r->req);
|
||||||
scsi_remove_request(r);
|
scsi_remove_request(r);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return rc;
|
break;
|
||||||
case INQUIRY:
|
|
||||||
DPRINTF("Inquiry (len %d)\n", len);
|
|
||||||
if (buf[1] & 0x2) {
|
|
||||||
/* Command support data - optional, not implemented */
|
|
||||||
BADF("optional INQUIRY command support request not implemented\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
else if (buf[1] & 0x1) {
|
|
||||||
/* Vital product data */
|
|
||||||
uint8_t page_code = buf[2];
|
|
||||||
if (len < 4) {
|
|
||||||
BADF("Error: Inquiry (EVPD[%02X]) buffer size %d is "
|
|
||||||
"less than 4\n", page_code, len);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (page_code) {
|
|
||||||
case 0x00:
|
|
||||||
{
|
|
||||||
/* Supported page codes, mandatory */
|
|
||||||
DPRINTF("Inquiry EVPD[Supported pages] "
|
|
||||||
"buffer size %d\n", len);
|
|
||||||
|
|
||||||
r->iov.iov_len = 0;
|
|
||||||
|
|
||||||
if (bdrv_get_type_hint(s->qdev.dinfo->bdrv) == BDRV_TYPE_CDROM) {
|
|
||||||
outbuf[r->iov.iov_len++] = 5;
|
|
||||||
} else {
|
|
||||||
outbuf[r->iov.iov_len++] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 "
|
|
||||||
"size %d too small, %d needed\n", len, 4);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTF("Inquiry EVPD[Serial number] buffer size %d\n", len);
|
|
||||||
l = MIN(len, strlen(s->drive_serial_str));
|
|
||||||
|
|
||||||
r->iov.iov_len = 0;
|
|
||||||
|
|
||||||
/* Supported page codes */
|
|
||||||
if (bdrv_get_type_hint(s->qdev.dinfo->bdrv) == BDRV_TYPE_CDROM) {
|
|
||||||
outbuf[r->iov.iov_len++] = 5;
|
|
||||||
} else {
|
|
||||||
outbuf[r->iov.iov_len++] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
case 0x83:
|
|
||||||
{
|
|
||||||
/* Device identification page, mandatory */
|
|
||||||
int max_len = 255 - 8;
|
|
||||||
int id_len = strlen(bdrv_get_device_name(s->qdev.dinfo->bdrv));
|
|
||||||
if (id_len > max_len)
|
|
||||||
id_len = max_len;
|
|
||||||
|
|
||||||
DPRINTF("Inquiry EVPD[Device identification] "
|
|
||||||
"buffer size %d\n", len);
|
|
||||||
r->iov.iov_len = 0;
|
|
||||||
if (bdrv_get_type_hint(s->qdev.dinfo->bdrv) == BDRV_TYPE_CDROM) {
|
|
||||||
outbuf[r->iov.iov_len++] = 5;
|
|
||||||
} else {
|
|
||||||
outbuf[r->iov.iov_len++] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
outbuf[r->iov.iov_len++] = 0x83; // this page
|
|
||||||
outbuf[r->iov.iov_len++] = 0x00;
|
|
||||||
outbuf[r->iov.iov_len++] = 3 + id_len;
|
|
||||||
|
|
||||||
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->iov.iov_len],
|
|
||||||
bdrv_get_device_name(s->qdev.dinfo->bdrv), id_len);
|
|
||||||
r->iov.iov_len += id_len;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BADF("Error: unsupported Inquiry (EVPD[%02X]) "
|
|
||||||
"buffer size %d\n", page_code, len);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
/* done with EVPD */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Standard INQUIRY data */
|
|
||||||
if (buf[2] != 0) {
|
|
||||||
BADF("Error: Inquiry (STANDARD) page or code "
|
|
||||||
"is non-zero [%02X]\n", buf[2]);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PAGE CODE == 0 */
|
|
||||||
if (len < 5) {
|
|
||||||
BADF("Error: Inquiry (STANDARD) buffer size %d "
|
|
||||||
"is less than 5\n", len);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < 36) {
|
|
||||||
BADF("Error: Inquiry (STANDARD) buffer size %d "
|
|
||||||
"is less than 36 (TODO: only 5 required)\n", len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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->qdev.dinfo->bdrv) == BDRV_TYPE_CDROM) {
|
|
||||||
outbuf[0] = 5;
|
|
||||||
outbuf[1] = 0x80;
|
|
||||||
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
|
|
||||||
} else {
|
|
||||||
outbuf[0] = 0;
|
|
||||||
memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
|
|
||||||
}
|
|
||||||
memcpy(&outbuf[8], "QEMU ", 8);
|
|
||||||
memcpy(&outbuf[32], QEMU_VERSION, 4);
|
|
||||||
/* Identify device as SCSI-3 rev 1.
|
|
||||||
Some later commands are also implemented. */
|
|
||||||
outbuf[2] = 3;
|
|
||||||
outbuf[3] = 2; /* Format 2 */
|
|
||||||
outbuf[4] = len - 5; /* Additional Length = (Len - 1) - 4 */
|
|
||||||
/* Sync data transfer and TCQ. */
|
|
||||||
outbuf[7] = 0x10 | (r->req.bus->tcq ? 0x02 : 0);
|
|
||||||
r->iov.iov_len = len;
|
|
||||||
break;
|
|
||||||
case RESERVE:
|
case RESERVE:
|
||||||
DPRINTF("Reserve(6)\n");
|
DPRINTF("Reserve(6)\n");
|
||||||
if (buf[1] & 1)
|
if (buf[1] & 1)
|
||||||
|
@ -983,10 +968,6 @@ static int scsi_disk_initfn(SCSIDevice *dev)
|
||||||
if (nb_sectors)
|
if (nb_sectors)
|
||||||
nb_sectors--;
|
nb_sectors--;
|
||||||
s->max_lba = nb_sectors;
|
s->max_lba = nb_sectors;
|
||||||
strncpy(s->drive_serial_str, drive_get_serial(s->qdev.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);
|
qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue