Index | Thread | Search

From:
Dave Voutila <dv@sisu.io>
Subject:
teaching vmd virtio 1.2
To:
tech@openbsd.org
Cc:
sf@openbsd.org, mlarkin@openbsd.org
Date:
Thu, 24 Jul 2025 20:27:57 -0400

Download raw body.

Thread
Testers wanted (who's running an archaic Linux guest?), but also looking
for ok's.

tl;dr: updates vmd's virtio implementation to support virtio 1.2 and
upgrade vioscsi and viornd to use the new 1.x implementation. In doing
so, cleans up core virtqueue handling for devices still in the virtio
0.9 dark ages.

The below diff has already seen testing in a previous form back in June,
but I've worked to include some critical feedback from sf@ related to
the virtio 1.x spec:

 - adjusts queue size and masks to be dynamic for v1.x devices (caps
   max queue size as IOV_MAX) and adds power-of-2 checks
 - adds pci revision support and sets the v1.x devices to rev 1 to
   report they are non-transitional devices and want only the v1.x
   protocol
 - cleans up the virtqueue reset handling across all devices to use a
   common function

These changes warrant some more tire kicking and scrutiny.

Hoping to get this into the tree early next week if people have time to
do a final review and testing. Next step is transitioning remaining
devices (net, block, vmmci).


diffstat refs/heads/master refs/heads/vmd-virtio-1.2
 M  usr.sbin/vmd/mc146818.c  |    1+    1-
 M  usr.sbin/vmd/pci.c       |   55+    9-
 M  usr.sbin/vmd/pci.h       |   16+    1-
 M  usr.sbin/vmd/vioblk.c    |   53+   87-
 M  usr.sbin/vmd/vionet.c    |   61+  120-
 M  usr.sbin/vmd/vioscsi.c   |  359+  599-
 M  usr.sbin/vmd/virtio.c    |  808+  290-
 M  usr.sbin/vmd/virtio.h    |  122+   78-
 M  usr.sbin/vmd/vm.c        |    2+    2-
 M  usr.sbin/vmd/vmd.h       |    3+    0-

10 files changed, 1480 insertions(+), 1187 deletions(-)

diff refs/heads/master refs/heads/vmd-virtio-1.2
commit - 8a2a58f837cee76d6c78d201d9b21d0665038866
commit + c3b5bc5a53cbaa3387d08a182804b055e58cbd4a
blob - 2c6b76c6c8387579b7c09672c1fcc34860c7593f
blob + 50e20c4646c71a4de6eeded38d458e5814c7dd1b
--- usr.sbin/vmd/mc146818.c
+++ usr.sbin/vmd/mc146818.c
@@ -127,7 +127,7 @@ rtc_fire1(int fd, short type, void *arg)
 	if (rtc.now - old > 5) {
 		log_debug("%s: RTC clock drift (%llds), requesting guest "
 		    "resync", __func__, (rtc.now - old));
-		vmmci_ctl(VMMCI_SYNCRTC);
+		vmmci_ctl(&vmmci, VMMCI_SYNCRTC);
 	}
 	evtimer_add(&rtc.sec, &rtc.sec_tv);
 }
blob - ef5bbeb94c3e1345147078952303d429b8a9de52
blob + 242397ad4aa9a9e54ec2d1dd049f1247bb858c03
--- usr.sbin/vmd/pci.c
+++ usr.sbin/vmd/pci.c
@@ -22,6 +22,7 @@
 #include <dev/pci/pcidevs.h>
 #include <dev/vmm/vmm.h>

+#include <stddef.h>
 #include <string.h>
 #include <unistd.h>

