scsi: move handling of REPORT LUNS and invalid LUNs to common code
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
parent
afa46c468a
commit
fdaef06917
175
hw/scsi-bus.c
175
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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user