Add initial CD-ROM support to VMD via vioscsi.
authorccardenas <ccardenas@openbsd.org>
Wed, 3 Jan 2018 05:39:56 +0000 (05:39 +0000)
committerccardenas <ccardenas@openbsd.org>
Wed, 3 Jan 2018 05:39:56 +0000 (05:39 +0000)
* 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@

16 files changed:
usr.sbin/vmctl/main.c
usr.sbin/vmctl/vmctl.8
usr.sbin/vmctl/vmctl.c
usr.sbin/vmctl/vmctl.h
usr.sbin/vmd/Makefile
usr.sbin/vmd/config.c
usr.sbin/vmd/parse.y
usr.sbin/vmd/vioscsi.c [new file with mode: 0644]
usr.sbin/vmd/vioscsi.h [new file with mode: 0644]
usr.sbin/vmd/virtio.c
usr.sbin/vmd/virtio.h
usr.sbin/vmd/vm.c
usr.sbin/vmd/vm.conf.5
usr.sbin/vmd/vmd.c
usr.sbin/vmd/vmd.h
usr.sbin/vmd/vmm.c

index 91fac99..3295046 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: main.c,v 1.33 2017/10/07 19:48:30 guenther Exp $      */
+/*     $OpenBSD: main.c,v 1.34 2018/01/03 05:39:56 ccardenas Exp $     */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -69,7 +69,7 @@ struct ctl_command ctl_commands[] = {
        { "reload",     CMD_RELOAD,     ctl_reload,     "" },
        { "reset",      CMD_RESET,      ctl_reset,      "[all|vms|switches]" },
        { "start",      CMD_START,      ctl_start,      "\"name\""
-           " [-Lc] [-b image] [-m size]\n"
+           " [-Lc] [-b image] [-r image] [-m size]\n"
            "\t\t[-n switch] [-i count] [-d disk]*" },
        { "status",     CMD_STATUS,     ctl_status,     "[id]" },
        { "stop",       CMD_STOP,       ctl_stop,       "id" },
@@ -203,7 +203,8 @@ vmmaction(struct parse_result *res)
        switch (res->action) {
        case CMD_START:
                ret = vm_start(res->id, res->name, res->size, res->nifs,
-                   res->nets, res->ndisks, res->disks, res->path);
+                   res->nets, res->ndisks, res->disks, res->path,
+                   res->isopath);
                if (ret) {
                        errno = ret;
                        err(1, "start VM operation failed");
@@ -324,6 +325,7 @@ parse_free(struct parse_result *res)
 
        free(res->name);
        free(res->path);
+       free(res->isopath);
        for (i = 0; i < res->ndisks; i++)
                free(res->disks[i]);
        free(res->disks);
@@ -571,7 +573,7 @@ ctl_start(struct parse_result *res, int argc, char *argv[])
        argc--;
        argv++;
 
-       while ((ch = getopt(argc, argv, "b:cLm:n:d:i:")) != -1) {
+       while ((ch = getopt(argc, argv, "b:r:cLm:n:d:i:")) != -1) {
                switch (ch) {
                case 'b':
                        if (res->path)
@@ -581,6 +583,14 @@ ctl_start(struct parse_result *res, int argc, char *argv[])
                        if ((res->path = strdup(path)) == NULL)
                                errx(1, "strdup");
                        break;
+               case 'r':
+                       if (res->isopath)
+                               errx(1, "iso image specified multiple times");
+                       if (realpath(optarg, path) == NULL)
+                               err(1, "invalid iso image path");
+                       if ((res->isopath = strdup(path)) == NULL)
+                               errx(1, "strdup");
+                       break;
                case 'c':
                        tty_autoconnect = 1;
                        break;
index 2efcf03..bcdbca3 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: vmctl.8,v 1.35 2017/11/05 20:01:09 reyk Exp $
+.\"    $OpenBSD: vmctl.8,v 1.36 2018/01/03 05:39:56 ccardenas Exp $
 .\"
 .\" Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: November 5 2017 $
+.Dd $Mdocdate: January 3 2018 $
 .Dt VMCTL 8
 .Os
 .Sh NAME
@@ -84,6 +84,7 @@ to standard output and terminate it.
 .It Xo Cm start Ar name
 .Op Fl Lc
 .Op Fl b Ar path
+.Op Fl r Ar path
 .Op Fl d Ar path
 .Op Fl i Ar count
 .Op Fl m Ar size
@@ -126,6 +127,10 @@ See
 in
 .Xr vm.conf 5
 for more information.
+.It Fl r Ar path
+ISO image file for virtual CD-ROM. This image file will be available in the
+selected VM as a SCSI CD-ROM device attached to a virtio SCSI adapter (eg.
+.Xr vioscsi 4 )
 .El
 .Pp
 Note that the VM name supplied to the 'start' command can only consist of
index c6cd8e3..fbe0ff6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmctl.c,v 1.44 2017/09/08 07:08:49 mlarkin Exp $      */
+/*     $OpenBSD: vmctl.c,v 1.45 2018/01/03 05:39:56 ccardenas Exp $    */
 
 /*
  * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
@@ -61,6 +61,7 @@ int info_console;
  *  ndisks: number of disk images
  *  disks: disk image file names
  *  kernel: kernel image to load
+ *  iso: iso image file
  *
  * Return:
  *  0 if the request to start the VM was sent successfully.
@@ -68,7 +69,7 @@ int info_console;
  */
 int
 vm_start(uint32_t start_id, const char *name, int memsize, int nnics,
-    char **nics, int ndisks, char **disks, char *kernel)
+    char **nics, int ndisks, char **disks, char *kernel, char *iso)
 {
        struct vmop_create_params *vmc;
        struct vm_create_params *vcp;
@@ -84,6 +85,8 @@ vm_start(uint32_t start_id, const char *name, int memsize, int nnics,
                flags |= VMOP_CREATE_DISK;
        if (kernel)
                flags |= VMOP_CREATE_KERNEL;
+       if (iso)
+               flags |= VMOP_CREATE_CDROM;
        if (flags != 0) {
                if (memsize < 1)
                        memsize = VM_DEFAULT_MEMORY;
@@ -155,6 +158,9 @@ vm_start(uint32_t start_id, const char *name, int memsize, int nnics,
        if (kernel != NULL)
                strlcpy(vcp->vcp_kernel, kernel, VMM_MAX_KERNEL_PATH);
 
+       if (iso != NULL)
+               strlcpy(vcp->vcp_cdrom, iso, VMM_MAX_PATH_CDROM);
+
        imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, -1,
            vmc, sizeof(struct vmop_create_params));
 
@@ -209,6 +215,15 @@ vm_start_complete(struct imsg *imsg, int *ret, int autoconnect)
                                    "not regular files");
                                *ret = ENOENT;
                                break;
+                       case VMD_CDROM_MISSING:
+                               warnx("could not find specified iso image");
+                               *ret = ENOENT;
+                               break;
+                       case VMD_CDROM_INVALID:
+                               warnx("specified iso image is "
+                                   "not a regular file");
+                               *ret = ENOENT;
+                               break;
                        default:
                                errno = res;
                                warn("start vm command failed");
index 0fc019e..203c89b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmctl.h,v 1.17 2017/08/15 15:51:54 jasper Exp $       */
+/*     $OpenBSD: vmctl.h,v 1.18 2018/01/03 05:39:56 ccardenas Exp $    */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -45,6 +45,7 @@ struct parse_result {
        uint32_t                 id;
        char                    *name;
        char                    *path;
+       char                    *isopath;
        long long                size;
        int                      nifs;
        char                    **nets;
@@ -82,7 +83,7 @@ __dead void
 /* vmctl.c */
 int     create_imagefile(const char *, long);
 int     vm_start(uint32_t, const char *, int, int, char **, int,
-           char **, char *);
+           char **, char *, char *);
 int     vm_start_complete(struct imsg *, int *, int);
 void    terminate_vm(uint32_t, const char *);
 int     terminate_vm_complete(struct imsg *, int *);
index 0f10087..d31a8cb 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.16 2017/07/03 22:21:47 espie Exp $
+#      $OpenBSD: Makefile,v 1.17 2018/01/03 05:39:56 ccardenas Exp $
 
 .if ${MACHINE} == "amd64" || ${MACHINE} == "i386"
 
@@ -6,7 +6,7 @@ PROG=           vmd
 SRCS=          vmd.c control.c log.c priv.c proc.c config.c vmm.c
 SRCS+=         vm.c loadfile_elf.c pci.c virtio.c i8259.c mc146818.c
 SRCS+=         ns8250.c i8253.c vmboot.c ufs.c disklabel.c dhcp.c packet.c
-SRCS+=         parse.y atomicio.c
+SRCS+=         parse.y atomicio.c vioscsi.c
 
 CFLAGS+=       -Wall -I${.CURDIR}
 CFLAGS+=       -Wstrict-prototypes -Wmissing-prototypes
index e0d4339..399279b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.37 2017/11/04 07:57:14 mlarkin Exp $     */
+/*     $OpenBSD: config.c,v 1.38 2018/01/03 05:39:56 ccardenas Exp $   */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -169,6 +169,7 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
        unsigned int             i;
        int                      fd = -1, vmboot = 0;
        int                      kernfd = -1, *diskfds = NULL, *tapfds = NULL;
+       int                      cdromfd = -1;
        int                      saved_errno = 0;
        char                     ifname[IF_NAMESIZE], *s;
        char                     path[PATH_MAX];
@@ -234,6 +235,30 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
                }
        }
 
+       /* Open CDROM image for child */
+       if (strlen(vcp->vcp_cdrom)) {
+               /* Stat cdrom to ensure it is a regular file */
+               if ((cdromfd =
+                   open(vcp->vcp_cdrom, O_RDONLY)) == -1) {
+                       log_warn("%s: can't open cdrom %s", __func__,
+                           vcp->vcp_cdrom);
+                       errno = VMD_CDROM_MISSING;
+                       goto fail;
+               }
+               if (fstat(cdromfd, &stat_buf) == -1) {
+                       log_warn("%s: can't open cdrom %s", __func__,
+                           vcp->vcp_cdrom);
+                       errno = VMD_CDROM_MISSING;
+                       goto fail;
+               }
+               if (S_ISREG(stat_buf.st_mode) == 0) {
+                       log_warn("%s: cdrom %s is not a regular file", __func__,
+                           vcp->vcp_cdrom);
+                       errno = VMD_CDROM_INVALID;
+                       goto fail;
+               }
+       }
+
        /* Open disk images for child */
        for (i = 0 ; i < vcp->vcp_ndisks; i++) {
                 /* Stat disk[i] to ensure it is a regular file */
@@ -345,6 +370,12 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
                proc_compose_imsg(ps, PROC_VMM, -1,
                    IMSG_VMDOP_START_VM_REQUEST, vm->vm_vmid, kernfd,
                    vmc, sizeof(*vmc));
+
+       if (strlen(vcp->vcp_cdrom))
+               proc_compose_imsg(ps, PROC_VMM, -1,
+                   IMSG_VMDOP_START_VM_CDROM, vm->vm_vmid, cdromfd,
+                   NULL, 0);
+
        for (i = 0; i < vcp->vcp_ndisks; i++) {
                proc_compose_imsg(ps, PROC_VMM, -1,
                    IMSG_VMDOP_START_VM_DISK, vm->vm_vmid, diskfds[i],
@@ -372,6 +403,8 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
 
        if (kernfd != -1)
                close(kernfd);
+       if (cdromfd != -1)
+               close(cdromfd);
        if (diskfds != NULL) {
                for (i = 0; i < vcp->vcp_ndisks; i++)
                        close(diskfds[i]);
@@ -477,3 +510,28 @@ config_getif(struct privsep *ps, struct imsg *imsg)
        errno = EINVAL;
        return (-1);
 }
+
+int
+config_getcdrom(struct privsep *ps, struct imsg *imsg)
+{
+       struct vmd_vm   *vm;
+
+       errno = 0;
+       if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
+               errno = ENOENT;
+               return (-1);
+       }
+
+       if (imsg->fd == -1) {
+               log_debug("invalid cdrom id");
+               goto fail;
+       }
+
+       vm->vm_cdrom = imsg->fd;
+       return (0);
+ fail:
+       if (imsg->fd != -1)
+               close(imsg->fd);
+       errno = EINVAL;
+       return (-1);
+}
index b909660..7655ba5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.31 2017/11/11 02:50:07 mlarkin Exp $      */
+/*     $OpenBSD: parse.y,v 1.32 2018/01/03 05:39:56 ccardenas Exp $    */
 
 /*
  * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org>
@@ -113,8 +113,8 @@ typedef struct {
 
 
 %token INCLUDE ERROR
-%token ADD BOOT DISABLE DISK DOWN ENABLE GROUP INTERFACE LLADDR LOCAL LOCKED
-%token MEMORY NIFS OWNER PATH PREFIX RDOMAIN SIZE SWITCH UP VM VMID
+%token ADD BOOT CDROM DISABLE DISK DOWN ENABLE GROUP INTERFACE LLADDR LOCAL
+%token LOCKED MEMORY NIFS OWNER PATH PREFIX RDOMAIN SIZE SWITCH UP VM VMID
 %token <v.number>      NUMBER
 %token <v.string>      STRING
 %type  <v.lladdr>      lladdr
@@ -388,6 +388,23 @@ vm_opts            : disable                       {
                        free($2);
                        vmc.vmc_flags |= VMOP_CREATE_KERNEL;
                }
+               | CDROM string                  {
+                       if (vcp->vcp_cdrom[0] != '\0') {
+                               yyerror("cdrom specified more than once");
+                               free($2);
+                               YYERROR;
+
+                       }
+                       if (strlcpy(vcp->vcp_cdrom, $2,
+                           sizeof(vcp->vcp_cdrom)) >=
+                           sizeof(vcp->vcp_cdrom)) {
+                               yyerror("cdrom name too long");
+                               free($2);
+                               YYERROR;
+                       }
+                       free($2);
+                       vmc.vmc_flags |= VMOP_CREATE_CDROM;
+               }
                | NIFS NUMBER                   {
                        if (vcp->vcp_nnics != 0) {
                                yyerror("interfaces specified more than once");
@@ -632,6 +649,7 @@ lookup(char *s)
        static const struct keywords keywords[] = {
                { "add",                ADD },
                { "boot",               BOOT },
+               { "cdrom",              CDROM },
                { "disable",            DISABLE },
                { "disk",               DISK },
                { "down",               DOWN },
diff --git a/usr.sbin/vmd/vioscsi.c b/usr.sbin/vmd/vioscsi.c
new file mode 100644 (file)
index 0000000..290de68
--- /dev/null
@@ -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);
+}
diff --git a/usr.sbin/vmd/vioscsi.h b/usr.sbin/vmd/vioscsi.h
new file mode 100644 (file)
index 0000000..ffaa21f
--- /dev/null
@@ -0,0 +1,231 @@
+/*     $OpenBSD: vioscsi.h,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.
+ */
+
+/* Constants */
+#define VIOSCSI_SEG_MAX                        17
+#define VIOSCSI_CDB_LEN                        32
+#define VIOSCSI_SENSE_LEN              96
+
+#define VIOSCSI_NUM_QUEUES             1
+#define VIOSCSI_CMD_PER_LUN            1
+#define VIOSCSI_MAX_TARGET             1
+#define VIOSCSI_MAX_LUN                        1
+
+#define VIOSCSI_BLOCK_SIZE_CDROM       2048
+
+#define READ_TOC_START_TRACK           0x01
+#define READ_TOC_LAST_TRACK            0x01
+#define READ_TOC_LEAD_OUT_TRACK                0xaa
+#define READ_TOC_ADR_CTL               0x14
+
+#define SENSE_DEFAULT_ASCQ             0x00
+#define SENSE_LBA_OUT_OF_RANGE         0x21
+#define SENSE_ILLEGAL_CDB_FIELD                0x24
+#define SENSE_MEDIUM_NOT_PRESENT       0x3a
+
+#define INQUIRY_RESPONSE_FORMAT                0x02
+#define INQUIRY_VENDOR                 "OpenBSD "
+#define INQUIRY_VENDOR_LEN             8
+#define INQUIRY_PRODUCT                        "VMM CD-ROM      "
+#define INQUIRY_PRODUCT_LEN            16
+#define INQUIRY_REVISION               "001 "
+#define INQUIRY_REVISION_LEN           4
+
+#define MODE_MEDIUM_TYPE_CODE          0x70
+#define MODE_ERR_RECOVERY_PAGE_CODE    0x01
+#define MODE_ERR_RECOVERY_PAGE_LEN     0x0a
+#define MODE_READ_RETRY_COUNT          0x05
+#define MODE_CDVD_CAP_PAGE_CODE                0x2a
+#define MODE_CDVD_CAP_READ_CODE                0x08
+#define MODE_CDVD_CAP_NUM_LEVELS       0x02
+
+#define GESN_HEADER_LEN                        0x06
+
+#define G_CONFIG_REPLY_SIZE            56
+#define G_CONFIG_REPLY_SIZE_HEX                0x0034
+
+/* Opcodes not defined in scsi */
+#define GET_EVENT_STATUS_NOTIFICATION  0x4a
+#define GET_CONFIGURATION              0x46
+#define READ_DISC_INFORMATION          0x51
+#define MECHANISM_STATUS               0xbd
+
+/* Sizes for reply structures */
+#define TOC_DATA_SIZE                  20
+#define GESN_SIZE                      8
+#define RESP_SENSE_LEN                 14
+
+/* Structures for Opcodes defined locally */
+struct scsi_mechanism_status {
+       u_int8_t opcode;
+       u_int8_t unused[7];
+       u_int8_t length[2];
+       u_int8_t unused1;
+       u_int8_t control;
+};
+
+struct scsi_mechanism_status_header {
+       u_int8_t byte1;
+       u_int8_t byte2;
+       u_int8_t addr[3];
+       u_int8_t num_slots;
+       u_int8_t slot_len[2];
+};
+
+struct scsi_read_disc_information {
+       u_int8_t opcode;
+       u_int8_t byte2;
+       u_int8_t unused[5];
+       u_int8_t length[2];
+       u_int8_t control;
+};
+
+struct scsi_gesn {
+       u_int8_t opcode;
+       u_int8_t byte2;
+       u_int8_t unused[2];
+       u_int8_t notify_class;
+       u_int8_t unused1[2];
+       u_int8_t length[2];
+       u_int8_t control;
+};
+
+struct scsi_gesn_event_header {
+       u_int8_t length[2];
+       u_int8_t notification;
+#define GESN_NOTIFY_NONE               0x0
+#define GESN_NOTIFY_OP_CHANGE          0x1
+#define GESN_NOTIFY_POWER_MGMT         0x2
+#define GESN_NOTIFY_EXT_REQUEST                0x3
+#define GESN_NOTIFY_MEDIA              0x4
+#define GESN_NOTIFY_MULTIPLE_HOSTS     0x5
+#define GESN_NOTIFY_DEVICE_BUSY                0x6
+#define GESN_NOTIFY_RESERVED           0x7
+       u_int8_t supported_event;
+#define GESN_EVENT_NONE                        0x1
+#define GESN_EVENT_OP_CHANGE           0x2
+#define GESN_EVENT_POWER_MGMT          0x4
+#define GESN_EVENT_EXT_REQUEST         0x8
+#define GESN_EVENT_MEDIA               0x10
+#define GESN_EVENT_MULTIPLE_HOSTS      0x20
+#define GESN_EVENT_DEVICE_BUSY         0x40
+#define GESN_EVENT_RESERVED            0x80
+};
+
+struct scsi_gesn_power_event {
+       u_int8_t event_code;
+#define GESN_CODE_NOCHG                        0x0
+#define GESN_CODE_PWRCHG_SUCCESS       0x1
+#define GESN_CODE_PWRCHG_FAIL          0x2
+#define GESN_CODE_RESERVED             0x3
+       u_int8_t status;
+#define GESN_STATUS_RESERVED           0x0
+#define GESN_STATUS_ACTIVE             0x1
+#define GESN_STATUS_IDLE               0x2
+#define GESN_STATUS_STANDBY            0x3
+#define GESN_STATUS_SLEEP              0x4
+       u_int8_t unused[2];
+};
+
+struct scsi_get_configuration {
+       u_int8_t opcode;
+       u_int8_t byte2;
+       u_int8_t feature[2];
+       u_int8_t unused[3];
+       u_int8_t length[2];
+       u_int8_t control;
+};
+
+struct scsi_config_feature_header {
+       u_int8_t length[4];
+       u_int8_t unused[2];
+       u_int8_t current_profile[2];
+       /* Complete Profile List in MMC-5, 5.3.1, Table 89 */
+#define CONFIG_PROFILE_RESERVED                0x0000
+#define CONFIG_PROFILE_CD_ROM          0x0008
+#define CONFIG_PROFILE_NON_CONFORM     0xffff
+};
+
+struct scsi_config_generic_descriptor {
+       u_int8_t feature_code[2];
+       /* Complete Feature Code List in MMC-5, 5.2.3, Table 86 */
+#define CONFIG_FEATURE_CODE_PROFILE            0x0000
+#define CONFIG_FEATURE_CODE_CORE               0x0001
+#define CONFIG_FEATURE_CODE_MORPHING           0x0002
+#define CONFIG_FEATURE_CODE_REMOVE_MEDIA       0x0004
+#define CONFIG_FEATURE_CODE_RANDOM_READ                0x0010
+       u_int8_t byte3;
+#define CONFIG_PROFILELIST_BYTE3               0x03
+       u_int8_t length;
+#define CONFIG_PROFILELIST_LENGTH              0x04
+};
+
+struct scsi_config_profile_descriptor {
+       u_int8_t profile_number[2];
+       u_int8_t byte3;
+#define CONFIG_PROFILE_BYTE3                   0x01
+       u_int8_t unused;
+};
+
+struct scsi_config_core_descriptor {
+       u_int8_t feature_code[2];
+       u_int8_t byte3;
+#define CONFIG_CORE_BYTE3              0x11
+       u_int8_t length;
+#define CONFIG_CORE_LENGTH             0x08
+       u_int8_t phy_std[4];
+       /* Complete PHYs List in MMC-5, 5.3.2, Table 91 */
+#define CONFIG_CORE_PHY_SCSI           0x00000001
+       u_int8_t unused[4];
+};
+
+struct scsi_config_morphing_descriptor {
+       u_int8_t feature_code[2];
+       u_int8_t byte3;
+#define CONFIG_MORPHING_BYTE3          0x07
+       u_int8_t length;
+#define CONFIG_MORPHING_LENGTH         0x04
+       /* OCE (bit 1), always set and ASYNC (bit 0) Bit */
+       u_int8_t byte5;
+#define CONFIG_MORPHING_BYTE5          0x2
+       u_int8_t unused[3];
+};
+
+struct scsi_config_remove_media_descriptor {
+       u_int8_t feature_code[2];
+       u_int8_t byte3;
+#define CONFIG_REMOVE_MEDIA_BYTE3      0x03
+       u_int8_t length;
+#define CONFIG_REMOVE_MEDIA_LENGTH     0x04
+       /* Ejection Type */
+       u_int8_t byte5;
+#define CONFIG_REMOVE_MEDIA_BYTE5      0x09
+       u_int8_t unused[3];
+};
+
+struct scsi_config_random_read_descriptor {
+       u_int8_t feature_code[2];
+       u_int8_t byte3;
+#define CONFIG_RANDOM_READ_BYTE3       0x03
+       u_int8_t length;
+#define CONFIG_RANDOM_READ_LENGTH      0x08
+       u_int8_t block_size[4];
+       u_int8_t blocking_type[2];
+#define CONFIG_RANDOM_READ_BLOCKING_TYPE       0x0010
+       u_int8_t unused[2];
+};
index 00a11a5..c7c28cd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: virtio.c,v 1.54 2017/09/17 23:07:56 pd Exp $  */
+/*     $OpenBSD: virtio.c,v 1.55 2018/01/03 05:39:56 ccardenas Exp $   */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -24,6 +24,7 @@
 #include <dev/pci/pcidevs.h>
 #include <dev/pv/virtioreg.h>
 #include <dev/pv/vioblkreg.h>
+#include <dev/pv/vioscsireg.h>
 
 #include <net/if.h>
 #include <netinet/in.h>
@@ -41,6 +42,7 @@
 #include "vmd.h"
 #include "vmm.h"
 #include "virtio.h"
+#include "vioscsi.h"
 #include "loadfile.h"
 #include "atomicio.h"
 
@@ -49,6 +51,7 @@ extern char *__progname;
 struct viornd_dev viornd;
 struct vioblk_dev *vioblk;
 struct vionet_dev *vionet;
+struct vioscsi_dev *vioscsi;
 struct vmmci_dev vmmci;
 
 int nr_vionet;
@@ -62,14 +65,6 @@ int nr_vioblk;
 #define VMMCI_F_ACK            (1<<1)
 #define VMMCI_F_SYNCRTC                (1<<2)
 
-struct ioinfo {
-       uint8_t *buf;
-       ssize_t len;
-       off_t offset;
-       int fd;
-       int error;
-};
-
 const char *
 vioblk_cmd_name(uint32_t type)
 {
@@ -1666,7 +1661,8 @@ vmmci_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
 }
 
 void
-virtio_init(struct vmd_vm *vm, int *child_disks, int *child_taps)
+virtio_init(struct vmd_vm *vm, int child_cdrom, int *child_disks,
+    int *child_taps)
 {
        struct vmop_create_params *vmc = &vm->vm_params;
        struct vm_create_params *vcp = &vmc->vmc_params;
@@ -1830,6 +1826,51 @@ virtio_init(struct vmd_vm *vm, int *child_disks, int *child_taps)
                }
        }
 
+       /* vioscsi cdrom */
+       if (strlen(vcp->vcp_cdrom)) {
+               vioscsi = calloc(1, sizeof(struct vioscsi_dev));
+               if (vioscsi == NULL) {
+                       log_warn("%s: calloc failure allocating vioscsi",
+                           __progname);
+                       return;
+               }
+
+               if (pci_add_device(&id, PCI_VENDOR_QUMRANET,
+                   PCI_PRODUCT_QUMRANET_VIO_SCSI,
+                   PCI_CLASS_MASS_STORAGE,
+                   PCI_SUBCLASS_MASS_STORAGE_SCSI,
+                   PCI_VENDOR_OPENBSD,
+                   PCI_PRODUCT_VIRTIO_SCSI, 1, NULL)) {
+                       log_warnx("%s: can't add PCI vioscsi device",
+                           __progname);
+                       return;
+               }
+
+               if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, vioscsi_io, vioscsi)) {
+                       log_warnx("%s: can't add bar for vioscsi device",
+                           __progname);
+                       return;
+               }
+
+               for ( i = 0; i < VIRTIO_MAX_QUEUES; i++) {
+                       vioscsi->vq[i].qs = VIOSCSI_QUEUE_SIZE;
+                       vioscsi->vq[i].vq_availoffset =
+                           sizeof(struct vring_desc) * VIOSCSI_QUEUE_SIZE;
+                       vioscsi->vq[i].vq_usedoffset = VIRTQUEUE_ALIGN(
+                           sizeof(struct vring_desc) * VIOSCSI_QUEUE_SIZE
+                           + sizeof(uint16_t) * (2 + VIOSCSI_QUEUE_SIZE));
+                       vioscsi->vq[i].last_avail = 0;
+               }
+               sz = lseek(child_cdrom, 0, SEEK_END);
+               vioscsi->fd = child_cdrom;
+               vioscsi->locked = 0;
+               vioscsi->lba = 0;
+               vioscsi->sz = sz;
+               vioscsi->n_blocks = sz >> 11; /* num of 2048 blocks in file */
+               vioscsi->max_xfer = VIOSCSI_BLOCK_SIZE_CDROM;
+               vioscsi->pci_id = id;
+       }
+
        /* virtio control device */
        if (pci_add_device(&id, PCI_VENDOR_OPENBSD,
            PCI_PRODUCT_OPENBSD_CONTROL,
@@ -1990,7 +2031,40 @@ vioblk_restore(int fd, struct vm_create_params *vcp, int *child_disks)
 }
 
 int
-virtio_restore(int fd, struct vmd_vm *vm, int *child_disks, int *child_taps)
+vioscsi_restore(int fd, struct vm_create_params *vcp, int child_cdrom)
+{
+       off_t sz;
+
+       vioscsi = calloc(1, sizeof(struct vioscsi_dev));
+       if (vioscsi == NULL) {
+               log_warn("%s: calloc failure allocating vioscsi", __progname);
+               return (-1);
+       }
+
+       log_debug("%s: receiving vioscsi", __func__);
+
+       if (atomicio(read, fd, vioscsi, sizeof(struct vioscsi_dev)) !=
+           sizeof(struct vioscsi_dev)) {
+               log_warnx("%s: error reading vioscsi from fd", __func__);
+               return (-1);
+       }
+
+       sz = lseek(child_cdrom, 0, SEEK_END);
+
+       if (pci_set_bar_fn(vioscsi->pci_id, 0, vioscsi_io, NULL)) {
+               log_warnx("%s: can't set bar fn for vmm control device",
+                   __progname);
+               return (-1);
+       }
+
+       vioscsi->fd = child_cdrom;
+
+       return (0);
+}
+
+int
+virtio_restore(int fd, struct vmd_vm *vm, int child_cdrom, int *child_disks,
+    int *child_taps)
 {
        struct vmop_create_params *vmc = &vm->vm_params;
        struct vm_create_params *vcp = &vmc->vmc_params;
@@ -2002,6 +2076,9 @@ virtio_restore(int fd, struct vmd_vm *vm, int *child_disks, int *child_taps)
        if ((ret = vioblk_restore(fd, vcp, child_disks)) == -1)
                return ret;
 
+       if ((ret = vioscsi_restore(fd, vcp, child_cdrom)) == -1)
+               return ret;
+
        if ((ret = vionet_restore(fd, vm, child_taps)) == -1)
                return ret;
 
@@ -2059,6 +2136,18 @@ vioblk_dump(int fd)
        return (0);
 }
 
+int
+vioscsi_dump(int fd)
+{
+       log_debug("%s: sending vioscsi", __func__);
+       if (atomicio(vwrite, fd, &vioscsi, sizeof(vioscsi)) !=
+           sizeof(vioscsi)) {
+               log_warnx("%s: error writing vioscsi to fd", __func__);
+               return (-1);
+       }
+       return (0);
+}
+
 int
 virtio_dump(int fd)
 {
@@ -2070,6 +2159,9 @@ virtio_dump(int fd)
        if ((ret = vioblk_dump(fd)) == -1)
                return ret;
 
+       if ((ret = vioscsi_dump(fd)) == -1)
+               return ret;
+
        if ((ret = vionet_dump(fd)) == -1)
                return ret;
 
index c7977d5..55640d6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: virtio.h,v 1.21 2017/09/17 23:07:56 pd Exp $  */
+/*     $OpenBSD: virtio.h,v 1.22 2018/01/03 05:39:56 ccardenas Exp $   */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -28,6 +28,9 @@
 #define VIOBLK_QUEUE_SIZE      128
 #define VIOBLK_QUEUE_MASK      (VIOBLK_QUEUE_SIZE - 1)
 
+#define VIOSCSI_QUEUE_SIZE     128
+#define VIOSCSI_QUEUE_MASK     (VIOSCSI_QUEUE_SIZE - 1)
+
 #define VIONET_QUEUE_SIZE      128
 #define VIONET_QUEUE_MASK      (VIONET_QUEUE_SIZE - 1)
 
 #define VMMCI_TIMEOUT          3
 #define VMMCI_SHUTDOWN_TIMEOUT 30
 
-/* All the devices we support have either 1 or 2 queues */
-#define VIRTIO_MAX_QUEUES      2
+/* All the devices we support have either 1, 2 or 3 queues */
+/* viornd - 1 queue
+ * vioblk - 1 queue
+ * vionet - 2 queues
+ * vioscsi - 3 queues
+ */
+#define VIRTIO_MAX_QUEUES      3
 
 /*
  * This struct stores notifications from a virtio driver. There is
@@ -111,6 +119,31 @@ struct vioblk_dev {
        uint8_t pci_id;
 };
 
+/* vioscsi will use at least 3 queues - 5.6.2 Virtqueues
+ * Current implementation will use 3
+ * 0 - control
+ * 1 - event
+ * 2 - requests
+ */
+struct vioscsi_dev {
+       struct virtio_io_cfg cfg;
+
+       struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
+
+       /* is the device locked */
+       int locked;
+       int fd;
+       /* size of iso file in bytes */
+       uint64_t sz;
+       /* last block address read */
+       uint64_t lba;
+       /* number of blocks represented in iso */
+       uint64_t n_blocks;
+       uint32_t max_xfer;
+
+       uint8_t pci_id;
+};
+
 struct vionet_dev {
        pthread_mutex_t mutex;
        struct event event;
@@ -167,10 +200,18 @@ struct vmmci_dev {
        uint8_t pci_id;
 };
 
+struct ioinfo {
+       uint8_t *buf;
+       ssize_t len;
+       off_t offset;
+       int fd;
+       int error;
+};
+
 /* virtio.c */
-void virtio_init(struct vmd_vm *, int *, int *);
+void virtio_init(struct vmd_vm *, int, int *, int *);
 int virtio_dump(int);
-int virtio_restore(int, struct vmd_vm *, int *, int *);
+int virtio_restore(int, struct vmd_vm *, int, int *, int *);
 uint32_t vring_size(uint32_t);
 
 int virtio_rnd_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
@@ -205,6 +246,14 @@ void vmmci_ack(unsigned int);
 void vmmci_timeout(int, short, void *);
 
 const char *vioblk_cmd_name(uint32_t);
+int vioscsi_dump(int);
+int vioscsi_restore(int, struct vm_create_params *, int);
 
 /* dhcp.c */
 ssize_t dhcp_request(struct vionet_dev *, char *, size_t, char **);
+
+/* vioscsi.c */
+int vioscsi_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
+void vioscsi_update_qs(struct vioscsi_dev *);
+void vioscsi_update_qa(struct vioscsi_dev *);
+int vioscsi_notifyq(struct vioscsi_dev *);
index 05292be..32c5c52 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vm.c,v 1.30 2017/11/29 02:46:10 mlarkin Exp $ */
+/*     $OpenBSD: vm.c,v 1.31 2018/01/03 05:39:56 ccardenas Exp $       */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -65,7 +65,8 @@
 
 io_fn_t ioports_map[MAX_PORTS];
 
-int run_vm(int *, int *, struct vmop_create_params *, struct vcpu_reg_state *);
+int run_vm(int, int *, int *, struct vmop_create_params *,
+    struct vcpu_reg_state *);
 void vm_dispatch_vmm(int, short, void *);
 void *event_thread(void *);
 void *vcpu_run_loop(void *);
@@ -74,8 +75,8 @@ int vcpu_reset(uint32_t, uint32_t, struct vcpu_reg_state *);
 void create_memory_map(struct vm_create_params *);
 int alloc_guest_mem(struct vm_create_params *);
 int vmm_create_vm(struct vm_create_params *);
-void init_emulated_hw(struct vmop_create_params *, int *, int *);
-void restore_emulated_hw(struct vm_create_params *,int , int *, int *);
+void init_emulated_hw(struct vmop_create_params *, int, int *, int *);
+void restore_emulated_hw(struct vm_create_params *, int, int *, int *,int);
 void vcpu_exit_inout(struct vm_run_params *);
 uint8_t vcpu_exit_pci(struct vm_run_params *);
 int vcpu_pic_intr(uint32_t, uint32_t, uint8_t);
@@ -359,7 +360,7 @@ start_vm(struct vmd_vm *vm, int fd)
 
        if (vm->vm_received) {
                restore_emulated_hw(vcp, vm->vm_receive_fd, nicfds,
-                   vm->vm_disks);
+                   vm->vm_disks, vm->vm_cdrom);
                mc146818_start();
                restore_mem(vm->vm_receive_fd, vcp);
        }
@@ -368,7 +369,7 @@ start_vm(struct vmd_vm *vm, int fd)
                fatal("setup vm pipe");
 
        /* Execute the vcpu run loop(s) for this VM */
-       ret = run_vm(vm->vm_disks, nicfds, &vm->vm_params, &vrs);
+       ret = run_vm(vm->vm_cdrom, vm->vm_disks, nicfds, &vm->vm_params, &vrs);
 
        return (ret);
 }
@@ -897,8 +898,8 @@ vmm_create_vm(struct vm_create_params *vcp)
  * Initializes the userspace hardware emulation
  */
 void
-init_emulated_hw(struct vmop_create_params *vmc, int *child_disks,
-    int *child_taps)
+init_emulated_hw(struct vmop_create_params *vmc, int child_cdrom,
+    int *child_disks, int *child_taps)
 {
        struct vm_create_params *vcp = &vmc->vmc_params;
        int i;
@@ -951,7 +952,7 @@ init_emulated_hw(struct vmop_create_params *vmc, int *child_disks,
        pci_init();
 
        /* Initialize virtio devices */
-       virtio_init(current_vm, child_disks, child_taps);
+       virtio_init(current_vm, child_cdrom, child_disks, child_taps);
 }
 /*
  * restore_emulated_hw
@@ -960,7 +961,7 @@ init_emulated_hw(struct vmop_create_params *vmc, int *child_disks,
  */
 void
 restore_emulated_hw(struct vm_create_params *vcp, int fd,
-    int *child_taps, int *child_disks)
+    int *child_taps, int *child_disks, int child_cdrom)
 {
        /* struct vm_create_params *vcp = &vmc->vmc_params; */
        int i;
@@ -1000,7 +1001,7 @@ restore_emulated_hw(struct vm_create_params *vcp, int fd,
        ioports_map[PCI_MODE1_DATA_REG + 2] = vcpu_exit_pci;
        ioports_map[PCI_MODE1_DATA_REG + 3] = vcpu_exit_pci;
        pci_restore(fd);
-       virtio_restore(fd, current_vm, child_disks, child_taps);
+       virtio_restore(fd, current_vm, child_cdrom, child_disks, child_taps);
 }
 
 /*
@@ -1009,6 +1010,7 @@ restore_emulated_hw(struct vm_create_params *vcp, int fd,
  * Runs the VM whose creation parameters are specified in vcp
  *
  * Parameters:
+ *  child_cdrom: previously-opened child ISO disk file descriptor
  *  child_disks: previously-opened child VM disk file file descriptors
  *  child_taps: previously-opened child tap file descriptors
  *  vmc: vmop_create_params struct containing the VM's desired creation
@@ -1020,8 +1022,8 @@ restore_emulated_hw(struct vm_create_params *vcp, int fd,
  *  !0 : the VM exited abnormally or failed to start
  */
 int
-run_vm(int *child_disks, int *child_taps, struct vmop_create_params *vmc,
-    struct vcpu_reg_state *vrs)
+run_vm(int child_cdrom, int *child_disks, int *child_taps,
+    struct vmop_create_params *vmc, struct vcpu_reg_state *vrs)
 {
        struct vm_create_params *vcp = &vmc->vmc_params;
        struct vm_rwregs_params vregsp;
@@ -1035,6 +1037,9 @@ run_vm(int *child_disks, int *child_taps, struct vmop_create_params *vmc,
        if (vcp == NULL)
                return (EINVAL);
 
+       if (child_cdrom == -1 && strlen(vcp->vcp_cdrom))
+               return (EINVAL);
+
        if (child_disks == NULL && vcp->vcp_ndisks != 0)
                return (EINVAL);
 
@@ -1066,7 +1071,7 @@ run_vm(int *child_disks, int *child_taps, struct vmop_create_params *vmc,
            vcp->vcp_name);
 
        if (!current_vm->vm_received)
-               init_emulated_hw(vmc, child_disks, child_taps);
+               init_emulated_hw(vmc, child_cdrom, child_disks, child_taps);
 
        ret = pthread_mutex_init(&threadmutex, NULL);
        if (ret) {
index 7863d9b..9119ac3 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: vm.conf.5,v 1.26 2017/11/11 07:36:56 jmc Exp $
+.\" $OpenBSD: vm.conf.5,v 1.27 2018/01/03 05:39:56 ccardenas Exp $
 .\"
 .\" Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
 .\" Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: November 11 2017 $
+.Dd $Mdocdate: January 3 2018 $
 .Dt VM.CONF 5
 .Os
 .Sh NAME
@@ -119,6 +119,8 @@ Followed by a block of parameters that is enclosed in curly brackets:
 Kernel or BIOS image to load when booting the VM.
 If not specified, the default is to boot using the BIOS image in
 .Pa /etc/firmware/vmm-bios .
+.It Cm cdrom Ar path
+ISO image file.
 .It Cm enable
 Automatically start the VM.
 This is the default if neither
index 469a285..f0ca847 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmd.c,v 1.76 2017/12/06 13:29:02 abieber Exp $        */
+/*     $OpenBSD: vmd.c,v 1.77 2018/01/03 05:39:56 ccardenas Exp $      */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -1064,6 +1064,10 @@ vm_stop(struct vmd_vm *vm, int keeptty)
                close(vm->vm_kernel);
                vm->vm_kernel = -1;
        }
+       if (vm->vm_cdrom != -1) {
+               close(vm->vm_cdrom);
+               vm->vm_cdrom = -1;
+       }
        if (!keeptty) {
                vm_closetty(vm);
                vm->vm_uid = 0;
@@ -1194,6 +1198,7 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc,
                }
        }
        vm->vm_kernel = -1;
+       vm->vm_cdrom = -1;
        vm->vm_iev.ibuf.fd = -1;
 
        if (++env->vmd_nvm == 0)
index 0263cb4..1b9a183 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmd.h,v 1.66 2017/11/11 02:50:08 mlarkin Exp $        */
+/*     $OpenBSD: vmd.h,v 1.67 2018/01/03 05:39:56 ccardenas Exp $      */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -59,6 +59,8 @@
 #define VMD_DISK_MISSING       1002
 #define VMD_DISK_INVALID       1003
 #define VMD_VM_STOP_INVALID    1004
+#define VMD_CDROM_MISSING      1005
+#define VMD_CDROM_INVALID      1006
 
 /* 100.64.0.0/10 from rfc6598 (IPv4 Prefix for Shared Address Space) */
 #define VMD_DHCP_PREFIX                "100.64.0.0/10"
@@ -71,6 +73,7 @@
 
 enum imsg_type {
        IMSG_VMDOP_START_VM_REQUEST = IMSG_PROC_MAX,
+       IMSG_VMDOP_START_VM_CDROM,
        IMSG_VMDOP_START_VM_DISK,
        IMSG_VMDOP_START_VM_IF,
        IMSG_VMDOP_START_VM_END,
@@ -139,6 +142,7 @@ struct vmop_create_params {
 #define VMOP_CREATE_MEMORY     0x02
 #define VMOP_CREATE_NETWORK    0x04
 #define VMOP_CREATE_DISK       0x08
+#define VMOP_CREATE_CDROM      0x10
 
        /* userland-only part of the create params */
        unsigned int             vmc_ifflags[VMM_MAX_NICS_PER_VM];
@@ -209,6 +213,7 @@ struct vmd_vm {
        pid_t                    vm_pid;
        uint32_t                 vm_vmid;
        int                      vm_kernel;
+       int                      vm_cdrom;
        int                      vm_disks[VMM_MAX_DISKS_PER_VM];
        struct vmd_if            vm_ifs[VMM_MAX_NICS_PER_VM];
        char                    *vm_ttyname;
@@ -354,6 +359,7 @@ int  config_setvm(struct privsep *, struct vmd_vm *, uint32_t, uid_t);
 int     config_getvm(struct privsep *, struct imsg *);
 int     config_getdisk(struct privsep *, struct imsg *);
 int     config_getif(struct privsep *, struct imsg *);
+int     config_getcdrom(struct privsep *, struct imsg *);
 
 /* vmboot.c */
 FILE   *vmboot_open(int, int, struct vmboot_params *);
index 087dfb3..cb20c38 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmm.c,v 1.78 2017/10/24 07:58:52 mlarkin Exp $        */
+/*     $OpenBSD: vmm.c,v 1.79 2018/01/03 05:39:56 ccardenas Exp $      */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -121,6 +121,13 @@ vmm_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
                        cmd = IMSG_VMDOP_START_VM_RESPONSE;
                }
                break;
+       case IMSG_VMDOP_START_VM_CDROM:
+               res = config_getcdrom(ps, imsg);
+               if (res == -1) {
+                       res = errno;
+                       cmd = IMSG_VMDOP_START_VM_RESPONSE;
+               }
+               break;
        case IMSG_VMDOP_START_VM_DISK:
                res = config_getdisk(ps, imsg);
                if (res == -1) {
@@ -635,6 +642,9 @@ vmm_start_vm(struct imsg *imsg, uint32_t *id)
                close(vm->vm_kernel);
                vm->vm_kernel = -1;
 
+               close(vm->vm_cdrom);
+               vm->vm_cdrom = -1;
+
                close(vm->vm_tty);
                vm->vm_tty = -1;