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:
Paolo Bonzini 2011-08-03 10:49:14 +02:00 committed by Anthony Liguori
parent afa46c468a
commit fdaef06917
4 changed files with 177 additions and 29 deletions

View File

@ -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;

View File

@ -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

View File

@ -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) {

View File

@ -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,