diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2009-11-26 15:34:04 +0100 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2009-12-03 09:41:39 -0600 |
commit | 0b06c05979075cbf224724f5ac1a3f4d713ccdd8 (patch) | |
tree | a76b7dd301131450a86ee31b3679efafdcbbfded /hw | |
parent | 51ad87c963dfd3fb12316338cee8b75395bd1686 (diff) |
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>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/scsi-disk.c | 305 |
1 files changed, 143 insertions, 162 deletions
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 7597b5226a..f94d51382d 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -58,7 +58,6 @@ struct SCSIDiskState This is the number of 512 byte blocks in a single scsi sector. */ int cluster_size; uint64_t max_lba; - char drive_serial_str[21]; QEMUBH *bh; }; @@ -306,6 +305,141 @@ static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) 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) { 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; scsi_dev_clear_sense(req->dev); break; + case INQUIRY: + buflen = scsi_disk_emulate_inquiry(req, outbuf); + if (buflen < 0) + goto illegal_request; + break; default: goto illegal_request; } @@ -438,170 +577,16 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, switch (command) { case TEST_UNIT_READY: case REQUEST_SENSE: + case INQUIRY: rc = scsi_disk_emulate_command(&r->req, outbuf); if (rc > 0) { r->iov.iov_len = rc; } else { scsi_req_complete(&r->req); scsi_remove_request(r); + return 0; } - return rc; - 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; + break; case RESERVE: DPRINTF("Reserve(6)\n"); if (buf[1] & 1) @@ -983,10 +968,6 @@ static int scsi_disk_initfn(SCSIDevice *dev) if (nb_sectors) 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); return 0; } |