@@ -54,7 +55,7 @@ const uint8_t pci_pic_irqs[PCI_MAX_PIC_IRQS] = {3, 5,
  *  barfn: callback function invoked on BAR access
  *  cookie: cookie passed to barfn on access
  *
- * Returns 0 if the BAR was added successfully, 1 otherwise.
+ * Returns the index of the BAR if added successfully, -1 otherwise.
  */
 int
 pci_add_bar(uint8_t id, uint32_t type, void *barfn, void *cookie)
@@ -63,18 +64,18 @@ pci_add_bar(uint8_t id, uint32_t type, void *barfn, vo

 	/* Check id */
 	if (id >= pci.pci_dev_ct)
-		return (1);
+		return (-1);

 	/* Can only add PCI_MAX_BARS BARs to any device */
 	bar_ct = pci.pci_devices[id].pd_bar_ct;
 	if (bar_ct >= PCI_MAX_BARS)
-		return (1);
+		return (-1);

 	/* Compute BAR address and add */
 	bar_reg_idx = (PCI_MAPREG_START + (bar_ct * 4)) / 4;
 	if (type == PCI_MAPREG_TYPE_MEM) {
 		if (pci.pci_next_mmio_bar >= PCI_MMIO_BAR_END)
-			return (1);
+			return (-1);

 		pci.pci_devices[id].pd_cfg_space[bar_reg_idx] =
 		    PCI_MAPREG_MEM_ADDR(pci.pci_next_mmio_bar);
@@ -88,7 +89,7 @@ pci_add_bar(uint8_t id, uint32_t type, void *barfn, vo
 #ifdef __amd64__
 	else if (type == PCI_MAPREG_TYPE_IO) {
 		if (pci.pci_next_io_bar >= VM_PCI_IO_BAR_END)
-			return (1);
+			return (-1);

 		pci.pci_devices[id].pd_cfg_space[bar_reg_idx] =
 		    PCI_MAPREG_IO_ADDR(pci.pci_next_io_bar) |
@@ -104,7 +105,7 @@ pci_add_bar(uint8_t id, uint32_t type, void *barfn, vo
 	}
 #endif /* __amd64__ */

-	return (0);
+	return ((int)bar_ct);
 }

 int
@@ -156,6 +157,7 @@ pci_get_dev_irq(uint8_t id)
  *  subclass: PCI 'subclass' of the new device
  *  subsys_vid: subsystem VID of the new device
  *  subsys_id: subsystem ID of the new device
+ *  rev_id: revision id
  *  irq_needed: 1 if an IRQ should be assigned to this PCI device, 0 otherwise
  *  csfunc: PCI config space callback function when the guest VM accesses
  *      CS of this PCI device
@@ -167,7 +169,7 @@ pci_get_dev_irq(uint8_t id)
 int
 pci_add_device(uint8_t *id, uint16_t vid, uint16_t pid, uint8_t class,
     uint8_t subclass, uint16_t subsys_vid, uint16_t subsys_id,
-    uint8_t irq_needed, pci_cs_fn_t csfunc)
+    uint8_t rev_id, uint8_t irq_needed, pci_cs_fn_t csfunc)
 {
 	/* Exceeded max devices? */
 	if (pci.pci_dev_ct >= PCI_CONFIG_MAX_DEV)
@@ -182,6 +184,7 @@ pci_add_device(uint8_t *id, uint16_t vid, uint16_t pid

 	pci.pci_devices[*id].pd_vid = vid;
 	pci.pci_devices[*id].pd_did = pid;
+	pci.pci_devices[*id].pd_rev = rev_id;
 	pci.pci_devices[*id].pd_class = class;
 	pci.pci_devices[*id].pd_subclass = subclass;
 	pci.pci_devices[*id].pd_subsys_vid = subsys_vid;
@@ -204,6 +207,34 @@ pci_add_device(uint8_t *id, uint16_t vid, uint16_t pid
 	return (0);
 }

+int
+pci_add_capability(uint8_t id, struct pci_cap *cap)
+{
+	uint8_t cid;
+	struct pci_dev *dev = NULL;
+
+	if (id >= pci.pci_dev_ct)
+		return (-1);
+	dev = &pci.pci_devices[id];
+
+	if (dev->pd_cap_ct >= PCI_MAX_CAPS)
+		return (-1);
+	cid = dev->pd_cap_ct;
+
+	memcpy(&dev->pd_caps[cid], cap, sizeof(dev->pd_caps[0]));
+
+	/* Update the linkage. */
+	if (cid > 0)
+		dev->pd_caps[cid - 1].pc_next = (sizeof(struct pci_cap) * cid) +
+		    offsetof(struct pci_dev, pd_caps);
+
+	dev->pd_cap_ct++;
+	dev->pd_cap = offsetof(struct pci_dev, pd_caps);
+	dev->pd_status |= (PCI_STATUS_CAPLIST_SUPPORT >> 16);
+
+	return (cid);
+}
+
 /*
  * pci_init
  *
@@ -216,15 +247,18 @@ pci_init(void)
 	uint8_t id;

 	memset(&pci, 0, sizeof(pci));
+
+	/* Check if changes to struct pci_dev create an invalid config space. */
+	CTASSERT(sizeof(pci.pci_devices[0].pd_cfg_space) <= 256);
+
 	pci.pci_next_mmio_bar = PCI_MMIO_BAR_BASE;
-
 #ifdef __amd64__
 	pci.pci_next_io_bar = VM_PCI_IO_BAR_BASE;
 #endif /* __amd64__ */

 	if (pci_add_device(&id, PCI_VENDOR_OPENBSD, PCI_PRODUCT_OPENBSD_PCHB,
 	    PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_HOST,
-	    PCI_VENDOR_OPENBSD, 0, 0, NULL)) {
+	    PCI_VENDOR_OPENBSD, 0, 0, 0, NULL)) {
 		log_warnx("%s: can't add PCI host bridge", __progname);
 		return;
 	}
@@ -442,3 +476,15 @@ pci_find_first_device(uint16_t subsys_id)
 			return (i);
 	return (-1);
 }
+
+/*
+ * Retrieve the subsystem identifier for a PCI device if found, otherwise 0.
+ */
+uint16_t
+pci_get_subsys_id(uint8_t pci_id)
+{
+	if (pci_id >= pci.pci_dev_ct)
+		return (0);
+	else
+		return (pci.pci_devices[pci_id].pd_subsys_id);
+}
blob - bb874674aafa7d26bfd790d182f45daf4d5cb014
blob + 1d417572421db8b189ef85b89ab6bf74305db8d2
--- usr.sbin/vmd/pci.h
+++ usr.sbin/vmd/pci.h
@@ -30,6 +30,7 @@
 #define PCI_MODE1_DATA_REG	0x0cfc
 #define PCI_CONFIG_MAX_DEV	32
 #define PCI_MAX_BARS		6
+#define PCI_MAX_CAPS		8

 #define PCI_BAR_TYPE_IO		0x0
 #define PCI_BAR_TYPE_MMIO	0x1
@@ -44,6 +45,15 @@ typedef int (*pci_iobar_fn_t)(int dir, uint16_t reg, u
     void *, uint8_t);
 typedef int (*pci_mmiobar_fn_t)(int dir, uint32_t ofs, uint32_t *data);

+/*
+ * Represents a PCI Capability entry with enough space for the virtio-specific
+ * capabilities.
+ */
+struct pci_cap {
+	uint8_t		pc_vndr;	/* Vendor-specific ID */
+	uint8_t		pc_next;	/* Link to next capability */
+	uint8_t		pc_extra[22];	/* Enough space for Virtio PCI data. */
+} __packed;

 struct pci_dev {
 	union {
@@ -73,9 +83,12 @@ struct pci_dev {
 			uint8_t pd_int;
 			uint8_t pd_min_grant;
 			uint8_t pd_max_grant;
+			struct pci_cap pd_caps[PCI_MAX_CAPS];
 		} __packed;
 	};
+
 	uint8_t pd_bar_ct;
+	uint8_t pd_cap_ct;
 	pci_cs_fn_t pd_csfunc;

 	uint8_t pd_bartype[PCI_MAX_BARS];
@@ -97,10 +110,12 @@ struct pci {
 int pci_find_first_device(uint16_t);
 void pci_init(void);
 int pci_add_device(uint8_t *, uint16_t, uint16_t, uint8_t, uint8_t, uint16_t,
-    uint16_t, uint8_t, pci_cs_fn_t);
+    uint16_t, uint8_t, uint8_t, pci_cs_fn_t);
+int pci_add_capability(uint8_t, struct pci_cap *);
 int pci_add_bar(uint8_t, uint32_t, void *, void *);
 int pci_set_bar_fn(uint8_t, uint8_t, void *, void *);
 uint8_t pci_get_dev_irq(uint8_t);
+uint16_t pci_get_subsys_id(uint8_t);

 #ifdef __amd64__
 void pci_handle_address_reg(struct vm_run_params *);
blob - a2da2897ae2f9a3c88609ef22c16628148bbb2b3
blob + 778f74040593e8c2c8e17840d0afeb2e731c2224
--- usr.sbin/vmd/vioblk.c
+++ usr.sbin/vmd/vioblk.c
@@ -35,18 +35,16 @@

 extern char *__progname;
 extern struct vmd_vm *current_vm;
-struct iovec io_v[VIOBLK_QUEUE_SIZE];
+struct iovec io_v[VIRTIO_QUEUE_SIZE_MAX];

 static const char *disk_type(int);
 static uint32_t handle_io_read(struct viodev_msg *, struct virtio_dev *,
     int8_t *);
 static int handle_io_write(struct viodev_msg *, struct virtio_dev *);

-static void vioblk_update_qs(struct vioblk_dev *);
-static void vioblk_update_qa(struct vioblk_dev *);
-static int vioblk_notifyq(struct vioblk_dev *);
-static ssize_t vioblk_rw(struct vioblk_dev *, int, off_t,
-    struct vring_desc *, struct vring_desc **);
+static int vioblk_notifyq(struct virtio_dev *);
+static ssize_t vioblk_rw(struct vioblk_dev *, struct virtio_vq_info *, int,
+    off_t, struct vring_desc *, struct vring_desc **);

 static void dev_dispatch_vm(int, short, void *);
 static void handle_sync_io(int, short, void *);
@@ -243,43 +241,6 @@ vioblk_cmd_name(uint32_t type)
 	}
 }

-static void
-vioblk_update_qa(struct vioblk_dev *dev)
-{
-	struct virtio_vq_info *vq_info;
-	void *hva = NULL;
-
-	/* Invalid queue? */
-	if (dev->cfg.queue_select > 0)
-		return;
-
-	vq_info = &dev->vq[dev->cfg.queue_select];
-	vq_info->q_gpa = (uint64_t)dev->cfg.queue_pfn * VIRTIO_PAGE_SIZE;
-
-	hva = hvaddr_mem(vq_info->q_gpa, vring_size(VIOBLK_QUEUE_SIZE));
-	if (hva == NULL)
-		fatal("vioblk_update_qa");
-	vq_info->q_hva = hva;
-}
-
-static void
-vioblk_update_qs(struct vioblk_dev *dev)
-{
-	struct virtio_vq_info *vq_info;
-
-	/* Invalid queue? */
-	if (dev->cfg.queue_select > 0) {
-		dev->cfg.queue_size = 0;
-		return;
-	}
-
-	vq_info = &dev->vq[dev->cfg.queue_select];
-
-	/* Update queue pfn/size based on queue select */
-	dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
-	dev->cfg.queue_size = vq_info->qs;
-}
-
 /*
  * Process virtqueue notifications. If an unrecoverable error occurs, puts
  * device into a "needs reset" state.
@@ -287,20 +248,22 @@ vioblk_update_qs(struct vioblk_dev *dev)
  * Returns 1 if an we need to assert an IRQ.
  */
 static int
-vioblk_notifyq(struct vioblk_dev *dev)
+vioblk_notifyq(struct virtio_dev *dev)
 {
 	uint32_t cmd_len;
 	uint16_t idx, cmd_desc_idx;
 	uint8_t ds;
 	off_t offset;
 	ssize_t sz;
-	int is_write, notify = 0, i;
+	int is_write, notify = 0;
 	char *vr;
+	size_t i;
 	struct vring_desc *table, *desc;
 	struct vring_avail *avail;
 	struct vring_used *used;
 	struct virtio_blk_req_hdr *cmd;
 	struct virtio_vq_info *vq_info;
+	struct vioblk_dev *vioblk = &dev->vioblk;

 	/* Invalid queue? */
 	if (dev->cfg.queue_notify > 0)
@@ -319,7 +282,7 @@ vioblk_notifyq(struct vioblk_dev *dev)

 	while (idx != avail->idx) {
 		/* Retrieve Command descriptor. */
-		cmd_desc_idx = avail->ring[idx & VIOBLK_QUEUE_MASK];
+		cmd_desc_idx = avail->ring[idx & vq_info->mask];
 		desc = &table[cmd_desc_idx];
 		cmd_len = desc->len;

@@ -342,7 +305,7 @@ vioblk_notifyq(struct vioblk_dev *dev)
 			goto reset;

 		/* Advance to the 2nd descriptor. */
-		desc = &table[desc->next & VIOBLK_QUEUE_MASK];
+		desc = &table[desc->next & vq_info->mask];

 		/* Process each available command & chain. */
 		switch (cmd->type) {
@@ -351,7 +314,8 @@ vioblk_notifyq(struct vioblk_dev *dev)
 			/* Read (IN) & Write (OUT) */
 			is_write = (cmd->type == VIRTIO_BLK_T_OUT) ? 1 : 0;
 			offset = cmd->sector * VIRTIO_BLK_SECTOR_SIZE;
-			sz = vioblk_rw(dev, is_write, offset, table, &desc);
+			sz = vioblk_rw(vioblk, vq_info, is_write, offset, table,
+			    &desc);
 			if (sz == -1)
 				ds = VIRTIO_BLK_S_IOERR;
 			else
@@ -376,8 +340,8 @@ vioblk_notifyq(struct vioblk_dev *dev)
 		/* Advance to the end of the chain, if needed. */
 		i = 0;
 		while (desc->flags & VRING_DESC_F_NEXT) {
-			desc = &table[desc->next & VIOBLK_QUEUE_MASK];
-			if (++i >= VIOBLK_QUEUE_SIZE) {
+			desc = &table[desc->next & vq_info->mask];
+			if (++i >= vq_info->qs) {
 				/*
 				 * If we encounter an infinite/looping chain,
 				 * not much we can do but say we need a reset.
@@ -398,11 +362,11 @@ vioblk_notifyq(struct vioblk_dev *dev)
 			log_warnx("%s: can't write device status data "
 			    "@ 0x%llx",__func__, desc->addr);

-		dev->cfg.isr_status |= 1;
+		dev->isr |= 1;
 		notify = 1;

-		used->ring[used->idx & VIOBLK_QUEUE_MASK].id = cmd_desc_idx;
-		used->ring[used->idx & VIOBLK_QUEUE_MASK].len = cmd_len;
+		used->ring[used->idx & vq_info->mask].id = cmd_desc_idx;
+		used->ring[used->idx & vq_info->mask].len = cmd_len;

 		__sync_synchronize();
 		used->idx++;
@@ -417,8 +381,8 @@ reset:
 	 * When setting the "needs reset" flag, the driver is notified
 	 * via a configuration change interrupt.
 	 */
-	dev->cfg.device_status |= DEVICE_NEEDS_RESET;
-	dev->cfg.isr_status |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
+	dev->status |= DEVICE_NEEDS_RESET;
+	dev->isr |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
 	return (1);
 }

@@ -575,37 +539,39 @@ handle_io_write(struct viodev_msg *msg, struct virtio_
 		    virtio_reg_name(msg->reg));
 		break;
 	case VIRTIO_CONFIG_GUEST_FEATURES:
-		vioblk->cfg.guest_feature = data;
+		dev->cfg.guest_feature = data;
 		break;
 	case VIRTIO_CONFIG_QUEUE_PFN:
-		vioblk->cfg.queue_pfn = data;
-		vioblk_update_qa(vioblk);
+		dev->cfg.queue_pfn = data;
+		virtio_update_qa(dev);
+		if (dev->cfg.queue_select < dev->num_queues)
+			vioblk->seg_max = dev->vq[dev->cfg.queue_select].qs - 2;
+		else {
+			DPRINTF("%s: segment max set to zero", __func__);
+			vioblk->seg_max = 0;
+		}
 		break;
 	case VIRTIO_CONFIG_QUEUE_SELECT:
-		vioblk->cfg.queue_select = data;
-		vioblk_update_qs(vioblk);
+		dev->cfg.queue_select = data;
+		virtio_update_qs(dev);
 		break;
 	case VIRTIO_CONFIG_QUEUE_NOTIFY:
 		/* XXX We should be stricter about status checks. */
-		if (!(vioblk->cfg.device_status & DEVICE_NEEDS_RESET)) {
-			vioblk->cfg.queue_notify = data;
-			if (vioblk_notifyq(vioblk))
+		if (!(dev->status & DEVICE_NEEDS_RESET)) {
+			dev->cfg.queue_notify = data;
+			if (vioblk_notifyq(dev))
 				intr = 1;
 		}
 		break;
 	case VIRTIO_CONFIG_DEVICE_STATUS:
-		vioblk->cfg.device_status = data;
-		if (vioblk->cfg.device_status == 0) {
-			vioblk->cfg.guest_feature = 0;
-			vioblk->cfg.queue_pfn = 0;
-			vioblk_update_qa(vioblk);
-			vioblk->cfg.queue_size = 0;
-			vioblk_update_qs(vioblk);
-			vioblk->cfg.queue_select = 0;
-			vioblk->cfg.queue_notify = 0;
-			vioblk->cfg.isr_status = 0;
-			vioblk->vq[0].last_avail = 0;
-			vioblk->vq[0].notified_avail = 0;
+		dev->status = data;
+		if (dev->status == 0) {
+			dev->cfg.guest_feature = 0;
+			dev->cfg.queue_pfn = 0;
+			dev->cfg.queue_select = 0;
+			dev->cfg.queue_notify = 0;
+			dev->isr = 0;
+			virtio_vq_init(dev, 0);
 			virtio_deassert_irq(dev, msg->vcpu);
 		}
 		break;
@@ -750,29 +716,29 @@ handle_io_read(struct viodev_msg *msg, struct virtio_d
 		/* XXX handle invalid sz */
 		break;
 	case VIRTIO_CONFIG_DEVICE_FEATURES:
-		data = vioblk->cfg.device_feature;
+		data = dev->cfg.device_feature;
 		break;
 	case VIRTIO_CONFIG_GUEST_FEATURES:
-		data = vioblk->cfg.guest_feature;
+		data = dev->cfg.guest_feature;
 		break;
 	case VIRTIO_CONFIG_QUEUE_PFN:
-		data = vioblk->cfg.queue_pfn;
+		data = dev->cfg.queue_pfn;
 		break;
 	case VIRTIO_CONFIG_QUEUE_SIZE:
-		data = vioblk->cfg.queue_size;
+		data = dev->cfg.queue_size;
 		break;
 	case VIRTIO_CONFIG_QUEUE_SELECT:
-		data = vioblk->cfg.queue_select;
+		data = dev->cfg.queue_select;
 		break;
 	case VIRTIO_CONFIG_QUEUE_NOTIFY:
-		data = vioblk->cfg.queue_notify;
+		data = dev->cfg.queue_notify;
 		break;
 	case VIRTIO_CONFIG_DEVICE_STATUS:
-		data = vioblk->cfg.device_status;
+		data = dev->status;
 		break;
 	case VIRTIO_CONFIG_ISR_STATUS:
-		data = vioblk->cfg.isr_status;
-		vioblk->cfg.isr_status = 0;
+		data = dev->isr;
+		dev->isr = 0;
 		if (intr != NULL)
 			*intr = INTR_STATE_DEASSERT;
 		break;
@@ -791,8 +757,8 @@ handle_io_read(struct viodev_msg *msg, struct virtio_d
  * On error, returns -1 and descriptor (desc) remains at its current position.
  */
 static ssize_t
-vioblk_rw(struct vioblk_dev *dev, int is_write, off_t offset,
-    struct vring_desc *desc_tbl, struct vring_desc **desc)
+vioblk_rw(struct vioblk_dev *dev, struct virtio_vq_info *vq_info, int is_write,
+    off_t offset, struct vring_desc *desc_tbl, struct vring_desc **desc)
 {
 	struct iovec *iov = NULL;
 	ssize_t sz = 0;
@@ -830,7 +796,7 @@ vioblk_rw(struct vioblk_dev *dev, int is_write, off_t
 		}

 		/* Advance to the next descriptor. */
-		*desc = &desc_tbl[(*desc)->next & VIOBLK_QUEUE_MASK];
+		*desc = &desc_tbl[(*desc)->next & vq_info->mask];
 	} while ((*desc)->flags & VRING_DESC_F_NEXT);

 	/*
blob - b55efb57613e670d9160a3d063b77ab2d4c384c0
blob + a74a35df10b38785a26c03dea6c698008cfc1f83
--- usr.sbin/vmd/vionet.c
+++ usr.sbin/vmd/vionet.c
@@ -52,7 +52,7 @@ struct packet {

 static void *rx_run_loop(void *);
 static void *tx_run_loop(void *);
-static int vionet_rx(struct vionet_dev *, int);
+static int vionet_rx(struct virtio_dev *, int);
 static ssize_t vionet_rx_copy(struct vionet_dev *, int, const struct iovec *,
     int, size_t);
 static ssize_t vionet_rx_zerocopy(struct vionet_dev *, int,
@@ -85,8 +85,8 @@ struct vm_dev_pipe pipe_tx;
 int pipe_inject[2];
 #define READ	0
 #define WRITE	1
-struct iovec iov_rx[VIONET_QUEUE_SIZE];
-struct iovec iov_tx[VIONET_QUEUE_SIZE];
+struct iovec iov_rx[VIRTIO_QUEUE_SIZE_MAX];
+struct iovec iov_tx[VIRTIO_QUEUE_SIZE_MAX];
 pthread_rwlock_t lock = NULL;		/* Guards device config state. */
 int resetting = 0;	/* Transient reset state used to coordinate reset. */
 int rx_enabled = 0;	/* 1: we expect to read the tap, 0: wait for notify. */
@@ -287,56 +287,6 @@ fail:
 }

 /*
- * Update the gpa and hva of the virtqueue.
- */
-static void
-vionet_update_qa(struct vionet_dev *dev)
-{
-	struct virtio_vq_info *vq_info;
-	void *hva = NULL;
-
-	/* Invalid queue? */
-	if (dev->cfg.queue_select > 1)
-		return;
-
-	vq_info = &dev->vq[dev->cfg.queue_select];
-	vq_info->q_gpa = (uint64_t)dev->cfg.queue_pfn * VIRTIO_PAGE_SIZE;
-	dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
-
-	if (vq_info->q_gpa == 0)
-		vq_info->q_hva = NULL;
-
-	hva = hvaddr_mem(vq_info->q_gpa, vring_size(VIONET_QUEUE_SIZE));
-	if (hva == NULL)
-		fatalx("%s: hva == NULL", __func__);
-
-	vq_info->q_hva = hva;
-}
-
-/*
- * Update the queue size.
- */
-static void
-vionet_update_qs(struct vionet_dev *dev)
-{
-	struct virtio_vq_info *vq_info;
-
-	/* Invalid queue? */
-	if (dev->cfg.queue_select > 1) {
-		log_warnx("%s: !!! invalid queue selector %d", __func__,
-		    dev->cfg.queue_select);
-		dev->cfg.queue_size = 0;
-		return;
-	}
-
-	vq_info = &dev->vq[dev->cfg.queue_select];
-
-	/* Update queue pfn/size based on queue select */
-	dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
-	dev->cfg.queue_size = vq_info->qs;
-}
-
-/*
  * vionet_rx
  *
  * Pull packet from the provided fd and fill the receive-side virtqueue. We
@@ -346,11 +296,12 @@ vionet_update_qs(struct vionet_dev *dev)
  * or 0 if no notification is needed.
  */
 static int
-vionet_rx(struct vionet_dev *dev, int fd)
+vionet_rx(struct virtio_dev *dev, int fd)
 {
 	uint16_t idx, hdr_idx;
 	char *vr = NULL;
 	size_t chain_len = 0, iov_cnt;
+	struct vionet_dev *vionet = &dev->vionet;
 	struct vring_desc *desc, *table;
 	struct vring_avail *avail;
 	struct vring_used *used;
@@ -360,7 +311,7 @@ vionet_rx(struct vionet_dev *dev, int fd)
 	ssize_t sz;
 	uint8_t status = 0;

-	status = dev->cfg.device_status & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK;
+	status = dev->status & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK;
 	if (status != VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) {
 		log_warnx("%s: driver not ready", __func__);
 		return (0);
@@ -379,8 +330,8 @@ vionet_rx(struct vionet_dev *dev, int fd)
 	used->flags |= VRING_USED_F_NO_NOTIFY;

 	while (idx != avail->idx) {
-		hdr_idx = avail->ring[idx & VIONET_QUEUE_MASK];
-		desc = &table[hdr_idx & VIONET_QUEUE_MASK];
+		hdr_idx = avail->ring[idx & vq_info->mask];
+		desc = &table[hdr_idx & vq_info->mask];
 		if (!DESC_WRITABLE(desc)) {
 			log_warnx("%s: invalid descriptor state", __func__);
 			goto reset;
@@ -422,7 +373,7 @@ vionet_rx(struct vionet_dev *dev, int fd)
 		 * and lengths.
 		 */
 		while (desc->flags & VRING_DESC_F_NEXT) {
-			desc = &table[desc->next & VIONET_QUEUE_MASK];
+			desc = &table[desc->next & vq_info->mask];
 			if (!DESC_WRITABLE(desc)) {
 				log_warnx("%s: invalid descriptor state",
 				    __func__);
@@ -456,11 +407,11 @@ vionet_rx(struct vionet_dev *dev, int fd)
 		 * If we're enforcing hardware address or handling an injected
 		 * packet, we need to use a copy-based approach.
 		 */
-		if (dev->lockedmac || fd != dev->data_fd)
-			sz = vionet_rx_copy(dev, fd, iov_rx, iov_cnt,
+		if (vionet->lockedmac || fd != vionet->data_fd)
+			sz = vionet_rx_copy(vionet, fd, iov_rx, iov_cnt,
 			    chain_len);
 		else
-			sz = vionet_rx_zerocopy(dev, fd, iov_rx, iov_cnt);
+			sz = vionet_rx_zerocopy(vionet, fd, iov_rx, iov_cnt);
 		if (sz == -1)
 			goto reset;
 		if (sz == 0)	/* No packets, so bail out for now. */
@@ -473,8 +424,8 @@ vionet_rx(struct vionet_dev *dev, int fd)
 		sz += sizeof(struct virtio_net_hdr);

 		/* Mark our buffers as used. */
-		used->ring[used->idx & VIONET_QUEUE_MASK].id = hdr_idx;
-		used->ring[used->idx & VIONET_QUEUE_MASK].len = sz;
+		used->ring[used->idx & vq_info->mask].id = hdr_idx;
+		used->ring[used->idx & vq_info->mask].len = sz;
 		__sync_synchronize();
 		used->idx++;
 		idx++;
@@ -630,14 +581,13 @@ static void
 vionet_rx_event(int fd, short event, void *arg)
 {
 	struct virtio_dev	*dev = (struct virtio_dev *)arg;
-	struct vionet_dev	*vionet = &dev->vionet;
 	int			 ret = 0;

 	if (!(event & EV_READ))
 		fatalx("%s: invalid event type", __func__);

 	pthread_rwlock_rdlock(&lock);
-	ret = vionet_rx(vionet, fd);
+	ret = vionet_rx(dev, fd);
 	pthread_rwlock_unlock(&lock);

 	if (ret == 0) {
@@ -648,12 +598,12 @@ vionet_rx_event(int fd, short event, void *arg)
 	pthread_rwlock_wrlock(&lock);
 	if (ret == 1) {
 		/* Notify the driver. */
-		vionet->cfg.isr_status |= 1;
+		dev->isr |= 1;
 	} else {
 		/* Need a reset. Something went wrong. */
 		log_warnx("%s: requesting device reset", __func__);
-		vionet->cfg.device_status |= DEVICE_NEEDS_RESET;
-		vionet->cfg.isr_status |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
+		dev->status |= DEVICE_NEEDS_RESET;
+		dev->isr |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
 	}
 	pthread_rwlock_unlock(&lock);

@@ -663,9 +613,7 @@ vionet_rx_event(int fd, short event, void *arg)
 static void
 vionet_notifyq(struct virtio_dev *dev)
 {
-	struct vionet_dev	*vionet = &dev->vionet;
-
-	switch (vionet->cfg.queue_notify) {
+	switch (dev->cfg.queue_notify) {
 	case RXQ:
 		rx_enabled = 1;
 		vm_pipe_send(&pipe_rx, VIRTIO_NOTIFY);
@@ -679,7 +627,7 @@ vionet_notifyq(struct virtio_dev *dev)
 		 * well as any bogus queue IDs.
 		 */
 		log_debug("%s: notify for unimplemented queue ID %d",
-		    __func__, vionet->cfg.queue_notify);
+		    __func__, dev->cfg.queue_notify);
 		break;
 	}
 }
@@ -702,14 +650,13 @@ vionet_tx(struct virtio_dev *dev)
 	struct packet pkt;
 	uint8_t status = 0;

-	status = vionet->cfg.device_status
-	    & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK;
+	status = dev->status & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK;
 	if (status != VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) {
 		log_warnx("%s: driver not ready", __func__);
 		return (0);
 	}

-	vq_info = &vionet->vq[TXQ];
+	vq_info = &dev->vq[TXQ];
 	idx = vq_info->last_avail;
 	vr = vq_info->q_hva;
 	if (vr == NULL)
@@ -721,8 +668,8 @@ vionet_tx(struct virtio_dev *dev)
 	used = (struct vring_used *)(vr + vq_info->vq_usedoffset);

 	while (idx != avail->idx) {
-		hdr_idx = avail->ring[idx & VIONET_QUEUE_MASK];
-		desc = &table[hdr_idx & VIONET_QUEUE_MASK];
+		hdr_idx = avail->ring[idx & vq_info->mask];
+		desc = &table[hdr_idx & vq_info->mask];
 		if (DESC_WRITABLE(desc)) {
 			log_warnx("%s: invalid descriptor state", __func__);
 			goto reset;
@@ -756,7 +703,7 @@ vionet_tx(struct virtio_dev *dev)
 		 * Walk the chain and collect remaining addresses and lengths.
 		 */
 		while (desc->flags & VRING_DESC_F_NEXT) {
-			desc = &table[desc->next & VIONET_QUEUE_MASK];
+			desc = &table[desc->next & vq_info->mask];
 			if (DESC_WRITABLE(desc)) {
 				log_warnx("%s: invalid descriptor state",
 				    __func__);
@@ -826,8 +773,8 @@ vionet_tx(struct virtio_dev *dev)
 		}
 		chain_len += sizeof(struct virtio_net_hdr);
 drop:
-		used->ring[used->idx & VIONET_QUEUE_MASK].id = hdr_idx;
-		used->ring[used->idx & VIONET_QUEUE_MASK].len = chain_len;
+		used->ring[used->idx & vq_info->mask].id = hdr_idx;
+		used->ring[used->idx & vq_info->mask].len = chain_len;
 		__sync_synchronize();
 		used->idx++;
 		idx++;
@@ -1013,12 +960,12 @@ handle_sync_io(int fd, short event, void *arg)
 static void
 handle_io_write(struct viodev_msg *msg, struct virtio_dev *dev)
 {
-	struct vionet_dev	*vionet = &dev->vionet;
 	uint32_t		 data = msg->data;
 	int			 pause_devices = 0;

+	DPRINTF("%s: write reg=%d data=0x%x", __func__, msg->reg, data);
+
 	pthread_rwlock_wrlock(&lock);
-
 	switch (msg->reg) {
 	case VIRTIO_CONFIG_DEVICE_FEATURES:
 	case VIRTIO_CONFIG_QUEUE_SIZE:
@@ -1027,18 +974,18 @@ handle_io_write(struct viodev_msg *msg, struct virtio_
 		    virtio_reg_name(msg->reg));
 		break;
 	case VIRTIO_CONFIG_GUEST_FEATURES:
-		vionet->cfg.guest_feature = data;
+		dev->cfg.guest_feature = data;
 		break;
 	case VIRTIO_CONFIG_QUEUE_PFN:
-		vionet->cfg.queue_pfn = data;
-		vionet_update_qa(vionet);
+		dev->cfg.queue_pfn = data;
+		virtio_update_qa(dev);
 		break;
 	case VIRTIO_CONFIG_QUEUE_SELECT:
-		vionet->cfg.queue_select = data;
-		vionet_update_qs(vionet);
+		dev->cfg.queue_select = data;
+		virtio_update_qs(dev);
 		break;
 	case VIRTIO_CONFIG_QUEUE_NOTIFY:
-		vionet->cfg.queue_notify = data;
+		dev->cfg.queue_notify = data;
 		vionet_notifyq(dev);
 		break;
 	case VIRTIO_CONFIG_DEVICE_STATUS:
@@ -1047,12 +994,12 @@ handle_io_write(struct viodev_msg *msg, struct virtio_
 			pause_devices = 1;
 		} else {
 			// XXX is this correct?
-			vionet->cfg.device_status = data;
+			dev->status = data;
 		}
 		break;
 	}
-
 	pthread_rwlock_unlock(&lock);
+
 	if (pause_devices) {
 		rx_enabled = 0;
 		vionet_deassert_pic_irq(dev);
@@ -1065,10 +1012,11 @@ static uint32_t
 handle_io_read(struct viodev_msg *msg, struct virtio_dev *dev, int8_t *intr)
 {
 	struct vionet_dev *vionet = &dev->vionet;
-	uint32_t data;
+	uint32_t data = -1;

+	DPRINTF("%s: read reg=%d data=%u", __func__, msg->reg, data);
+
 	pthread_rwlock_rdlock(&lock);
-
 	switch (msg->reg) {
 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 1:
@@ -1080,39 +1028,39 @@ handle_io_read(struct viodev_msg *msg, struct virtio_d
 		    VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI];
 		break;
 	case VIRTIO_CONFIG_DEVICE_FEATURES:
-		data = vionet->cfg.device_feature;
+		data = dev->cfg.device_feature;
 		break;
 	case VIRTIO_CONFIG_GUEST_FEATURES:
-		data = vionet->cfg.guest_feature;
+		data = dev->cfg.guest_feature;
 		break;
 	case VIRTIO_CONFIG_QUEUE_PFN:
-		data = vionet->cfg.queue_pfn;
+		data = dev->cfg.queue_pfn;
 		break;
 	case VIRTIO_CONFIG_QUEUE_SIZE:
-		data = vionet->cfg.queue_size;
+		data = dev->cfg.queue_size;
 		break;
 	case VIRTIO_CONFIG_QUEUE_SELECT:
-		data = vionet->cfg.queue_select;
+		data = dev->cfg.queue_select;
 		break;
 	case VIRTIO_CONFIG_QUEUE_NOTIFY:
-		data = vionet->cfg.queue_notify;
+		data = dev->cfg.queue_notify;
 		break;
 	case VIRTIO_CONFIG_DEVICE_STATUS:
-		data = vionet->cfg.device_status;
+		data = dev->status;
 		break;
 	case VIRTIO_CONFIG_ISR_STATUS:
 		pthread_rwlock_unlock(&lock);
 		pthread_rwlock_wrlock(&lock);
-		data = vionet->cfg.isr_status;
-		vionet->cfg.isr_status = 0;
+		data = dev->isr;
+		dev->isr = 0;
 		if (intr != NULL)
 			*intr = INTR_STATE_DEASSERT;
 		break;
 	default:
 		data = 0xFFFFFFFF;
 	}
-
 	pthread_rwlock_unlock(&lock);
+
 	return (data);
 }

@@ -1220,7 +1168,6 @@ static void
 read_pipe_tx(int fd, short event, void *arg)
 {
 	struct virtio_dev	*dev = (struct virtio_dev*)arg;
-	struct vionet_dev	*vionet = &dev->vionet;
 	enum pipe_msg_type	 msg;
 	int			 ret = 0;

@@ -1260,12 +1207,12 @@ read_pipe_tx(int fd, short event, void *arg)
 	pthread_rwlock_wrlock(&lock);
 	if (ret == 1) {
 		/* Notify the driver. */
-		vionet->cfg.isr_status |= 1;
+		dev->isr |= 1;
 	} else {
 		/* Need a reset. Something went wrong. */
 		log_warnx("%s: requesting device reset", __func__);
-		vionet->cfg.device_status |= DEVICE_NEEDS_RESET;
-		vionet->cfg.isr_status |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
+		dev->status |= DEVICE_NEEDS_RESET;
+		dev->isr |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
 	}
 	pthread_rwlock_unlock(&lock);

@@ -1295,21 +1242,15 @@ read_pipe_main(int fd, short event, void *arg)
 		if (resetting == 0) {
 			log_debug("%s: resetting virtio network device %d",
 			    __func__, vionet->idx);
-
 			pthread_rwlock_wrlock(&lock);
-			vionet->cfg.device_status = 0;
-			vionet->cfg.guest_feature = 0;
-			vionet->cfg.queue_pfn = 0;
-			vionet_update_qa(vionet);
-			vionet->cfg.queue_size = 0;
-			vionet_update_qs(vionet);
-			vionet->cfg.queue_select = 0;
-			vionet->cfg.queue_notify = 0;
-			vionet->cfg.isr_status = 0;
-			vionet->vq[RXQ].last_avail = 0;
-			vionet->vq[RXQ].notified_avail = 0;
-			vionet->vq[TXQ].last_avail = 0;
-			vionet->vq[TXQ].notified_avail = 0;
+			dev->status = 0;
+			dev->cfg.guest_feature = 0;
+			dev->cfg.queue_pfn = 0;
+			dev->cfg.queue_select = 0;
+			dev->cfg.queue_notify = 0;
+			dev->isr = 0;
+			virtio_vq_init(dev, TXQ);
+			virtio_vq_init(dev, RXQ);
 			pthread_rwlock_unlock(&lock);
 		}
 		break;
blob - 95bc1f46dee702c630f98249195eacd45443b9b5
blob + 5bf14df65ddb9d92543a0b268dce874cdf13aa1c
--- usr.sbin/vmd/vioscsi.c
+++ usr.sbin/vmd/vioscsi.c
@@ -32,6 +32,16 @@
 #include "vioscsi.h"
 #include "virtio.h"

+#define VIOSCSI_DEBUG	0
+#ifdef DPRINTF
+#undef DPRINTF
+#endif
+#if VIOSCSI_DEBUG
+#define DPRINTF		log_debug
+#else
+#define DPRINTF(x...)	do {} while(0)
+#endif	/* VIOSCSI_DEBUG */
+
 extern char *__progname;

 static void
@@ -66,24 +76,24 @@ vioscsi_prepare_resp(struct virtio_scsi_res_hdr *resp,
 }

 static struct vring_desc*
-vioscsi_next_ring_desc(struct vring_desc* desc, struct vring_desc* cur,
-    uint16_t *idx)
+vioscsi_next_ring_desc(struct virtio_vq_info *vq_info, struct vring_desc* desc,
+    struct vring_desc* cur, uint16_t *idx)
 {
-	*idx = cur->next & VIOSCSI_QUEUE_MASK;
+	*idx = cur->next & vq_info->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)
+vioscsi_next_ring_item(struct virtio_vq_info *vq_info,
+    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->ring[used->idx & vq_info->mask].id = idx;
+	used->ring[used->idx & vq_info->mask].len = desc->len;
 	__sync_synchronize();
 	used->idx++;

-	dev->vq[dev->cfg.queue_notify].last_avail =
-	    avail->idx & VIOSCSI_QUEUE_MASK;
+	vq_info->last_avail = avail->idx & vq_info->mask;
 }

 static const char *
@@ -150,31 +160,25 @@ vioscsi_op_names(uint8_t type)
 	}
 }

+#if VIOSCSI_DEBUG
 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_PFN: return "queue pfn";
-	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";
+	case VIRTIO_SCSI_CONFIG_NUM_QUEUES: return "NUM_QUEUES";
+	case VIRTIO_SCSI_CONFIG_SEG_MAX: return "SEG_MAX";
+	case VIRTIO_SCSI_CONFIG_MAX_SECTORS: return "MAX_SECTORS";
+	case VIRTIO_SCSI_CONFIG_CMD_PER_LUN: return "CMD_PER_LUN";
+	case VIRTIO_SCSI_CONFIG_EVENT_INFO_SIZE: return "EVENT_INFO_SIZE";
+	case VIRTIO_SCSI_CONFIG_SENSE_SIZE: return "SENSE_SIZE";
+	case VIRTIO_SCSI_CONFIG_CDB_SIZE: return "CDB_SIZE";
+	case VIRTIO_SCSI_CONFIG_MAX_CHANNEL: return "MAX_CHANNEL";
+	case VIRTIO_SCSI_CONFIG_MAX_TARGET: return "MAX_TARGET";
+	case VIRTIO_SCSI_CONFIG_MAX_LUN: return "MAX_LUN";
 	default: return "unknown";
 	}
 }
+#endif	/* VIOSCSI_DEBUG */

 static void
 vioscsi_free_info(struct ioinfo *info)
@@ -186,7 +190,7 @@ vioscsi_free_info(struct ioinfo *info)
 }

 static struct ioinfo *
-vioscsi_start_read(struct vioscsi_dev *dev, off_t block, size_t n_blocks)
+vioscsi_start_read(struct virtio_dev *dev, off_t block, size_t n_blocks)
 {
 	struct ioinfo *info;

@@ -214,10 +218,16 @@ nomem:
 }

 static const uint8_t *
-vioscsi_finish_read(struct vioscsi_dev *dev, struct ioinfo *info)
+vioscsi_finish_read(struct virtio_dev *dev, struct ioinfo *info)
 {
-	struct virtio_backing *f = &dev->file;
+	struct virtio_backing *f = NULL;
+	struct vioscsi_dev *vioscsi = NULL;

+	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
+		fatalx("%s: virtio device is not a scsi device", __func__);
+	vioscsi = &dev->vioscsi;
+
+	f = &vioscsi->file;
 	if (f->pread(f->p, info->buf, info->len, info->offset) != info->len) {
 		log_warn("vioscsi read error");
 		return NULL;
@@ -227,16 +237,16 @@ vioscsi_finish_read(struct vioscsi_dev *dev, struct io
 }

 static int
-vioscsi_handle_tur(struct vioscsi_dev *dev, struct virtio_scsi_req_hdr *req,
-    struct virtio_vq_acct *acct)
+vioscsi_handle_tur(struct virtio_dev *dev, struct virtio_vq_info *vq_info,
+    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;

 	memset(&resp, 0, sizeof(resp));
 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->req_desc, &(acct->resp_idx));

 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);

@@ -245,9 +255,9 @@ vioscsi_handle_tur(struct vioscsi_dev *dev, struct vir
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -255,7 +265,7 @@ vioscsi_handle_tur(struct vioscsi_dev *dev, struct vir
 }

 static int
-vioscsi_handle_inquiry(struct vioscsi_dev *dev,
+vioscsi_handle_inquiry(struct virtio_dev *dev, struct virtio_vq_info *vq_info,
     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
 {
 	int ret = 0;
@@ -289,8 +299,8 @@ vioscsi_handle_inquiry(struct vioscsi_dev *dev,
 	memcpy(inq_data->revision, INQUIRY_REVISION, INQUIRY_REVISION_LEN);

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->req_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
 	    "idx %d req_idx %d global_idx %d", __func__, acct->resp_desc->addr,
@@ -303,8 +313,8 @@ vioscsi_handle_inquiry(struct vioscsi_dev *dev,
 	}

 	/* Move index for inquiry_data */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->resp_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing inq_data to 0x%llx size %d at "
 	    "local idx %d req_idx %d global_idx %d",
@@ -318,9 +328,9 @@ vioscsi_handle_inquiry(struct vioscsi_dev *dev,
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -331,8 +341,9 @@ inq_out:
 }

 static int
-vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_mode_sense(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
@@ -400,7 +411,7 @@ vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
 		}

 		/* Move index for response */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		DPRINTF("%s: writing resp to 0x%llx size %d "
@@ -417,7 +428,7 @@ vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
 		}

 		/* Move index for mode_reply */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->resp_desc, &(acct->resp_idx));

 		DPRINTF("%s: writing mode_reply to 0x%llx "
@@ -437,9 +448,9 @@ vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
 		free(mode_reply);

 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	} else {
 mode_sense_error:
@@ -449,7 +460,7 @@ mode_sense_error:
 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);

 		/* Move index for response */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -459,9 +470,9 @@ mode_sense_error:
 		}

 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}
 mode_sense_out:
@@ -469,8 +480,9 @@ mode_sense_out:
 }

 static int
-vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_mode_sense_big(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
@@ -538,7 +550,7 @@ vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
 		}

 		/* Move index for response */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		DPRINTF("%s: writing resp to 0x%llx size %d "
@@ -555,7 +567,7 @@ vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
 		}

 		/* Move index for mode_reply */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->resp_desc, &(acct->resp_idx));

 		DPRINTF("%s: writing mode_reply to 0x%llx "
@@ -575,9 +587,9 @@ vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
 		free(mode_reply);

 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	} else {
 mode_sense_big_error:
@@ -587,7 +599,7 @@ mode_sense_big_error:
 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);

 		/* Move index for response */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -597,9 +609,9 @@ mode_sense_big_error:
 		}

 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}
 mode_sense_big_out:
@@ -607,13 +619,19 @@ mode_sense_big_out:
 }

 static int
-vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_read_capacity(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info,struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
 	struct scsi_read_cap_data *r_cap_data;
+	struct vioscsi_dev *vioscsi = NULL;

+	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
+		fatalx("%s: virtio device is not a scsi device", __func__);
+	vioscsi = &dev->vioscsi;
+
 #if DEBUG
 	struct scsi_read_capacity *r_cap =
 	    (struct scsi_read_capacity *)(req->cdb);
@@ -633,7 +651,7 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
 	}

 	DPRINTF("%s: ISO has %lld bytes and %lld blocks",
-	    __func__, dev->sz, dev->n_blocks);
+	    __func__, vioscsi->sz, vioscsi->n_blocks);

 	/*
 	 * determine if num blocks of iso image > UINT32_MAX
@@ -641,20 +659,20 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
 	 * indicating to hosts that READ_CAPACITY_16 should
 	 * be called to retrieve the full size
 	 */
-	if (dev->n_blocks >= UINT32_MAX) {
+	if (vioscsi->n_blocks >= 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);
+		    __func__, vioscsi->sz, UINT32_MAX);
 	} else {
-		_lto4b(dev->n_blocks - 1, r_cap_data->addr);
+		_lto4b(vioscsi->n_blocks - 1, r_cap_data->addr);
 		_lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data->length);
 	}

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->req_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
 	    "idx %d req_idx %d global_idx %d",
@@ -668,8 +686,8 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
 	}

 	/* Move index for r_cap_data */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->resp_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing r_cap_data to 0x%llx size %d at "
 	    "local idx %d req_idx %d global_idx %d",
@@ -683,9 +701,9 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -696,13 +714,19 @@ read_capacity_out:
 }

 static int
-vioscsi_handle_read_capacity_16(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_read_capacity_16(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
 	struct scsi_read_cap_data_16 *r_cap_data_16;
+	struct vioscsi_dev *vioscsi = NULL;

+	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
+		fatalx("%s: virtio device is not a scsi device", __func__);
+	vioscsi = &dev->vioscsi;
+
 #if DEBUG
 	struct scsi_read_capacity_16 *r_cap_16 =
 	    (struct scsi_read_capacity_16 *)(req->cdb);
@@ -722,14 +746,14 @@ vioscsi_handle_read_capacity_16(struct vioscsi_dev *de
 	}

 	DPRINTF("%s: ISO has %lld bytes and %lld blocks", __func__,
-	    dev->sz, dev->n_blocks);
+	    dev->vioscsi.sz, dev->vioscsi.n_blocks);

-	_lto8b(dev->n_blocks - 1, r_cap_data_16->addr);
+	_lto8b(vioscsi->n_blocks - 1, r_cap_data_16->addr);
 	_lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data_16->length);

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->req_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
 	    "idx %d req_idx %d global_idx %d",
@@ -743,8 +767,8 @@ vioscsi_handle_read_capacity_16(struct vioscsi_dev *de
 	}

 	/* Move index for r_cap_data_16 */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->resp_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing r_cap_data_16 to 0x%llx size %d "
 	    "at local idx %d req_idx %d global_idx %d",
@@ -758,9 +782,9 @@ vioscsi_handle_read_capacity_16(struct vioscsi_dev *de
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -771,8 +795,9 @@ read_capacity_16_out:
 }

 static int
-vioscsi_handle_report_luns(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_report_luns(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
@@ -796,7 +821,7 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);

 		/* Move index for response */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -805,9 +830,9 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
 			    acct->resp_desc->addr);
 		} else {
 			ret = 1;
-			dev->cfg.isr_status = 1;
+			dev->isr = 1;
 			/* Move ring indexes */
-			vioscsi_next_ring_item(dev, acct->avail, acct->used,
+			vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 			    acct->req_desc, acct->req_idx);
 		}
 		goto rpl_out;
@@ -828,8 +853,8 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
 	    VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->req_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
 	    "idx %d req_idx %d global_idx %d", __func__, acct->resp_desc->addr,
@@ -842,8 +867,8 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
 	}

 	/* Move index for reply_rpl */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->resp_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing reply_rpl to 0x%llx size %d at "
 	    "local idx %d req_idx %d global_idx %d",
@@ -857,9 +882,9 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -870,8 +895,9 @@ rpl_out:
 }

 static int
-vioscsi_handle_read_6(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_read_6(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
@@ -879,26 +905,32 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
 	uint32_t read_lba;
 	struct ioinfo *info;
 	struct scsi_rw *read_6;
+	struct vioscsi_dev *vioscsi = NULL;

+	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
+		fatalx("%s: virtio device is not a scsi device", __func__);
+	vioscsi = &dev->vioscsi;
+
 	memset(&resp, 0, sizeof(resp));
 	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];

 	DPRINTF("%s: READ Addr 0x%08x Len %d (%d)",
-	    __func__, read_lba, read_6->length, read_6->length * dev->max_xfer);
+	    __func__, read_lba, read_6->length,
+	    read_6->length * dev->vioscsi.max_xfer);

 	/* check if lba is in range */
-	if (read_lba > dev->n_blocks - 1) {
+	if (read_lba > vioscsi->n_blocks - 1) {
 		DPRINTF("%s: requested block out of range req: %ud max: %lld",
-		    __func__, read_lba, dev->n_blocks);
+		    __func__, read_lba, vioscsi->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 */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -907,9 +939,9 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
 			    acct->resp_desc->addr);
 		} else {
 			ret = 1;
-			dev->cfg.isr_status = 1;
+			dev->isr = 1;
 			/* Move ring indexes */
-			vioscsi_next_ring_item(dev, acct->avail, acct->used,
+			vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 			    acct->req_desc, acct->req_idx);
 		}
 		goto read_6_out;
@@ -933,7 +965,7 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
 		    SENSE_MEDIUM_NOT_PRESENT, SENSE_DEFAULT_ASCQ);

 		/* Move index for response */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -942,9 +974,9 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
 			    acct->resp_desc->addr);
 		} else {
 			ret = 1;
-			dev->cfg.isr_status = 1;
+			dev->isr = 1;
 			/* Move ring indexes */
-			vioscsi_next_ring_item(dev, acct->avail, acct->used,
+			vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 			    acct->req_desc, acct->req_idx);
 		}

@@ -954,8 +986,8 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->req_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
 	    "idx %d req_idx %d global_idx %d",
@@ -969,8 +1001,8 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
 	}

 	/* Move index for read_buf */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->resp_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing read_buf to 0x%llx size %d at "
 	    "local idx %d req_idx %d global_idx %d",
@@ -982,9 +1014,9 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -995,8 +1027,9 @@ read_6_out:
 }

 static int
-vioscsi_handle_read_10(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_read_10(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
@@ -1007,7 +1040,12 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
 	struct ioinfo *info;
 	struct scsi_rw_10 *read_10;
 	size_t chunk_len = 0;
+	struct vioscsi_dev *vioscsi = NULL;

+	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
+		fatalx("%s: virtio device is not a scsi device", __func__);
+	vioscsi = &dev->vioscsi;
+
 	memset(&resp, 0, sizeof(resp));
 	read_10 = (struct scsi_rw_10 *)(req->cdb);
 	read_lba = _4btol(read_10->addr);
@@ -1015,19 +1053,19 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
 	chunk_offset = 0;

 	DPRINTF("%s: READ_10 Addr 0x%08x Len %d (%d)",
-	    __func__, read_lba, read_10_len, read_10_len * dev->max_xfer);
+	    __func__, read_lba, read_10_len, read_10_len * vioscsi->max_xfer);

 	/* check if lba is in range */
-	if (read_lba > dev->n_blocks - 1) {
+	if (read_lba > vioscsi->n_blocks - 1) {
 		DPRINTF("%s: requested block out of range req: %ud max: %lld",
-		    __func__, read_lba, dev->n_blocks);
+		    __func__, read_lba, vioscsi->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 */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -1035,9 +1073,9 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
 			    __func__, acct->resp_desc->addr);
 		} else {
 			ret = 1;
-			dev->cfg.isr_status = 1;
+			dev->isr = 1;
 			/* Move ring indexes */
-			vioscsi_next_ring_item(dev, acct->avail, acct->used,
+			vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 			    acct->req_desc, acct->req_idx);
 		}

@@ -1061,7 +1099,7 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
 		    SENSE_MEDIUM_NOT_PRESENT, SENSE_DEFAULT_ASCQ);

 		/* Move index for response */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -1069,9 +1107,9 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
 			    __func__, acct->resp_desc->addr);
 		} else {
 			ret = 1;
-			dev->cfg.isr_status = 1;
+			dev->isr = 1;
 			/* Move ring indexes */
-			vioscsi_next_ring_item(dev, acct->avail, acct->used,
+			vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 			    acct->req_desc, acct->req_idx);
 		}

@@ -1081,8 +1119,8 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->req_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
 	    "idx %d req_idx %d global_idx %d",
@@ -1103,7 +1141,7 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
 	 */
 	do {
 		/* Move index for read_buf */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->resp_desc, &(acct->resp_idx));

 		DPRINTF("%s: writing read_buf to 0x%llx size "
@@ -1130,9 +1168,9 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
 	} while (chunk_offset < info->len);

 	ret = 1;
-	dev->cfg.isr_status = 1;
+	dev->isr = 1;
 	/* Move ring indexes */
-	vioscsi_next_ring_item(dev, acct->avail, acct->used, acct->req_desc,
+	vioscsi_next_ring_item(vq_info, acct->avail, acct->used, acct->req_desc,
 	    acct->req_idx);

 free_read_10:
@@ -1142,35 +1180,41 @@ read_10_out:
 }

 static int
-vioscsi_handle_prevent_allow(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_prevent_allow(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
+	struct vioscsi_dev *vioscsi = NULL;
 	struct virtio_scsi_res_hdr resp;

+	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
+		fatalx("%s: virtio device is not a scsi device", __func__);
+	vioscsi = &dev->vioscsi;
+
 	memset(&resp, 0, sizeof(resp));
 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->req_desc, &(acct->resp_idx));

 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);

-	if (dev->locked) {
+	if (vioscsi->locked) {
 		DPRINTF("%s: unlocking medium", __func__);
 	} else {
 		DPRINTF("%s: locking medium", __func__);
 	}

-	dev->locked = dev->locked ? 0 : 1;
+	vioscsi->locked = vioscsi->locked ? 0 : 1;

 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -1178,8 +1222,9 @@ vioscsi_handle_prevent_allow(struct vioscsi_dev *dev,
 }

 static int
-vioscsi_handle_mechanism_status(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_mechanism_status(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
@@ -1200,7 +1245,7 @@ vioscsi_handle_mechanism_status(struct vioscsi_dev *de
 	    VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 	    acct->req_desc, &(acct->resp_idx));

 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -1210,8 +1255,8 @@ vioscsi_handle_mechanism_status(struct vioscsi_dev *de
 	}

 	/* Move index for mech_status_header */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->resp_desc, &(acct->resp_idx));

 	if (write_mem(acct->resp_desc->addr, mech_status_header,
 		sizeof(struct scsi_mechanism_status_header))) {
@@ -1221,9 +1266,9 @@ vioscsi_handle_mechanism_status(struct vioscsi_dev *de
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -1234,8 +1279,9 @@ mech_out:
 }

 static int
-vioscsi_handle_read_toc(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_read_toc(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
@@ -1243,7 +1289,12 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
 	uint8_t toc_data[TOC_DATA_SIZE];
 	uint8_t *toc_data_p;
 	struct scsi_read_toc *toc = (struct scsi_read_toc *)(req->cdb);
+	struct vioscsi_dev *vioscsi = NULL;

+	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
+		fatalx("%s: virtio device is not a scsi device", __func__);
+	vioscsi = &dev->vioscsi;
+
 	DPRINTF("%s: %s - MSF %d Track 0x%02x Addr 0x%04x",
 	    __func__, vioscsi_op_names(toc->opcode), ((toc->byte2 >> 1) & 1),
 	    toc->from_track, _2btol(toc->data_len));
@@ -1261,7 +1312,7 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);

 		/* Move index for response */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -1271,9 +1322,9 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
 		}

 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);

 		goto read_toc_out;
@@ -1322,7 +1373,7 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
 	*toc_data_p++ = READ_TOC_LEAD_OUT_TRACK;
 	*toc_data_p++ = 0x0;

-	_lto4b((uint32_t)dev->n_blocks, toc_data_p);
+	_lto4b((uint32_t)vioscsi->n_blocks, toc_data_p);
 	toc_data_p += 4;

 	toc_data_len = toc_data_p - toc_data;
@@ -1332,8 +1383,8 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->req_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
 	    "idx %d req_idx %d global_idx %d",
@@ -1347,8 +1398,8 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
 	}

 	/* Move index for toc descriptor */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->resp_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing toc_data to 0x%llx size %d at "
 	    "local idx %d req_idx %d global_idx %d",
@@ -1360,9 +1411,9 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -1371,8 +1422,9 @@ read_toc_out:
 }

 static int
-vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_read_disc_info(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
@@ -1387,7 +1439,7 @@ vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
 	    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 	    acct->req_desc, &(acct->resp_idx));

 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -1395,9 +1447,9 @@ vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -1405,8 +1457,9 @@ vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
 }

 static int
-vioscsi_handle_gesn(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_gesn(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
@@ -1414,7 +1467,12 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
 	struct scsi_gesn *gesn;
 	struct scsi_gesn_event_header *gesn_event_header;
 	struct scsi_gesn_power_event *gesn_power_event;
+	struct vioscsi_dev *vioscsi = NULL;

+	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
+		fatalx("%s: virtio device is not a scsi device", __func__);
+	vioscsi = &dev->vioscsi;
+
 	memset(&resp, 0, sizeof(resp));
 	gesn = (struct scsi_gesn *)(req->cdb);
 	DPRINTF("%s: GESN Method %s", __func__,
@@ -1427,7 +1485,7 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);

 		/* Move index for response */
-		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+		acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 		    acct->req_desc, &(acct->resp_idx));

 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
@@ -1437,9 +1495,9 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
 		}

 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);

 		goto gesn_out;
@@ -1454,14 +1512,14 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,

 	/* set event descriptor */
 	gesn_power_event->event_code = GESN_CODE_NOCHG;
-	if (dev->locked)
+	if (vioscsi->locked)
 		gesn_power_event->status = GESN_STATUS_ACTIVE;
 	else
 		gesn_power_event->status = GESN_STATUS_IDLE;

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->req_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
 	    "idx %d req_idx %d global_idx %d",
@@ -1475,8 +1533,8 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
 	}

 	/* Move index for gesn_reply */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->resp_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing gesn_reply to 0x%llx size %d at "
 	    "local idx %d req_idx %d global_idx %d",
