+static uint8_t *scsi_get_buf(SCSIRequest *req)
+{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+ return (uint8_t *)r->iov.iov_base;
+}
+
+static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+ 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 (s->drive_kind == SCSI_CD) {
+ 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 */
+ {
+ int pages;
+ DPRINTF("Inquiry EVPD[Supported pages] "
+ "buffer size %zd\n", req->cmd.xfer);
+ pages = buflen++;
+ outbuf[buflen++] = 0x00; // list of supported pages (this page)
+ outbuf[buflen++] = 0x80; // unit serial number
+ outbuf[buflen++] = 0x83; // device identification
+ if (s->drive_kind == SCSI_HD) {
+ outbuf[buflen++] = 0xb0; // block limits
+ outbuf[buflen++] = 0xb2; // thin provisioning
+ }
+ outbuf[pages] = buflen - pages - 1; // number of pages
+ break;
+ }
+ case 0x80: /* Device serial number, optional */
+ {
+ int l = strlen(s->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, s->serial, l);
+ buflen += l;
+ break;
+ }
+
+ case 0x83: /* Device identification page, mandatory */
+ {
+ int max_len = 255 - 8;
+ int id_len = strlen(bdrv_get_device_name(s->bs));
+
+ if (id_len > max_len)
+ id_len = max_len;
+ DPRINTF("Inquiry EVPD[Device identification] "
+ "buffer size %zd\n", req->cmd.xfer);
+
+ outbuf[buflen++] = 4 + 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(s->bs), id_len);
+ buflen += id_len;
+ break;
+ }
+ case 0xb0: /* block limits */
+ {
+ unsigned int unmap_sectors =
+ s->qdev.conf.discard_granularity / s->qdev.blocksize;
+ unsigned int min_io_size =
+ s->qdev.conf.min_io_size / s->qdev.blocksize;
+ unsigned int opt_io_size =
+ s->qdev.conf.opt_io_size / s->qdev.blocksize;
+
+ if (s->drive_kind == SCSI_CD) {
+ DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
+ page_code);
+ return -1;
+ }
+ /* required VPD size with unmap support */
+ outbuf[3] = buflen = 0x3c;
+
+ memset(outbuf + 4, 0, buflen - 4);
+
+ /* optimal transfer length granularity */
+ outbuf[6] = (min_io_size >> 8) & 0xff;
+ outbuf[7] = min_io_size & 0xff;
+
+ /* optimal transfer length */
+ outbuf[12] = (opt_io_size >> 24) & 0xff;
+ outbuf[13] = (opt_io_size >> 16) & 0xff;
+ outbuf[14] = (opt_io_size >> 8) & 0xff;
+ outbuf[15] = opt_io_size & 0xff;
+
+ /* optimal unmap granularity */
+ outbuf[28] = (unmap_sectors >> 24) & 0xff;
+ outbuf[29] = (unmap_sectors >> 16) & 0xff;
+ outbuf[30] = (unmap_sectors >> 8) & 0xff;
+ outbuf[31] = unmap_sectors & 0xff;
+ break;
+ }
+ case 0xb2: /* thin provisioning */
+ {
+ outbuf[3] = buflen = 8;
+ outbuf[4] = 0;
+ outbuf[5] = 0x40; /* write same with unmap supported */
+ outbuf[6] = 0;
+ outbuf[7] = 0;
+ 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;
+ }
+
+ 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 (s->drive_kind == SCSI_CD) {
+ outbuf[0] = 5;
+ outbuf[1] = 0x80;
+ memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
+ } else {
+ outbuf[0] = 0;
+ outbuf[1] = s->removable ? 0x80 : 0;
+ memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
+ }
+ memcpy(&outbuf[8], "QEMU ", 8);
+ memset(&outbuf[32], 0, 4);
+ memcpy(&outbuf[32], s->version, MIN(4, strlen(s->version)));
+ /*
+ * We claim conformance to SPC-3, which is required for guests
+ * to ask for modern features like READ CAPACITY(16) or the
+ * block characteristics VPD page by default. Not all of SPC-3
+ * is actually implemented, but we're good enough.
+ */
+ outbuf[2] = 5;
+ outbuf[3] = 2; /* Format 2 */
+
+ if (buflen > 36) {
+ outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */
+ } else {
+ /* If the allocation length of CDB is too small,
+ the additional length is not adjusted */
+ outbuf[4] = 36 - 5;
+ }
+
+ /* Sync data transfer and TCQ. */
+ outbuf[7] = 0x10 | (req->bus->tcq ? 0x02 : 0);
+ return buflen;
+}
+
+static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p,
+ int page_control)