diff options
-rw-r--r-- | hw/scsi-bus.c | 93 | ||||
-rw-r--r-- | hw/scsi-defs.h | 1 | ||||
-rw-r--r-- | hw/scsi.h | 2 |
3 files changed, 94 insertions, 2 deletions
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index bec8d8282b..564b840eb7 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -149,6 +149,24 @@ struct SCSIReqOps reqops_invalid_opcode = { .send_command = scsi_invalid_command }; +/* SCSIReqOps implementation for unit attention conditions. */ + +static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf) +{ + if (req->dev && req->dev->unit_attention.key == UNIT_ATTENTION) { + scsi_req_build_sense(req, req->dev->unit_attention); + } else if (req->bus->unit_attention.key == UNIT_ATTENTION) { + scsi_req_build_sense(req, req->bus->unit_attention); + } + scsi_req_complete(req, CHECK_CONDITION); + return 0; +} + +struct SCSIReqOps reqops_unit_attention = { + .size = sizeof(SCSIRequest), + .send_command = scsi_unit_attention +}; + /* SCSIReqOps implementation for REPORT LUNS and for commands sent to an invalid LUN. */ @@ -344,6 +362,7 @@ SCSIRequest *scsi_req_alloc(SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag, SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, uint8_t *buf, void *hba_private) { + SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus); SCSIRequest *req; SCSICommand cmd; @@ -358,7 +377,15 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, cmd.lba); } - if (lun != d->lun || + if ((d->unit_attention.key == UNIT_ATTENTION || + bus->unit_attention.key == UNIT_ATTENTION) && + (buf[0] != INQUIRY && + buf[0] != REPORT_LUNS && + buf[0] != GET_CONFIGURATION && + buf[0] != GET_EVENT_STATUS_NOTIFICATION)) { + req = scsi_req_alloc(&reqops_unit_attention, d, tag, lun, + hba_private); + } else if (lun != d->lun || buf[0] == REPORT_LUNS || buf[0] == REQUEST_SENSE) { req = scsi_req_alloc(&reqops_target_command, d, tag, lun, @@ -377,13 +404,68 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req) return req->ops->get_buf(req); } +static void scsi_clear_unit_attention(SCSIRequest *req) +{ + SCSISense *ua; + if (req->dev->unit_attention.key != UNIT_ATTENTION && + req->bus->unit_attention.key != UNIT_ATTENTION) { + return; + } + + /* + * If an INQUIRY command enters the enabled command state, + * the device server shall [not] clear any unit attention condition; + * See also MMC-6, paragraphs 6.5 and 6.6.2. + */ + if (req->cmd.buf[0] == INQUIRY || + req->cmd.buf[0] == GET_CONFIGURATION || + req->cmd.buf[0] == GET_EVENT_STATUS_NOTIFICATION) { + return; + } + + if (req->dev->unit_attention.key == UNIT_ATTENTION) { + ua = &req->dev->unit_attention; + } else { + ua = &req->bus->unit_attention; + } + + /* + * If a REPORT LUNS command enters the enabled command state, [...] + * the device server shall clear any pending unit attention condition + * with an additional sense code of REPORTED LUNS DATA HAS CHANGED. + */ + if (req->cmd.buf[0] == REPORT_LUNS && + !(ua->asc == SENSE_CODE(REPORTED_LUNS_CHANGED).asc && + ua->ascq == SENSE_CODE(REPORTED_LUNS_CHANGED).ascq)) { + return; + } + + *ua = SENSE_CODE(NO_SENSE); +} + int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) { + int ret; + assert(len >= 14); if (!req->sense_len) { return 0; } - return scsi_build_sense(req->sense, req->sense_len, buf, len, true); + + ret = scsi_build_sense(req->sense, req->sense_len, buf, len, true); + + /* + * FIXME: clearing unit attention conditions upon autosense should be done + * only if the UA_INTLCK_CTRL field in the Control mode page is set to 00b + * (SAM-5, 5.14). + * + * We assume UA_INTLCK_CTRL to be 00b for HBAs that support autosense, and + * 10b for HBAs that do not support it (do not call scsi_req_get_sense). + * In the latter case, scsi_req_complete clears unit attention conditions + * after moving them to the device's sense buffer. + */ + scsi_clear_unit_attention(req); + return ret; } int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed) @@ -983,6 +1065,13 @@ void scsi_req_complete(SCSIRequest *req, int status) } req->dev->sense_len = req->sense_len; + /* + * Unit attention state is now stored in the device's sense buffer + * if the HBA didn't do autosense. Clear the pending unit attention + * flags. + */ + scsi_clear_unit_attention(req); + scsi_req_ref(req); scsi_req_dequeue(req); req->bus->ops->complete(req, req->status); diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h index 977c38b721..ea288fab07 100644 --- a/hw/scsi-defs.h +++ b/hw/scsi-defs.h @@ -78,6 +78,7 @@ #define READ_TOC 0x43 #define REPORT_DENSITY_SUPPORT 0x44 #define GET_CONFIGURATION 0x46 +#define GET_EVENT_STATUS_NOTIFICATION 0x4a #define LOG_SELECT 0x4c #define LOG_SENSE 0x4d #define MODE_SELECT_10 0x55 @@ -62,6 +62,7 @@ struct SCSIDevice uint32_t id; BlockConf conf; SCSIDeviceInfo *info; + SCSISense unit_attention; uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; QTAILQ_HEAD(, SCSIRequest) requests; @@ -105,6 +106,7 @@ struct SCSIBus { BusState qbus; int busnr; + SCSISense unit_attention; int tcq, ndev; const SCSIBusOps *ops; |