@@ -1489,9 +1547,9 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -1500,8 +1558,9 @@ gesn_out:
 }

 static int
-vioscsi_handle_get_config(struct vioscsi_dev *dev,
-    struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
+vioscsi_handle_get_config(struct virtio_dev *dev,
+    struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
+    struct virtio_vq_acct *acct)
 {
 	int ret = 0;
 	struct virtio_scsi_res_hdr resp;
@@ -1513,6 +1572,7 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
 	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;
+	struct vioscsi_dev *vioscsi = NULL;

 #if DEBUG
 	struct scsi_get_configuration *get_configuration =
@@ -1522,6 +1582,10 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
 	    _2btol(get_configuration->length));
 #endif /* DEBUG */

+	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
+		fatalx("%s: virtio device is not a scsi device", __func__);
+	vioscsi = &dev->vioscsi;
+
 	get_conf_reply = (uint8_t*)calloc(G_CONFIG_REPLY_SIZE, sizeof(uint8_t));

 	if (get_conf_reply == NULL)
@@ -1592,10 +1656,11 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
 	    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->n_blocks >= UINT32_MAX)
+	if (vioscsi->n_blocks >= UINT32_MAX)
 		_lto4b(UINT32_MAX, config_random_read_desc->block_size);
 	else
-		_lto4b(dev->n_blocks - 1, config_random_read_desc->block_size);
+		_lto4b(vioscsi->n_blocks - 1,
+		    config_random_read_desc->block_size);
 	_lto2b(CONFIG_RANDOM_READ_BLOCKING_TYPE,
 	    config_random_read_desc->blocking_type);

