static int get_stream_blocksize(BlockDriverState *bdrv) { uint8_t cmd[6]; uint8_t buf[12]; uint8_t sensebuf[8]; sg_io_hdr_t io_header; int ret; memset(cmd, 0, sizeof(cmd)); memset(buf, 0, sizeof(buf)); cmd[0] = MODE_SENSE; cmd[4] = sizeof(buf); memset(&io_header, 0, sizeof(io_header)); io_header.interface_id = 'S'; io_header.dxfer_direction = SG_DXFER_FROM_DEV; io_header.dxfer_len = sizeof(buf); io_header.dxferp = buf; io_header.cmdp = cmd; io_header.cmd_len = sizeof(cmd); io_header.mx_sb_len = sizeof(sensebuf); io_header.sbp = sensebuf; io_header.timeout = 6000; /* XXX */ ret = bdrv_ioctl(bdrv, SG_IO, &io_header); if (ret < 0 || io_header.driver_status || io_header.host_status) { return -1; } return (buf[9] << 16) | (buf[10] << 8) | buf[11]; }
static int get_blocksize(BlockDriverState *bdrv) { uint8_t cmd[10]; uint8_t buf[8]; uint8_t sensebuf[8]; sg_io_hdr_t io_header; int ret; memset(cmd, 0, sizeof(cmd)); memset(buf, 0, sizeof(buf)); cmd[0] = READ_CAPACITY; memset(&io_header, 0, sizeof(io_header)); io_header.interface_id = 'S'; io_header.dxfer_direction = SG_DXFER_FROM_DEV; io_header.dxfer_len = sizeof(buf); io_header.dxferp = buf; io_header.cmdp = cmd; io_header.cmd_len = sizeof(cmd); io_header.mx_sb_len = sizeof(sensebuf); io_header.sbp = sensebuf; io_header.timeout = 6000; /* XXX */ ret = bdrv_ioctl(bdrv, SG_IO, &io_header); if (ret < 0) return -1; return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; }
static void virtio_blk_handle_scsi(VirtIOBlockReq *req) { struct sg_io_hdr hdr; int ret; int status; int i; /* * We require at least one output segment each for the virtio_blk_outhdr * and the SCSI command block. * * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr * and the sense buffer pointer in the input segments. */ if (req->elem.out_num < 2 || req->elem.in_num < 3) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); return; } /* * No support for bidirection commands yet. */ if (req->elem.out_num > 2 && req->elem.in_num > 3) { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); return; } /* * The scsi inhdr is placed in the second-to-last input segment, just * before the regular inhdr. */ req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; memset(&hdr, 0, sizeof(struct sg_io_hdr)); hdr.interface_id = 'S'; hdr.cmd_len = req->elem.out_sg[1].iov_len; hdr.cmdp = req->elem.out_sg[1].iov_base; hdr.dxfer_len = 0; if (req->elem.out_num > 2) { /* * If there are more than the minimally required 2 output segments * there is write payload starting from the third iovec. */ hdr.dxfer_direction = SG_DXFER_TO_DEV; hdr.iovec_count = req->elem.out_num - 2; for (i = 0; i < hdr.iovec_count; i++) hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len; hdr.dxferp = req->elem.out_sg + 2; } else if (req->elem.in_num > 3) { /* * If we have more than 3 input segments the guest wants to actually * read data. */ hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.iovec_count = req->elem.in_num - 3; for (i = 0; i < hdr.iovec_count; i++) hdr.dxfer_len += req->elem.in_sg[i].iov_len; hdr.dxferp = req->elem.in_sg; } else { /* * Some SCSI commands don't actually transfer any data. */ hdr.dxfer_direction = SG_DXFER_NONE; } hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base; hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len; ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr); if (ret) { status = VIRTIO_BLK_S_UNSUPP; hdr.status = ret; hdr.resid = hdr.dxfer_len; } else if (hdr.status) { status = VIRTIO_BLK_S_IOERR; } else { status = VIRTIO_BLK_S_OK; } stl_p(&req->scsi->errors, hdr.status); stl_p(&req->scsi->residual, hdr.resid); stl_p(&req->scsi->sense_len, hdr.sb_len_wr); stl_p(&req->scsi->data_len, hdr.dxfer_len); virtio_blk_req_complete(req, status); }
static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) { return bdrv_ioctl(bs->file, req, buf); }
int virtio_blk_handle_scsi_req(VirtIOBlock *blk, VirtQueueElement *elem) { int status = VIRTIO_BLK_S_OK; struct virtio_scsi_inhdr *scsi = NULL; VirtIODevice *vdev = VIRTIO_DEVICE(blk); #ifdef __linux__ int i; struct sg_io_hdr hdr; #endif /* * We require at least one output segment each for the virtio_blk_outhdr * and the SCSI command block. * * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr * and the sense buffer pointer in the input segments. */ if (elem->out_num < 2 || elem->in_num < 3) { status = VIRTIO_BLK_S_IOERR; goto fail; } /* * The scsi inhdr is placed in the second-to-last input segment, just * before the regular inhdr. */ scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base; if (!blk->blk.scsi) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } /* * No support for bidirection commands yet. */ if (elem->out_num > 2 && elem->in_num > 3) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } #ifdef __linux__ memset(&hdr, 0, sizeof(struct sg_io_hdr)); hdr.interface_id = 'S'; hdr.cmd_len = elem->out_sg[1].iov_len; hdr.cmdp = elem->out_sg[1].iov_base; hdr.dxfer_len = 0; if (elem->out_num > 2) { /* * If there are more than the minimally required 2 output segments * there is write payload starting from the third iovec. */ hdr.dxfer_direction = SG_DXFER_TO_DEV; hdr.iovec_count = elem->out_num - 2; for (i = 0; i < hdr.iovec_count; i++) hdr.dxfer_len += elem->out_sg[i + 2].iov_len; hdr.dxferp = elem->out_sg + 2; } else if (elem->in_num > 3) { /* * If we have more than 3 input segments the guest wants to actually * read data. */ hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.iovec_count = elem->in_num - 3; for (i = 0; i < hdr.iovec_count; i++) hdr.dxfer_len += elem->in_sg[i].iov_len; hdr.dxferp = elem->in_sg; } else { /* * Some SCSI commands don't actually transfer any data. */ hdr.dxfer_direction = SG_DXFER_NONE; } hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base; hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len; status = bdrv_ioctl(blk->bs, SG_IO, &hdr); if (status) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } /* * From SCSI-Generic-HOWTO: "Some lower level drivers (e.g. ide-scsi) * clear the masked_status field [hence status gets cleared too, see * block/scsi_ioctl.c] even when a CHECK_CONDITION or COMMAND_TERMINATED * status has occurred. However they do set DRIVER_SENSE in driver_status * field. Also a (sb_len_wr > 0) indicates there is a sense buffer. */ if (hdr.status == 0 && hdr.sb_len_wr > 0) { hdr.status = CHECK_CONDITION; } virtio_stl_p(vdev, &scsi->errors, hdr.status | (hdr.msg_status << 8) | (hdr.host_status << 16) | (hdr.driver_status << 24)); virtio_stl_p(vdev, &scsi->residual, hdr.resid); virtio_stl_p(vdev, &scsi->sense_len, hdr.sb_len_wr); virtio_stl_p(vdev, &scsi->data_len, hdr.dxfer_len); return status; #else abort(); #endif fail: /* Just put anything nonzero so that the ioctl fails in the guest. */ if (scsi) { virtio_stl_p(vdev, &scsi->errors, 255); } return status; }
static void virtio_blk_handle_scsi(VirtIOBlockReq *req) { struct sg_io_hdr hdr; int ret; int status; int i; if ((req->dev->vdev.guest_features & (1 << VIRTIO_BLK_F_SCSI)) == 0) { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); g_free(req); return; } /* * We require at least one output segment each for the virtio_blk_outhdr * and the SCSI command block. * * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr * and the sense buffer pointer in the input segments. */ if (req->elem.out_num < 2 || req->elem.in_num < 3) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); g_free(req); return; } /* * No support for bidirection commands yet. */ if (req->elem.out_num > 2 && req->elem.in_num > 3) { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); g_free(req); return; } /* * The scsi inhdr is placed in the second-to-last input segment, just * before the regular inhdr. */ req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; memset(&hdr, 0, sizeof(struct sg_io_hdr)); hdr.interface_id = 'S'; hdr.cmd_len = req->elem.out_sg[1].iov_len; hdr.cmdp = req->elem.out_sg[1].iov_base; hdr.dxfer_len = 0; if (req->elem.out_num > 2) { /* * If there are more than the minimally required 2 output segments * there is write payload starting from the third iovec. */ hdr.dxfer_direction = SG_DXFER_TO_DEV; hdr.iovec_count = req->elem.out_num - 2; for (i = 0; i < hdr.iovec_count; i++) hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len; hdr.dxferp = req->elem.out_sg + 2; } else if (req->elem.in_num > 3) { /* * If we have more than 3 input segments the guest wants to actually * read data. */ hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.iovec_count = req->elem.in_num - 3; for (i = 0; i < hdr.iovec_count; i++) hdr.dxfer_len += req->elem.in_sg[i].iov_len; hdr.dxferp = req->elem.in_sg; } else { /* * Some SCSI commands don't actually transfer any data. */ hdr.dxfer_direction = SG_DXFER_NONE; } hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base; hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len; ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr); if (ret) { status = VIRTIO_BLK_S_UNSUPP; hdr.status = ret; hdr.resid = hdr.dxfer_len; } else if (hdr.status) { status = VIRTIO_BLK_S_IOERR; } else { status = VIRTIO_BLK_S_OK; } /* * From SCSI-Generic-HOWTO: "Some lower level drivers (e.g. ide-scsi) * clear the masked_status field [hence status gets cleared too, see * block/scsi_ioctl.c] even when a CHECK_CONDITION or COMMAND_TERMINATED * status has occurred. However they do set DRIVER_SENSE in driver_status * field. Also a (sb_len_wr > 0) indicates there is a sense buffer. */ if (hdr.status == 0 && hdr.sb_len_wr > 0) { hdr.status = CHECK_CONDITION; } stl_p(&req->scsi->errors, hdr.status | (hdr.msg_status << 8) | (hdr.host_status << 16) | (hdr.driver_status << 24)); stl_p(&req->scsi->residual, hdr.resid); stl_p(&req->scsi->sense_len, hdr.sb_len_wr); stl_p(&req->scsi->data_len, hdr.dxfer_len); virtio_blk_req_complete(req, status); g_free(req); }
static int scsi_generic_initfn(SCSIDevice *s) { int sg_version; struct sg_scsi_id scsiid; if (!s->conf.bs) { error_report("drive property not set"); return -1; } /* check we are really using a /dev/sg* file */ if (!bdrv_is_sg(s->conf.bs)) { error_report("not /dev/sg*"); return -1; } if (bdrv_get_on_error(s->conf.bs, 0) != BLOCK_ERR_STOP_ENOSPC) { error_report("Device doesn't support drive option werror"); return -1; } if (bdrv_get_on_error(s->conf.bs, 1) != BLOCK_ERR_REPORT) { error_report("Device doesn't support drive option rerror"); return -1; } /* check we are using a driver managing SG_IO (version 3 and after */ if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 || sg_version < 30000) { error_report("scsi generic interface too old"); return -1; } /* get LUN of the /dev/sg? */ if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) { error_report("SG_GET_SCSI_ID ioctl failed"); return -1; } /* define device state */ s->type = scsiid.scsi_type; DPRINTF("device type %d\n", s->type); if (s->type == TYPE_DISK || s->type == TYPE_ROM) { add_boot_device_path(s->conf.bootindex, &s->qdev, NULL); } switch (s->type) { case TYPE_TAPE: s->blocksize = get_stream_blocksize(s->conf.bs); if (s->blocksize == -1) { s->blocksize = 0; } break; /* Make a guess for block devices, we'll fix it when the guest sends. * READ CAPACITY. If they don't, they likely would assume these sizes * anyway. (TODO: they could also send MODE SENSE). */ case TYPE_ROM: case TYPE_WORM: s->blocksize = 2048; break; default: s->blocksize = 512; break; } DPRINTF("block size %d\n", s->blocksize); return 0; }
static int scsi_generic_initfn(SCSIDevice *dev) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, dev); int sg_version; struct sg_scsi_id scsiid; if (!s->qdev.conf.bs) { error_report("scsi-generic: drive property not set"); return -1; } s->bs = s->qdev.conf.bs; /* check we are really using a /dev/sg* file */ if (!bdrv_is_sg(s->bs)) { error_report("scsi-generic: not /dev/sg*"); return -1; } if (bdrv_get_on_error(s->bs, 0) != BLOCK_ERR_STOP_ENOSPC) { error_report("Device doesn't support drive option werror"); return -1; } if (bdrv_get_on_error(s->bs, 1) != BLOCK_ERR_REPORT) { error_report("Device doesn't support drive option rerror"); return -1; } /* check we are using a driver managing SG_IO (version 3 and after */ if (bdrv_ioctl(s->bs, SG_GET_VERSION_NUM, &sg_version) < 0 || sg_version < 30000) { error_report("scsi-generic: scsi generic interface too old"); return -1; } /* get LUN of the /dev/sg? */ if (bdrv_ioctl(s->bs, SG_GET_SCSI_ID, &scsiid)) { error_report("scsi-generic: SG_GET_SCSI_ID ioctl failed"); return -1; } /* define device state */ s->lun = scsiid.lun; DPRINTF("LUN %d\n", s->lun); s->qdev.type = scsiid.scsi_type; DPRINTF("device type %d\n", s->qdev.type); if (s->qdev.type == TYPE_TAPE) { s->qdev.blocksize = get_stream_blocksize(s->bs); if (s->qdev.blocksize == -1) s->qdev.blocksize = 0; } else { s->qdev.blocksize = get_blocksize(s->bs); /* removable media returns 0 if not present */ if (s->qdev.blocksize <= 0) { if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) s->qdev.blocksize = 2048; else s->qdev.blocksize = 512; } } 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; }