aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/scsi-bus.c175
-rw-r--r--hw/scsi-defs.h3
-rw-r--r--hw/scsi-disk.c21
-rw-r--r--hw/scsi-generic.c7
4 files changed, 177 insertions, 29 deletions
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index c7e7b08..8b229af 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -149,6 +149,172 @@ struct SCSIReqOps reqops_invalid_opcode = {
.send_command = scsi_invalid_command
};
+/* SCSIReqOps implementation for REPORT LUNS and for commands sent to
+ an invalid LUN. */
+
+typedef struct SCSITargetReq SCSITargetReq;
+
+struct SCSITargetReq {
+ SCSIRequest req;
+ int len;
+ uint8_t buf[64];
+};
+
+static void store_lun(uint8_t *outbuf, int lun)
+{
+ if (lun < 256) {
+ outbuf[1] = lun;
+ return;
+ }
+ outbuf[1] = (lun & 255);
+ outbuf[0] = (lun >> 8) | 0x40;
+}
+
+static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
+{
+ int len;
+ if (r->req.cmd.xfer < 16) {
+ return false;
+ }
+ if (r->req.cmd.buf[2] > 2) {
+ return false;
+ }
+ len = MIN(sizeof r->buf, r->req.cmd.xfer);
+ memset(r->buf, 0, len);
+ if (r->req.dev->lun != 0) {
+ r->buf[3] = 16;
+ r->len = 24;
+ store_lun(&r->buf[16], r->req.dev->lun);
+ } else {
+ r->buf[3] = 8;
+ r->len = 16;
+ }
+ return true;
+}
+
+static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
+{
+ assert(r->req.dev->lun != r->req.lun);
+ if (r->req.cmd.buf[1] & 0x2) {
+ /* Command support data - optional, not implemented */
+ return false;
+ }
+
+ if (r->req.cmd.buf[1] & 0x1) {
+ /* Vital product data */
+ 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++] = 0x00;
+
+ switch (page_code) {
+ case 0x00: /* Supported page codes, mandatory */
+ {
+ int pages;
+ pages = r->len++;
+ r->buf[r->len++] = 0x00; /* list of supported pages (this page) */
+ r->buf[pages] = r->len - pages - 1; /* number of pages */
+ break;
+ }
+ default:
+ return false;
+ }
+ /* done with EVPD */
+ assert(r->len < sizeof(r->buf));
+ r->len = MIN(r->req.cmd.xfer, r->len);
+ return true;
+ }
+
+ /* Standard INQUIRY data */
+ if (r->req.cmd.buf[2] != 0) {
+ return false;
+ }
+
+ /* PAGE CODE == 0 */
+ if (r->req.cmd.xfer < 5) {
+ return -1;
+ }
+
+ r->len = MIN(r->req.cmd.xfer, 36);
+ memset(r->buf, 0, r->len);
+ if (r->req.lun != 0) {
+ r->buf[0] = TYPE_NO_LUN;
+ } else {
+ r->buf[0] = TYPE_NOT_PRESENT | TYPE_INACTIVE;
+ r->buf[2] = 5; /* Version */
+ r->buf[3] = 2 | 0x10; /* HiSup, response data format */
+ r->buf[4] = r->len - 5; /* Additional Length = (Len - 1) - 4 */
+ r->buf[7] = 0x10 | (r->req.bus->tcq ? 0x02 : 0); /* Sync, TCQ. */
+ memcpy(&r->buf[8], "QEMU ", 8);
+ memcpy(&r->buf[16], "QEMU TARGET ", 16);
+ strncpy((char *) &r->buf[32], QEMU_VERSION, 4);
+ }
+ return true;
+}
+
+static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+
+ switch (buf[0]) {
+ case REPORT_LUNS:
+ if (!scsi_target_emulate_report_luns(r)) {
+ goto illegal_request;
+ }
+ break;
+ case INQUIRY:
+ if (!scsi_target_emulate_inquiry(r)) {
+ goto illegal_request;
+ }
+ break;
+ default:
+ scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED));
+ scsi_req_complete(req, CHECK_CONDITION);
+ return 0;
+ illegal_request:
+ scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD));
+ scsi_req_complete(req, CHECK_CONDITION);
+ return 0;
+ }
+
+ if (!r->len) {
+ scsi_req_complete(req, GOOD);
+ }
+ return r->len;
+}
+
+static void scsi_target_read_data(SCSIRequest *req)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+ uint32_t n;
+
+ n = r->len;
+ if (n > 0) {
+ r->len = 0;
+ scsi_req_data(&r->req, n);
+ } else {
+ scsi_req_complete(&r->req, GOOD);
+ }
+}
+
+static uint8_t *scsi_target_get_buf(SCSIRequest *req)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+
+ return r->buf;
+}
+
+struct SCSIReqOps reqops_target_command = {
+ .size = sizeof(SCSITargetReq),
+ .send_command = scsi_target_send_command,
+ .read_data = scsi_target_read_data,
+ .get_buf = scsi_target_get_buf,
+};
+
+
SCSIRequest *scsi_req_alloc(SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag,
uint32_t lun, void *hba_private)
{
@@ -184,7 +350,14 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
trace_scsi_req_parsed_lba(d->id, lun, tag, buf[0],
cmd.lba);
}
- req = d->info->alloc_req(d, tag, lun, hba_private);
+
+ if ((lun != d->lun && buf[0] != REQUEST_SENSE) ||
+ buf[0] == REPORT_LUNS) {
+ req = scsi_req_alloc(&reqops_target_command, d, tag, lun,
+ hba_private);
+ } else {
+ req = d->info->alloc_req(d, tag, lun, hba_private);
+ }
}
req->cmd = cmd;
diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h
index 27010b7..977c38b 100644
--- a/hw/scsi-defs.h
+++ b/hw/scsi-defs.h
@@ -176,5 +176,8 @@
#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
#define TYPE_RBC 0x0e /* Simplified Direct-Access Device */
#define TYPE_OSD 0x11 /* Object-storage Device */
+#define TYPE_WLUN 0x1e /* Well known LUN */
+#define TYPE_NOT_PRESENT 0x1f
+#define TYPE_INACTIVE 0x20
#define TYPE_NO_LUN 0x7f
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 1abf909..fe7368b 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -482,11 +482,6 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
memset(outbuf, 0, buflen);
- if (req->lun) {
- outbuf[0] = 0x7f; /* LUN not supported */
- return buflen;
- }
-
outbuf[0] = s->qdev.type & 0x1f;
if (s->qdev.type == TYPE_ROM) {
outbuf[1] = 0x80;
@@ -918,13 +913,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
}
DPRINTF("Unsupported Service Action In\n");
goto illegal_request;
- case REPORT_LUNS:
- if (req->cmd.xfer < 16)
- goto illegal_request;
- memset(outbuf, 0, 16);
- outbuf[3] = 8;
- buflen = 16;
- break;
case VERIFY_10:
break;
default:
@@ -974,14 +962,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
}
#endif
- if (req->lun) {
- /* Only LUN 0 supported. */
- DPRINTF("Unimplemented LUN %d\n", req->lun);
- if (command != REQUEST_SENSE && command != INQUIRY) {
- scsi_check_condition(r, SENSE_CODE(LUN_NOT_SUPPORTED));
- return 0;
- }
- }
switch (command) {
case TEST_UNIT_READY:
case REQUEST_SENSE:
@@ -999,7 +979,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
case READ_TOC:
case GET_CONFIGURATION:
case SERVICE_ACTION_IN:
- case REPORT_LUNS:
case VERIFY_10:
rc = scsi_disk_emulate_command(r, outbuf);
if (rc < 0) {
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 3b43f1c..70265d0 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -287,13 +287,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
int ret;
- if (cmd[0] != REQUEST_SENSE && req->lun != s->qdev.lun) {
- DPRINTF("Unimplemented LUN %d\n", req->lun);
- scsi_req_build_sense(&r->req, SENSE_CODE(LUN_NOT_SUPPORTED));
- scsi_req_complete(&r->req, CHECK_CONDITION);
- return 0;
- }
-
scsi_req_fixup(&r->req);
DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,