@@ -1603,7 +1668,7 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);

 	/* Move index for response */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
 	    acct->req_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
@@ -1618,8 +1683,8 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
 	}

 	/* Move index for get_conf_reply */
-	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
-	    &(acct->resp_idx));
+	acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
+	    acct->resp_desc, &(acct->resp_idx));

 	DPRINTF("%s: writing get_conf_reply to 0x%llx size %d "
 	    "at local idx %d req_idx %d global_idx %d",
@@ -1633,9 +1698,9 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
 		    __func__, acct->resp_desc->addr);
 	} else {
 		ret = 1;
-		dev->cfg.isr_status = 1;
+		dev->isr = 1;
 		/* Move ring indexes */
-		vioscsi_next_ring_item(dev, acct->avail, acct->used,
+		vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
 		    acct->req_desc, acct->req_idx);
 	}

@@ -1649,421 +1714,111 @@ 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;
+	struct virtio_dev *dev = (struct virtio_dev *)cookie;
+	struct vioscsi_dev *vioscsi = NULL;

+	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
+		fatalx("%s: virtio device is not a scsi device", __func__);
+	vioscsi = &dev->vioscsi;
+
 	*intr = 0xFF;

-	DPRINTF("%s: request %s reg %u, %s sz %u", __func__,
-	    dir ? "READ" : "WRITE", reg, vioscsi_reg_name(reg), sz);
+	DPRINTF("%s: request %s reg %s sz %u", __func__,
+	    dir ? "READ" : "WRITE", vioscsi_reg_name(reg), sz);

-	if (dir == 0) {
+	if (dir == VEI_DIR_OUT) {
 		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));
+		case VIRTIO_SCSI_CONFIG_SENSE_SIZE:
+			/* XXX support writing to sense size register */
+			if (*data != VIOSCSI_SENSE_LEN)
+				log_warnx("%s: guest write to sense size "
+				    "register ignored", __func__);
 			break;
-		case VIRTIO_CONFIG_GUEST_FEATURES:
-			dev->cfg.guest_feature = *data;
-			DPRINTF("%s: guest feature set to %u",
-			    __func__, dev->cfg.guest_feature);
+		case VIRTIO_SCSI_CONFIG_CDB_SIZE:
+			/* XXX support writing CDB size. */
+			if (*data != VIOSCSI_CDB_LEN)
+				log_warnx("%s: guest write to cdb size "
+				    "register ignored", __func__);
 			break;
-		case VIRTIO_CONFIG_QUEUE_PFN:
-			dev->cfg.queue_pfn = *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;
-			DPRINTF("%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_pfn = 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:
+			log_warnx("%s: invalid register 0x%04x", __func__, reg);
 			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;
-			}
+		case VIRTIO_SCSI_CONFIG_NUM_QUEUES:
+			/* Number of request queues, not number of all queues */
+			if (sz == sizeof(uint32_t))
+				*data = (uint32_t)(VIRTIO_SCSI_QUEUES);
+			else
+				log_warnx("%s: unaligned read of num queues "
+				    "register", __func__);
 			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)
+		case VIRTIO_SCSI_CONFIG_SEG_MAX:
+			if (sz == sizeof(uint32_t))
 				*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;
-			}
+			else
+				log_warnx("%s: unaligned read of seg max "
+				    "register", __func__);
 			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;
-			}
+		case VIRTIO_SCSI_CONFIG_MAX_SECTORS:
+			if (sz == sizeof(uint32_t))
+				*data = (uint32_t)(vioscsi->max_xfer);
+			else
+				log_warnx("%s: unaligned read of max sectors "
+				    "register", __func__);
 			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)
+		case VIRTIO_SCSI_CONFIG_CMD_PER_LUN:
+			if (sz == sizeof(uint32_t))
 				*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;
-			}
+			else
+				log_warnx("%s: unaligned read of cmd per lun "
+				    "register", __func__);
 			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;
-			}
+		case VIRTIO_SCSI_CONFIG_EVENT_INFO_SIZE:
+			*data = 0;
 			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)
+		case VIRTIO_SCSI_CONFIG_SENSE_SIZE:
+			if (sz == sizeof(uint32_t))
 				*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;
-			}
+			else
+				log_warnx("%s: unaligned read of sense size "
+				    "register", __func__);
 			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)
+		case VIRTIO_SCSI_CONFIG_CDB_SIZE:
+			if (sz == sizeof(uint32_t))
 				*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;
-			}
+			else
+				log_warnx("%s: unaligned read of cdb size "
+				    "register", __func__);
 			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 */
-
+		case VIRTIO_SCSI_CONFIG_MAX_CHANNEL:
 			/* defined by standard to be zero */
-			*data &= 0xFFFF0000;
+			*data = 0;
 			break;
-		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 29:
-			/* defined by standard to be zero */
-			*data &= 0xFFFF0000;
+		case VIRTIO_SCSI_CONFIG_MAX_TARGET:
+			if (sz == sizeof(uint16_t))
+				*data = (uint32_t)(VIOSCSI_MAX_TARGET);
+			else
+				log_warnx("%s: unaligned read of max target "
+				    "register", __func__);
 			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)
+		case VIRTIO_SCSI_CONFIG_MAX_LUN:
+			if (sz == sizeof(uint32_t))
 				*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;
-			}
+			else
+				log_warnx("%s: unaligned read of max lun "
+				    "register", __func__);
 			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_PFN:
-			*data = dev->cfg.queue_pfn;
-			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;
+		default:
+			log_warnx("%s: invalid register 0x%04x", __func__, reg);
+			*data = (uint32_t)(-1);
 		}
 	}

-
 	return (0);
 }

-void
-vioscsi_update_qs(struct vioscsi_dev *dev)
-{
-	struct virtio_vq_info *vq_info;
-
-	/* Invalid queue? */
-	if (dev->cfg.queue_select >= VIRTIO_MAX_QUEUES) {
-		dev->cfg.queue_size = 0;
-		return;
-	}
-
-	vq_info = &dev->vq[dev->cfg.queue_select];
-
-	/* Update queue pfn/size based on queue select */
-	dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
-	dev->cfg.queue_size = vq_info->qs;
-}
-
-void
-vioscsi_update_qa(struct vioscsi_dev *dev)
-{
-	struct virtio_vq_info *vq_info;
-	void *hva = NULL;
-
-	/* Invalid queue? */
-	if (dev->cfg.queue_select >= VIRTIO_MAX_QUEUES)
-		return;
-
-	vq_info = &dev->vq[dev->cfg.queue_select];
-	vq_info->q_gpa = (uint64_t)dev->cfg.queue_pfn * VIRTIO_PAGE_SIZE;
-
-	hva = hvaddr_mem(vq_info->q_gpa, vring_size(VIOSCSI_QUEUE_SIZE));
-	if (hva == NULL)
-		fatal("vioscsi_update_qa");
-	vq_info->q_hva = hva;
-}
-
 /*
  * Process message(s) in the queue(s)
  * vioscsi driver will be placing the following in the queue for each iteration
@@ -2075,22 +1830,17 @@ vioscsi_update_qa(struct vioscsi_dev *dev)
  *        0 otherwise
  */
 int
