/*#define DEBUG_VSCSI*/
#ifdef DEBUG_VSCSI
-#define dprintf(fmt, ...) \
+#define DPRINTF(fmt, ...) \
do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
#else
-#define dprintf(fmt, ...) \
+#define DPRINTF(fmt, ...) \
do { } while (0)
#endif
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) {
union viosrp_iu *iu = &req->iu;
uint64_t tag = iu->srp.rsp.tag;
int total_len = sizeof(iu->srp.rsp);
+ uint8_t sol_not = iu->srp.cmd.sol_not;
- dprintf("VSCSI: Sending resp status: 0x%x, "
+ DPRINTF("VSCSI: Sending resp status: 0x%x, "
"res_in: %d, res_out: %d\n", status, res_in, res_out);
memset(iu, 0, sizeof(struct srp_rsp));
/* Handle success vs. failure */
iu->srp.rsp.status = status;
if (status) {
- iu->srp.rsp.sol_not = (iu->srp.cmd.sol_not & 0x04) >> 2;
+ iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2;
if (req->senselen) {
req->iu.srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
req->iu.srp.rsp.sense_data_len = cpu_to_be32(req->senselen);
total_len += req->senselen;
}
} else {
- iu->srp.rsp.sol_not = (iu->srp.cmd.sol_not & 0x02) >> 1;
+ iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1;
}
vscsi_send_iu(s, req, total_len, VIOSRP_SRP_FORMAT);
switch (req->dma_fmt) {
case SRP_NO_DATA_DESC: {
- dprintf("VSCSI: no data descriptor\n");
+ DPRINTF("VSCSI: no data descriptor\n");
return 0;
}
case SRP_DATA_DESC_DIRECT: {
memcpy(ret, cmd->add_data + req->cdb_offset, sizeof(*ret));
assert(req->cur_desc_num == 0);
- dprintf("VSCSI: direct segment\n");
+ DPRINTF("VSCSI: direct segment\n");
break;
}
case SRP_DATA_DESC_INDIRECT: {
(cmd->add_data + req->cdb_offset);
if (n < req->local_desc) {
*ret = tmp->desc_list[n];
- dprintf("VSCSI: indirect segment local tag=0x%x desc#%d/%d\n",
+ DPRINTF("VSCSI: indirect segment local tag=0x%x desc#%d/%d\n",
req->qtag, n, req->local_desc);
} else if (n < req->total_desc) {
unsigned desc_offset = n * sizeof(struct srp_direct_buf);
if (desc_offset >= tbl_desc.len) {
- dprintf("VSCSI: #%d is ouf of range (%d bytes)\n",
+ DPRINTF("VSCSI: #%d is ouf of range (%d bytes)\n",
n, desc_offset);
return -1;
}
rc = spapr_vio_dma_read(&s->vdev, tbl_desc.va + desc_offset,
ret, sizeof(struct srp_direct_buf));
if (rc) {
- dprintf("VSCSI: spapr_vio_dma_read -> %d reading ext_desc\n",
+ DPRINTF("VSCSI: spapr_vio_dma_read -> %d reading ext_desc\n",
rc);
return -1;
}
- dprintf("VSCSI: indirect segment ext. tag=0x%x desc#%d/%d { va=%"PRIx64" len=%x }\n",
+ DPRINTF("VSCSI: indirect segment ext. tag=0x%x desc#%d/%d { va=%"PRIx64" len=%x }\n",
req->qtag, n, req->total_desc, tbl_desc.va, tbl_desc.len);
} else {
- dprintf("VSCSI: Out of descriptors !\n");
+ DPRINTF("VSCSI: Out of descriptors !\n");
return 0;
}
break;
*ret = vscsi_swap_desc(*ret);
if (buf_offset > ret->len) {
- dprintf(" offset=%x is out of a descriptor #%d boundary=%x\n",
+ DPRINTF(" offset=%x is out of a descriptor #%d boundary=%x\n",
buf_offset, req->cur_desc_num, ret->len);
return -1;
}
ret->va += buf_offset;
ret->len -= buf_offset;
- dprintf(" cur=%d offs=%x ret { va=%"PRIx64" len=%x }\n",
+ DPRINTF(" cur=%d offs=%x ret { va=%"PRIx64" len=%x }\n",
req->cur_desc_num, req->cur_desc_offset, ret->va, ret->len);
return ret->len ? 1 : 0;
int rc = 0;
uint32_t llen, total = 0;
- dprintf("VSCSI: indirect segment 0x%x bytes\n", len);
+ DPRINTF("VSCSI: indirect segment 0x%x bytes\n", len);
/* While we have data ... */
while (len) {
rc = spapr_vio_dma_write(&s->vdev, md.va, buf, llen);
}
if (rc) {
- dprintf("VSCSI: spapr_vio_dma_r/w(%d) -> %d\n", req->writing, rc);
+ DPRINTF("VSCSI: spapr_vio_dma_r/w(%d) -> %d\n", req->writing, rc);
break;
}
- dprintf("VSCSI: data: %02x %02x %02x %02x...\n",
+ DPRINTF("VSCSI: data: %02x %02x %02x %02x...\n",
buf[0], buf[1], buf[2], buf[3]);
len -= llen;
switch (req->dma_fmt) {
case SRP_NO_DATA_DESC:
- dprintf("VSCSI: no data desc transfer, skipping 0x%x bytes\n", len);
+ DPRINTF("VSCSI: no data desc transfer, skipping 0x%x bytes\n", len);
break;
case SRP_DATA_DESC_DIRECT:
err = vscsi_srp_direct_data(s, req, buf, len);
uint8_t *buf;
int rc = 0;
- dprintf("VSCSI: SCSI xfer complete tag=0x%x len=0x%x, req=%p\n",
+ DPRINTF("VSCSI: SCSI xfer complete tag=0x%x len=0x%x, req=%p\n",
sreq->tag, len, req);
if (req == NULL) {
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
vscsi_req *req = sreq->hba_private;
int32_t res_in = 0, res_out = 0;
- dprintf("VSCSI: SCSI cmd complete, tag=0x%x status=0x%x, req=%p\n",
+ DPRINTF("VSCSI: SCSI cmd complete, tag=0x%x status=0x%x, req=%p\n",
sreq->tag, status, req);
if (req == NULL) {
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
if (status == CHECK_CONDITION) {
req->senselen = scsi_req_get_sense(req->sreq, req->sense,
sizeof(req->sense));
- dprintf("VSCSI: Sense data, %d bytes:\n", req->senselen);
- dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ DPRINTF("VSCSI: Sense data, %d bytes:\n", req->senselen);
+ DPRINTF(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
req->sense[0], req->sense[1], req->sense[2], req->sense[3],
req->sense[4], req->sense[5], req->sense[6], req->sense[7]);
- dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ DPRINTF(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
req->sense[8], req->sense[9], req->sense[10], req->sense[11],
req->sense[12], req->sense[13], req->sense[14], req->sense[15]);
}
- dprintf("VSCSI: Command complete err=%d\n", status);
+ DPRINTF("VSCSI: Command complete err=%d\n", status);
if (status == 0) {
/* We handle overflows, not underflows for normal commands,
* but hopefully nobody cares
vscsi_put_req(req);
}
+static const VMStateDescription vmstate_spapr_vscsi_req = {
+ .name = "spapr_vscsi_req",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_BUFFER(crq.raw, vscsi_req),
+ VMSTATE_BUFFER(iu.srp.reserved, vscsi_req),
+ VMSTATE_UINT32(qtag, vscsi_req),
+ VMSTATE_BOOL(active, vscsi_req),
+ VMSTATE_UINT32(data_len, vscsi_req),
+ VMSTATE_BOOL(writing, vscsi_req),
+ VMSTATE_UINT32(senselen, vscsi_req),
+ VMSTATE_BUFFER(sense, vscsi_req),
+ VMSTATE_UINT8(dma_fmt, vscsi_req),
+ VMSTATE_UINT16(local_desc, vscsi_req),
+ VMSTATE_UINT16(total_desc, vscsi_req),
+ VMSTATE_UINT16(cdb_offset, vscsi_req),
+ /*Restart SCSI request from the beginning for now */
+ /*VMSTATE_UINT16(cur_desc_num, vscsi_req),
+ VMSTATE_UINT16(cur_desc_offset, vscsi_req),*/
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static void vscsi_save_request(QEMUFile *f, SCSIRequest *sreq)
+{
+ vscsi_req *req = sreq->hba_private;
+ assert(req->active);
+
+ vmstate_save_state(f, &vmstate_spapr_vscsi_req, req);
+
+ DPRINTF("VSCSI: saving tag=%u, current desc#%d, offset=%x\n",
+ req->qtag, req->cur_desc_num, req->cur_desc_offset);
+}
+
+static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq)
+{
+ SCSIBus *bus = sreq->bus;
+ VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(bus->qbus.parent);
+ vscsi_req *req;
+ int rc;
+
+ assert(sreq->tag < VSCSI_REQ_LIMIT);
+ req = &s->reqs[sreq->tag];
+ assert(!req->active);
+
+ memset(req, 0, sizeof(*req));
+ rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1);
+ if (rc) {
+ fprintf(stderr, "VSCSI: failed loading request tag#%u\n", sreq->tag);
+ return NULL;
+ }
+ assert(req->active);
+
+ req->sreq = scsi_req_ref(sreq);
+
+ DPRINTF("VSCSI: restoring tag=%u, current desc#%d, offset=%x\n",
+ req->qtag, req->cur_desc_num, req->cur_desc_offset);
+
+ return req;
+}
+
static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
{
union viosrp_iu *iu = &req->iu;
struct srp_login_rsp *rsp = &iu->srp.login_rsp;
uint64_t tag = iu->srp.rsp.tag;
- dprintf("VSCSI: Got login, sendin response !\n");
+ DPRINTF("VSCSI: Got login, sendin response !\n");
/* TODO handle case that requested size is wrong and
* buffer format is wrong
sdev = vscsi_device_find(&s->bus, be64_to_cpu(srp->cmd.lun), &lun);
if (!sdev) {
- dprintf("VSCSI: Command for lun %08" PRIx64 " with no drive\n", be64_to_cpu(srp->cmd.lun));
+ DPRINTF("VSCSI: Command for lun %08" PRIx64 " with no drive\n",
+ be64_to_cpu(srp->cmd.lun));
if (srp->cmd.cdb[0] == INQUIRY) {
vscsi_inquiry_no_target(s, req);
} else {
req->sreq = scsi_req_new(sdev, req->qtag, lun, srp->cmd.cdb, req);
n = scsi_req_enqueue(req->sreq);
- dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x LUN %d ret: %d\n",
+ DPRINTF("VSCSI: Queued command tag 0x%x CMD 0x%x LUN %d ret: %d\n",
req->qtag, srp->cmd.cdb[0], lun, n);
if (n) {
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)
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;
crq.s.IU_length = be16_to_cpu(crq.s.IU_length);
crq.s.IU_data_ptr = be64_to_cpu(crq.s.IU_data_ptr);
- dprintf("VSCSI: do_crq %02x %02x ...\n", crq.raw[0], crq.raw[1]);
+ DPRINTF("VSCSI: do_crq %02x %02x ...\n", crq.raw[0], crq.raw[1]);
switch (crq.s.valid) {
case 0xc0: /* Init command/response */
.transfer_data = vscsi_transfer_data,
.complete = vscsi_command_complete,
- .cancel = vscsi_request_cancelled
+ .cancel = vscsi_request_cancelled,
+ .save_request = vscsi_save_request,
+ .load_request = vscsi_load_request,
};
static void spapr_vscsi_reset(VIOsPAPRDevice *dev)
dev->crq.SendFunc = vscsi_do_crq;
- scsi_bus_new(&s->bus, &dev->qdev, &vscsi_scsi_info, NULL);
+ scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
+ &vscsi_scsi_info, NULL);
if (!dev->qdev.hotplugged) {
scsi_bus_legacy_handle_cmdline(&s->bus, &err);
if (err != NULL) {
DEFINE_PROP_END_OF_LIST(),
};
+static const VMStateDescription vmstate_spapr_vscsi = {
+ .name = "spapr_vscsi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_SPAPR_VIO(vdev, VSCSIState),
+ /* VSCSI state */
+ /* ???? */
+
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static void spapr_vscsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
k->dt_type = "vscsi";
k->dt_compatible = "IBM,v-scsi";
k->signal_mask = 0x00000001;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->props = spapr_vscsi_properties;
k->rtce_window_size = 0x10000000;
+ dc->vmsd = &vmstate_spapr_vscsi;
}
static const TypeInfo spapr_vscsi_info = {