aboutsummaryrefslogtreecommitdiff
path: root/hw/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'hw/scsi')
-rw-r--r--hw/scsi/lsi53c895a.c51
-rw-r--r--hw/scsi/scsi-bus.c47
-rw-r--r--hw/scsi/spapr_vscsi.c195
-rw-r--r--hw/scsi/srp.h7
-rw-r--r--hw/scsi/virtio-scsi.c1
5 files changed, 240 insertions, 61 deletions
diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c
index 0e51b94..cb30414 100644
--- a/hw/scsi/lsi53c895a.c
+++ b/hw/scsi/lsi53c895a.c
@@ -7,8 +7,11 @@
* This code is licensed under the LGPL.
*/
-/* ??? Need to check if the {read,write}[wl] routines work properly on
- big-endian targets. */
+/* Note:
+ * LSI53C810 emulation is incorrect, in the sense that it supports
+ * features added in later evolutions. This should not be a problem,
+ * as well-behaved operating systems will not try to use them.
+ */
#include <assert.h>
@@ -278,6 +281,7 @@ typedef struct {
uint32_t script_ram[2048];
} LSIState;
+#define TYPE_LSI53C810 "lsi53c810"
#define TYPE_LSI53C895A "lsi53c895a"
#define LSI53C895A(obj) \
@@ -998,12 +1002,6 @@ bad:
s->msg_action = 0;
}
-/* Sign extend a 24-bit value. */
-static inline int32_t sxt24(int32_t n)
-{
- return (n << 8) >> 8;
-}
-
#define LSI_BUF_SIZE 4096
static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count)
{
@@ -1083,7 +1081,7 @@ again:
/* Table indirect addressing. */
/* 32-bit Table indirect */
- offset = sxt24(addr);
+ offset = sextract32(addr, 0, 24);
pci_dma_read(pci_dev, s->dsa + offset, buf, 8);
/* byte count is stored in bits 0:23 only */
s->dbc = cpu_to_le32(buf[0]) & 0xffffff;
@@ -1183,13 +1181,13 @@ again:
uint32_t id;
if (insn & (1 << 25)) {
- id = read_dword(s, s->dsa + sxt24(insn));
+ id = read_dword(s, s->dsa + sextract32(insn, 0, 24));
} else {
id = insn;
}
id = (id >> 16) & 0xf;
if (insn & (1 << 26)) {
- addr = s->dsp + sxt24(addr);
+ addr = s->dsp + sextract32(addr, 0, 24);
}
s->dnad = addr;
switch (opcode) {
@@ -1385,7 +1383,7 @@ again:
if (cond == jmp) {
if (insn & (1 << 23)) {
/* Relative address. */
- addr = s->dsp + sxt24(addr);
+ addr = s->dsp + sextract32(addr, 0, 24);
}
switch ((insn >> 27) & 7) {
case 0: /* Jump */
@@ -1438,7 +1436,7 @@ again:
int i;
if (insn & (1 << 28)) {
- addr = s->dsa + sxt24(addr);
+ addr = s->dsa + sextract32(addr, 0, 24);
}
n = (insn & 7);
reg = (insn >> 16) & 0xff;
@@ -1521,7 +1519,7 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset)
used for diagnostics, so should be ok. */
return 0;
case 0xc: /* DSTAT */
- tmp = s->dstat | 0x80;
+ tmp = s->dstat | LSI_DSTAT_DFE;
if ((s->istat0 & LSI_ISTAT0_INTF) == 0)
s->dstat = 0;
lsi_update_irq(s);
@@ -1705,8 +1703,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
s->sxfer = val;
break;
case 0x06: /* SDID */
- if ((val & 0xf) != (s->ssid & 0xf))
+ if ((s->ssid & 0x80) && (val & 0xf) != (s->ssid & 0xf)) {
BADF("Destination ID does not match SSID\n");
+ }
s->sdid = val & 0xf;
break;
case 0x07: /* GPREG0 */
@@ -1748,6 +1747,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
case 0x17: /* MBOX1 */
s->mbox1 = val;
break;
+ case 0x18: /* CTEST0 */
+ /* nothing to do */
+ break;
case 0x1a: /* CTEST2 */
s->ctest2 = val & LSI_CTEST2_PCICIE;
break;
@@ -1876,8 +1878,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
int shift;
n = (offset - 0x58) >> 2;
shift = (offset & 3) * 8;
- s->scratch[n] &= ~(0xff << shift);
- s->scratch[n] |= (val & 0xff) << shift;
+ s->scratch[n] = deposit32(s->scratch[n], shift, 8, val);
} else {
BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val);
}
@@ -2113,7 +2114,7 @@ static int lsi_scsi_init(PCIDevice *dev)
"lsi-io", 256);
pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_io);
- pci_register_bar(dev, 1, 0, &s->mmio_io);
+ pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio_io);
pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io);
QTAILQ_INIT(&s->queue);
@@ -2151,9 +2152,23 @@ static const TypeInfo lsi_info = {
.class_init = lsi_class_init,
};
+static void lsi53c810_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->device_id = PCI_DEVICE_ID_LSI_53C810;
+}
+
+static TypeInfo lsi53c810_info = {
+ .name = TYPE_LSI53C810,
+ .parent = TYPE_LSI53C895A,
+ .class_init = lsi53c810_class_init,
+};
+
static void lsi53c895a_register_types(void)
{
type_register_static(&lsi_info);
+ type_register_static(&lsi53c810_info);
}
type_init(lsi53c895a_register_types)
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 5cd6137..24ec52f8 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -11,6 +11,8 @@ static char *scsibus_get_dev_path(DeviceState *dev);
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 uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len);
+static void scsi_target_free_buf(SCSIRequest *req);
static Property scsi_props[] = {
DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0),
@@ -224,7 +226,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
if (object_property_find(OBJECT(dev), "removable", NULL)) {
qdev_prop_set_bit(dev, "removable", removable);
}
- if (serial) {
+ if (serial && object_property_find(OBJECT(dev), "serial", NULL)) {
qdev_prop_set_string(dev, "serial", serial);
}
if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) {
@@ -317,7 +319,8 @@ typedef struct SCSITargetReq SCSITargetReq;
struct SCSITargetReq {
SCSIRequest req;
int len;
- uint8_t buf[2056];
+ uint8_t *buf;
+ int buf_len;
};
static void store_lun(uint8_t *outbuf, int lun)
@@ -361,14 +364,12 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
if (!found_lun0) {
n += 8;
}
- len = MIN(n + 8, r->req.cmd.xfer & ~7);
- if (len > sizeof(r->buf)) {
- /* TODO: > 256 LUNs? */
- return false;
- }
+ scsi_target_alloc_buf(&r->req, n + 8);
+
+ len = MIN(n + 8, r->req.cmd.xfer & ~7);
memset(r->buf, 0, len);
- stl_be_p(&r->buf, n);
+ stl_be_p(&r->buf[0], n);
i = found_lun0 ? 8 : 16;
QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) {
DeviceState *qdev = kid->child;
@@ -387,6 +388,9 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
{
assert(r->req.dev->lun != r->req.lun);
+
+ scsi_target_alloc_buf(&r->req, SCSI_INQUIRY_LEN);
+
if (r->req.cmd.buf[1] & 0x2) {
/* Command support data - optional, not implemented */
return false;
@@ -411,7 +415,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
return false;
}
/* done with EVPD */
- assert(r->len < sizeof(r->buf));
+ assert(r->len < r->buf_len);
r->len = MIN(r->req.cmd.xfer, r->len);
return true;
}
@@ -422,7 +426,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
}
/* PAGE CODE == 0 */
- r->len = MIN(r->req.cmd.xfer, 36);
+ r->len = MIN(r->req.cmd.xfer, SCSI_INQUIRY_LEN);
memset(r->buf, 0, r->len);
if (r->req.lun != 0) {
r->buf[0] = TYPE_NO_LUN;
@@ -455,8 +459,9 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
}
break;
case REQUEST_SENSE:
+ scsi_target_alloc_buf(&r->req, SCSI_SENSE_LEN);
r->len = scsi_device_get_sense(r->req.dev, r->buf,
- MIN(req->cmd.xfer, sizeof r->buf),
+ MIN(req->cmd.xfer, r->buf_len),
(req->cmd.buf[1] & 1) == 0);
if (r->req.dev->sense_is_ua) {
scsi_device_unit_attention_reported(req->dev);
@@ -501,11 +506,29 @@ static uint8_t *scsi_target_get_buf(SCSIRequest *req)
return r->buf;
}
+static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+
+ r->buf = g_malloc(len);
+ r->buf_len = len;
+
+ return r->buf;
+}
+
+static void scsi_target_free_buf(SCSIRequest *req)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+
+ g_free(r->buf);
+}
+
static const 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,
+ .free_req = scsi_target_free_buf,
};
@@ -1365,7 +1388,7 @@ int scsi_build_sense(uint8_t *in_buf, int in_len,
buf[7] = 10;
buf[12] = sense.asc;
buf[13] = sense.ascq;
- return MIN(len, 18);
+ return MIN(len, SCSI_SENSE_LEN);
} else {
/* Return descriptor format sense buffer */
buf[0] = 0x72;
diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index b2fcd4b..2a26042 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -117,6 +117,20 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
return NULL;
}
+static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag)
+{
+ vscsi_req *req;
+ int i;
+
+ for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
+ req = &s->reqs[i];
+ if (req->iu.srp.cmd.tag == srp_tag) {
+ return req;
+ }
+ }
+ return NULL;
+}
+
static void vscsi_put_req(vscsi_req *req)
{
if (req->sreq != NULL) {
@@ -755,40 +769,91 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
{
union viosrp_iu *iu = &req->iu;
- int fn;
+ vscsi_req *tmpreq;
+ int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE;
+ SCSIDevice *d;
+ uint64_t tag = iu->srp.rsp.tag;
+ uint8_t sol_not = iu->srp.cmd.sol_not;
fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n",
iu->srp.tsk_mgmt.tsk_mgmt_func);
- switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
-#if 0 /* We really don't deal with these for now */
- case SRP_TSK_ABORT_TASK:
- fn = ABORT_TASK;
- break;
- case SRP_TSK_ABORT_TASK_SET:
- fn = ABORT_TASK_SET;
- break;
- case SRP_TSK_CLEAR_TASK_SET:
- fn = CLEAR_TASK_SET;
- break;
- case SRP_TSK_LUN_RESET:
- fn = LOGICAL_UNIT_RESET;
- break;
- case SRP_TSK_CLEAR_ACA:
- fn = CLEAR_ACA;
- break;
-#endif
- default:
- fn = 0;
+ d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun);
+ if (!d) {
+ resp = SRP_TSK_MGMT_FIELDS_INVALID;
+ } else {
+ switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
+ case SRP_TSK_ABORT_TASK:
+ if (d->lun != lun) {
+ resp = SRP_TSK_MGMT_FIELDS_INVALID;
+ break;
+ }
+
+ tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag);
+ if (tmpreq && tmpreq->sreq) {
+ assert(tmpreq->sreq->hba_private);
+ scsi_req_cancel(tmpreq->sreq);
+ }
+ break;
+
+ case SRP_TSK_LUN_RESET:
+ if (d->lun != lun) {
+ resp = SRP_TSK_MGMT_FIELDS_INVALID;
+ break;
+ }
+
+ qdev_reset_all(&d->qdev);
+ break;
+
+ case SRP_TSK_ABORT_TASK_SET:
+ case SRP_TSK_CLEAR_TASK_SET:
+ if (d->lun != lun) {
+ resp = SRP_TSK_MGMT_FIELDS_INVALID;
+ break;
+ }
+
+ for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
+ tmpreq = &s->reqs[i];
+ if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) {
+ continue;
+ }
+ if (!tmpreq->active || !tmpreq->sreq) {
+ continue;
+ }
+ assert(tmpreq->sreq->hba_private);
+ scsi_req_cancel(tmpreq->sreq);
+ }
+ break;
+
+ case SRP_TSK_CLEAR_ACA:
+ resp = SRP_TSK_MGMT_NOT_SUPPORTED;
+ break;
+
+ default:
+ resp = SRP_TSK_MGMT_FIELDS_INVALID;
+ break;
+ }
}
- if (fn) {
- /* XXX Send/Handle target task management */
- ;
+
+ /* Compose the response here as */
+ memset(iu, 0, sizeof(struct srp_rsp) + 4);
+ iu->srp.rsp.opcode = SRP_RSP;
+ iu->srp.rsp.req_lim_delta = cpu_to_be32(1);
+ iu->srp.rsp.tag = tag;
+ iu->srp.rsp.flags |= SRP_RSP_FLAG_RSPVALID;
+ iu->srp.rsp.resp_data_len = cpu_to_be32(4);
+ if (resp) {
+ iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2;
} else {
- vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x20, 0);
- vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+ iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1;
}
- return !fn;
+
+ iu->srp.rsp.status = GOOD;
+ iu->srp.rsp.data[3] = resp;
+
+ vscsi_send_iu(s, req, sizeof(iu->srp.rsp) + 4, VIOSRP_SRP_FORMAT);
+
+ return 1;
}
static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req)
@@ -858,29 +923,97 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req)
return vscsi_send_iu(s, req, sizeof(*sinfo), VIOSRP_MAD_FORMAT);
}
+static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req)
+{
+ struct viosrp_capabilities *vcap;
+ struct capabilities cap = { };
+ uint16_t len, req_len;
+ uint64_t buffer;
+ int rc;
+
+ vcap = &req->iu.mad.capabilities;
+ req_len = len = be16_to_cpu(vcap->common.length);
+ buffer = be64_to_cpu(vcap->buffer);
+ if (len > sizeof(cap)) {
+ fprintf(stderr, "vscsi_send_capabilities: capabilities size mismatch !\n");
+
+ /*
+ * Just read and populate the structure that is known.
+ * Zero rest of the structure.
+ */
+ len = sizeof(cap);
+ }
+ rc = spapr_vio_dma_read(&s->vdev, buffer, &cap, len);
+ if (rc) {
+ fprintf(stderr, "vscsi_send_capabilities: DMA read failure !\n");
+ }
+
+ /*
+ * Current implementation does not suppport any migration or
+ * reservation capabilities. Construct the response telling the
+ * guest not to use them.
+ */
+ cap.flags = 0;
+ cap.migration.ecl = 0;
+ cap.reserve.type = 0;
+ cap.migration.common.server_support = 0;
+ cap.reserve.common.server_support = 0;
+
+ rc = spapr_vio_dma_write(&s->vdev, buffer, &cap, len);
+ if (rc) {
+ fprintf(stderr, "vscsi_send_capabilities: DMA write failure !\n");
+ }
+ if (req_len > len) {
+ /*
+ * Being paranoid and lets not worry about the error code
+ * here. Actual write of the cap is done above.
+ */
+ spapr_vio_dma_set(&s->vdev, (buffer + len), 0, (req_len - len));
+ }
+ vcap->common.status = rc ? cpu_to_be32(1) : 0;
+ return vscsi_send_iu(s, req, sizeof(*vcap), VIOSRP_MAD_FORMAT);
+}
+
static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req)
{
union mad_iu *mad = &req->iu.mad;
+ bool request_handled = false;
+ uint64_t retlen = 0;
switch (be32_to_cpu(mad->empty_iu.common.type)) {
case VIOSRP_EMPTY_IU_TYPE:
fprintf(stderr, "Unsupported EMPTY MAD IU\n");
+ retlen = sizeof(mad->empty_iu);
break;
case VIOSRP_ERROR_LOG_TYPE:
fprintf(stderr, "Unsupported ERROR LOG MAD IU\n");
- mad->error_log.common.status = cpu_to_be16(1);
- vscsi_send_iu(s, req, sizeof(mad->error_log), VIOSRP_MAD_FORMAT);
+ retlen = sizeof(mad->error_log);
break;
case VIOSRP_ADAPTER_INFO_TYPE:
vscsi_send_adapter_info(s, req);
+ request_handled = true;
break;
case VIOSRP_HOST_CONFIG_TYPE:
- mad->host_config.common.status = cpu_to_be16(1);
- vscsi_send_iu(s, req, sizeof(mad->host_config), VIOSRP_MAD_FORMAT);
+ retlen = sizeof(mad->host_config);
+ break;
+ case VIOSRP_CAPABILITIES_TYPE:
+ vscsi_send_capabilities(s, req);
+ request_handled = true;
break;
default:
fprintf(stderr, "VSCSI: Unknown MAD type %02x\n",
be32_to_cpu(mad->empty_iu.common.type));
+ /*
+ * PAPR+ says that "The length field is set to the length
+ * of the data structure(s) used in the command".
+ * As we did not recognize the request type, put zero there.
+ */
+ retlen = 0;
+ }
+
+ if (!request_handled) {
+ mad->empty_iu.common.status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED);
+ vscsi_send_iu(s, req, retlen, VIOSRP_MAD_FORMAT);
}
return 1;
diff --git a/hw/scsi/srp.h b/hw/scsi/srp.h
index 5e0cad5..d27f31d 100644
--- a/hw/scsi/srp.h
+++ b/hw/scsi/srp.h
@@ -90,6 +90,13 @@ enum {
SRP_REV16A_IB_IO_CLASS = 0x0100
};
+enum {
+ SRP_TSK_MGMT_COMPLETE = 0x00,
+ SRP_TSK_MGMT_FIELDS_INVALID = 0x02,
+ SRP_TSK_MGMT_NOT_SUPPORTED = 0x04,
+ SRP_TSK_MGMT_FAILED = 0x05
+};
+
struct srp_direct_buf {
uint64_t va;
uint32_t key;
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 3bd690d..26d95a1 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -693,6 +693,7 @@ static const TypeInfo virtio_scsi_common_info = {
.name = TYPE_VIRTIO_SCSI_COMMON,
.parent = TYPE_VIRTIO_DEVICE,
.instance_size = sizeof(VirtIOSCSICommon),
+ .abstract = true,
.class_init = virtio_scsi_common_class_init,
};