-vioscsi_notifyq(struct vioscsi_dev *dev)
+vioscsi_notifyq(struct virtio_dev *dev, uint16_t vq_idx)
 {
-	int cnt, ret = 0;
+	size_t cnt;
+	int ret = 0;
 	char *vr;
 	struct virtio_scsi_req_hdr req;
 	struct virtio_scsi_res_hdr resp;
 	struct virtio_vq_acct acct;
 	struct virtio_vq_info *vq_info;

-	ret = 0;
-
-	/* Invalid queue? */
-	if (dev->cfg.queue_notify >= VIRTIO_MAX_QUEUES)
-		return (ret);
-
-	vq_info = &dev->vq[dev->cfg.queue_notify];
+	vq_info = &dev->vq[vq_idx];
 	vr = vq_info->q_hva;
 	if (vr == NULL)
 		fatalx("%s: null vring", __func__);
@@ -2100,23 +1850,23 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
 	acct.avail = (struct vring_avail *)(vr + vq_info->vq_availoffset);
 	acct.used = (struct vring_used *)(vr + vq_info->vq_usedoffset);

-	acct.idx = vq_info->last_avail & VIOSCSI_QUEUE_MASK;
+	acct.idx = vq_info->last_avail & vq_info->mask;

-	if ((acct.avail->idx & VIOSCSI_QUEUE_MASK) == acct.idx) {
+	if ((acct.avail->idx & vq_info->mask) == acct.idx) {
 		log_debug("%s - nothing to do?", __func__);
 		return (0);
 	}

 	cnt = 0;
-	while (acct.idx != (acct.avail->idx & VIOSCSI_QUEUE_MASK)) {
+	while (acct.idx != (acct.avail->idx & vq_info->mask)) {

 		/* Guard against infinite descriptor chains */
-		if (++cnt >= VIOSCSI_QUEUE_SIZE) {
+		if (++cnt >= vq_info->qs) {
 			log_warnx("%s: invalid descriptor table", __func__);
 			goto out;
 		}

-		acct.req_idx = acct.avail->ring[acct.idx] & VIOSCSI_QUEUE_MASK;
+		acct.req_idx = acct.avail->ring[acct.idx] & vq_info->mask;
 		acct.req_desc = &(acct.desc[acct.req_idx]);

 		/* Clear resp for next message */
@@ -2155,8 +1905,8 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
 			    __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 */
-			acct.resp_desc = vioscsi_next_ring_desc(acct.desc,
-			    acct.req_desc, &(acct.resp_idx));
+			acct.resp_desc = vioscsi_next_ring_desc(vq_info,
+			    acct.desc, acct.req_desc, &(acct.resp_idx));

 			vioscsi_prepare_resp(&resp,
 			    VIRTIO_SCSI_S_BAD_TARGET, SCSI_OK, 0, 0, 0);
@@ -2175,79 +1925,89 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
 			}

 			ret = 1;
-			dev->cfg.isr_status = 1;
+			dev->isr = 1;

 			/* Move ring indexes (updates the used ring index) */
-			vioscsi_next_ring_item(dev, acct.avail, acct.used,
+			vioscsi_next_ring_item(vq_info, acct.avail, acct.used,
 			    acct.req_desc, acct.req_idx);
 			goto next_msg;
 		}

 		DPRINTF("%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]));
+		    __func__, vq_idx, 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:
-			ret = vioscsi_handle_tur(dev, &req, &acct);
+			ret = vioscsi_handle_tur(dev, vq_info, &req, &acct);
 			break;
 		case PREVENT_ALLOW:
-			ret = vioscsi_handle_prevent_allow(dev, &req, &acct);
+			ret = vioscsi_handle_prevent_allow(dev, vq_info, &req,
+			    &acct);
 			break;
 		case READ_TOC:
-			ret = vioscsi_handle_read_toc(dev, &req, &acct);
+			ret = vioscsi_handle_read_toc(dev, vq_info, &req,
+			    &acct);
 			break;
 		case READ_CAPACITY:
-			ret = vioscsi_handle_read_capacity(dev, &req, &acct);
+			ret = vioscsi_handle_read_capacity(dev, vq_info, &req,
+			    &acct);
 			break;
 		case READ_CAPACITY_16:
-			ret = vioscsi_handle_read_capacity_16(dev, &req, &acct);
+			ret = vioscsi_handle_read_capacity_16(dev, vq_info,
+			    &req, &acct);
 			break;
 		case READ_COMMAND:
-			ret = vioscsi_handle_read_6(dev, &req, &acct);
+			ret = vioscsi_handle_read_6(dev, vq_info, &req, &acct);
 			break;
 		case READ_10:
-			ret = vioscsi_handle_read_10(dev, &req, &acct);
+			ret = vioscsi_handle_read_10(dev, vq_info, &req, &acct);
 			break;
 		case INQUIRY:
-			ret = vioscsi_handle_inquiry(dev, &req, &acct);
+			ret = vioscsi_handle_inquiry(dev, vq_info, &req, &acct);
 			break;
 		case MODE_SENSE:
-			ret = vioscsi_handle_mode_sense(dev, &req, &acct);
+			ret = vioscsi_handle_mode_sense(dev, vq_info, &req,
+			    &acct);
 			break;
 		case MODE_SENSE_BIG:
-			ret = vioscsi_handle_mode_sense_big(dev, &req, &acct);
+			ret = vioscsi_handle_mode_sense_big(dev, vq_info, &req,
+			    &acct);
 			break;
 		case GET_EVENT_STATUS_NOTIFICATION:
-			ret = vioscsi_handle_gesn(dev, &req, &acct);
+			ret = vioscsi_handle_gesn(dev, vq_info, &req, &acct);
 			break;
 		case READ_DISC_INFORMATION:
-			ret = vioscsi_handle_read_disc_info(dev, &req, &acct);
+			ret = vioscsi_handle_read_disc_info(dev, vq_info, &req,
+			    &acct);
 			break;
 		case GET_CONFIGURATION:
-			ret = vioscsi_handle_get_config(dev, &req, &acct);
+			ret = vioscsi_handle_get_config(dev, vq_info, &req,
+			    &acct);
 			break;
 		case MECHANISM_STATUS:
-			ret = vioscsi_handle_mechanism_status(dev, &req, &acct);
+			ret = vioscsi_handle_mechanism_status(dev, vq_info,
+			    &req, &acct);
 			break;
 		case REPORT_LUNS:
-			ret = vioscsi_handle_report_luns(dev, &req, &acct);
+			ret = vioscsi_handle_report_luns(dev, vq_info, &req,
+			    &acct);
 			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, acct.avail, acct.used,
+			vioscsi_next_ring_item(vq_info, acct.avail, acct.used,
 			    acct.req_desc, acct.req_idx);
 			break;
 		}
 next_msg:
 		/* Increment to the next queue slot */
-		acct.idx = (acct.idx + 1) & VIOSCSI_QUEUE_MASK;
+		acct.idx = (acct.idx + 1) & vq_info->mask;
 	}
 out:
 	return (ret);
blob - 561f287fcef819ccf99203916caf5c1e87960925
blob + 8a1bfe12a6e1192699916cc9199ff59c7187c611
--- usr.sbin/vmd/virtio.c
+++ usr.sbin/vmd/virtio.c
@@ -36,6 +36,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <stddef.h>

 #include "atomicio.h"
 #include "pci.h"
@@ -43,12 +44,22 @@
 #include "virtio.h"
 #include "vmd.h"

+#define VIRTIO_DEBUG	0
+#ifdef DPRINTF
+#undef DPRINTF
+#endif
+#if VIRTIO_DEBUG
+#define DPRINTF		log_debug
+#else
+#define DPRINTF(x...)	do {} while(0)
+#endif	/* VIRTIO_DEBUG */
+
 extern struct vmd *env;
 extern char *__progname;

-struct viornd_dev viornd;
-struct vioscsi_dev *vioscsi;
-struct vmmci_dev vmmci;
+struct virtio_dev viornd;
+struct virtio_dev *vioscsi = NULL;
+struct virtio_dev vmmci;

 /* Devices emulated in subprocesses are inserted into this list. */
 SLIST_HEAD(virtio_dev_head, virtio_dev) virtio_devs;
@@ -64,12 +75,54 @@ SLIST_HEAD(virtio_dev_head, virtio_dev) virtio_devs;
 #define RXQ	0
 #define TXQ	1

+static void virtio_dev_init(struct virtio_dev *, uint8_t, uint16_t, uint16_t,
+    uint64_t, uint32_t);
 static int virtio_dev_launch(struct vmd_vm *, struct virtio_dev *);
 static void virtio_dispatch_dev(int, short, void *);
 static int handle_dev_msg(struct viodev_msg *, struct virtio_dev *);
 static int virtio_dev_closefds(struct virtio_dev *);
+static void virtio_pci_add_cap(uint8_t, uint8_t, uint8_t, uint32_t);
 static void vmmci_pipe_dispatch(int, short, void *);

+static int virtio_io_dispatch(int, uint16_t, uint32_t *, uint8_t *, void *,
+    uint8_t);
+static int virtio_io_cfg(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
+static int virtio_io_isr(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
+static int virtio_io_notify(int, uint16_t, uint32_t *, uint8_t *, void *,
+    uint8_t);
+static int viornd_notifyq(struct virtio_dev *, uint16_t);
+
+static void vmmci_ack(struct virtio_dev *, unsigned int);
+
+#if VIRTIO_DEBUG
+static const char *
+virtio1_reg_name(uint16_t reg)
+{
+	switch (reg) {
+	case VIO1_PCI_DEVICE_FEATURE_SELECT: return "DEVICE_FEATURE_SELECT";
+	case VIO1_PCI_DEVICE_FEATURE: return "DEVICE_FEATURE";
+	case VIO1_PCI_DRIVER_FEATURE_SELECT: return "DRIVER_FEATURE_SELECT";
+	case VIO1_PCI_DRIVER_FEATURE: return "DRIVER_FEATURE";
+	case VIO1_PCI_CONFIG_MSIX_VECTOR: return "CONFIG_MSIX_VECTOR";
+	case VIO1_PCI_NUM_QUEUES: return "NUM_QUEUES";
+	case VIO1_PCI_DEVICE_STATUS: return "DEVICE_STATUS";
+	case VIO1_PCI_CONFIG_GENERATION: return "CONFIG_GENERATION";
+	case VIO1_PCI_QUEUE_SELECT: return "QUEUE_SELECT";
+	case VIO1_PCI_QUEUE_SIZE: return "QUEUE_SIZE";
+	case VIO1_PCI_QUEUE_MSIX_VECTOR: return "QUEUE_MSIX_VECTOR";
+	case VIO1_PCI_QUEUE_ENABLE: return "QUEUE_ENABLE";
+	case VIO1_PCI_QUEUE_NOTIFY_OFF: return "QUEUE_NOTIFY_OFF";
+	case VIO1_PCI_QUEUE_DESC: return "QUEUE_DESC";
+	case VIO1_PCI_QUEUE_DESC + 4: return "QUEUE_DESC (HIGH)";
+	case VIO1_PCI_QUEUE_AVAIL: return "QUEUE_AVAIL";
+	case VIO1_PCI_QUEUE_AVAIL + 4: return "QUEUE_AVAIL (HIGH)";
+	case VIO1_PCI_QUEUE_USED: return "QUEUE_USED";
+	case VIO1_PCI_QUEUE_USED + 4: return "QUEUE_USED (HIGH)";
+	default: return "UNKNOWN";
+	}
+}
+#endif	/* VIRTIO_DEBUG */
+
 const char *
 virtio_reg_name(uint8_t reg)
 {
@@ -111,62 +164,126 @@ vring_size(uint32_t vq_size)

 /* Update queue select */
 void
-viornd_update_qs(void)
+virtio_update_qs(struct virtio_dev *dev)
 {
-	struct virtio_vq_info *vq_info;
+	struct virtio_vq_info *vq_info = NULL;

-	/* Invalid queue? */
-	if (viornd.cfg.queue_select > 0) {
-		viornd.cfg.queue_size = 0;
-		return;
+	if (dev->driver_feature & VIRTIO_F_VERSION_1) {
+		/* Invalid queue */
+		if (dev->pci_cfg.queue_select >= dev->num_queues) {
+			dev->pci_cfg.queue_size = 0;
+			dev->pci_cfg.queue_enable = 0;
+			return;
+		}
+		vq_info = &dev->vq[dev->pci_cfg.queue_select];
+		dev->pci_cfg.queue_size = vq_info->qs;
+		dev->pci_cfg.queue_desc = vq_info->q_gpa;
+		dev->pci_cfg.queue_avail = vq_info->q_gpa + vq_info->vq_availoffset;
+		dev->pci_cfg.queue_used = vq_info->q_gpa + vq_info->vq_usedoffset;
+		dev->pci_cfg.queue_enable = vq_info->vq_enabled;
+	} else {
+		/* Invalid queue? */
+		if (dev->cfg.queue_select >= dev->num_queues) {
+			dev->cfg.queue_size = 0;
+			return;
+		}
+		vq_info = &dev->vq[dev->cfg.queue_select];
+		dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
+		dev->cfg.queue_size = vq_info->qs;
 	}
-
-	vq_info = &viornd.vq[viornd.cfg.queue_select];
-
-	/* Update queue pfn/size based on queue select */
-	viornd.cfg.queue_pfn = vq_info->q_gpa >> 12;
-	viornd.cfg.queue_size = vq_info->qs;
 }

-/* Update queue address */
+/* Update queue address. */
 void
-viornd_update_qa(void)
+virtio_update_qa(struct virtio_dev *dev)
 {
-	struct virtio_vq_info *vq_info;
+	struct virtio_vq_info *vq_info = NULL;
 	void *hva = NULL;

-	/* Invalid queue? */
-	if (viornd.cfg.queue_select > 0)
-		return;
+	if (dev->driver_feature & VIRTIO_F_VERSION_1) {
+		if (dev->pci_cfg.queue_select >= dev->num_queues) {
+			log_warnx("%s: invalid queue index", __func__);
+			return;
+		}
+		vq_info = &dev->vq[dev->pci_cfg.queue_select];
+		vq_info->q_gpa = dev->pci_cfg.queue_desc;

-	vq_info = &viornd.vq[viornd.cfg.queue_select];
-	vq_info->q_gpa = (uint64_t)viornd.cfg.queue_pfn * VIRTIO_PAGE_SIZE;
+		/*
+		 * Queue size is adjustable by the guest in Virtio 1.x.
+		 * We validate the max size at time of write and not here.
+		 */
+		vq_info->qs = dev->pci_cfg.queue_size;
+		vq_info->mask = vq_info->qs - 1;

-	hva = hvaddr_mem(vq_info->q_gpa, vring_size(VIORND_QUEUE_SIZE));
-	if (hva == NULL)
-		fatalx("viornd_update_qa");
-	vq_info->q_hva = hva;
+		if (vq_info->qs > 0 && vq_info->qs % 2 == 0) {
+			vq_info->vq_availoffset = dev->pci_cfg.queue_avail -
+			    dev->pci_cfg.queue_desc;
+			vq_info->vq_usedoffset = dev->pci_cfg.queue_used -
+			    dev->pci_cfg.queue_desc;
+			vq_info->vq_enabled = (dev->pci_cfg.queue_enable == 1);
+		} else {
+			vq_info->vq_availoffset = 0;
+			vq_info->vq_usedoffset = 0;
+			vq_info->vq_enabled = 0;
+		}
+		dev->pci_cfg.config_generation++;
+	} else {
+		/* Invalid queue? */
+		if (dev->cfg.queue_select >= dev->num_queues) {
+			log_warnx("%s: invalid queue index", __func__);
+			return;
+		}
+		vq_info = &dev->vq[dev->cfg.queue_select];
+		vq_info->q_gpa = (uint64_t)dev->cfg.queue_pfn *
+		    VIRTIO_PAGE_SIZE;
+
+		/* Queue size is immutable in Virtio 0.9. */
+		vq_info->vq_availoffset = sizeof(struct vring_desc) *
+		    vq_info->qs;
+		vq_info->vq_usedoffset = VIRTQUEUE_ALIGN(
+			sizeof(struct vring_desc) * vq_info->qs +
+			sizeof(uint16_t) * (2 + vq_info->qs));
+	}
+
+	/* Update any host va mappings. */
+	if (vq_info->q_gpa > 0) {
+		hva = hvaddr_mem(vq_info->q_gpa, vring_size(vq_info->qs));
+		if (hva == NULL)
+			fatalx("%s: failed to translate gpa to hva", __func__);
+		vq_info->q_hva = hva;
+	} else {
+		vq_info->q_hva = NULL;
+		vq_info->last_avail = 0;
+		vq_info->notified_avail = 0;
+	}
 }

-int
-viornd_notifyq(void)
+static int
+viornd_notifyq(struct virtio_dev *dev, uint16_t idx)
 {
 	size_t sz;
-	int dxx, ret;
+	int dxx, ret = 0;
 	uint16_t aidx, uidx;
 	char *vr, *rnd_data;
-	struct vring_desc *desc;
-	struct vring_avail *avail;
-	struct vring_used *used;
-	struct virtio_vq_info *vq_info;
+	struct vring_desc *desc = NULL;
+	struct vring_avail *avail = NULL;
+	struct vring_used *used = NULL;
+	struct virtio_vq_info *vq_info = NULL;

-	ret = 0;
+	if (dev->device_id != PCI_PRODUCT_VIRTIO_ENTROPY)
+		fatalx("%s: device is not an entropy device", __func__);

-	/* Invalid queue? */
-	if (viornd.cfg.queue_notify > 0)
+	if (idx >= dev->num_queues) {
+		log_warnx("%s: invalid virtqueue index", __func__);
 		return (0);
+	}
+	vq_info = &dev->vq[idx];

-	vq_info = &viornd.vq[viornd.cfg.queue_notify];
+	if (!vq_info->vq_enabled) {
+		log_warnx("%s: virtqueue not enabled", __func__);
+		return (0);
+	}
+
 	vr = vq_info->q_hva;
 	if (vr == NULL)
 		fatalx("%s: null vring", __func__);
@@ -175,107 +292,424 @@ viornd_notifyq(void)
 	avail = (struct vring_avail *)(vr + vq_info->vq_availoffset);
 	used = (struct vring_used *)(vr + vq_info->vq_usedoffset);

-	aidx = avail->idx & VIORND_QUEUE_MASK;
-	uidx = used->idx & VIORND_QUEUE_MASK;
+	aidx = avail->idx & vq_info->mask;
+	uidx = used->idx & vq_info->mask;

-	dxx = avail->ring[aidx] & VIORND_QUEUE_MASK;
+	dxx = avail->ring[aidx] & vq_info->mask;

 	sz = desc[dxx].len;
 	if (sz > MAXPHYS)
 		fatalx("viornd descriptor size too large (%zu)", sz);

 	rnd_data = malloc(sz);
+	if (rnd_data == NULL)
+		fatal("memory allocaiton error for viornd data");

-	if (rnd_data != NULL) {
-		arc4random_buf(rnd_data, sz);
-		if (write_mem(desc[dxx].addr, rnd_data, sz)) {
-			log_warnx("viornd: can't write random data @ "
-			    "0x%llx",
-			    desc[dxx].addr);
-		} else {
-			/* ret == 1 -> interrupt needed */
-			/* XXX check VIRTIO_F_NO_INTR */
-			ret = 1;
-			viornd.cfg.isr_status = 1;
-			used->ring[uidx].id = dxx;
-			used->ring[uidx].len = sz;
-			__sync_synchronize();
-			used->idx++;
-		}
-		free(rnd_data);
-	} else
-		fatal("memory allocation error for viornd data");
+	arc4random_buf(rnd_data, sz);
+	if (write_mem(desc[dxx].addr, rnd_data, sz)) {
+		log_warnx("viornd: can't write random data @ 0x%llx",
+		    desc[dxx].addr);
+	} else {
+		/* ret == 1 -> interrupt needed */
+		/* XXX check VIRTIO_F_NO_INTR */
+		ret = 1;
+		viornd.isr = 1;
+		used->ring[uidx].id = dxx;
+		used->ring[uidx].len = sz;
+		__sync_synchronize();
+		used->idx++;
+	}
+	free(rnd_data);

 	return (ret);
 }

-int
-virtio_rnd_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
-    void *unused, uint8_t sz)
+static int
+virtio_io_dispatch(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
+    void *arg, uint8_t sz)
 {
+	struct virtio_dev *dev = (struct virtio_dev *)arg;
+	uint16_t actual = reg & 0x00FF;
+
+	switch (reg & 0xFF00) {
+	case VIO1_CFG_BAR_OFFSET:
+		return virtio_io_cfg(dir, actual, data, intr, arg, sz);
+	case VIO1_DEV_BAR_OFFSET:
+		if (dev->device_id == PCI_PRODUCT_VIRTIO_SCSI)
+			return vioscsi_io(dir, actual, data, intr, arg, sz);
+		else if (dir == VEI_DIR_IN) {
+			log_debug("%s: no device specific handler", __func__);
+			*data = (uint32_t)(-1);
+		}
+		break;
+	case VIO1_NOTIFY_BAR_OFFSET:
+		return virtio_io_notify(dir, actual, data, intr, arg, sz);
+	case VIO1_ISR_BAR_OFFSET:
+		return virtio_io_isr(dir, actual, data, intr, arg, sz);
+	default:
+		log_debug("%s: no handler for reg 0x%04x", __func__, reg);
+		if (dir == VEI_DIR_IN)
+			*data = (uint32_t)(-1);
+	}
+	return (0);
+}
+
+static int
+virtio_io_cfg(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
+    void *arg, uint8_t sz)
+{
+	uint16_t i;
+	struct virtio_dev *dev = (struct virtio_dev *)arg;
+	struct virtio_pci_common_cfg *pci_cfg = &dev->pci_cfg;
+
 	*intr = 0xFF;

-	if (dir == 0) {
+	if (dir == VEI_DIR_OUT) {
 		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, virtio_reg_name(reg));
+		case VIO1_PCI_DEVICE_FEATURE_SELECT:
+			if (sz != 4)
+				log_warnx("%s: unaligned write to device "
+				    "feature select (sz=%u)", __func__, sz);
+			else
+				pci_cfg->device_feature_select = *data;
 			break;
-		case VIRTIO_CONFIG_GUEST_FEATURES:
-			viornd.cfg.guest_feature = *data;
+		case VIO1_PCI_DEVICE_FEATURE:
+			log_warnx("%s: illegal write to device feature "
+			    "register", __progname);
 			break;
-		case VIRTIO_CONFIG_QUEUE_PFN:
-			viornd.cfg.queue_pfn = *data;
-			viornd_update_qa();
+		case VIO1_PCI_DRIVER_FEATURE_SELECT:
+			if (sz != 4)
+				log_warnx("%s: unaligned write to driver "
+				    "feature select register (sz=%u)", __func__,
+				    sz);
+			else
+				pci_cfg->driver_feature_select = *data;
 			break;
-		case VIRTIO_CONFIG_QUEUE_SELECT:
-			viornd.cfg.queue_select = *data;
-			viornd_update_qs();
+		case VIO1_PCI_DRIVER_FEATURE:
+			if (sz != 4) {
+				log_warnx("%s: unaligned write to driver "
+				    "feature register (sz=%u)", __func__, sz);
+				break;
+			}
+			if (pci_cfg->driver_feature_select > 1) {
+				/* We only support a 64-bit feature space. */
+				DPRINTF("%s: ignoring driver feature write",
+				    __func__);
+				break;
+			}
+			pci_cfg->driver_feature = *data;
+			if (pci_cfg->driver_feature_select == 0)
+				dev->driver_feature |= pci_cfg->driver_feature;
+			else
+				dev->driver_feature |=
+				    ((uint64_t)pci_cfg->driver_feature << 32);
+			dev->driver_feature &= dev->device_feature;
+			DPRINTF("%s: driver features 0x%llx", __func__,
+			    dev->driver_feature);
 			break;
-		case VIRTIO_CONFIG_QUEUE_NOTIFY:
-			viornd.cfg.queue_notify = *data;
-			if (viornd_notifyq())
-				*intr = 1;
+		case VIO1_PCI_CONFIG_MSIX_VECTOR:
+			/* Ignore until we support MSIX. */
 			break;
-		case VIRTIO_CONFIG_DEVICE_STATUS:
-			viornd.cfg.device_status = *data;
+		case VIO1_PCI_NUM_QUEUES:
+			log_warnx("%s: illegal write to num queues register",
+			    __progname);
 			break;
+		case VIO1_PCI_DEVICE_STATUS:
+			if (sz != 1) {
+				log_warnx("%s: unaligned write to device "
+				    "status register (sz=%u)", __func__, sz);
+				break;
+			}
+			dev->status = *data;
+			if (dev->status == 0) {
+				/* Reset device and virtqueues (if any). */
+				dev->driver_feature = 0;
+				dev->isr = 0;
+
+				pci_cfg->queue_select = 0;
+				virtio_update_qs(dev);
+
+				if (dev->num_queues > 0) {
+					/*
+					 * Reset virtqueues to initial state and
+					 * set to disabled status. Clear PCI
+					 * configuration registers.
+					 */
+					for (i = 0; i < dev->num_queues; i++)
+						virtio_vq_init(dev, i);
+				}
+				pci_cfg->config_generation++;
+			}
+
+			DPRINTF("%s: dev %u status [%s%s%s%s%s%s]", __func__,
+			    dev->pci_id,
+			    (*data & VIRTIO_CONFIG_DEVICE_STATUS_ACK) ?
+			    "[ack]" : "",
+			    (*data & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER) ?
+			    "[driver]" : "",
+			    (*data & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) ?
+			    "[driver ok]" : "",
+			    (*data & VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK) ?
+			    "[features ok]" : "",
+			    (*data &
+				VIRTIO_CONFIG_DEVICE_STATUS_DEVICE_NEEDS_RESET)
+			    ? "[needs reset]" : "",
+			    (*data & VIRTIO_CONFIG_DEVICE_STATUS_FAILED) ?
+			    "[failed]" : "");
+
+			break;
+		case VIO1_PCI_CONFIG_GENERATION:
+			log_warnx("%s: illegal write to config generation "
+			    "register", __progname);
+			break;
+		case VIO1_PCI_QUEUE_SELECT:
+			pci_cfg->queue_select = *data;
+			virtio_update_qs(dev);
+			break;
+		case VIO1_PCI_QUEUE_SIZE:
+			if (*data <= VIRTIO_QUEUE_SIZE_MAX)
+				pci_cfg->queue_size = *data;
+			else {
+				log_warnx("%s: clamping queue size", __func__);
+				pci_cfg->queue_size = VIRTIO_QUEUE_SIZE_MAX;
+			}
+			virtio_update_qa(dev);
+			break;
+		case VIO1_PCI_QUEUE_MSIX_VECTOR:
+			/* Ignore until we support MSI-X. */
+			break;
+		case VIO1_PCI_QUEUE_ENABLE:
+			pci_cfg->queue_enable = *data;
+			virtio_update_qa(dev);
+			break;
+		case VIO1_PCI_QUEUE_NOTIFY_OFF:
+			log_warnx("%s: illegal write to queue notify offset "
+			    "register", __progname);
+			break;
+		case VIO1_PCI_QUEUE_DESC:
+			if (sz != 4) {
+				log_warnx("%s: unaligned write to queue "
+				    "desc. register (sz=%u)", __func__, sz);
+				break;
+			}
+			pci_cfg->queue_desc &= 0xffffffff00000000;
+			pci_cfg->queue_desc |= (uint64_t)*data;
+			virtio_update_qa(dev);
+			break;
+		case VIO1_PCI_QUEUE_DESC + 4:
+			if (sz != 4) {
+				log_warnx("%s: unaligned write to queue "
+				    "desc. register (sz=%u)", __func__, sz);
+				break;
+			}
+			pci_cfg->queue_desc &= 0x00000000ffffffff;
+			pci_cfg->queue_desc |= ((uint64_t)*data << 32);
+			virtio_update_qa(dev);
+			break;
+		case VIO1_PCI_QUEUE_AVAIL:
+			if (sz != 4) {
+				log_warnx("%s: unaligned write to queue "
+				    "available register (sz=%u)", __func__, sz);
+				break;
+			}
+			pci_cfg->queue_avail &= 0xffffffff00000000;
+			pci_cfg->queue_avail |= (uint64_t)*data;
+			virtio_update_qa(dev);
+			break;
+		case VIO1_PCI_QUEUE_AVAIL + 4:
+			if (sz != 4) {
+				log_warnx("%s: unaligned write to queue "
+				    "available register (sz=%u)", __func__, sz);
+				break;
+			}
+			pci_cfg->queue_avail &= 0x00000000ffffffff;
+			pci_cfg->queue_avail |= ((uint64_t)*data << 32);
+			virtio_update_qa(dev);
+			break;
+		case VIO1_PCI_QUEUE_USED:
+			if (sz != 4) {
+				log_warnx("%s: unaligned write to queue used "
+				    "register (sz=%u)", __func__, sz);
+				break;
+			}
+			pci_cfg->queue_used &= 0xffffffff00000000;
+			pci_cfg->queue_used |= (uint64_t)*data;
+			virtio_update_qa(dev);
+			break;
+		case VIO1_PCI_QUEUE_USED + 4:
+			if (sz != 4) {
+				log_warnx("%s: unaligned write to queue used "
+				    "register (sz=%u)", __func__, sz);
+				break;
+			}
+			pci_cfg->queue_used &= 0x00000000ffffffff;
+			pci_cfg->queue_used |= ((uint64_t)*data << 32);
+			virtio_update_qa(dev);
+			break;
+		default:
+			log_warnx("%s: invalid register 0x%04x", __func__, reg);
 		}
 	} else {
 		switch (reg) {
-		case VIRTIO_CONFIG_DEVICE_FEATURES:
-			*data = viornd.cfg.device_feature;
+		case VIO1_PCI_DEVICE_FEATURE_SELECT:
+			*data = pci_cfg->device_feature_select;
 			break;
-		case VIRTIO_CONFIG_GUEST_FEATURES:
-			*data = viornd.cfg.guest_feature;
+		case VIO1_PCI_DEVICE_FEATURE:
+			if (pci_cfg->device_feature_select == 0)
+				*data = dev->device_feature & (uint32_t)(-1);
+			else if (pci_cfg->device_feature_select == 1)
+				*data = dev->device_feature >> 32;
+			else {
+				DPRINTF("%s: ignoring device feature read",
+				    __func__);
+				*data = 0;
+			}
 			break;
-		case VIRTIO_CONFIG_QUEUE_PFN:
-			*data = viornd.cfg.queue_pfn;
+		case VIO1_PCI_DRIVER_FEATURE_SELECT:
+			*data = pci_cfg->driver_feature_select;
 			break;
-		case VIRTIO_CONFIG_QUEUE_SIZE:
-			*data = viornd.cfg.queue_size;
+		case VIO1_PCI_DRIVER_FEATURE:
+			if (pci_cfg->driver_feature_select == 0)
+				*data = dev->driver_feature & (uint32_t)(-1);
+			else if (pci_cfg->driver_feature_select == 1)
+				*data = dev->driver_feature >> 32;
+			else {
+				DPRINTF("%s: ignoring driver feature read",
+				    __func__);
+				*data = 0;
+			}
 			break;
-		case VIRTIO_CONFIG_QUEUE_SELECT:
-			*data = viornd.cfg.queue_select;
+		case VIO1_PCI_CONFIG_MSIX_VECTOR:
+			*data = VIRTIO_MSI_NO_VECTOR;	/* Unsupported */
 			break;
-		case VIRTIO_CONFIG_QUEUE_NOTIFY:
-			*data = viornd.cfg.queue_notify;
+		case VIO1_PCI_NUM_QUEUES:
+			*data = dev->num_queues;
 			break;
-		case VIRTIO_CONFIG_DEVICE_STATUS:
-			*data = viornd.cfg.device_status;
+		case VIO1_PCI_DEVICE_STATUS:
+			*data = dev->status;
 			break;
-		case VIRTIO_CONFIG_ISR_STATUS:
-			*data = viornd.cfg.isr_status;
-			viornd.cfg.isr_status = 0;
-			vcpu_deassert_irq(viornd.vm_id, 0, viornd.irq);
+		case VIO1_PCI_CONFIG_GENERATION:
+			*data = pci_cfg->config_generation;
 			break;
+		case VIO1_PCI_QUEUE_SELECT:
+			*data = pci_cfg->queue_select;
+			break;
+		case VIO1_PCI_QUEUE_SIZE:
+			*data = pci_cfg->queue_size;
+			break;
+		case VIO1_PCI_QUEUE_MSIX_VECTOR:
+			*data = VIRTIO_MSI_NO_VECTOR;	/* Unsupported */
+			break;
+		case VIO1_PCI_QUEUE_ENABLE:
+			*data = pci_cfg->queue_enable;
+			break;
+		case VIO1_PCI_QUEUE_NOTIFY_OFF:
+			*data = pci_cfg->queue_notify_off;
+			break;
+		case VIO1_PCI_QUEUE_DESC:
+			*data = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_desc);
+			break;
+		case VIO1_PCI_QUEUE_DESC + 4:
+			*data = (uint32_t)(pci_cfg->queue_desc >> 32);
+			break;
+		case VIO1_PCI_QUEUE_AVAIL:
+			*data = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_avail);
+			break;
+		case VIO1_PCI_QUEUE_AVAIL + 4:
+			*data = (uint32_t)(pci_cfg->queue_avail >> 32);
+			break;
+		case VIO1_PCI_QUEUE_USED:
+			*data = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_used);
+			break;
+		case VIO1_PCI_QUEUE_USED + 4:
+			*data = (uint32_t)(pci_cfg->queue_used >> 32);
+			break;
+		default:
+			log_warnx("%s: invalid register 0x%04x", __func__, reg);
 		}
 	}
+
+	DPRINTF("%s: dev=%u %s sz=%u dir=%s data=0x%04x", __func__, dev->pci_id,
+	    virtio1_reg_name(reg), sz, (dir == VEI_DIR_OUT) ? "w" : "r", *data);
+
 	return (0);
 }

+static int
+virtio_io_isr(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
+    void *arg, uint8_t sz)
+{
+	struct virtio_dev *dev = (struct virtio_dev *)arg;
+	*intr = 0xFF;
+
+	DPRINTF("%s: dev=%u, reg=0x%04x, sz=%u, dir=%s", __func__,
+	    dev->pci_id, reg, sz,
+	    (dir == VEI_DIR_OUT) ? "write" : "read");
+
+	/* XXX for now limit to in-process devices */
+	if (dev->device_id == PCI_PRODUCT_VIRTIO_BLOCK ||
+	    dev->device_id == PCI_PRODUCT_VIRTIO_NETWORK)
+		fatalx("%s: cannot use on multi-process virtio dev", __func__);
+
+	if (dir == VEI_DIR_IN) {
+		*data = dev->isr;
+		dev->isr = 0;
+		vcpu_deassert_irq(dev->vm_id, 0, dev->irq);
+	}
+
+	return (0);
+}
+
+static int
+virtio_io_notify(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
+    void *arg, uint8_t sz)
+{
+	int raise_intr = 0;
+	struct virtio_dev *dev = (struct virtio_dev *)arg;
+	uint16_t vq_idx = (uint16_t)(0x0000ffff & *data);
+
+	*intr = 0xFF;
+
+	DPRINTF("%s: reg=0x%04x, sz=%u, vq_idx=%u, dir=%s", __func__, reg, sz,
+	    vq_idx, (dir == VEI_DIR_OUT) ? "write" : "read");
+
+	/* XXX for now limit to in-process devices */
+	if (dev->device_id == PCI_PRODUCT_VIRTIO_BLOCK ||
+	    dev->device_id == PCI_PRODUCT_VIRTIO_NETWORK)
+		fatalx("%s: cannot use on multi-process virtio dev", __func__);
+
+	if (vq_idx >= dev->num_queues) {
+		log_warnx("%s: invalid virtqueue index %u", __func__, vq_idx);
+		return (0);
+	}
+
+	if (dir == VEI_DIR_IN) {
+		/* Behavior is undefined. */
+		*data = (uint32_t)(-1);
+		return (0);
+	}
+
+	switch (dev->device_id) {
+	case PCI_PRODUCT_VIRTIO_ENTROPY:
+		raise_intr = viornd_notifyq(dev, vq_idx);
+		break;
+	case PCI_PRODUCT_VIRTIO_SCSI:
+		raise_intr = vioscsi_notifyq(dev, vq_idx);
+		break;
+	case PCI_PRODUCT_VIRTIO_VMMCI:
+		/* Does not use a virtqueue. */
+		break;
+	default:
+		log_warnx("%s: invalid device type %u", __func__,
+		    dev->device_id);
+	}
+
+	if (raise_intr)
+		*intr = 1;
+
+	return (0);
+}
+
 /*
  * vmmci_ctl
  *
@@ -284,20 +718,24 @@ virtio_rnd_io(int dir, uint16_t reg, uint32_t *data, u
  * Called by the vm process's event(3) loop.
  */
 int
-vmmci_ctl(unsigned int cmd)
+vmmci_ctl(struct virtio_dev *dev, unsigned int cmd)
 {
 	int ret = 0;
 	struct timeval tv = { 0, 0 };
+	struct vmmci_dev *v = NULL;

-	mutex_lock(&vmmci.mutex);
+	if (dev->device_id != PCI_PRODUCT_VIRTIO_VMMCI)
+		fatalx("%s: device is not a vmmci device", __func__);
+	v = &dev->vmmci;

-	if ((vmmci.cfg.device_status &
-	    VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) == 0) {
+	mutex_lock(&v->mutex);
+
+	if ((dev->status & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) == 0) {
 		ret = -1;
 		goto unlock;
 	}

-	if (cmd == vmmci.cmd)
+	if (cmd == v->cmd)
 		goto unlock;

 	switch (cmd) {
@@ -306,7 +744,7 @@ vmmci_ctl(unsigned int cmd)
 	case VMMCI_SHUTDOWN:
 	case VMMCI_REBOOT:
 		/* Update command */
-		vmmci.cmd = cmd;
+		v->cmd = cmd;

 		/*
 		 * vmm VMs do not support powerdown, send a reboot request
@@ -316,20 +754,20 @@ vmmci_ctl(unsigned int cmd)
 			cmd = VMMCI_REBOOT;

 		/* Trigger interrupt */
-		vmmci.cfg.isr_status = VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
-		vcpu_assert_irq(vmmci.vm_id, 0, vmmci.irq);
+		dev->isr = VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
+		vcpu_assert_irq(dev->vm_id, 0, dev->irq);

 		/* Add ACK timeout */
 		tv.tv_sec = VMMCI_TIMEOUT_SHORT;
-		evtimer_add(&vmmci.timeout, &tv);
+		evtimer_add(&v->timeout, &tv);
 		break;
 	case VMMCI_SYNCRTC:
 		if (vmmci.cfg.guest_feature & VMMCI_F_SYNCRTC) {
 			/* RTC updated, request guest VM resync of its RTC */
-			vmmci.cmd = cmd;
+			v->cmd = cmd;

-			vmmci.cfg.isr_status = VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
-			vcpu_assert_irq(vmmci.vm_id, 0, vmmci.irq);
+			dev->isr = VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
+			vcpu_assert_irq(dev->vm_id, 0, dev->irq);
 		} else {
 			log_debug("%s: RTC sync skipped (guest does not "
 			    "support RTC sync)\n", __func__);
@@ -340,7 +778,7 @@ vmmci_ctl(unsigned int cmd)
 	}

 unlock:
-	mutex_unlock(&vmmci.mutex);
+	mutex_unlock(&v->mutex);

 	return (ret);
 }
@@ -352,9 +790,15 @@ unlock:
  *
  * Called by the vcpu thread. Must be called with the mutex held.
  */
-void
-vmmci_ack(unsigned int cmd)
+static void
+vmmci_ack(struct virtio_dev *dev, unsigned int cmd)
 {
+	struct vmmci_dev *v = NULL;
+
+	if (dev->device_id != PCI_PRODUCT_VIRTIO_VMMCI)
+		fatalx("%s: device is not a vmmci device", __func__);
+	v = &dev->vmmci;
+
 	switch (cmd) {
 	case VMMCI_NONE:
 		break;
@@ -365,10 +809,10 @@ vmmci_ack(unsigned int cmd)
 		 * timeout to give the VM a chance to reboot before the
 		 * timer is expired.
 		 */
-		if (vmmci.cmd == 0) {
+		if (v->cmd == 0) {
 			log_debug("%s: vm %u requested shutdown", __func__,
-			    vmmci.vm_id);
-			vm_pipe_send(&vmmci.dev_pipe, VMMCI_SET_TIMEOUT_SHORT);
+			    dev->vm_id);
+			vm_pipe_send(&v->dev_pipe, VMMCI_SET_TIMEOUT_SHORT);
 			return;
 		}
 		/* FALLTHROUGH */
@@ -380,16 +824,16 @@ vmmci_ack(unsigned int cmd)
 		 * rc.shutdown on the VM), so increase the timeout before
 		 * killing it forcefully.
 		 */
-		if (cmd == vmmci.cmd) {
+		if (cmd == v->cmd) {
 			log_debug("%s: vm %u acknowledged shutdown request",
-			    __func__, vmmci.vm_id);
-			vm_pipe_send(&vmmci.dev_pipe, VMMCI_SET_TIMEOUT_LONG);
+			    __func__, dev->vm_id);
+			vm_pipe_send(&v->dev_pipe, VMMCI_SET_TIMEOUT_LONG);
 		}
 		break;
 	case VMMCI_SYNCRTC:
 		log_debug("%s: vm %u acknowledged RTC sync request",
-		    __func__, vmmci.vm_id);
-		vmmci.cmd = VMMCI_NONE;
+		    __func__, dev->vm_id);
+		v->cmd = VMMCI_NONE;
 		break;
 	default:
 		log_warnx("%s: illegal request %u", __func__, cmd);
@@ -400,17 +844,32 @@ vmmci_ack(unsigned int cmd)
 void
 vmmci_timeout(int fd, short type, void *arg)
 {
-	log_debug("%s: vm %u shutdown", __progname, vmmci.vm_id);
-	vm_shutdown(vmmci.cmd == VMMCI_REBOOT ? VMMCI_REBOOT : VMMCI_SHUTDOWN);
+	struct virtio_dev *dev = (struct virtio_dev *)arg;
+	struct vmmci_dev *v = NULL;
+
+	if (dev->device_id != PCI_PRODUCT_VIRTIO_VMMCI)
+		fatalx("%s: device is not a vmmci device", __func__);
+	v = &dev->vmmci;
+
+	log_debug("%s: vm %u shutdown", __progname, dev->vm_id);
+	vm_shutdown(v->cmd == VMMCI_REBOOT ? VMMCI_REBOOT : VMMCI_SHUTDOWN);
 }

 int
 vmmci_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
-    void *unused, uint8_t sz)
+    void *arg, uint8_t sz)
 {
+	struct virtio_dev	*dev = (struct virtio_dev *)arg;
+	struct vmmci_dev	*v = NULL;
+
+	if (dev->device_id != PCI_PRODUCT_VIRTIO_VMMCI)
+		fatalx("%s: device is not a vmmci device (%u)",
+		    __func__, dev->device_id);
+	v = &dev->vmmci;
+
 	*intr = 0xFF;

-	mutex_lock(&vmmci.mutex);
+	mutex_lock(&v->mutex);
 	if (dir == 0) {
 		switch (reg) {
 		case VIRTIO_CONFIG_DEVICE_FEATURES:
@@ -420,72 +879,72 @@ vmmci_io(int dir, uint16_t reg, uint32_t *data, uint8_
 			    __progname, *data, virtio_reg_name(reg));
 			break;
 		case VIRTIO_CONFIG_GUEST_FEATURES:
-			vmmci.cfg.guest_feature = *data;
+			dev->cfg.guest_feature = *data;
 			break;
 		case VIRTIO_CONFIG_QUEUE_PFN:
-			vmmci.cfg.queue_pfn = *data;
+			dev->cfg.queue_pfn = *data;
 			break;
 		case VIRTIO_CONFIG_QUEUE_SELECT:
-			vmmci.cfg.queue_select = *data;
+			dev->cfg.queue_select = *data;
 			break;
 		case VIRTIO_CONFIG_QUEUE_NOTIFY:
-			vmmci.cfg.queue_notify = *data;
+			dev->cfg.queue_notify = *data;
 			break;
 		case VIRTIO_CONFIG_DEVICE_STATUS:
-			vmmci.cfg.device_status = *data;
+			dev->status = *data;
 			break;
 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
-			vmmci_ack(*data);
+			vmmci_ack(dev, *data);
 			break;
 		}
 	} else {
 		switch (reg) {
 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
-			*data = vmmci.cmd;
+			*data = v->cmd;
 			break;
 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4:
 			/* Update time once when reading the first register */
-			gettimeofday(&vmmci.time, NULL);
-			*data = (uint64_t)vmmci.time.tv_sec;
+			gettimeofday(&v->time, NULL);
+			*data = (uint64_t)v->time.tv_sec;
 			break;
 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8:
-			*data = (uint64_t)vmmci.time.tv_sec << 32;
+			*data = (uint64_t)v->time.tv_sec << 32;
 			break;
 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12:
-			*data = (uint64_t)vmmci.time.tv_usec;
+			*data = (uint64_t)v->time.tv_usec;
 			break;
 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16:
-			*data = (uint64_t)vmmci.time.tv_usec << 32;
+			*data = (uint64_t)v->time.tv_usec << 32;
 			break;
 		case VIRTIO_CONFIG_DEVICE_FEATURES:
-			*data = vmmci.cfg.device_feature;
+			*data = dev->cfg.device_feature;
 			break;
 		case VIRTIO_CONFIG_GUEST_FEATURES:
-			*data = vmmci.cfg.guest_feature;
+			*data = dev->cfg.guest_feature;
 			break;
 		case VIRTIO_CONFIG_QUEUE_PFN:
-			*data = vmmci.cfg.queue_pfn;
+			*data = dev->cfg.queue_pfn;
 			break;
 		case VIRTIO_CONFIG_QUEUE_SIZE:
-			*data = vmmci.cfg.queue_size;
+			*data = dev->cfg.queue_size;
 			break;
 		case VIRTIO_CONFIG_QUEUE_SELECT:
-			*data = vmmci.cfg.queue_select;
+			*data = dev->cfg.queue_select;
 			break;
 		case VIRTIO_CONFIG_QUEUE_NOTIFY:
-			*data = vmmci.cfg.queue_notify;
+			*data = dev->cfg.queue_notify;
 			break;
 		case VIRTIO_CONFIG_DEVICE_STATUS:
-			*data = vmmci.cfg.device_status;
+			*data = dev->status;
 			break;
 		case VIRTIO_CONFIG_ISR_STATUS:
-			*data = vmmci.cfg.isr_status;
-			vmmci.cfg.isr_status = 0;
-			vcpu_deassert_irq(vmmci.vm_id, 0, vmmci.irq);
+			*data = dev->isr;
+			dev->isr = 0;
+			vcpu_deassert_irq(dev->vm_id, 0, dev->irq);
 			break;
 		}
 	}
-	mutex_unlock(&vmmci.mutex);
+	mutex_unlock(&v->mutex);

 	return (0);
 }
@@ -506,18 +965,20 @@ virtio_get_base(int fd, char *path, size_t npath, int
 static void
 vmmci_pipe_dispatch(int fd, short event, void *arg)
 {
-	enum pipe_msg_type msg;
-	struct timeval tv = { 0, 0 };
+	struct virtio_dev	*dev = (struct virtio_dev *)arg;
+	struct vmmci_dev 	*v = &dev->vmmci;
+	struct timeval		 tv = { 0, 0 };
+	enum pipe_msg_type	 msg;

-	msg = vm_pipe_recv(&vmmci.dev_pipe);
+	msg = vm_pipe_recv(&v->dev_pipe);
 	switch (msg) {
 	case VMMCI_SET_TIMEOUT_SHORT:
 		tv.tv_sec = VMMCI_TIMEOUT_SHORT;
-		evtimer_add(&vmmci.timeout, &tv);
+		evtimer_add(&v->timeout, &tv);
 		break;
 	case VMMCI_SET_TIMEOUT_LONG:
 		tv.tv_sec = VMMCI_TIMEOUT_LONG;
-		evtimer_add(&vmmci.timeout, &tv);
+		evtimer_add(&v->timeout, &tv);
 		break;
 	default:
 		log_warnx("%s: invalid pipe message type %d", __func__, msg);
@@ -531,98 +992,67 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
 	struct vmop_create_params *vmc = &vm->vm_params;
 	struct vm_create_params *vcp = &vmc->vmc_params;
 	struct virtio_dev *dev;
-	uint8_t id;
-	uint8_t i, j;
-	int ret = 0;
+	uint8_t id, i, j;
+	int bar_id, ret = 0;

-	/* Virtio entropy device */
+	SLIST_INIT(&virtio_devs);
+
+	/* Virtio 1.x Entropy Device */
 	if (pci_add_device(&id, PCI_VENDOR_QUMRANET,
-	    PCI_PRODUCT_QUMRANET_VIO_RNG, PCI_CLASS_SYSTEM,
-	    PCI_SUBCLASS_SYSTEM_MISC,
-	    PCI_VENDOR_OPENBSD,
-	    PCI_PRODUCT_VIRTIO_ENTROPY, 1, NULL)) {
+	    PCI_PRODUCT_QUMRANET_VIO1_RNG, PCI_CLASS_SYSTEM,
+	    PCI_SUBCLASS_SYSTEM_MISC, PCI_VENDOR_OPENBSD,
+	    PCI_PRODUCT_VIRTIO_ENTROPY, 1, 1, NULL)) {
 		log_warnx("%s: can't add PCI virtio rng device",
 		    __progname);
 		return;
 	}
+	virtio_dev_init(&viornd, id, VIORND_QUEUE_SIZE_DEFAULT,
+	    VIRTIO_RND_QUEUES, VIRTIO_F_VERSION_1, vcp->vcp_id);

-	if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_rnd_io, NULL)) {
+	bar_id = pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_io_dispatch,
+	    &viornd);
+	if (bar_id == -1 || bar_id > 0xff) {
 		log_warnx("%s: can't add bar for virtio rng device",
 		    __progname);
 		return;
 	}
+	virtio_pci_add_cap(id, VIRTIO_PCI_CAP_COMMON_CFG, bar_id, 0);
+	virtio_pci_add_cap(id, VIRTIO_PCI_CAP_ISR_CFG, bar_id, 0);
+	virtio_pci_add_cap(id, VIRTIO_PCI_CAP_NOTIFY_CFG, bar_id, 0);

-	memset(&viornd, 0, sizeof(viornd));
-	viornd.vq[0].qs = VIORND_QUEUE_SIZE;
-	viornd.vq[0].vq_availoffset = sizeof(struct vring_desc) *
-	    VIORND_QUEUE_SIZE;
-	viornd.vq[0].vq_usedoffset = VIRTQUEUE_ALIGN(
-	    sizeof(struct vring_desc) * VIORND_QUEUE_SIZE
-	    + sizeof(uint16_t) * (2 + VIORND_QUEUE_SIZE));
-	viornd.pci_id = id;
-	viornd.irq = pci_get_dev_irq(id);
-	viornd.vm_id = vcp->vcp_id;
-
-	SLIST_INIT(&virtio_devs);
-
+	/* Virtio 0.9 Network Devices */
 	if (vmc->vmc_nnics > 0) {
 		for (i = 0; i < vmc->vmc_nnics; i++) {
-			dev = calloc(1, sizeof(struct virtio_dev));
+			dev = malloc(sizeof(struct virtio_dev));
 			if (dev == NULL) {
 				log_warn("%s: calloc failure allocating vionet",
 				    __progname);
 				return;
 			}
-			/* Virtio network */
-			dev->dev_type = VMD_DEVTYPE_NET;
-
 			if (pci_add_device(&id, PCI_VENDOR_QUMRANET,
 				PCI_PRODUCT_QUMRANET_VIO_NET, PCI_CLASS_SYSTEM,
 				PCI_SUBCLASS_SYSTEM_MISC, PCI_VENDOR_OPENBSD,
-				PCI_PRODUCT_VIRTIO_NETWORK, 1, NULL)) {
+				PCI_PRODUCT_VIRTIO_NETWORK, 0, 1, NULL)) {
 				log_warnx("%s: can't add PCI virtio net device",
 				    __progname);
 				return;
 			}
-			dev->pci_id = id;
-			dev->sync_fd = -1;
-			dev->async_fd = -1;
-			dev->vm_id = vcp->vcp_id;
-			dev->vm_vmid = vm->vm_vmid;
-			dev->irq = pci_get_dev_irq(id);
-
-			/* The vionet pci bar function is called by the vcpu. */
+			virtio_dev_init(dev, id, VIONET_QUEUE_SIZE_DEFAULT,
+			    VIRTIO_NET_QUEUES, VIRTIO_NET_F_MAC, vcp->vcp_id);
 			if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_pci_io,
-			    dev)) {
+			    dev) == -1) {
 				log_warnx("%s: can't add bar for virtio net "
 				    "device", __progname);
 				return;
 			}

