diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 9637ccb52e..af956704e8 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -7,6 +7,8 @@ #include "trace.h" static char *scsibus_get_fw_dev_path(DeviceState *dev); +static int scsi_build_sense(uint8_t *in_buf, int in_len, + uint8_t *buf, int len, bool fixed); static struct BusInfo scsi_bus_info = { .name = "SCSI", @@ -144,6 +146,7 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, req->lun = lun; req->hba_private = hba_private; req->status = -1; + req->sense_len = 0; trace_scsi_req_alloc(req->dev->id, req->lun, req->tag); return req; } @@ -161,11 +164,28 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req) int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) { - if (req->dev->info->get_sense) { - return req->dev->info->get_sense(req, buf, len); - } else { + assert(len >= 14); + if (!req->sense_len) { return 0; } + return scsi_build_sense(req->sense, req->sense_len, buf, len, true); +} + +int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed) +{ + return scsi_build_sense(dev->sense, dev->sense_len, buf, len, fixed); +} + +void scsi_req_build_sense(SCSIRequest *req, SCSISense sense) +{ + trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag, + sense.key, sense.asc, sense.ascq); + memset(req->sense, 0, 18); + req->sense[0] = 0xf0; + req->sense[2] = sense.key; + req->sense[12] = sense.asc; + req->sense[13] = sense.ascq; + req->sense_len = 18; } int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf) @@ -484,14 +504,40 @@ const struct SCSISense sense_code_LUN_FAILURE = { /* * scsi_build_sense * - * Build a sense buffer + * Convert between fixed and descriptor sense buffers */ -int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed) +int scsi_build_sense(uint8_t *in_buf, int in_len, + uint8_t *buf, int len, bool fixed) { + bool fixed_in; + SCSISense sense; if (!fixed && len < 8) { return 0; } + if (in_len == 0) { + sense.key = NO_SENSE; + sense.asc = 0; + sense.ascq = 0; + } else { + fixed_in = (in_buf[0] & 2) == 0; + + if (fixed == fixed_in) { + memcpy(buf, in_buf, MIN(len, in_len)); + return MIN(len, in_len); + } + + if (fixed_in) { + sense.key = in_buf[2]; + sense.asc = in_buf[12]; + sense.ascq = in_buf[13]; + } else { + sense.key = in_buf[1]; + sense.asc = in_buf[2]; + sense.ascq = in_buf[3]; + } + } + memset(buf, 0, len); if (fixed) { /* Return fixed format sense buffer */ @@ -686,6 +732,17 @@ void scsi_req_complete(SCSIRequest *req, int status) { assert(req->status == -1); req->status = status; + + assert(req->sense_len < sizeof(req->sense)); + if (status == GOOD) { + req->sense_len = 0; + } + + if (req->sense_len) { + memcpy(req->dev->sense, req->sense, req->sense_len); + } + req->dev->sense_len = req->sense_len; + scsi_req_ref(req); scsi_req_dequeue(req); req->bus->ops->complete(req, req->status); diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 38ebe042cb..e266d6fe9a 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -71,7 +71,6 @@ struct SCSIDiskState QEMUBH *bh; char *version; char *serial; - SCSISense sense; }; static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); @@ -97,20 +96,13 @@ static void scsi_free_request(SCSIRequest *req) qemu_vfree(r->iov.iov_base); } -static void scsi_disk_clear_sense(SCSIDiskState *s) +/* Helper function for command completion with sense. */ +static void scsi_check_condition(SCSIDiskReq *r, SCSISense sense) { - memset(&s->sense, 0, sizeof(s->sense)); -} - -/* Helper function for command completion. */ -static void scsi_command_complete(SCSIDiskReq *r, int status, SCSISense sense) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - DPRINTF("Command complete tag=0x%x status=%d sense=%d/%d/%d\n", r->req.tag, status, sense.key, sense.asc, sense.ascq); - s->sense = sense; - scsi_req_complete(&r->req, status); + scsi_req_build_sense(&r->req, sense); + scsi_req_complete(&r->req, CHECK_CONDITION); } /* Cancel a pending data transfer. */ @@ -162,7 +154,8 @@ static void scsi_read_data(SCSIRequest *req) } DPRINTF("Read sector_count=%d\n", r->sector_count); if (r->sector_count == 0) { - scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); + /* This also clears the sense buffer for REQUEST SENSE. */ + scsi_req_complete(&r->req, GOOD); return; } @@ -210,16 +203,13 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) } else { switch (error) { case ENOMEM: - scsi_command_complete(r, CHECK_CONDITION, - SENSE_CODE(TARGET_FAILURE)); + scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE)); break; case EINVAL: - scsi_command_complete(r, CHECK_CONDITION, - SENSE_CODE(INVALID_FIELD)); + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); break; default: - scsi_command_complete(r, CHECK_CONDITION, - SENSE_CODE(IO_ERROR)); + scsi_check_condition(r, SENSE_CODE(IO_ERROR)); break; } bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); @@ -245,7 +235,7 @@ static void scsi_write_complete(void * opaque, int ret) r->sector += n; r->sector_count -= n; if (r->sector_count == 0) { - scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); + scsi_req_complete(&r->req, GOOD); } else { len = r->sector_count * 512; if (len > SCSI_DMA_BUF_SIZE) { @@ -314,7 +304,7 @@ static void scsi_dma_restart_bh(void *opaque) case SCSI_REQ_STATUS_RETRY_FLUSH: ret = scsi_disk_emulate_command(r, r->iov.iov_base); if (ret == 0) { - scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); + scsi_req_complete(&r->req, GOOD); } } } @@ -342,14 +332,6 @@ static uint8_t *scsi_get_buf(SCSIRequest *req) return (uint8_t *)r->iov.iov_base; } -/* Copy sense information into the provided buffer */ -static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - - return scsi_build_sense(s->sense, outbuf, len, len > 14); -} - static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); @@ -827,9 +809,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) case REQUEST_SENSE: if (req->cmd.xfer < 4) goto illegal_request; - buflen = scsi_build_sense(s->sense, outbuf, req->cmd.xfer, - req->cmd.xfer > 13); - scsi_disk_clear_sense(s); + buflen = scsi_device_get_sense(&s->qdev, outbuf, req->cmd.xfer, + (req->cmd.buf[1] & 1) == 0); break; case INQUIRY: buflen = scsi_disk_emulate_inquiry(req, outbuf); @@ -960,21 +941,21 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) case VERIFY_10: break; default: - scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); return -1; } return buflen; not_ready: if (!bdrv_is_inserted(s->bs)) { - scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(NO_MEDIUM)); + scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); } else { - scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_READY)); + scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); } return -1; illegal_request: - scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD)); + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); return -1; } @@ -998,7 +979,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) if (scsi_req_parse(&r->req, buf) != 0) { BADF("Unsupported command length, command %x\n", command); - scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); return 0; } #ifdef DEBUG_SCSI @@ -1015,8 +996,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) /* Only LUN 0 supported. */ DPRINTF("Unimplemented LUN %d\n", req->lun); if (command != REQUEST_SENSE && command != INQUIRY) { - scsi_command_complete(r, CHECK_CONDITION, - SENSE_CODE(LUN_NOT_SUPPORTED)); + scsi_check_condition(r, SENSE_CODE(LUN_NOT_SUPPORTED)); return 0; } } @@ -1124,17 +1104,17 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) break; default: DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); - scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); return 0; fail: - scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD)); + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); return 0; illegal_lba: - scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LBA_OUT_OF_RANGE)); + scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); return 0; } if (r->sector_count == 0 && r->iov.iov_len == 0) { - scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); + scsi_req_complete(&r->req, GOOD); } len = r->sector_count * 512 + r->iov.iov_len; if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { @@ -1266,7 +1246,6 @@ static SCSIDeviceInfo scsi_disk_info[] = { .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, - .get_sense = scsi_get_sense, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), @@ -1287,7 +1266,6 @@ static SCSIDeviceInfo scsi_disk_info[] = { .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, - .get_sense = scsi_get_sense, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_END_OF_LIST(), @@ -1307,7 +1285,6 @@ static SCSIDeviceInfo scsi_disk_info[] = { .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, - .get_sense = scsi_get_sense, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 71cbfb0a44..37c598271a 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -61,41 +61,8 @@ struct SCSIGenericState SCSIDevice qdev; BlockDriverState *bs; int lun; - int driver_status; - uint8_t sensebuf[SCSI_SENSE_BUF_SIZE]; - uint8_t senselen; }; -static void scsi_set_sense(SCSIGenericState *s, SCSISense sense) -{ - s->senselen = scsi_build_sense(sense, s->sensebuf, SCSI_SENSE_BUF_SIZE, 0); - s->driver_status = SG_ERR_DRIVER_SENSE; -} - -static void scsi_clear_sense(SCSIGenericState *s) -{ - memset(s->sensebuf, 0, SCSI_SENSE_BUF_SIZE); - s->senselen = 0; - s->driver_status = 0; -} - -static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) -{ - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); - int size = SCSI_SENSE_BUF_SIZE; - - if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) { - size = scsi_build_sense(SENSE_CODE(NO_SENSE), s->sensebuf, - SCSI_SENSE_BUF_SIZE, 0); - } - if (size > len) { - size = len; - } - memcpy(outbuf, s->sensebuf, size); - - return size; -} - static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, void *hba_private) { @@ -117,12 +84,10 @@ static void scsi_command_complete(void *opaque, int ret) { int status; SCSIGenericReq *r = (SCSIGenericReq *)opaque; - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); r->req.aiocb = NULL; - s->driver_status = r->io_header.driver_status; - if (s->driver_status & SG_ERR_DRIVER_SENSE) - s->senselen = r->io_header.sb_len_wr; + if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) + r->req.sense_len = r->io_header.sb_len_wr; if (ret != 0) { switch (ret) { @@ -131,24 +96,24 @@ static void scsi_command_complete(void *opaque, int ret) break; case -EINVAL: status = CHECK_CONDITION; - scsi_set_sense(s, SENSE_CODE(INVALID_FIELD)); + scsi_req_build_sense(&r->req, SENSE_CODE(INVALID_FIELD)); break; case -ENOMEM: status = CHECK_CONDITION; - scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE)); + scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE)); break; default: status = CHECK_CONDITION; - scsi_set_sense(s, SENSE_CODE(IO_ERROR)); + scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR)); break; } } else { - if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { + if (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT) { status = BUSY; BADF("Driver Timeout\n"); } else if (r->io_header.status) { status = r->io_header.status; - } else if (s->driver_status & SG_ERR_DRIVER_SENSE) { + } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { status = CHECK_CONDITION; } else { status = GOOD; @@ -176,16 +141,14 @@ static int execute_command(BlockDriverState *bdrv, SCSIGenericReq *r, int direction, BlockDriverCompletionFunc *complete) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); - r->io_header.interface_id = 'S'; r->io_header.dxfer_direction = direction; r->io_header.dxferp = r->buf; r->io_header.dxfer_len = r->buflen; r->io_header.cmdp = r->req.cmd.buf; r->io_header.cmd_len = r->req.cmd.len; - r->io_header.mx_sb_len = sizeof(s->sensebuf); - r->io_header.sbp = s->sensebuf; + r->io_header.mx_sb_len = sizeof(r->req.sense); + r->io_header.sbp = r->req.sense; r->io_header.timeout = MAX_UINT; r->io_header.usr_ptr = r; r->io_header.flags |= SG_FLAG_DIRECT_IO; @@ -234,21 +197,19 @@ static void scsi_read_data(SCSIRequest *req) return; } - if (r->req.cmd.buf[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE) - { - s->senselen = MIN(r->len, s->senselen); - memcpy(r->buf, s->sensebuf, s->senselen); + if (r->req.cmd.buf[0] == REQUEST_SENSE) { r->io_header.driver_status = 0; r->io_header.status = 0; - r->io_header.dxfer_len = s->senselen; + r->io_header.dxfer_len = + scsi_device_get_sense(&s->qdev, r->buf, r->req.cmd.xfer, + (r->req.cmd.buf[1] & 1) == 0); r->len = -1; - DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, s->senselen); + DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, r->io_header.dxfer_len); DPRINTF("Sense: %d %d %d %d %d %d %d %d\n", r->buf[0], r->buf[1], r->buf[2], r->buf[3], r->buf[4], r->buf[5], r->buf[6], r->buf[7]); - scsi_req_data(&r->req, s->senselen); - /* Clear sensebuf after REQUEST_SENSE */ - scsi_clear_sense(s); + scsi_req_data(&r->req, r->io_header.dxfer_len); + /* The sense buffer is cleared when we return GOOD */ return; } @@ -342,7 +303,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) if (cmd[0] != REQUEST_SENSE && req->lun != s->lun) { DPRINTF("Unimplemented LUN %d\n", req->lun); - scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED)); + scsi_req_build_sense(&r->req, SENSE_CODE(LUN_NOT_SUPPORTED)); scsi_req_complete(&r->req, CHECK_CONDITION); return 0; } @@ -533,8 +494,6 @@ static int scsi_generic_initfn(SCSIDevice *dev) } } DPRINTF("block size %d\n", s->qdev.blocksize); - s->driver_status = 0; - memset(s->sensebuf, 0, sizeof(s->sensebuf)); bdrv_set_removable(s->bs, 0); return 0; } @@ -553,7 +512,6 @@ static SCSIDeviceInfo scsi_generic_info = { .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, - .get_sense = scsi_get_sense, .qdev.props = (Property[]) { DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/scsi.h b/hw/scsi.h index 18d3643da1..c1cb987cf6 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -27,6 +27,8 @@ typedef struct SCSISense { uint8_t ascq; } SCSISense; +#define SCSI_SENSE_BUF_SIZE 96 + struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; @@ -42,6 +44,8 @@ struct SCSIRequest { enum SCSIXferMode mode; } cmd; BlockDriverAIOCB *aiocb; + uint8_t sense[SCSI_SENSE_BUF_SIZE]; + uint32_t sense_len; bool enqueued; void *hba_private; QTAILQ_ENTRY(SCSIRequest) next; @@ -53,6 +57,8 @@ struct SCSIDevice uint32_t id; BlockConf conf; SCSIDeviceInfo *info; + uint8_t sense[SCSI_SENSE_BUF_SIZE]; + uint32_t sense_len; QTAILQ_HEAD(, SCSIRequest) requests; int blocksize; int type; @@ -76,7 +82,6 @@ struct SCSIDeviceInfo { void (*write_data)(SCSIRequest *req); void (*cancel_io)(SCSIRequest *req); uint8_t *(*get_buf)(SCSIRequest *req); - int (*get_sense)(SCSIRequest *req, uint8_t *buf, int len); }; struct SCSIBusOps { @@ -137,7 +142,6 @@ extern const struct SCSISense sense_code_LUN_FAILURE; #define SENSE_CODE(x) sense_code_ ## x -int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed); int scsi_sense_valid(SCSISense sense); SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, @@ -149,6 +153,7 @@ void scsi_req_free(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); void scsi_req_unref(SCSIRequest *req); +void scsi_req_build_sense(SCSIRequest *req, SCSISense sense); int scsi_req_parse(SCSIRequest *req, uint8_t *buf); void scsi_req_print(SCSIRequest *req); void scsi_req_continue(SCSIRequest *req); @@ -159,5 +164,6 @@ int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len); void scsi_req_abort(SCSIRequest *req, int status); void scsi_req_cancel(SCSIRequest *req); void scsi_device_purge_requests(SCSIDevice *sdev); +int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); #endif diff --git a/trace-events b/trace-events index 19d31e3541..0daf2cc4c3 100644 --- a/trace-events +++ b/trace-events @@ -251,6 +251,7 @@ disable scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "target %d lun %d tag %d command %d dir %d length %d" disable scsi_req_parsed_lba(int target, int lun, int tag, int cmd, uint64_t lba) "target %d lun %d tag %d command %d lba %"PRIu64"" disable scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d" +disable scsi_req_build_sense(int target, int lun, int tag, int key, int asc, int ascq) "target %d lun %d tag %d key %#02x asc %#02x ascq %#02x" # vl.c disable vm_state_notify(int running, int reason) "running %d reason %d"