diff options
-rw-r--r-- | hw/scsi-bus.c | 175 | ||||
-rw-r--r-- | hw/scsi-defs.h | 3 | ||||
-rw-r--r-- | hw/scsi-disk.c | 21 | ||||
-rw-r--r-- | hw/scsi-generic.c | 7 |
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, |