-			dev->vionet.vq[RXQ].qs = VIONET_QUEUE_SIZE;
-			dev->vionet.vq[RXQ].vq_availoffset =
-			    sizeof(struct vring_desc) * VIONET_QUEUE_SIZE;
-			dev->vionet.vq[RXQ].vq_usedoffset = VIRTQUEUE_ALIGN(
-				sizeof(struct vring_desc) * VIONET_QUEUE_SIZE
-				+ sizeof(uint16_t) * (2 + VIONET_QUEUE_SIZE));
-			dev->vionet.vq[RXQ].last_avail = 0;
-			dev->vionet.vq[RXQ].notified_avail = 0;
-
-			dev->vionet.vq[TXQ].qs = VIONET_QUEUE_SIZE;
-			dev->vionet.vq[TXQ].vq_availoffset =
-			    sizeof(struct vring_desc) * VIONET_QUEUE_SIZE;
-			dev->vionet.vq[TXQ].vq_usedoffset = VIRTQUEUE_ALIGN(
-				sizeof(struct vring_desc) * VIONET_QUEUE_SIZE
-				+ sizeof(uint16_t) * (2 + VIONET_QUEUE_SIZE));
-			dev->vionet.vq[TXQ].last_avail = 0;
-			dev->vionet.vq[TXQ].notified_avail = 0;
-
+			/* Device specific initializiation. */
+			dev->dev_type = VMD_DEVTYPE_NET;
+			dev->vm_vmid = vm->vm_vmid;
 			dev->vionet.data_fd = child_taps[i];

 			/* MAC address has been assigned by the parent */
 			memcpy(&dev->vionet.mac, &vmc->vmc_macs[i], 6);
