diff options
Diffstat (limited to 'hw/scsi-bus.c')
-rw-r--r-- | hw/scsi-bus.c | 162 |
1 files changed, 116 insertions, 46 deletions
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index f10f3ec25c..5ad1013be1 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -12,17 +12,26 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev); static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); static void scsi_req_dequeue(SCSIRequest *req); -static struct BusInfo scsi_bus_info = { - .name = "SCSI", - .size = sizeof(SCSIBus), - .get_dev_path = scsibus_get_dev_path, - .get_fw_dev_path = scsibus_get_fw_dev_path, - .props = (Property[]) { - DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), - DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1), - DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1), - DEFINE_PROP_END_OF_LIST(), - }, +static Property scsi_props[] = { + DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), + DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1), + DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void scsi_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->get_dev_path = scsibus_get_dev_path; + k->get_fw_dev_path = scsibus_get_fw_dev_path; +} + +static const TypeInfo scsi_bus_info = { + .name = TYPE_SCSI_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(SCSIBus), + .class_init = scsi_bus_class_init, }; static int next_scsi_bus; @@ -65,7 +74,7 @@ static void scsi_device_unit_attention_reported(SCSIDevice *s) /* Create a scsi bus, and attach devices to it. */ void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info) { - qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL); + qbus_create_inplace(&bus->qbus, TYPE_SCSI_BUS, host, NULL); bus->busnr = next_scsi_bus++; bus->info = info; bus->qbus.allow_hotplug = 1; @@ -205,7 +214,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, if (bootindex >= 0) { qdev_prop_set_int32(dev, "bootindex", bootindex); } - if (qdev_prop_exists(dev, "removable")) { + if (object_property_find(OBJECT(dev), "removable", NULL)) { qdev_prop_set_bit(dev, "removable", removable); } if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) { @@ -306,7 +315,7 @@ static void store_lun(uint8_t *outbuf, int lun) static bool scsi_target_emulate_report_luns(SCSITargetReq *r) { - DeviceState *qdev; + BusChild *kid; int i, len, n; int channel, id; bool found_lun0; @@ -321,7 +330,8 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r) id = r->req.dev->id; found_lun0 = false; n = 0; - QTAILQ_FOREACH(qdev, &r->req.bus->qbus.children, sibling) { + QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) { + DeviceState *qdev = kid->child; SCSIDevice *dev = SCSI_DEVICE(qdev); if (dev->channel == channel && dev->id == id) { @@ -343,7 +353,8 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r) memset(r->buf, 0, len); stl_be_p(&r->buf, n); i = found_lun0 ? 8 : 16; - QTAILQ_FOREACH(qdev, &r->req.bus->qbus.children, sibling) { + QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) { + DeviceState *qdev = kid->child; SCSIDevice *dev = SCSI_DEVICE(qdev); if (dev->channel == channel && dev->id == id) { @@ -406,7 +417,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r) r->buf[7] = 0x10 | (r->req.bus->info->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); + pstrcpy((char *) &r->buf[32], 4, qemu_get_version()); } return true; } @@ -723,20 +734,16 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) switch (buf[0] >> 5) { case 0: cmd->xfer = buf[4]; - cmd->len = 6; break; case 1: case 2: cmd->xfer = lduw_be_p(&buf[7]); - cmd->len = 10; break; case 4: cmd->xfer = ldl_be_p(&buf[10]) & 0xffffffffULL; - cmd->len = 16; break; case 5: cmd->xfer = ldl_be_p(&buf[6]) & 0xffffffffULL; - cmd->len = 12; break; default: return -1; @@ -760,11 +767,9 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case SYNCHRONIZE_CACHE_16: case LOCATE_16: case LOCK_UNLOCK_CACHE: - case LOAD_UNLOAD: case SET_CD_SPEED: case SET_LIMITS: case WRITE_LONG_10: - case MOVE_MEDIUM: case UPDATE_BLOCK: case RESERVE_TRACK: case SET_READ_AHEAD: @@ -874,7 +879,6 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu case READ_REVERSE: case RECOVER_BUFFERED_DATA: case WRITE_6: - cmd->len = 6; cmd->xfer = buf[4] | (buf[3] << 8) | (buf[2] << 16); if (buf[1] & 0x01) { /* fixed */ cmd->xfer *= dev->blocksize; @@ -884,22 +888,34 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu case READ_REVERSE_16: case VERIFY_16: case WRITE_16: - cmd->len = 16; cmd->xfer = buf[14] | (buf[13] << 8) | (buf[12] << 16); if (buf[1] & 0x01) { /* fixed */ cmd->xfer *= dev->blocksize; } break; case REWIND: - case START_STOP: - cmd->len = 6; + case LOAD_UNLOAD: cmd->xfer = 0; break; case SPACE_16: cmd->xfer = buf[13] | (buf[12] << 8); break; case READ_POSITION: - cmd->xfer = buf[8] | (buf[7] << 8); + switch (buf[1] & 0x1f) /* operation code */ { + case SHORT_FORM_BLOCK_ID: + case SHORT_FORM_VENDOR_SPECIFIC: + cmd->xfer = 20; + break; + case LONG_FORM: + cmd->xfer = 32; + break; + case EXTENDED_FORM: + cmd->xfer = buf[8] | (buf[7] << 8); + break; + default: + return -1; + } + break; case FORMAT_UNIT: cmd->xfer = buf[4] | (buf[3] << 8); @@ -911,6 +927,29 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu return 0; } +static int scsi_req_medium_changer_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) +{ + switch (buf[0]) { + /* medium changer commands */ + case EXCHANGE_MEDIUM: + case INITIALIZE_ELEMENT_STATUS: + case INITIALIZE_ELEMENT_STATUS_WITH_RANGE: + case MOVE_MEDIUM: + case POSITION_TO_ELEMENT: + cmd->xfer = 0; + break; + case READ_ELEMENT_STATUS: + cmd->xfer = buf[9] | (buf[8] << 8) | (buf[7] << 16); + break; + + /* generic commands */ + default: + return scsi_req_length(cmd, dev, buf); + } + return 0; +} + + static void scsi_cmd_xfer_mode(SCSICommand *cmd) { if (!cmd->xfer) { @@ -990,11 +1029,36 @@ int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) { int rc; - if (dev->type == TYPE_TAPE) { + switch (buf[0] >> 5) { + case 0: + cmd->len = 6; + break; + case 1: + case 2: + cmd->len = 10; + break; + case 4: + cmd->len = 16; + break; + case 5: + cmd->len = 12; + break; + default: + return -1; + } + + switch (dev->type) { + case TYPE_TAPE: rc = scsi_req_stream_length(cmd, dev, buf); - } else { + break; + case TYPE_MEDIUM_CHANGER: + rc = scsi_req_medium_changer_length(cmd, dev, buf); + break; + default: rc = scsi_req_length(cmd, dev, buf); + break; } + if (rc != 0) return rc; @@ -1172,7 +1236,8 @@ static const char *scsi_command_name(uint8_t cmd) [ REQUEST_SENSE ] = "REQUEST_SENSE", [ FORMAT_UNIT ] = "FORMAT_UNIT", [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", - [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS", + [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS/INITIALIZE ELEMENT STATUS", + /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operation code */ [ READ_6 ] = "READ_6", [ WRITE_6 ] = "WRITE_6", [ SET_CAPACITY ] = "SET_CAPACITY", @@ -1189,14 +1254,16 @@ static const char *scsi_command_name(uint8_t cmd) [ COPY ] = "COPY", [ ERASE ] = "ERASE", [ MODE_SENSE ] = "MODE_SENSE", - [ START_STOP ] = "START_STOP", + [ START_STOP ] = "START_STOP/LOAD_UNLOAD", + /* LOAD_UNLOAD and START_STOP use the same operation code */ [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC", [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", [ READ_CAPACITY_10 ] = "READ_CAPACITY_10", [ READ_10 ] = "READ_10", [ WRITE_10 ] = "WRITE_10", - [ SEEK_10 ] = "SEEK_10", + [ SEEK_10 ] = "SEEK_10/POSITION_TO_ELEMENT", + /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */ [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10", [ VERIFY_10 ] = "VERIFY_10", [ SEARCH_HIGH ] = "SEARCH_HIGH", @@ -1207,7 +1274,8 @@ static const char *scsi_command_name(uint8_t cmd) /* READ_POSITION and PRE_FETCH use the same operation code */ [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE", [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE", - [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA", + [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA/INITIALIZE_ELEMENT_STATUS_WITH_RANGE", + /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use the same operation code */ [ MEDIUM_SCAN ] = "MEDIUM_SCAN", [ COMPARE ] = "COMPARE", [ COPY_VERIFY ] = "COPY_VERIFY", @@ -1252,6 +1320,7 @@ static const char *scsi_command_name(uint8_t cmd) [ REPORT_LUNS ] = "REPORT_LUNS", [ BLANK ] = "BLANK", [ MOVE_MEDIUM ] = "MOVE_MEDIUM", + [ EXCHANGE_MEDIUM ] = "EXCHANGE MEDIUM", [ LOAD_UNLOAD ] = "LOAD_UNLOAD", [ READ_12 ] = "READ_12", [ WRITE_12 ] = "WRITE_12", @@ -1378,7 +1447,7 @@ void scsi_req_complete(SCSIRequest *req, int status) assert(req->status == -1); req->status = status; - assert(req->sense_len < sizeof(req->sense)); + assert(req->sense_len <= sizeof(req->sense)); if (status == GOOD) { req->sense_len = 0; } @@ -1452,12 +1521,10 @@ static char *scsibus_get_dev_path(DeviceState *dev) { SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev); DeviceState *hba = dev->parent_bus->parent; - char *id = NULL; + char *id; char *path; - if (hba && hba->parent_bus && hba->parent_bus->info->get_dev_path) { - id = hba->parent_bus->info->get_dev_path(hba); - } + id = qdev_get_dev_path(hba); if (id) { path = g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun); } else { @@ -1480,10 +1547,11 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev) SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun) { - DeviceState *qdev; + BusChild *kid; SCSIDevice *target_dev = NULL; - QTAILQ_FOREACH_REVERSE(qdev, &bus->qbus.children, ChildrenHead, sibling) { + QTAILQ_FOREACH_REVERSE(kid, &bus->qbus.children, ChildrenHead, sibling) { + DeviceState *qdev = kid->child; SCSIDevice *dev = SCSI_DEVICE(qdev); if (dev->channel == channel && dev->id == id) { @@ -1507,10 +1575,9 @@ static void put_scsi_requests(QEMUFile *f, void *pv, size_t size) QTAILQ_FOREACH(req, &s->requests, next) { assert(!req->io_canceled); assert(req->status == -1); - assert(req->retry); assert(req->enqueued); - qemu_put_sbyte(f, 1); + qemu_put_sbyte(f, req->retry ? 1 : 2); qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf)); qemu_put_be32s(f, &req->tag); qemu_put_be32s(f, &req->lun); @@ -1528,8 +1595,9 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) { SCSIDevice *s = pv; SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); + int8_t sbyte; - while (qemu_get_sbyte(f)) { + while ((sbyte = qemu_get_sbyte(f)) > 0) { uint8_t buf[SCSI_CMD_BUF_SIZE]; uint32_t tag; uint32_t lun; @@ -1539,6 +1607,7 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) qemu_get_be32s(f, &tag); qemu_get_be32s(f, &lun); req = scsi_req_new(s, tag, lun, buf, NULL); + req->retry = (sbyte == 1); if (bus->info->load_request) { req->hba_private = bus->info->load_request(f, req); } @@ -1547,7 +1616,6 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) } /* Just restart it later. */ - req->retry = true; scsi_req_enqueue_internal(req); /* At this point, the request will be kept alive by the reference @@ -1595,10 +1663,11 @@ const VMStateDescription vmstate_scsi_device = { static void scsi_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); - k->bus_info = &scsi_bus_info; + k->bus_type = TYPE_SCSI_BUS; k->init = scsi_qdev_init; k->unplug = qdev_simple_unplug_cb; k->exit = scsi_qdev_exit; + k->props = scsi_props; } static TypeInfo scsi_device_type_info = { @@ -1612,6 +1681,7 @@ static TypeInfo scsi_device_type_info = { static void scsi_register_types(void) { + type_register_static(&scsi_bus_info); type_register_static(&scsi_device_type_info); } |