summaryrefslogtreecommitdiffstats
path: root/usr.sbin/vmd/vioscsi.c
diff options
context:
space:
mode:
authorccardenas <ccardenas@openbsd.org>2018-01-03 05:39:56 +0000
committerccardenas <ccardenas@openbsd.org>2018-01-03 05:39:56 +0000
commit95ab188f112e9222b375a8ba5e758d43cbe60f74 (patch)
tree7fbcf54a8b3658689db8aa8d37c436d9917a4d99 /usr.sbin/vmd/vioscsi.c
parentAdd remap_bynode() since I use it in the rkpcie(4) implementation. (diff)
downloadwireguard-openbsd-95ab188f112e9222b375a8ba5e758d43cbe60f74.tar.xz
wireguard-openbsd-95ab188f112e9222b375a8ba5e758d43cbe60f74.zip
Add initial CD-ROM support to VMD via vioscsi.
* Adds 'cdrom' keyword to vm.conf(5) and '-r' to vmctl(8) * Support various sized ISOs (Limitation of 4G ISOs on Linux guests) * Known working guests: OpenBSD (primary), Alpine Linux (primary), CentOS 6 (secondary), Ubuntu 17.10 (secondary). NOTE: Secondary indicates some issue(s) preventing full/reliable functionality outside the scope of the vioscsi work. * If the attached disks are non-bootable (i.e. empty), SeaBIOS (vmd's default BIOS) will boot from CD-ROM. ok mlarkin@, jca@
Diffstat (limited to 'usr.sbin/vmd/vioscsi.c')
-rw-r--r--usr.sbin/vmd/vioscsi.c2218
1 files changed, 2218 insertions, 0 deletions
diff --git a/usr.sbin/vmd/vioscsi.c b/usr.sbin/vmd/vioscsi.c
new file mode 100644
index 00000000000..290de68e453
--- /dev/null
+++ b/usr.sbin/vmd/vioscsi.c
@@ -0,0 +1,2218 @@
+/* $OpenBSD: vioscsi.c,v 1.1 2018/01/03 05:39:56 ccardenas Exp $ */
+
+/*
+ * Copyright (c) 2017 Carlos Cardenas <ccardenas@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <dev/pv/vioscsireg.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+#include <scsi/cd.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vmd.h"
+#include "vioscsi.h"
+#include "virtio.h"
+
+extern char *__progname;
+
+static void
+vioscsi_prepare_resp(struct virtio_scsi_res_hdr *resp, uint8_t vio_status,
+ uint8_t scsi_status, uint8_t err_flags, uint8_t add_sense_code,
+ uint8_t add_sense_code_qual)
+{
+ /* Set lower 8 bits of status and response fields */
+ resp->response &= 0xFFFFFF00;
+ resp->response |= vio_status;
+ resp->status &= 0xFFFFFF00;
+ resp->status |= scsi_status;
+
+ resp->sense_len = 0;
+
+ /* determine if we need to populate the sense field */
+ if (scsi_status == SCSI_CHECK) {
+ /*
+ * sense data is a 96 byte field.
+ * We only need to use the first 14 bytes
+ * - set the sense_len accordingly
+ * - set error_code to Current Command
+ * ref scsi/scsi_all.h:struct scsi_sense_data
+ */
+ memset(resp->sense, 0, VIOSCSI_SENSE_LEN);
+ resp->sense_len = RESP_SENSE_LEN;
+ resp->sense[0] = SSD_ERRCODE_CURRENT;
+ resp->sense[2] = err_flags;
+ resp->sense[12] = add_sense_code;
+ resp->sense[13] = add_sense_code_qual;
+ }
+}
+
+static struct vring_desc*
+vioscsi_next_ring_desc(struct vring_desc* desc, struct vring_desc* cur,
+ uint16_t *idx)
+{
+ *idx = cur->next & VIOSCSI_QUEUE_MASK;
+ return &desc[*idx];
+}
+
+static void
+vioscsi_next_ring_item(struct vioscsi_dev *dev, struct vring_avail *avail,
+ struct vring_used *used, struct vring_desc *desc, uint16_t idx)
+{
+ used->ring[used->idx & VIOSCSI_QUEUE_MASK].id = idx;
+ used->ring[used->idx & VIOSCSI_QUEUE_MASK].len = desc->len;
+ used->idx++;
+
+ dev->vq[dev->cfg.queue_notify].last_avail =
+ avail->idx & VIOSCSI_QUEUE_MASK;
+}
+
+static const char *
+vioscsi_op_names(uint8_t type)
+{
+ switch(type) {
+ /* defined in scsi_all.h */
+ case TEST_UNIT_READY: return "TEST_UNIT_READY";
+ case REQUEST_SENSE: return "REQUEST_SENSE";
+ case INQUIRY: return "INQUIRY";
+ case MODE_SELECT: return "MODE_SELECT";
+ case RESERVE: return "RESERVE";
+ case RELEASE: return "RELEASE";
+ case MODE_SENSE: return "MODE_SENSE";
+ case START_STOP: return "START_STOP";
+ case RECEIVE_DIAGNOSTIC: return "RECEIVE_DIAGNOSTIC";
+ case SEND_DIAGNOSTIC: return "SEND_DIAGNOSTIC";
+ case PREVENT_ALLOW: return "PREVENT_ALLOW";
+ case POSITION_TO_ELEMENT: return "POSITION_TO_ELEMENT";
+ case WRITE_BUFFER: return "WRITE_BUFFER";
+ case READ_BUFFER: return "READ_BUFFER";
+ case CHANGE_DEFINITION: return "CHANGE_DEFINITION";
+ case MODE_SELECT_BIG: return "MODE_SELECT_BIG";
+ case MODE_SENSE_BIG: return "MODE_SENSE_BIG";
+ case REPORT_LUNS: return "REPORT_LUNS";
+ /* defined in scsi_disk.h */
+ case REASSIGN_BLOCKS: return "REASSIGN_BLOCKS";
+ case READ_COMMAND: return "READ_COMMAND";
+ case WRITE_COMMAND: return "WRITE_COMMAND";
+ case READ_CAPACITY: return "READ_CAPACITY";
+ case READ_CAPACITY_16: return "READ_CAPACITY_16";
+ case READ_BIG: return "READ_BIG";
+ case WRITE_BIG: return "WRITE_BIG";
+ case READ_12: return "READ_12";
+ case WRITE_12: return "WRITE_12";
+ case READ_16: return "READ_16";
+ case WRITE_16: return "WRITE_16";
+ case SYNCHRONIZE_CACHE: return "SYNCHRONIZE_CACHE";
+ case WRITE_SAME_10: return "WRITE_SAME_10";
+ case WRITE_SAME_16: return "WRITE_SAME_16";
+ /* defined in cd.h */
+ case READ_SUBCHANNEL: return "READ_SUBCHANNEL";
+ case READ_TOC: return "READ_TOC";
+ case READ_HEADER: return "READ_HEADER";
+ case PLAY: return "PLAY";
+ case PLAY_MSF: return "PLAY_MSF";
+ case PLAY_TRACK: return "PLAY_TRACK";
+ case PLAY_TRACK_REL: return "PLAY_TRACK_REL";
+ case PAUSE: return "PAUSE";
+ case READ_TRACK_INFO: return "READ_TRACK_INFO";
+ case CLOSE_TRACK: return "CLOSE_TRACK";
+ case BLANK: return "BLANK";
+ case PLAY_BIG: return "PLAY_BIG";
+ case LOAD_UNLOAD: return "LOAD_UNLOAD";
+ case PLAY_TRACK_REL_BIG: return "PLAY_TRACK_REL_BIG";
+ case SET_CD_SPEED: return "SET_CD_SPEED";
+ /* defined locally */
+ case READ_DISC_INFORMATION: return "READ_DISC_INFORMATION";
+ case GET_CONFIGURATION: return "GET_CONFIGURATION";
+ case MECHANISM_STATUS: return "MECHANISM_STATUS";
+ case GET_EVENT_STATUS_NOTIFICATION:
+ return "GET_EVENT_STATUS_NOTIFICATION";
+ default: return "UNKNOWN";
+ }
+}
+
+static const char *
+vioscsi_reg_name(uint8_t reg)
+{
+ switch (reg) {
+ case VIRTIO_CONFIG_DEVICE_FEATURES: return "device feature";
+ case VIRTIO_CONFIG_GUEST_FEATURES: return "guest feature";
+ case VIRTIO_CONFIG_QUEUE_ADDRESS: return "queue address";
+ case VIRTIO_CONFIG_QUEUE_SIZE: return "queue size";
+ case VIRTIO_CONFIG_QUEUE_SELECT: return "queue select";
+ case VIRTIO_CONFIG_QUEUE_NOTIFY: return "queue notify";
+ case VIRTIO_CONFIG_DEVICE_STATUS: return "device status";
+ case VIRTIO_CONFIG_ISR_STATUS: return "isr status";
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI: return "num_queues";
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4: return "seg_max";
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8: return "max_sectors";
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12: return "cmd_per_lun";
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16: return "event_info_size";
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 20: return "sense_size";
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 24: return "cdb_size";
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 28: return "max_channel";
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 30: return "max_target";
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 32: return "max_lun";
+ default: return "unknown";
+ }
+}
+
+static void
+vioscsi_free_info(struct ioinfo *info)
+{
+ if (!info)
+ return;
+ free(info->buf);
+ free(info);
+}
+
+static struct ioinfo *
+vioscsi_start_read(struct vioscsi_dev *dev, off_t block, ssize_t n_blocks)
+{
+ struct ioinfo *info;
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ goto nomem;
+ info->buf = malloc(n_blocks * VIOSCSI_BLOCK_SIZE_CDROM);
+ if (info->buf == NULL)
+ goto nomem;
+ info->len = n_blocks * VIOSCSI_BLOCK_SIZE_CDROM;
+ info->offset = block * VIOSCSI_BLOCK_SIZE_CDROM;
+ info->fd = dev->fd;
+
+ return info;
+
+nomem:
+ free(info);
+ log_warn("malloc errror vioscsi read");
+ return (NULL);
+}
+
+static const uint8_t *
+vioscsi_finish_read(struct ioinfo *info)
+{
+ if (pread(info->fd, info->buf, info->len, info->offset) != info->len) {
+ info->error = errno;
+ log_warn("vioscsi read error");
+ return NULL;
+ }
+
+ return info->buf;
+}
+
+int
+vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
+ void *cookie, uint8_t sz)
+{
+ struct vioscsi_dev *dev = (struct vioscsi_dev *)cookie;
+
+ *intr = 0xFF;
+
+ log_debug("%s: request %s reg %u,%s sz %u", __func__,
+ dir ? "READ" : "WRITE", reg, vioscsi_reg_name(reg), sz);
+
+ if (dir == 0) {
+ switch (reg) {
+ case VIRTIO_CONFIG_DEVICE_FEATURES:
+ case VIRTIO_CONFIG_QUEUE_SIZE:
+ case VIRTIO_CONFIG_ISR_STATUS:
+ log_warnx("%s: illegal write %x to %s",
+ __progname, *data, vioscsi_reg_name(reg));
+ break;
+ case VIRTIO_CONFIG_GUEST_FEATURES:
+ dev->cfg.guest_feature = *data;
+ log_debug("%s: guest feature set to %u",
+ __func__, dev->cfg.guest_feature);
+ break;
+ case VIRTIO_CONFIG_QUEUE_ADDRESS:
+ dev->cfg.queue_address = *data;
+ vioscsi_update_qa(dev);
+ break;
+ case VIRTIO_CONFIG_QUEUE_SELECT:
+ dev->cfg.queue_select = *data;
+ vioscsi_update_qs(dev);
+ break;
+ case VIRTIO_CONFIG_QUEUE_NOTIFY:
+ dev->cfg.queue_notify = *data;
+ if (vioscsi_notifyq(dev))
+ *intr = 1;
+ break;
+ case VIRTIO_CONFIG_DEVICE_STATUS:
+ dev->cfg.device_status = *data;
+ log_debug("%s: device status set to %u",
+ __func__, dev->cfg.device_status);
+ if (dev->cfg.device_status == 0) {
+ log_debug("%s: device reset", __func__);
+ dev->cfg.guest_feature = 0;
+ dev->cfg.queue_address = 0;
+ vioscsi_update_qa(dev);
+ dev->cfg.queue_size = 0;
+ vioscsi_update_qs(dev);
+ dev->cfg.queue_select = 0;
+ dev->cfg.queue_notify = 0;
+ dev->cfg.isr_status = 0;
+ dev->vq[0].last_avail = 0;
+ dev->vq[1].last_avail = 0;
+ dev->vq[2].last_avail = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (reg) {
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
+ /* VIRTIO_SCSI_CONFIG_NUM_QUEUES, 32bit */
+ if (sz == 4)
+ *data = (uint32_t)VIOSCSI_NUM_QUEUES;
+ else if (sz == 1) {
+ /* read first byte of num_queues */
+ *data &= 0xFFFFFF00;
+ *data |= (uint32_t)(VIOSCSI_NUM_QUEUES) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 1:
+ if (sz == 1) {
+ /* read second byte of num_queues */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_NUM_QUEUES >> 8) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 2:
+ if (sz == 1) {
+ /* read third byte of num_queues */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_NUM_QUEUES >> 16) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 3:
+ if (sz == 1) {
+ /* read fourth byte of num_queues */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_NUM_QUEUES >> 24) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4:
+ /* VIRTIO_SCSI_CONFIG_SEG_MAX, 32bit */
+ if (sz == 4)
+ *data = (uint32_t)(VIOSCSI_SEG_MAX);
+ else if (sz == 1) {
+ /* read first byte of seg_max */
+ *data &= 0xFFFFFF00;
+ *data |= (uint32_t)(VIOSCSI_SEG_MAX) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 5:
+ if (sz == 1) {
+ /* read second byte of seg_max */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_SEG_MAX >> 8) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 6:
+ if (sz == 1) {
+ /* read third byte of seg_max */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_SEG_MAX >> 16) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 7:
+ if (sz == 1) {
+ /* read fourth byte of seg_max */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_SEG_MAX >> 24) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8:
+ /* VIRTIO_SCSI_CONFIG_MAX_SECTORS, 32bit */
+ if (sz == 4)
+ *data = (uint32_t)(dev->max_xfer);
+ else if (sz == 1) {
+ /* read first byte of max_xfer */
+ *data &= 0xFFFFFF00;
+ *data |= (uint32_t)(dev->max_xfer) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 9:
+ if (sz == 1) {
+ /* read second byte of max_xfer */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(dev->max_xfer >> 8) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 10:
+ if (sz == 1) {
+ /* read third byte of max_xfer */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(dev->max_xfer >> 16) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 11:
+ if (sz == 1) {
+ /* read fourth byte of max_xfer */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(dev->max_xfer >> 24) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12:
+ /* VIRTIO_SCSI_CONFIG_CMD_PER_LUN, 32bit */
+ if (sz == 4)
+ *data = (uint32_t)(VIOSCSI_CMD_PER_LUN);
+ else if (sz == 1) {
+ /* read first byte of cmd_per_lun */
+ *data &= 0xFFFFFF00;
+ *data |= (uint32_t)(VIOSCSI_CMD_PER_LUN) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 13:
+ if (sz == 1) {
+ /* read second byte of cmd_per_lun */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_CMD_PER_LUN >> 8) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 14:
+ if (sz == 1) {
+ /* read third byte of cmd_per_lun */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_CMD_PER_LUN >> 16) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 15:
+ if (sz == 1) {
+ /* read fourth byte of cmd_per_lun */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_CMD_PER_LUN >> 24) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16:
+ /* VIRTIO_SCSI_CONFIG_EVENT_INFO_SIZE, 32bit */
+ *data = 0x00;
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 20:
+ /* VIRTIO_SCSI_CONFIG_SENSE_SIZE, 32bit */
+ if (sz == 4)
+ *data = (uint32_t)(VIOSCSI_SENSE_LEN);
+ else if (sz == 1) {
+ /* read first byte of sense_size */
+ *data &= 0xFFFFFF00;
+ *data |= (uint32_t)(VIOSCSI_SENSE_LEN) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 21:
+ if (sz == 1) {
+ /* read second byte of sense_size */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_SENSE_LEN >> 8) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 22:
+ if (sz == 1) {
+ /* read third byte of sense_size */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_SENSE_LEN >> 16) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 23:
+ if (sz == 1) {
+ /* read fourth byte of sense_size */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_SENSE_LEN >> 24) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 24:
+ /* VIRTIO_SCSI_CONFIG_CDB_SIZE, 32bit */
+ if (sz == 4)
+ *data = (uint32_t)(VIOSCSI_CDB_LEN);
+ else if (sz == 1) {
+ /* read first byte of cdb_len */
+ *data &= 0xFFFFFF00;
+ *data |= (uint32_t)(VIOSCSI_CDB_LEN) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 25:
+ if (sz == 1) {
+ /* read second byte of cdb_len */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_CDB_LEN >> 8) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 26:
+ if (sz == 1) {
+ /* read third byte of cdb_len */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_CDB_LEN >> 16) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 27:
+ if (sz == 1) {
+ /* read fourth byte of cdb_len */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_CDB_LEN >> 24) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 28:
+ /* VIRTIO_SCSI_CONFIG_MAX_CHANNEL, 16bit */
+ *data &= 0xFFFF0000; /* defined by standard to be zero */
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 29:
+ *data &= 0xFFFF0000; /* defined by standard to be zero */
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 30:
+ /* VIRTIO_SCSI_CONFIG_MAX_TARGET, 16bit */
+ if (sz == 2) {
+ *data &= 0xFFFF0000;
+ *data |=
+ (uint32_t)(VIOSCSI_MAX_TARGET) & 0xFFFF;
+ } else if (sz == 1) {
+ /* read first byte of max_target */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_MAX_TARGET) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 31:
+ if (sz == 1) {
+ /* read second byte of max_target */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_MAX_TARGET >> 8) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 32:
+ /* VIRTIO_SCSI_CONFIG_MAX_LUN, 32bit */
+ if (sz == 4)
+ *data = (uint32_t)(VIOSCSI_MAX_LUN);
+ else if (sz == 1) {
+ /* read first byte of max_lun */
+ *data &= 0xFFFFFF00;
+ *data |= (uint32_t)(VIOSCSI_MAX_LUN) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 33:
+ if (sz == 1) {
+ /* read second byte of max_lun */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_MAX_LUN >> 8) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 34:
+ if (sz == 1) {
+ /* read third byte of max_lun */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_MAX_LUN >> 16) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 35:
+ if (sz == 1) {
+ /* read fourth byte of max_lun */
+ *data &= 0xFFFFFF00;
+ *data |=
+ (uint32_t)(VIOSCSI_MAX_LUN >> 24) & 0xFF;
+ }
+ break;
+ case VIRTIO_CONFIG_DEVICE_FEATURES:
+ *data = dev->cfg.device_feature;
+ break;
+ case VIRTIO_CONFIG_GUEST_FEATURES:
+ *data = dev->cfg.guest_feature;
+ break;
+ case VIRTIO_CONFIG_QUEUE_ADDRESS:
+ *data = dev->cfg.queue_address;
+ break;
+ case VIRTIO_CONFIG_QUEUE_SIZE:
+ if (sz == 4)
+ *data = dev->cfg.queue_size;
+ else if (sz == 2) {
+ *data &= 0xFFFF0000;
+ *data |= (uint16_t)dev->cfg.queue_size;
+ } else if (sz == 1) {
+ *data &= 0xFFFFFF00;
+ *data |= (uint8_t)dev->cfg.queue_size;
+ }
+ break;
+ case VIRTIO_CONFIG_QUEUE_SELECT:
+ *data = dev->cfg.queue_select;
+ break;
+ case VIRTIO_CONFIG_QUEUE_NOTIFY:
+ *data = dev->cfg.queue_notify;
+ break;
+ case VIRTIO_CONFIG_DEVICE_STATUS:
+ if (sz == 4)
+ *data = dev->cfg.device_status;
+ else if (sz == 2) {
+ *data &= 0xFFFF0000;
+ *data |= (uint16_t)dev->cfg.device_status;
+ } else if (sz == 1) {
+ *data &= 0xFFFFFF00;
+ *data |= (uint8_t)dev->cfg.device_status;
+ }
+ break;
+ case VIRTIO_CONFIG_ISR_STATUS:
+ *data = dev->cfg.isr_status;
+ dev->cfg.isr_status = 0;
+ break;
+ }
+ }
+
+
+ return (0);
+}
+
+void
+vioscsi_update_qs(struct vioscsi_dev *dev)
+{
+ /* Invalid queue? */
+ if(dev->cfg.queue_select > VIRTIO_MAX_QUEUES) {
+ dev->cfg.queue_size = 0;
+ return;
+ }
+
+ /* Update queue address/size based on queue select */
+ dev->cfg.queue_address = dev->vq[dev->cfg.queue_select].qa;
+ dev->cfg.queue_size = dev->vq[dev->cfg.queue_select].qs;
+}
+
+void
+vioscsi_update_qa(struct vioscsi_dev *dev)
+{
+ /* Invalid queue? */
+ if(dev->cfg.queue_select > VIRTIO_MAX_QUEUES)
+ return;
+
+ dev->vq[dev->cfg.queue_select].qa = dev->cfg.queue_address;
+}
+
+/*
+ * Process message(s) in the queue(s)
+ * vioscsi driver will be placing the following in the queue for each iteration
+ * virtio_scsi_req_hdr with a possible SCSI_DATA_OUT buffer
+ * along with a virtio_scsi_res_hdr with a possible SCSI_DATA_IN buffer
+ * for consumption.
+ *
+ * Return 1 if an interrupt should be generated (response written)
+ * 0 otherwise
+ */
+int
+vioscsi_notifyq(struct vioscsi_dev *dev)
+{
+ uint64_t q_gpa;
+ uint32_t vr_sz;
+ uint16_t idx, req_idx, resp_idx;
+ int ret;
+ char *vr;
+ struct vring_desc *desc, *req_desc, *resp_desc;
+ struct vring_avail *avail;
+ struct vring_used *used;
+ struct virtio_scsi_req_hdr req;
+ struct virtio_scsi_res_hdr resp;
+
+ /* various helper values */
+ uint16_t inq_len;
+ uint32_t r_cap_addr;
+ uint64_t r_cap_addr_16;
+ uint16_t toc_len;
+ uint16_t toc_data_len;
+ uint8_t toc_data[TOC_DATA_SIZE];
+ uint8_t *toc_data_p;
+ const uint8_t *read_buf;
+ uint32_t read_lba;
+ uint16_t read_10_len;
+ struct ioinfo *info;
+ off_t chunk_offset;
+ uint8_t mode_page_ctl;
+ uint8_t mode_page_code;
+ uint8_t *mode_reply;
+ uint8_t mode_reply_len;
+ uint16_t mode_sense_len;
+ uint8_t gesn_reply[GESN_SIZE];
+ uint16_t get_conf_feature;
+ uint16_t get_conf_len;
+ uint8_t *get_conf_reply;
+ uint16_t mech_status_len;
+
+ /* helper structs */
+ struct scsi_inquiry *inq;
+ struct scsi_inquiry_data *inq_data;
+ struct scsi_read_capacity *r_cap;
+ struct scsi_read_cap_data *r_cap_data;
+ struct scsi_read_capacity_16 *r_cap_16;
+ struct scsi_read_cap_data_16 *r_cap_data_16;
+ struct scsi_read_toc *toc;
+ struct scsi_rw *read_6;
+ struct scsi_rw_big *read_10;
+ struct scsi_mode_sense *mode_sense;
+ struct scsi_mode_sense_big *mode_sense_10;
+ struct scsi_mechanism_status *mech_status;
+ struct scsi_mechanism_status_header *mech_status_header;
+ struct scsi_read_disc_information *read_disc;
+ struct scsi_gesn *gesn;
+ struct scsi_gesn_event_header *gesn_event_header;
+ struct scsi_gesn_power_event *gesn_power_event;
+ struct scsi_get_configuration *get_configuration;
+ struct scsi_config_feature_header *config_feature_header;
+ struct scsi_config_generic_descriptor *config_generic_desc;
+ struct scsi_config_profile_descriptor *config_profile_desc;
+ struct scsi_config_core_descriptor *config_core_desc;
+ struct scsi_config_morphing_descriptor *config_morphing_desc;
+ struct scsi_config_remove_media_descriptor *config_remove_media_desc;
+ struct scsi_config_random_read_descriptor *config_random_read_desc;
+
+ ret = 0;
+
+ /* Invalid queue? */
+ if (dev->cfg.queue_notify > VIRTIO_MAX_QUEUES)
+ return (ret);
+
+ vr_sz = vring_size(VIOSCSI_QUEUE_SIZE);
+ q_gpa = dev->vq[dev->cfg.queue_notify].qa;
+ q_gpa = q_gpa * VIRTIO_PAGE_SIZE;
+
+ vr = calloc(1, vr_sz);
+ if (vr == NULL) {
+ log_warn("%s: calloc error getting vioscsi ring", __func__);
+ return (ret);
+ }
+
+ if (read_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error reading gpa 0x%llx", __func__, q_gpa);
+ goto out;
+ }
+
+ /* Compute offsets in ring of descriptors, avail ring, and used ring */
+ desc = (struct vring_desc *)(vr);
+ avail = (struct vring_avail *)(vr +
+ dev->vq[dev->cfg.queue_notify].vq_availoffset);
+ used = (struct vring_used *)(vr +
+ dev->vq[dev->cfg.queue_notify].vq_usedoffset);
+
+ idx = dev->vq[dev->cfg.queue_notify].last_avail & VIOSCSI_QUEUE_MASK;
+
+ if ((avail->idx & VIOSCSI_QUEUE_MASK) == idx) {
+ log_warnx("%s:nothing to do?", __func__);
+ goto out;
+ }
+
+ while (idx != (avail->idx & VIOSCSI_QUEUE_MASK)) {
+
+ req_idx = avail->ring[idx] & VIOSCSI_QUEUE_MASK;
+ req_desc = &desc[req_idx];
+
+ /* Clear resp for next message */
+ memset(&resp, 0, sizeof(resp));
+
+ if ((req_desc->flags & VRING_DESC_F_NEXT) == 0) {
+ log_warnx("%s: unchained req descriptor received "
+ "(idx %d)", __func__, req_idx);
+ goto out;
+ }
+
+ /* Read command from descriptor ring */
+ if (read_mem(req_desc->addr, &req, req_desc->len)) {
+ log_warnx("%s: command read_mem error @ 0x%llx",
+ __func__, req_desc->addr);
+ goto out;
+ }
+
+ /*
+ * req.lun is defined by virtio as
+ * lun[0] - Always set to 1
+ * lun[1] - Target, negotiated as VIOSCSI_MAX_TARGET
+ * lun[2-3] - represent single level LUN structure
+ * lun[4-7] - Zero
+ * At this current time, we are only servicing one device per
+ * bus (1:0:X:0).
+ *
+ * Various implementations will attempt to scan all possible
+ * targets (256) looking for devices or scan for all possible
+ * LUNs in a single level. When Target is greater than
+ * VIOSCSI_MAX_TARGET or when lun[3] is greater than zero,
+ * respond with a BAD_TARGET response.
+ */
+ if (req.lun[1] >= VIOSCSI_MAX_TARGET || req.lun[3] > 0) {
+ log_debug("%s: Ignore CMD 0x%02x,%s on lun %u:%u:%u:%u",
+ __func__, req.cdb[0], vioscsi_op_names(req.cdb[0]),
+ req.lun[0], req.lun[1], req.lun[2], req.lun[3]);
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc, req_desc,
+ &resp_idx);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_BAD_TARGET, SCSI_OK, 0, 0, 0);
+
+ if (write_mem(resp_desc->addr, &resp, resp_desc->len)) {
+ log_warnx("%s: unable to write BAD_TARGET"
+ " resp status data @ 0x%llx",
+ __func__, resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ goto next_msg;
+ }
+
+ log_debug("%s: Queue %d id 0x%llx lun %u:%u:%u:%u"
+ " cdb OP 0x%02x,%s",
+ __func__, dev->cfg.queue_notify, req.id,
+ req.lun[0], req.lun[1], req.lun[2], req.lun[3],
+ req.cdb[0], vioscsi_op_names(req.cdb[0]));
+
+ /* opcode is first byte */
+ switch(req.cdb[0]) {
+ case TEST_UNIT_READY:
+ case START_STOP:
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc, req_desc,
+ &resp_idx);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
+
+ if (write_mem(resp_desc->addr, &resp, resp_desc->len)) {
+ log_warnx("%s: unable to write OK resp status "
+ "data @ 0x%llx", __func__, resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ break;
+ case PREVENT_ALLOW:
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc, req_desc,
+ &resp_idx);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
+
+ if (dev->locked) {
+ log_debug("%s: unlocking medium", __func__);
+ } else {
+ log_debug("%s: locking medium", __func__);
+ }
+
+ dev->locked = dev->locked ? 0 : 1;
+
+ if (write_mem(resp_desc->addr, &resp, resp_desc->len)) {
+ log_warnx("%s: unable to write OK resp status "
+ "data @ 0x%llx", __func__, resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ break;
+ case READ_TOC:
+ toc = (struct scsi_read_toc *)(req.cdb);
+ toc_len = (uint16_t)_2btol(toc->data_len);
+ log_debug("%s: %s - MSF %d Track 0x%02x Addr 0x%04x",
+ __func__, vioscsi_op_names(toc->opcode),
+ ((toc->byte2 >> 1) & 1), toc->from_track, toc_len);
+
+ memset(toc_data, 0, sizeof(toc_data));
+
+ /* Tracks should be 0, 1, or LEAD_OUT_TRACK, 0xaa */
+ if (toc->from_track > 1 &&
+ toc->from_track != READ_TOC_LEAD_OUT_TRACK) {
+ /* illegal request */
+ log_debug("%s: illegal request Track 0x%02x",
+ __func__, toc->from_track);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_CHECK,
+ SKEY_ILLEGAL_REQUEST,
+ SENSE_ILLEGAL_CDB_FIELD,
+ SENSE_DEFAULT_ASCQ);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ goto next_msg;
+ }
+
+ /*
+ * toc_data is defined as:
+ * [0-1]: TOC Data Length, typically 0x1a
+ * [2]: First Track, 1
+ * [3]: Last Track, 1
+ *
+ * Track 1 Descriptor
+ * [0]: Reserved, 0
+ * [1]: ADR,Control, 0x14
+ * [2]: Track #, 1
+ * [3]: Reserved, 0
+ * [4-7]: Track Start Address, LBA
+ *
+ * Track 0xaa (Lead Out) Descriptor
+ * [0]: Reserved, 0
+ * [1]: ADR,Control, 0x14
+ * [2]: Track #, 0xaa
+ * [3]: Reserved, 0
+ * [4-7]: Track Start Address, LBA
+ */
+ toc_data_p = toc_data + 2;
+ *toc_data_p++ = READ_TOC_START_TRACK;
+ *toc_data_p++ = READ_TOC_LAST_TRACK;
+ if (toc->from_track <= 1) {
+ /* first track descriptor */
+ *toc_data_p++ = 0x0;
+ *toc_data_p++ = READ_TOC_ADR_CTL;
+ *toc_data_p++ = READ_TOC_START_TRACK;
+ *toc_data_p++ = 0x0;
+ /* start addr for first track is 0 */
+ *toc_data_p++ = 0x0;
+ *toc_data_p++ = 0x0;
+ *toc_data_p++ = 0x0;
+ *toc_data_p++ = 0x0;
+ }
+
+ /* last track descriptor */
+ *toc_data_p++ = 0x0;
+ *toc_data_p++ = READ_TOC_ADR_CTL;
+ *toc_data_p++ = READ_TOC_LEAD_OUT_TRACK;
+ *toc_data_p++ = 0x0;
+
+ _lto4b((uint32_t)dev->n_blocks, toc_data_p);
+ toc_data_p += 4;
+
+ toc_data_len = toc_data_p - toc_data;
+ _lto2b((uint32_t)toc_data_len - 2, toc_data);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc, req_desc,
+ &resp_idx);
+
+ dprintf("%s: writing resp to 0x%llx size %d at local "
+ "idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, &resp, resp_desc->len)) {
+ log_warnx("%s: unable to write OK resp status "
+ "data @ 0x%llx", __func__, resp_desc->addr);
+ goto out;
+ }
+
+ /* Move index for toc descriptor */
+ resp_desc = vioscsi_next_ring_desc(desc, resp_desc,
+ &resp_idx);
+
+ dprintf("%s: writing toc_data to 0x%llx size %d at "
+ "local idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, toc_data,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write toc descriptor"
+ "data @ 0x%llx", __func__, resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ break;
+ case READ_CAPACITY:
+ r_cap = (struct scsi_read_capacity *)(req.cdb);
+ r_cap_addr = _4btol(r_cap->addr);
+ log_debug("%s: %s - Addr 0x%08x", __func__,
+ vioscsi_op_names(r_cap->opcode),
+ r_cap_addr);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
+
+ r_cap_data =
+ calloc(1, sizeof(struct scsi_read_cap_data));
+
+ if (r_cap_data == NULL) {
+ log_warnx("%s: cannot alloc r_cap_data",
+ __func__);
+ goto out;
+ }
+
+ log_debug("%s: ISO has %lld bytes and %lld blocks",
+ __func__, dev->sz, dev->n_blocks);
+
+ /*
+ * determine if size of iso image > UINT32_MAX
+ * if it is, set addr to UINT32_MAX (0xffffffff)
+ * indicating to hosts that READ_CAPACITY_16 should
+ * be called to retrieve the full size
+ */
+ if (dev->sz >= UINT32_MAX) {
+ _lto4b(UINT32_MAX, r_cap_data->addr);
+ _lto4b(VIOSCSI_BLOCK_SIZE_CDROM,
+ r_cap_data->length);
+ log_warnx("%s: ISO sz %lld is bigger than "
+ "UINT32_MAX %u, all data may not be read",
+ __func__, dev->sz, UINT32_MAX);
+ } else {
+ _lto4b(dev->n_blocks - 1, r_cap_data->addr);
+ _lto4b(VIOSCSI_BLOCK_SIZE_CDROM,
+ r_cap_data->length);
+ }
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc, req_desc,
+ &resp_idx);
+
+ dprintf("%s: writing resp to 0x%llx size %d at local "
+ "idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, &resp, resp_desc->len)) {
+ log_warnx("%s: unable to write OK resp status "
+ "data @ 0x%llx", __func__, resp_desc->addr);
+ free(r_cap_data);
+ goto out;
+ }
+
+ /* Move index for r_cap_data */
+ resp_desc = vioscsi_next_ring_desc(desc, resp_desc,
+ &resp_idx);
+
+ dprintf("%s: writing r_cap_data to 0x%llx size %d at "
+ "local idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, r_cap_data,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write read_cap_data"
+ " response to gpa @ 0x%llx",
+ __func__, resp_desc->addr);
+ free(r_cap_data);
+ goto out;
+ }
+
+ free(r_cap_data);
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ break;
+ case READ_CAPACITY_16:
+ r_cap_16 = (struct scsi_read_capacity_16 *)(req.cdb);
+ r_cap_addr_16 = _8btol(r_cap_16->addr);
+ log_debug("%s: %s - Addr 0x%016llx", __func__,
+ vioscsi_op_names(r_cap_16->opcode),
+ r_cap_addr_16);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
+
+ r_cap_data_16 =
+ calloc(1, sizeof(struct scsi_read_cap_data_16));
+
+ if (r_cap_data_16 == NULL) {
+ log_warnx("%s: cannot alloc r_cap_data_16",
+ __func__);
+ goto out;
+ }
+
+ log_debug("%s: ISO has %lld bytes and %lld blocks",
+ __func__, dev->sz, dev->n_blocks);
+
+ _lto8b(dev->n_blocks - 1, r_cap_data_16->addr);
+ _lto4b(VIOSCSI_BLOCK_SIZE_CDROM,
+ r_cap_data_16->length);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc, req_desc,
+ &resp_idx);
+
+ dprintf("%s: writing resp to 0x%llx size %d at local "
+ "idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, &resp, resp_desc->len)) {
+ log_warnx("%s: unable to write OK resp status "
+ "data @ 0x%llx", __func__, resp_desc->addr);
+ free(r_cap_data);
+ goto out;
+ }
+
+ /* Move index for r_cap_data_16 */
+ resp_desc = vioscsi_next_ring_desc(desc, resp_desc,
+ &resp_idx);
+
+ dprintf("%s: writing r_cap_data_16 to 0x%llx size %d "
+ "at local idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, r_cap_data_16,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write read_cap_data_16"
+ " response to gpa @ 0x%llx",
+ __func__, resp_desc->addr);
+ free(r_cap_data);
+ goto out;
+ }
+
+ free(r_cap_data_16);
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ break;
+ case READ_COMMAND:
+ read_6 = (struct scsi_rw *)(req.cdb);
+ read_lba = ((read_6->addr[0] & SRW_TOPADDR) << 16 ) |
+ (read_6->addr[1] << 8) | read_6->addr[2];
+
+ log_debug("%s: READ Addr 0x%08x Len %d (%d)",
+ __func__, read_lba, read_6->length,
+ read_6->length * dev->max_xfer);
+
+ /* check if lba is in range */
+ if (read_lba > dev->n_blocks - 1) {
+ log_debug("%s: requested block out of range "
+ "req: %ud max: %lld", __func__,
+ read_lba, dev->n_blocks);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_CHECK,
+ SKEY_ILLEGAL_REQUEST,
+ SENSE_LBA_OUT_OF_RANGE,
+ SENSE_DEFAULT_ASCQ);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ goto next_msg;
+ }
+
+ info = vioscsi_start_read(dev, read_lba,
+ read_6->length);
+
+ if (info == NULL) {
+ log_warnx("%s: cannot alloc for read",
+ __func__);
+ goto out;
+ }
+
+ /* read block */
+ read_buf = vioscsi_finish_read(info);
+
+ if (read_buf == NULL) {
+ vioscsi_free_info(info);
+
+ log_warnx("%s: error reading position %ud",
+ __func__, read_lba);
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_CHECK,
+ SKEY_MEDIUM_ERROR,
+ SENSE_MEDIUM_NOT_PRESENT,
+ SENSE_DEFAULT_ASCQ);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ goto next_msg;
+ }
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc, req_desc,
+ &resp_idx);
+
+ dprintf("%s: writing resp to 0x%llx size %d at local "
+ "idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, &resp, resp_desc->len)) {
+ log_warnx("%s: unable to write OK resp status "
+ "data @ 0x%llx", __func__, resp_desc->addr);
+ vioscsi_free_info(info);
+ goto out;
+ }
+
+ /* Move index for read_buf */
+ resp_desc = vioscsi_next_ring_desc(desc, resp_desc,
+ &resp_idx);
+
+ dprintf("%s: writing read_buf to 0x%llx size %d at "
+ "local idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, read_buf,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write read_buf"
+ " to gpa @ 0x%llx", __func__,
+ resp_desc->addr);
+ vioscsi_free_info(info);
+ goto out;
+ }
+
+ vioscsi_free_info(info);
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ break;
+ case READ_BIG:
+ read_10 = (struct scsi_rw_big *)(req.cdb);
+ read_lba = _4btol(read_10->addr);
+ read_10_len = _2btol(read_10->length);
+ chunk_offset = 0;
+
+ log_debug("%s: READ_10 Addr 0x%08x Len %d (%d)",
+ __func__, read_lba, read_10_len,
+ read_10_len * dev->max_xfer);
+
+ /* check if lba is in range */
+ if (read_lba > dev->n_blocks - 1) {
+ log_debug("%s: requested block out of range "
+ "req: %ud max: %lld", __func__,
+ read_lba, dev->n_blocks);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_CHECK,
+ SKEY_ILLEGAL_REQUEST,
+ SENSE_LBA_OUT_OF_RANGE,
+ SENSE_DEFAULT_ASCQ);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ goto next_msg;
+ }
+
+ info = vioscsi_start_read(dev, read_lba, read_10_len);
+
+ if (info == NULL) {
+ log_warnx("%s: cannot alloc for read",
+ __func__);
+ goto out;
+ }
+
+ /* read block */
+ read_buf = vioscsi_finish_read(info);
+
+ if (read_buf == NULL) {
+ vioscsi_free_info(info);
+
+ log_warnx("%s: error reading position %ud",
+ __func__, read_lba);
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_CHECK,
+ SKEY_MEDIUM_ERROR,
+ SENSE_MEDIUM_NOT_PRESENT,
+ SENSE_DEFAULT_ASCQ);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ goto next_msg;
+ }
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc, req_desc,
+ &resp_idx);
+
+ dprintf("%s: writing resp to 0x%llx size %d at local "
+ "idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, &resp, resp_desc->len)) {
+ log_warnx("%s: unable to write OK resp status "
+ "data @ 0x%llx", __func__, resp_desc->addr);
+ vioscsi_free_info(info);
+ goto out;
+ }
+
+ /*
+ * Perform possible chunking of writes of read_buf
+ * based on the segment length allocated by the host.
+ * At least one write will be performed.
+ * If chunk_offset == info->len, no more writes
+ */
+ do {
+ /* Move index for read_buf */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ resp_desc, &resp_idx);
+
+ dprintf("%s: writing read_buf to 0x%llx size "
+ "%d at local idx %d req_idx %d "
+ "global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr,
+ read_buf + chunk_offset, resp_desc->len)) {
+ log_warnx("%s: unable to write read_buf"
+ " to gpa @ 0x%llx", __func__,
+ resp_desc->addr);
+ vioscsi_free_info(info);
+ goto out;
+ }
+ chunk_offset += resp_desc->len;
+ } while (chunk_offset < info->len);
+
+ vioscsi_free_info(info);
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ break;
+ case INQUIRY:
+ inq = (struct scsi_inquiry *)(req.cdb);
+ inq_len = (uint16_t)_2btol(inq->length);
+
+ log_debug("%s: INQ - EVPD %d PAGE_CODE 0x%08x LEN %d",
+ __func__, inq->flags & SI_EVPD, inq->pagecode,
+ inq_len);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
+
+ inq_data = calloc(1, sizeof(struct scsi_inquiry_data));
+
+ if (inq_data == NULL) {
+ log_warnx("%s: cannot alloc inq_data",
+ __func__);
+ goto out;
+ }
+
+ inq_data->device = T_CDROM;
+ inq_data->dev_qual2 = SID_REMOVABLE;
+ /* Leave version zero to say we don't comply */
+ inq_data->response_format = INQUIRY_RESPONSE_FORMAT;
+ inq_data->additional_length = SID_SCSI2_ALEN;
+ memcpy(inq_data->vendor, INQUIRY_VENDOR,
+ INQUIRY_VENDOR_LEN);
+ memcpy(inq_data->product, INQUIRY_PRODUCT,
+ INQUIRY_PRODUCT_LEN);
+ memcpy(inq_data->revision, INQUIRY_REVISION,
+ INQUIRY_REVISION_LEN);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc, req_desc,
+ &resp_idx);
+
+ dprintf("%s: writing resp to 0x%llx size %d at local "
+ "idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, &resp, resp_desc->len)) {
+ log_warnx("%s: unable to write OK resp status "
+ "data @ 0x%llx", __func__, resp_desc->addr);
+ free(inq_data);
+ goto out;
+ }
+
+ /* Move index for inquiry_data */
+ resp_desc = vioscsi_next_ring_desc(desc, resp_desc,
+ &resp_idx);
+
+ dprintf("%s: writing inq_data to 0x%llx size %d at "
+ "local idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, inq_data,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write inquiry"
+ " response to gpa @ 0x%llx",
+ __func__, resp_desc->addr);
+ free(inq_data);
+ goto out;
+ }
+
+ free(inq_data);
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ break;
+ case MODE_SENSE:
+ mode_sense = (struct scsi_mode_sense *)(req.cdb);
+ mode_page_ctl = mode_sense->page & SMS_PAGE_CTRL;
+ mode_page_code = mode_sense->page & SMS_PAGE_CODE;
+
+ log_debug("%s: M_SENSE - DBD %d Page Ctrl 0x%x"
+ " Code 0x%x Len %u",
+ __func__, mode_sense->byte2 & SMS_DBD,
+ mode_page_ctl, mode_page_code, mode_sense->length);
+
+ if (mode_page_ctl == SMS_PAGE_CTRL_CURRENT &&
+ (mode_page_code == ERR_RECOVERY_PAGE ||
+ mode_page_code == CDVD_CAPABILITIES_PAGE)) {
+ /*
+ * mode sense header is 4 bytes followed
+ * by a variable page
+ * ERR_RECOVERY_PAGE is 12 bytes
+ * CDVD_CAPABILITIES_PAGE is 27 bytes
+ */
+ switch(mode_page_code) {
+ case ERR_RECOVERY_PAGE:
+ mode_reply_len = 16;
+ mode_reply =
+ (uint8_t*)calloc(mode_reply_len,
+ sizeof(uint8_t));
+ if (mode_reply == NULL)
+ goto out;
+
+ /* set the page header */
+ *mode_reply = mode_reply_len - 1;
+ *(mode_reply + 1) =
+ MODE_MEDIUM_TYPE_CODE;
+
+ /* set the page data, 7.3.2.1 mmc-5 */
+ *(mode_reply + 4) =
+ MODE_ERR_RECOVERY_PAGE_CODE;
+ *(mode_reply + 5) =
+ MODE_ERR_RECOVERY_PAGE_LEN;
+ *(mode_reply + 7) =
+ MODE_READ_RETRY_COUNT;
+ break;
+ case CDVD_CAPABILITIES_PAGE:
+ mode_reply_len = 31;
+ mode_reply =
+ (uint8_t*)calloc(mode_reply_len,
+ sizeof(uint8_t));
+ if (mode_reply == NULL)
+ goto out;
+
+ /* set the page header */
+ *mode_reply = mode_reply_len - 1;
+ *(mode_reply + 1) =
+ MODE_MEDIUM_TYPE_CODE;
+
+ /* set the page data, 6.3.11 mmc-3 */
+ *(mode_reply + 4) =
+ MODE_CDVD_CAP_PAGE_CODE;
+ *(mode_reply + 5) = mode_reply_len - 6;
+ *(mode_reply + 6) =
+ MODE_CDVD_CAP_READ_CODE;
+ _lto2b(MODE_CDVD_CAP_NUM_LEVELS,
+ mode_reply + 14);
+ break;
+ default:
+ goto mode_sense_error;
+ break;
+ }
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ dprintf("%s: writing resp to 0x%llx size %d "
+ "at local idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write OK"
+ " resp status data @ 0x%llx",
+ __func__, resp_desc->addr);
+ free(mode_reply);
+ goto out;
+ }
+
+ /* Move index for mode_reply */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ resp_desc, &resp_idx);
+
+ dprintf("%s: writing mode_reply to 0x%llx "
+ "size %d at local idx %d req_idx %d "
+ "global_idx %d",__func__, resp_desc->addr,
+ resp_desc->len, resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, mode_reply,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write "
+ "mode_reply to gpa @ 0x%llx",
+ __func__, resp_desc->addr);
+ free(mode_reply);
+ goto out;
+ }
+
+ free(mode_reply);
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ } else {
+mode_sense_error:
+ /* send back un-supported */
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_CHECK,
+ SKEY_ILLEGAL_REQUEST,
+ SENSE_ILLEGAL_CDB_FIELD,
+ SENSE_DEFAULT_ASCQ);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ }
+ break;
+ case MODE_SENSE_BIG:
+ mode_sense_10 = (struct scsi_mode_sense_big *)(req.cdb);
+ mode_page_ctl = mode_sense_10->page & SMS_PAGE_CTRL;
+ mode_page_code = mode_sense_10->page & SMS_PAGE_CODE;
+ mode_sense_len =
+ (uint16_t)_2btol(mode_sense_10->length);
+
+ log_debug("%s: M_SENSE_10 - DBD %d Page Ctrl 0x%x"
+ " Code 0x%x Len %u",
+ __func__, mode_sense_10->byte2 & SMS_DBD,
+ mode_page_ctl, mode_page_code, mode_sense_len);
+
+ if (mode_page_ctl == SMS_PAGE_CTRL_CURRENT &&
+ (mode_page_code == ERR_RECOVERY_PAGE ||
+ mode_page_code == CDVD_CAPABILITIES_PAGE)) {
+ /*
+ * mode sense header is 8 bytes followed
+ * by a variable page
+ * ERR_RECOVERY_PAGE is 12 bytes
+ * CDVD_CAPABILITIES_PAGE is 27 bytes
+ */
+ switch(mode_page_code) {
+ case ERR_RECOVERY_PAGE:
+ mode_reply_len = 20;
+ mode_reply =
+ (uint8_t*)calloc(mode_reply_len,
+ sizeof(uint8_t));
+ if (mode_reply == NULL)
+ goto out;
+
+ /* set the page header */
+ _lto2b(mode_reply_len - 2, mode_reply);
+ *(mode_reply + 2) =
+ MODE_MEDIUM_TYPE_CODE;
+
+ /* set the page data, 7.3.2.1 mmc-5 */
+ *(mode_reply + 8) =
+ MODE_ERR_RECOVERY_PAGE_CODE;
+ *(mode_reply + 9) =
+ MODE_ERR_RECOVERY_PAGE_LEN;
+ *(mode_reply + 11) =
+ MODE_READ_RETRY_COUNT;
+ break;
+ case CDVD_CAPABILITIES_PAGE:
+ mode_reply_len = 35;
+ mode_reply =
+ (uint8_t*)calloc(mode_reply_len,
+ sizeof(uint8_t));
+ if (mode_reply == NULL)
+ goto out;
+
+ /* set the page header */
+ _lto2b(mode_reply_len - 2, mode_reply);
+ *(mode_reply + 2) =
+ MODE_MEDIUM_TYPE_CODE;
+
+ /* set the page data, 6.3.11 mmc-3 */
+ *(mode_reply + 8) =
+ MODE_CDVD_CAP_PAGE_CODE;
+ *(mode_reply + 9) = mode_reply_len - 6;
+ *(mode_reply + 10) =
+ MODE_CDVD_CAP_READ_CODE;
+ _lto2b(MODE_CDVD_CAP_NUM_LEVELS,
+ mode_reply + 18);
+ break;
+ default:
+ goto mode_sense_10_error;
+ break;
+ }
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ dprintf("%s: writing resp to 0x%llx size %d "
+ "at local idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write OK"
+ " resp status data @ 0x%llx",
+ __func__, resp_desc->addr);
+ free(mode_reply);
+ goto out;
+ }
+
+ /* Move index for mode_reply */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ resp_desc, &resp_idx);
+
+ dprintf("%s: writing mode_reply to 0x%llx "
+ "size %d at local idx %d req_idx %d "
+ "global_idx %d",__func__, resp_desc->addr,
+ resp_desc->len, resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, mode_reply,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write "
+ "mode_reply to gpa @ 0x%llx",
+ __func__, resp_desc->addr);
+ free(mode_reply);
+ goto out;
+ }
+
+ free(mode_reply);
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ } else {
+mode_sense_10_error:
+ /* send back un-supported */
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_CHECK,
+ SKEY_ILLEGAL_REQUEST,
+ SENSE_ILLEGAL_CDB_FIELD,
+ SENSE_DEFAULT_ASCQ);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ }
+ break;
+ case GET_EVENT_STATUS_NOTIFICATION:
+ gesn = (struct scsi_gesn *)(req.cdb);
+ log_debug("%s: GESN Method %s", __func__,
+ gesn->byte2 ? "Polling" : "Asynchronous");
+
+ if (gesn->byte2 == 0) {
+ /* we don't support asynchronous */
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_CHECK,
+ SKEY_ILLEGAL_REQUEST,
+ SENSE_ILLEGAL_CDB_FIELD,
+ SENSE_DEFAULT_ASCQ);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ goto out;
+ }
+ memset(gesn_reply, 0, sizeof(gesn_reply));
+ gesn_event_header =
+ (struct scsi_gesn_event_header *)(gesn_reply);
+ gesn_power_event =
+ (struct scsi_gesn_power_event *)(gesn_reply + 4);
+ /* set event header length and notification */
+ _lto2b(GESN_HEADER_LEN, gesn_event_header->length);
+ gesn_event_header->notification =
+ GESN_NOTIFY_POWER_MGMT;
+ gesn_event_header->supported_event =
+ GESN_EVENT_POWER_MGMT;
+
+ /* set event descriptor */
+ gesn_power_event->event_code = GESN_CODE_NOCHG;
+ if (dev->locked)
+ gesn_power_event->status = GESN_STATUS_ACTIVE;
+ else
+ gesn_power_event->status = GESN_STATUS_IDLE;
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc, req_desc,
+ &resp_idx);
+
+ dprintf("%s: writing resp to 0x%llx size %d at local "
+ "idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, &resp, resp_desc->len)) {
+ log_warnx("%s: unable to write OK resp status "
+ "data @ 0x%llx", __func__, resp_desc->addr);
+ free(inq_data);
+ goto out;
+ }
+
+ /* Move index for gesn_reply */
+ resp_desc = vioscsi_next_ring_desc(desc, resp_desc,
+ &resp_idx);
+
+ dprintf("%s: writing gesn_reply to 0x%llx size %d at "
+ "local idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, gesn_reply,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write gesn_reply"
+ " response to gpa @ 0x%llx",
+ __func__, resp_desc->addr);
+ free(inq_data);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+
+ break;
+ case READ_DISC_INFORMATION:
+ read_disc =
+ (struct scsi_read_disc_information *)(req.cdb);
+ log_debug("%s: Disc Info %x", __func__,
+ read_disc->byte2);
+
+ /* send back unsupported */
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_CHECK,
+ SKEY_ILLEGAL_REQUEST,
+ SENSE_ILLEGAL_CDB_FIELD,
+ SENSE_DEFAULT_ASCQ);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ goto out;
+ }
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ break;
+ case GET_CONFIGURATION:
+ get_configuration =
+ (struct scsi_get_configuration *)(req.cdb);
+ get_conf_feature =
+ (uint16_t)_2btol(get_configuration->feature);
+ get_conf_len =
+ (uint16_t)_2btol(get_configuration->length);
+ log_debug("%s: Conf RT %x Feature %d Len %d",
+ __func__, get_configuration->byte2,
+ get_conf_feature, get_conf_len);
+
+ get_conf_reply = (uint8_t*)calloc(G_CONFIG_REPLY_SIZE,
+ sizeof(uint8_t));
+
+ if (get_conf_reply == NULL)
+ goto out;
+
+ /*
+ * Use MMC-5 6.6 for structure and
+ * MMC-5 5.2 to send back:
+ * feature header - 8 bytes
+ * feature descriptor for profile list - 8 bytes
+ * feature descriptor for core feature - 12 bytes
+ * feature descriptor for morphing feature - 8 bytes
+ * feature descriptor for removable media - 8 bytes
+ * feature descriptor for random read feature - 12 bytes
+ */
+
+ config_feature_header =
+ (struct scsi_config_feature_header *)(get_conf_reply);
+ config_generic_desc =
+ (struct scsi_config_generic_descriptor *)(get_conf_reply + 8);
+ config_profile_desc =
+ (struct scsi_config_profile_descriptor *)(get_conf_reply + 12);
+ config_core_desc =
+ (struct scsi_config_core_descriptor *)(get_conf_reply + 16);
+ config_morphing_desc =
+ (struct scsi_config_morphing_descriptor *)(get_conf_reply + 28);
+ config_remove_media_desc =
+ (struct scsi_config_remove_media_descriptor *)(get_conf_reply + 36);
+ config_random_read_desc =
+ (struct scsi_config_random_read_descriptor *)(get_conf_reply + 44);
+
+ /* set size to be get_conf_reply - size field */
+ _lto4b(G_CONFIG_REPLY_SIZE_HEX,
+ config_feature_header->length);
+ /* set current profile to be non-conforming */
+ _lto2b(CONFIG_PROFILE_NON_CONFORM,
+ config_feature_header->current_profile);
+
+ /* fill out profile list feature */
+ _lto2b(CONFIG_FEATURE_CODE_PROFILE,
+ config_generic_desc->feature_code);
+ config_generic_desc->byte3 = CONFIG_PROFILELIST_BYTE3;
+ config_generic_desc->length = CONFIG_PROFILELIST_LENGTH;
+ /* fill out profile descriptor for NON_COFORM */
+ _lto2b(CONFIG_PROFILE_NON_CONFORM,
+ config_profile_desc->profile_number);
+ config_profile_desc->byte3 = CONFIG_PROFILE_BYTE3;
+
+ /* fill out core feature */
+ _lto2b(CONFIG_FEATURE_CODE_CORE,
+ config_core_desc->feature_code);
+ config_core_desc->byte3 = CONFIG_CORE_BYTE3;
+ config_core_desc->length = CONFIG_CORE_LENGTH;
+ _lto4b(CONFIG_CORE_PHY_SCSI, config_core_desc->phy_std);
+
+ /* fill out morphing feature */
+ _lto2b(CONFIG_FEATURE_CODE_MORPHING,
+ config_morphing_desc->feature_code);
+ config_morphing_desc->byte3 = CONFIG_MORPHING_BYTE3;
+ config_morphing_desc->length = CONFIG_MORPHING_LENGTH;
+ config_morphing_desc->byte5 = CONFIG_MORPHING_BYTE5;
+
+ /* fill out removable media feature */
+ _lto2b(CONFIG_FEATURE_CODE_REMOVE_MEDIA,
+ config_remove_media_desc->feature_code);
+ config_remove_media_desc->byte3 =
+ CONFIG_REMOVE_MEDIA_BYTE3;
+ config_remove_media_desc->length =
+ CONFIG_REMOVE_MEDIA_LENGTH;
+ config_remove_media_desc->byte5 =
+ CONFIG_REMOVE_MEDIA_BYTE5;
+
+ /* fill out random read feature */
+ _lto2b(CONFIG_FEATURE_CODE_RANDOM_READ,
+ config_random_read_desc->feature_code);
+ config_random_read_desc->byte3 =
+ CONFIG_RANDOM_READ_BYTE3;
+ config_random_read_desc->length =
+ CONFIG_RANDOM_READ_LENGTH;
+ if (dev->sz >= UINT32_MAX)
+ _lto4b(UINT32_MAX,
+ config_random_read_desc->block_size);
+ else
+ _lto4b(dev->n_blocks - 1,
+ config_random_read_desc->block_size);
+ _lto2b(CONFIG_RANDOM_READ_BLOCKING_TYPE,
+ config_random_read_desc->blocking_type);
+
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ dprintf("%s: writing resp to 0x%llx size %d at local "
+ "idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ free(get_conf_reply);
+ goto out;
+ }
+
+ /* Move index for get_conf_reply */
+ resp_desc = vioscsi_next_ring_desc(desc, resp_desc,
+ &resp_idx);
+
+ dprintf("%s: writing get_conf_reply to 0x%llx size %d "
+ "at local idx %d req_idx %d global_idx %d",
+ __func__, resp_desc->addr, resp_desc->len,
+ resp_idx, req_idx, idx);
+
+ if (write_mem(resp_desc->addr, get_conf_reply,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write get_conf_reply"
+ " response to gpa @ 0x%llx",
+ __func__, resp_desc->addr);
+ free(get_conf_reply);
+ goto out;
+ }
+
+ free(get_conf_reply);
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ break;
+ case MECHANISM_STATUS:
+ mech_status = (struct scsi_mechanism_status *)(req.cdb);
+ mech_status_len = (uint16_t)_2btol(mech_status->length);
+ log_debug("%s: MECH_STATUS Len %u", __func__,
+ mech_status_len);
+
+ mech_status_header =
+ calloc(1, sizeof(*mech_status_header));
+
+ if (mech_status_header == NULL)
+ goto out;
+
+ /* return a 0 header since we are not a changer */
+ vioscsi_prepare_resp(&resp,
+ VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
+
+ /* Move index for response */
+ resp_desc = vioscsi_next_ring_desc(desc,
+ req_desc, &resp_idx);
+
+ if (write_mem(resp_desc->addr, &resp,
+ resp_desc->len)) {
+ log_warnx("%s: unable to set ERR "
+ "status data @ 0x%llx", __func__,
+ resp_desc->addr);
+ free(mech_status_header);
+ goto out;
+ }
+
+ /* Move index for mech_status_header */
+ resp_desc = vioscsi_next_ring_desc(desc, resp_desc,
+ &resp_idx);
+
+ if (write_mem(resp_desc->addr, mech_status_header,
+ resp_desc->len)) {
+ log_warnx("%s: unable to write "
+ "mech_status_header response to "
+ "gpa @ 0x%llx",
+ __func__, resp_desc->addr);
+ free(mech_status_header);
+ goto out;
+ }
+
+ free(mech_status_header);
+
+ ret = 1;
+ dev->cfg.isr_status = 1;
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used,
+ req_desc, req_idx);
+
+ if (write_mem(q_gpa, vr, vr_sz)) {
+ log_warnx("%s: error writing vioring",
+ __func__);
+ }
+ break;
+ default:
+ log_warnx("%s: unsupported opcode 0x%02x,%s",
+ __func__, req.cdb[0], vioscsi_op_names(req.cdb[0]));
+ /* Move ring indexes */
+ vioscsi_next_ring_item(dev, avail, used, req_desc,
+ req_idx);
+
+ break;
+ }
+next_msg:
+ /* Increment to the next queue slot */
+ idx = (idx + 1) & VIOSCSI_QUEUE_MASK;
+ }
+out:
+ free(vr);
+ return (ret);
+}