-			dev->vionet.cfg.device_feature = VIRTIO_NET_F_MAC;
-
 			dev->vionet.lockedmac =
 			    vmc->vmc_ifflags[i] & VMIFF_LOCKED ? 1 : 0;
 			dev->vionet.local =
@@ -645,52 +1075,39 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
 		}
 	}

+	/* Virtio 0.9 Block Devices */
 	if (vmc->vmc_ndisks > 0) {
 		for (i = 0; i < vmc->vmc_ndisks; i++) {
-			dev = calloc(1, sizeof(struct virtio_dev));
+			dev = malloc(sizeof(struct virtio_dev));
 			if (dev == NULL) {
-				log_warn("%s: calloc failure allocating vioblk",
-				    __progname);
+				log_warn("%s: failure allocating vioblk",
+				    __func__);
 				return;
 			}
-
-			/* One vioblk device for each disk defined in vcp */
-			dev->dev_type = VMD_DEVTYPE_DISK;
-
 			if (pci_add_device(&id, PCI_VENDOR_QUMRANET,
 			    PCI_PRODUCT_QUMRANET_VIO_BLOCK,
 			    PCI_CLASS_MASS_STORAGE,
-			    PCI_SUBCLASS_MASS_STORAGE_SCSI,
-			    PCI_VENDOR_OPENBSD,
-			    PCI_PRODUCT_VIRTIO_BLOCK, 1, NULL)) {
+			    PCI_SUBCLASS_MASS_STORAGE_SCSI, PCI_VENDOR_OPENBSD,
+			    PCI_PRODUCT_VIRTIO_BLOCK, 0, 1, NULL)) {
 				log_warnx("%s: can't add PCI virtio block "
 				    "device", __progname);
 				return;
 			}
-			dev->pci_id = id;
-			dev->sync_fd = -1;
-			dev->async_fd = -1;
-			dev->vm_id = vcp->vcp_id;
-			dev->vm_vmid = vm->vm_vmid;
-			dev->irq = pci_get_dev_irq(id);
-
+			virtio_dev_init(dev, id, VIOBLK_QUEUE_SIZE_DEFAULT,
+			    VIRTIO_BLK_QUEUES, VIRTIO_BLK_F_SEG_MAX,
+			    vcp->vcp_id);
 			if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_pci_io,
-			    &dev->vioblk)) {
+			    dev) == -1) {
 				log_warnx("%s: can't add bar for virtio block "
 				    "device", __progname);
 				return;
 			}
-			dev->vioblk.vq[0].qs = VIOBLK_QUEUE_SIZE;
-			dev->vioblk.vq[0].vq_availoffset =
-			    sizeof(struct vring_desc) * VIOBLK_QUEUE_SIZE;
-			dev->vioblk.vq[0].vq_usedoffset = VIRTQUEUE_ALIGN(
-			    sizeof(struct vring_desc) * VIOBLK_QUEUE_SIZE
-			    + sizeof(uint16_t) * (2 + VIOBLK_QUEUE_SIZE));
-			dev->vioblk.vq[0].last_avail = 0;
-			dev->vioblk.cfg.device_feature =
-			    VIRTIO_BLK_F_SEG_MAX;
-			dev->vioblk.seg_max = VIOBLK_SEG_MAX;

+			/* Device specific initialization. */
+			dev->dev_type = VMD_DEVTYPE_DISK;
+			dev->vm_vmid = vm->vm_vmid;
+			dev->vioblk.seg_max = VIOBLK_SEG_MAX_DEFAULT;
+
 			/*
 			 * Initialize disk fds to an invalid fd (-1), then
 			 * set any child disk fds.
@@ -714,89 +1131,74 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
 			fatalx("failed to launch virtio device");
 	}

-	/* vioscsi cdrom */
+	/* Virtio 1.x SCSI CD-ROM */
 	if (strlen(vmc->vmc_cdrom)) {
-		vioscsi = calloc(1, sizeof(struct vioscsi_dev));
-		if (vioscsi == NULL) {
+		dev = malloc(sizeof(struct virtio_dev));
+		if (dev == 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)) {
+		    PCI_PRODUCT_QUMRANET_VIO1_SCSI, PCI_CLASS_MASS_STORAGE,
+		    PCI_SUBCLASS_MASS_STORAGE_SCSI, PCI_VENDOR_OPENBSD,
+		    PCI_PRODUCT_VIRTIO_SCSI, 1, 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)) {
+		virtio_dev_init(dev, id, VIOSCSI_QUEUE_SIZE_DEFAULT,
+		    VIRTIO_SCSI_QUEUES, VIRTIO_F_VERSION_1, vcp->vcp_id);
+		if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_io_dispatch, dev)
+		    == -1) {
 			log_warnx("%s: can't add bar for vioscsi device",
 			    __progname);
 			return;
 		}
+		virtio_pci_add_cap(id, VIRTIO_PCI_CAP_COMMON_CFG, bar_id, 0);
+		virtio_pci_add_cap(id, VIRTIO_PCI_CAP_DEVICE_CFG, bar_id, 36);
+		virtio_pci_add_cap(id, VIRTIO_PCI_CAP_ISR_CFG, bar_id, 0);
+		virtio_pci_add_cap(id, VIRTIO_PCI_CAP_NOTIFY_CFG, bar_id, 0);

-		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;
-		}
-		if (virtio_raw_init(&vioscsi->file, &vioscsi->sz, &child_cdrom,
-		    1) == -1) {
+		/* Device specific initialization. */
+		if (virtio_raw_init(&dev->vioscsi.file, &dev->vioscsi.sz,
+		    &child_cdrom, 1) == -1) {
 			log_warnx("%s: unable to determine iso format",
 			    __func__);
 			return;
 		}
-		vioscsi->locked = 0;
-		vioscsi->lba = 0;
-		vioscsi->n_blocks = vioscsi->sz / VIOSCSI_BLOCK_SIZE_CDROM;
-		vioscsi->max_xfer = VIOSCSI_BLOCK_SIZE_CDROM;
-		vioscsi->pci_id = id;
-		vioscsi->vm_id = vcp->vcp_id;
-		vioscsi->irq = pci_get_dev_irq(id);
+		dev->vioscsi.locked = 0;
+		dev->vioscsi.lba = 0;
+		dev->vioscsi.n_blocks = dev->vioscsi.sz /
+		    VIOSCSI_BLOCK_SIZE_CDROM;
+		dev->vioscsi.max_xfer = VIOSCSI_BLOCK_SIZE_CDROM;
 	}

-	/* virtio control device */
-	if (pci_add_device(&id, PCI_VENDOR_OPENBSD,
-	    PCI_PRODUCT_OPENBSD_CONTROL,
-	    PCI_CLASS_COMMUNICATIONS,
-	    PCI_SUBCLASS_COMMUNICATIONS_MISC,
-	    PCI_VENDOR_OPENBSD,
-	    PCI_PRODUCT_VIRTIO_VMMCI, 1, NULL)) {
+	/* Virtio 0.9 VMM Control Interface */
+	dev = &vmmci;
+	if (pci_add_device(&id, PCI_VENDOR_OPENBSD, PCI_PRODUCT_OPENBSD_CONTROL,
+	    PCI_CLASS_COMMUNICATIONS, PCI_SUBCLASS_COMMUNICATIONS_MISC,
+	    PCI_VENDOR_OPENBSD, PCI_PRODUCT_VIRTIO_VMMCI, 0, 1, NULL)) {
 		log_warnx("%s: can't add PCI vmm control device",
 		    __progname);
 		return;
 	}
-
-	if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, vmmci_io, NULL)) {
+	virtio_dev_init(dev, id, 0, 0,
+	    VMMCI_F_TIMESYNC | VMMCI_F_ACK | VMMCI_F_SYNCRTC, vcp->vcp_id);
+	if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, vmmci_io, dev) == -1) {
 		log_warnx("%s: can't add bar for vmm control device",
 		    __progname);
 		return;
 	}

-	memset(&vmmci, 0, sizeof(vmmci));
-	vmmci.cfg.device_feature = VMMCI_F_TIMESYNC | VMMCI_F_ACK |
-	    VMMCI_F_SYNCRTC;
-	vmmci.vm_id = vcp->vcp_id;
-	vmmci.irq = pci_get_dev_irq(id);
-	vmmci.pci_id = id;
-	ret = pthread_mutex_init(&vmmci.mutex, NULL);
+	ret = pthread_mutex_init(&dev->vmmci.mutex, NULL);
 	if (ret) {
 		errno = ret;
 		fatal("could not initialize vmmci mutex");
 	}
-
-	evtimer_set(&vmmci.timeout, vmmci_timeout, NULL);
-	vm_pipe_init(&vmmci.dev_pipe, vmmci_pipe_dispatch);
-	event_add(&vmmci.dev_pipe.read_ev, NULL);
+	evtimer_set(&dev->vmmci.timeout, vmmci_timeout, NULL);
+	vm_pipe_init2(&dev->vmmci.dev_pipe, vmmci_pipe_dispatch, dev);
+	event_add(&dev->vmmci.dev_pipe.read_ev, NULL);
 }

 /*
@@ -855,7 +1257,7 @@ virtio_shutdown(struct vmd_vm *vm)

 	/* Ensure that our disks are synced. */
 	if (vioscsi != NULL)
-		vioscsi->file.close(vioscsi->file.p, 0);
+		vioscsi->vioscsi.file.close(vioscsi->vioscsi.file.p, 0);

 	/*
 	 * Broadcast shutdown to child devices. We need to do this
@@ -923,6 +1325,122 @@ virtio_start(struct vmd_vm *vm)
 }

 /*
+ * Initialize a new virtio device structure.
+ */
+static void
+virtio_dev_init(struct virtio_dev *dev, uint8_t pci_id, uint16_t queue_size,
+    uint16_t num_queues, uint64_t features, uint32_t vm_id)
+{
+	size_t i;
+	uint16_t device_id;
+
+	if (num_queues > 0 && num_queues > VIRTIO_MAX_QUEUES)
+		fatalx("%s: num_queues too large", __func__);
+
+	device_id = pci_get_subsys_id(pci_id);
+	if (!device_id)
+		fatalx("%s: invalid pci device id %u", __func__, pci_id);
+
+	memset(dev, 0, sizeof(*dev));
+
+	dev->pci_id = pci_id;
+	dev->device_id = device_id;
+	dev->irq = pci_get_dev_irq(pci_id);
+	dev->vm_id = vm_id;
+
+	dev->device_feature = features;
+	dev->cfg.device_feature = features;
+
+	dev->num_queues = num_queues;
+	dev->queue_size = queue_size;
+	dev->cfg.queue_size = queue_size;
+
+	dev->async_fd = -1;
+	dev->sync_fd = -1;
+
+	if (num_queues > 0) {
+		for (i = 0; i < num_queues; i++)
+			virtio_vq_init(dev, i);
+	}
+}
+
+void
+virtio_vq_init(struct virtio_dev *dev, size_t idx)
+{
+	struct virtio_vq_info *vq_info = NULL;
+	int v1 = (dev->device_feature & VIRTIO_F_VERSION_1) ? 1 : 0;
+
+	if (idx >= dev->num_queues)
+		fatalx("%s: invalid virtqueue index", __func__);
+	vq_info = &dev->vq[idx];
+
+	vq_info->q_gpa = 0;
+	vq_info->qs = dev->queue_size;
+	vq_info->mask = dev->queue_size - 1;
+
+	if (v1) {
+		vq_info->vq_enabled = 0;
+		vq_info->vq_availoffset = 0;
+		vq_info->vq_usedoffset = 0;
+	} else {
+		/* Always enable on pre-1.0 virtio devices. */
+		vq_info->vq_enabled = 1;
+		vq_info->vq_availoffset =
+		    sizeof(struct vring_desc) * vq_info->qs;
+		vq_info->vq_usedoffset = VIRTQUEUE_ALIGN(
+		    sizeof(struct vring_desc) * vq_info->qs +
+		    sizeof(uint16_t) * (2 + vq_info->qs));
+	}
+
+	vq_info->last_avail = 0;
+	vq_info->notified_avail = 0;
+}
+
+
+static void
+virtio_pci_add_cap(uint8_t pci_id, uint8_t cfg_type, uint8_t bar_id,
+    uint32_t dev_cfg_len)
+{
+	struct virtio_pci_common_cap cap;
+
+	memset(&cap, 0, sizeof(cap));
+
+	cap.virtio.cap_vndr = PCI_CAP_VENDSPEC;
+	cap.virtio.cap_len = sizeof(struct virtio_pci_cap);
+	cap.virtio.bar = bar_id;
+	cap.virtio.cfg_type = cfg_type;
+
+	switch (cfg_type) {
+	case VIRTIO_PCI_CAP_COMMON_CFG:
+		cap.virtio.offset = VIO1_CFG_BAR_OFFSET;
+		cap.virtio.length = sizeof(struct virtio_pci_common_cfg);
+		break;
+	case VIRTIO_PCI_CAP_DEVICE_CFG:
+		/* XXX maybe inspect the virtio device and lookup the len. */
+		cap.virtio.offset = VIO1_DEV_BAR_OFFSET;
+		cap.virtio.length = dev_cfg_len;
+		break;
+	case VIRTIO_PCI_CAP_ISR_CFG:
+		cap.virtio.offset = VIO1_ISR_BAR_OFFSET;
+		cap.virtio.length = sizeof(uint8_t);
+		break;
+	case VIRTIO_PCI_CAP_NOTIFY_CFG:
+		cap.virtio.offset = VIO1_NOTIFY_BAR_OFFSET;
+		cap.virtio.length = sizeof(uint16_t);
+		cap.notify.notify_off_multiplier = 0;
+		break;
+	default:
+		fatalx("%s: invalid pci capability config type %u", __func__,
+		    cfg_type);
+	}
+
+	if (pci_add_capability(pci_id, &cap.pci) == -1) {
+		fatalx("%s: can't add capability for virtio pci device %u",
+		    __func__, pci_id);
+	}
+}
+
+/*
  * Fork+exec a child virtio device. Returns 0 on success.
  */
 static int
blob - 4bd6f68b41fcb8ce68781903e4364e543adc0075
blob + 01ca3c0d139e08067dd3c783e0dbec739a121df3
--- usr.sbin/vmd/virtio.h
+++ usr.sbin/vmd/virtio.h
@@ -19,11 +19,14 @@
 #include <sys/types.h>

 #include <dev/pv/virtioreg.h>
+#include <dev/pci/virtio_pcireg.h>
 #include <net/if_tun.h>

 #include <event.h>
+#include <stddef.h>

 #include "vmd.h"
+#include "pci.h"

 #ifndef _VIRTIO_H_
 #define _VIRTIO_H_
@@ -33,19 +36,54 @@
 #define ALIGNSZ(sz, align)	((sz + align - 1) & ~(align - 1))
 #define MIN(a,b)		(((a)<(b))?(a):(b))

+#define VIO1_PCI_DEVICE_FEATURE_SELECT					\
+	(offsetof(struct virtio_pci_common_cfg, device_feature_select))
+#define VIO1_PCI_DEVICE_FEATURE						\
+	(offsetof(struct virtio_pci_common_cfg, device_feature))
+#define VIO1_PCI_DRIVER_FEATURE_SELECT					\
+	(offsetof(struct virtio_pci_common_cfg, driver_feature_select))
+#define VIO1_PCI_DRIVER_FEATURE						\
+	(offsetof(struct virtio_pci_common_cfg, driver_feature))
+#define VIO1_PCI_CONFIG_MSIX_VECTOR					\
+	(offsetof(struct virtio_pci_common_cfg, config_msix_vector))
+#define VIO1_PCI_NUM_QUEUES						\
+	(offsetof(struct virtio_pci_common_cfg, num_queues))
+#define VIO1_PCI_DEVICE_STATUS						\
+	(offsetof(struct virtio_pci_common_cfg, device_status))
+#define VIO1_PCI_CONFIG_GENERATION					\
+	(offsetof(struct virtio_pci_common_cfg, config_generation))
+#define VIO1_PCI_QUEUE_SELECT						\
+	(offsetof(struct virtio_pci_common_cfg, queue_select))
+#define VIO1_PCI_QUEUE_SIZE						\
+	(offsetof(struct virtio_pci_common_cfg, queue_size))
+#define VIO1_PCI_QUEUE_MSIX_VECTOR					\
+	(offsetof(struct virtio_pci_common_cfg, queue_msix_vector))
+#define VIO1_PCI_QUEUE_ENABLE						\
+	(offsetof(struct virtio_pci_common_cfg, queue_enable))
+#define VIO1_PCI_QUEUE_NOTIFY_OFF					\
+	(offsetof(struct virtio_pci_common_cfg, queue_notify_off))
+#define VIO1_PCI_QUEUE_DESC						\
+	(offsetof(struct virtio_pci_common_cfg, queue_desc))
+#define VIO1_PCI_QUEUE_AVAIL						\
+	(offsetof(struct virtio_pci_common_cfg, queue_avail))
+#define VIO1_PCI_QUEUE_USED						\
+	(offsetof(struct virtio_pci_common_cfg, queue_used))
+
+#define VIO1_CFG_BAR_OFFSET		0x000
+#define VIO1_NOTIFY_BAR_OFFSET		0x100
+#define VIO1_ISR_BAR_OFFSET		0x200
+#define VIO1_DEV_BAR_OFFSET		0x300
+
 /* Queue sizes must be power of two and less than IOV_MAX (1024). */
-#define VIORND_QUEUE_SIZE	64
-#define VIORND_QUEUE_MASK	(VIORND_QUEUE_SIZE - 1)
+#define VIRTIO_QUEUE_SIZE_MAX		IOV_MAX
+#define VIORND_QUEUE_SIZE_DEFAULT	64

-#define VIOBLK_QUEUE_SIZE	128
-#define VIOBLK_QUEUE_MASK	(VIOBLK_QUEUE_SIZE - 1)
-#define VIOBLK_SEG_MAX		(VIOBLK_QUEUE_SIZE - 2)
+#define VIOBLK_QUEUE_SIZE_DEFAULT	128
+#define VIOBLK_SEG_MAX_DEFAULT		(VIOBLK_QUEUE_SIZE_DEFAULT - 2)

-#define VIOSCSI_QUEUE_SIZE	128
-#define VIOSCSI_QUEUE_MASK	(VIOSCSI_QUEUE_SIZE - 1)
+#define VIOSCSI_QUEUE_SIZE_DEFAULT	128

-#define VIONET_QUEUE_SIZE	256
-#define VIONET_QUEUE_MASK	(VIONET_QUEUE_SIZE - 1)
+#define VIONET_QUEUE_SIZE_DEFAULT	256

 /* Virtio network device is backed by tap(4), so inherit limits */
 #define VIONET_HARD_MTU		TUNMRU
@@ -62,6 +100,11 @@
  * vionet - 2 queues
  * vioscsi - 3 queues
  */
+#define VIRTIO_RND_QUEUES	1
+#define VIRTIO_BLK_QUEUES	1
+#define VIRTIO_NET_QUEUES	2
+#define VIRTIO_SCSI_QUEUES	3
+#define VIRTIO_VMMCI_QUEUES	0
 #define VIRTIO_MAX_QUEUES	3

 #define MAXPHYS	(64 * 1024)	/* max raw I/O transfer size */
@@ -74,6 +117,14 @@
 #define DESC_WRITABLE(/* struct vring_desc */ x)	\
 	(((x)->flags & VRING_DESC_F_WRITE) ? 1 : 0)

+struct virtio_pci_common_cap {
+	union {
+		struct pci_cap pci;
+		struct virtio_pci_cap virtio;
+		struct virtio_pci_notify_cap notify;
+		struct virtio_pci_cfg_cap cfg;
+	};
+} __packed;

 /*
  * VM <-> Device messaging.
@@ -104,18 +155,15 @@ struct viodev_msg {
 } __packed;

 /*
- * This struct stores notifications from a virtio driver. There is
- * one such struct per virtio device.
+ * Legacy Virtio 0.9 register state.
  */
 struct virtio_io_cfg {
 	uint32_t device_feature;
 	uint32_t guest_feature;
 	uint32_t queue_pfn;
-	uint16_t queue_size;
 	uint16_t queue_select;
+	uint16_t queue_size;
 	uint16_t queue_notify;
-	uint8_t device_status;
-	uint8_t isr_status;
 };

 struct virtio_backing {
@@ -143,6 +191,9 @@ struct virtio_vq_info {
 	/* Queue size: number of queue entries in virtq */
 	uint32_t qs;

+	/* Queue mask */
+	uint32_t mask;
+
 	/*
 	 * The offset of the 'available' ring within the virtq located at
 	 * guest physical address qa above
@@ -166,6 +217,8 @@ struct virtio_vq_info {
 	 * driver notified to the host.
 	 */
 	uint16_t notified_avail;
+
+	uint8_t vq_enabled;
 };

 /*
@@ -203,21 +256,13 @@ struct virtio_vq_acct {
 };

 struct viornd_dev {
-	struct virtio_io_cfg cfg;
-
-	struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
-
-	uint8_t pci_id;
-	int irq;
-	uint32_t vm_id;
+	/* Nothing */
 };

 struct vioblk_dev {
-	struct virtio_io_cfg cfg;
-	struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
 	struct virtio_backing file;
-
 	int disk_fd[VM_MAX_BASE_PER_DISK];	/* fds for disk image(s) */
+
 	uint8_t ndisk_fd;	/* number of valid disk fds */
 	uint64_t capacity;	/* size in 512 byte sectors */
 	uint32_t seg_max;	/* maximum number of segments */
@@ -232,31 +277,16 @@ struct vioblk_dev {
  * 2 - requests
  */
 struct vioscsi_dev {
-	struct virtio_io_cfg cfg;
-
-	struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
-
 	struct virtio_backing file;

-	/* is the device locked */
-	int locked;
-	/* 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;
+	int locked;		/* is the device locked? */
+	uint64_t sz;		/* size of iso file in bytes */
+	uint64_t lba;		/* last block address read */
+	uint64_t n_blocks;	/* number of blocks represented in iso */
 	uint32_t max_xfer;
-
-	uint8_t pci_id;
-	uint32_t vm_id;
-	int irq;
 };

 struct vionet_dev {
-	struct virtio_io_cfg cfg;
-	struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
-
 	int data_fd;		/* fd for our tap device */

 	uint8_t mac[6];
@@ -269,28 +299,6 @@ struct vionet_dev {
 	unsigned int idx;
 };

-struct virtio_dev {
-	union {
-		struct vioblk_dev vioblk;
-		struct vionet_dev vionet;
-	};
-
-	struct imsgev async_iev;
-	struct imsgev sync_iev;
-
-	int sync_fd;		/* fd for synchronous channel */
-	int async_fd;		/* fd for async channel */
-
-	uint8_t pci_id;
-	uint32_t vm_id;
-	uint32_t vm_vmid;
-	int irq;
-
-	pid_t dev_pid;
-	char dev_type;
-	SLIST_ENTRY(virtio_dev) dev_next;
-};
-
 struct virtio_net_hdr {
 	uint8_t flags;
 	uint8_t gso_type;
@@ -315,13 +323,9 @@ enum vmmci_cmd {
 };

 struct vmmci_dev {
-	struct virtio_io_cfg cfg;
 	struct event timeout;
 	struct timeval time;
 	enum vmmci_cmd cmd;
-	uint32_t vm_id;
-	int irq;
-	uint8_t pci_id;

 	pthread_mutex_t mutex;
 	struct vm_dev_pipe dev_pipe;
@@ -334,8 +338,53 @@ struct ioinfo {
 	off_t offset;
 };

+struct virtio_dev {
+	uint16_t device_id;			/* Virtio device id [r] */
+	union {
+		/* Multi-process enabled. */
+		struct vioblk_dev vioblk;
+		struct vionet_dev vionet;
+
+		/* In-process only. */
+		struct viornd_dev viornd;
+		struct vmmci_dev vmmci;
+		struct vioscsi_dev vioscsi;
+	};
+
+	struct virtio_io_cfg		cfg;		/* Virtio 0.9 */
+	struct virtio_pci_common_cfg	pci_cfg;	/* Virtio 1.x */
+	struct virtio_vq_info		vq[VIRTIO_MAX_QUEUES];	/* Virtqueues */
+
+	uint16_t num_queues;			/* number of virtqueues [r] */
+	uint16_t queue_size;			/* default queue size [r] */
+
+	uint8_t		isr;			/* isr status register [rw] */
+	uint8_t		status;			/* device status register [rw] */
+	uint64_t	device_feature;		/* device features [r] */
+	uint64_t 	driver_feature;		/* driver features [rw] */
+
+	uint8_t		pci_id;			/* pci device id [r] */
+	uint32_t	vm_id;			/* vmm(4) vm identifier [r] */
+	int		irq;			/* assigned irq [r] */
+
+	/* Multi-process emulation fields. */
+	struct imsgev async_iev;		/* async imsg event [r] */
+	struct imsgev sync_iev;			/* sync imsg event [r] */
+
+	int sync_fd;				/* fd for synchronous channel */
+	int async_fd;				/* fd for async channel */
+
+	uint32_t	vm_vmid;		/* vmd(8) vm identifier [r] */
+	pid_t		dev_pid;		/* pid of emulator process */
+	char		dev_type;		/* device type (as char) */
+	SLIST_ENTRY(virtio_dev) dev_next;
+};
+
 /* virtio.c */
+extern struct virtio_dev vmmci;
+
 void virtio_init(struct vmd_vm *, int, int[][VM_MAX_BASE_PER_DISK], int *);
+void virtio_vq_init(struct virtio_dev *, size_t);
 void virtio_broadcast_imsg(struct vmd_vm *, uint16_t, void *, uint16_t);
 void virtio_stop(struct vmd_vm *);
 void virtio_start(struct vmd_vm *);
@@ -348,10 +397,8 @@ int virtio_pci_io(int, uint16_t, uint32_t *, uint8_t *
 void virtio_assert_irq(struct virtio_dev *, int);
 void virtio_deassert_irq(struct virtio_dev *, int);

-int virtio_rnd_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
-void viornd_update_qs(void);
-void viornd_update_qa(void);
-int viornd_notifyq(void);
+void virtio_update_qs(struct virtio_dev *);
+void virtio_update_qa(struct virtio_dev *);

 ssize_t virtio_qcow2_get_base(int, char *, size_t, const char *);
 int virtio_qcow2_create(const char *, const char *, uint64_t);
@@ -362,8 +409,7 @@ int virtio_raw_init(struct virtio_backing *, off_t *,
 void vionet_set_hostmac(struct vmd_vm *, unsigned int, uint8_t *);

 int vmmci_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
-int vmmci_ctl(unsigned int);
-void vmmci_ack(unsigned int);
+int vmmci_ctl(struct virtio_dev *, unsigned int);
 void vmmci_timeout(int, short, void *);

 const char *vioblk_cmd_name(uint32_t);
@@ -373,9 +419,7 @@ ssize_t dhcp_request(struct virtio_dev *, char *, size

 /* 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 *);
+int vioscsi_notifyq(struct virtio_dev *, uint16_t);

 /* imsg handling */
 void	viodev_msg_read(struct imsg *, struct viodev_msg *);
blob - 96958e1f611bc01f1ed9224a54d7a0b1399bae3e
blob + 7908417f86e7210f8cd41e6bd96b17b812990252
--- usr.sbin/vmd/vm.c
+++ usr.sbin/vmd/vm.c
@@ -356,11 +356,11 @@ vm_dispatch_vmm(int fd, short event, void *arg)
 			    sizeof(verbose));
 			break;
 		case IMSG_VMDOP_VM_SHUTDOWN:
-			if (vmmci_ctl(VMMCI_SHUTDOWN) == -1)
+			if (vmmci_ctl(&vmmci, VMMCI_SHUTDOWN) == -1)
 				_exit(0);
 			break;
 		case IMSG_VMDOP_VM_REBOOT:
-			if (vmmci_ctl(VMMCI_REBOOT) == -1)
+			if (vmmci_ctl(&vmmci, VMMCI_REBOOT) == -1)
 				_exit(0);
 			break;
 		case IMSG_VMDOP_PAUSE_VM:
blob - a521d1358fffb4c23170c86931859231b35b5481
blob + 119d411f279311a87efae595a21748b31fbec853
--- usr.sbin/vmd/vmd.h
+++ usr.sbin/vmd/vmd.h
@@ -43,6 +43,9 @@

 #define nitems(_a)      (sizeof((_a)) / sizeof((_a)[0]))

+#define CTASSERT(x)	extern char  _ctassert[(x) ? 1 : -1 ] \
+			    __attribute__((__unused__))
+
 #define MB(x)	(x * 1024UL * 1024UL)
 #define GB(x)	(x * 1024UL * 1024UL * 1024UL)