Index | Thread | Search

From:
Dave Voutila <dv@sisu.io>
Subject:
Re: teaching vmd virtio 1.2
To:
tech@openbsd.org
Cc:
sf@openbsd.org, mlarkin@openbsd.org
Date:
Thu, 31 Jul 2025 05:49:31 -0400

Download raw body.

Thread
Dave Voutila <dv@sisu.io> writes:

> 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).
>

Had ample time on a flight and first day of hackathon, so here's a diff
that includes migrating virtio block and network devices as
well. Only device left is/will be vmmci.

Positive test reports on the previous diff and this just builds off of
that one.

I can either carve out the vioblk and vionet stuff from this diff and
commit separately or all at once.

OKs or feedback?


diffstat refs/heads/master refs/heads/virtio-1.2-uber
 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    |  104+  254-
 M  usr.sbin/vmd/vionet.c    |  413+  190-
 M  usr.sbin/vmd/vioscsi.c   |  359+  599-
 M  usr.sbin/vmd/virtio.c    |  837+  299-
 M  usr.sbin/vmd/virtio.h    |  131+   92-
 M  usr.sbin/vmd/vm.c        |    2+    2-
 M  usr.sbin/vmd/vmd.h       |    3+    0-

10 files changed, 1921 insertions(+), 1447 deletions(-)

diff refs/heads/master refs/heads/virtio-1.2-uber
commit - 788294299689adc0a6c392611e2b1f3c1288bdd5
commit + 52df8a6fe59487f743b8d5f5c80a4ff2adf0e0ba
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 + ebd63a04cd71c00a681c8c9175dc0af05b552b95
--- 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 uint32_t vio1_read(struct virtio_dev *, struct viodev_msg *, int *);
+static int vio1_write(struct virtio_dev *, struct viodev_msg *);
+static uint32_t vioblk_dev_read(struct virtio_dev *, struct viodev_msg *);

-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 *, uint16_t);
+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,26 +248,28 @@ 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, uint16_t vq_idx)
 {
 	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)
+	if (vq_idx > dev->num_queues)
 		return (0);

-	vq_info = &dev->vq[dev->cfg.queue_notify];
+	vq_info = &dev->vq[vq_idx];
 	idx = vq_info->last_avail;
 	vr = vq_info->q_hva;
 	if (vr == NULL)
@@ -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);
 }

@@ -498,7 +462,7 @@ handle_sync_io(int fd, short event, void *arg)
 	struct viodev_msg msg;
 	struct imsg imsg;
 	ssize_t n;
-	int8_t intr = INTR_STATE_NOOP;
+	int deassert = 0;

 	if (event & EV_READ) {
 		if ((n = imsgbuf_read(ibuf)) == -1)
@@ -538,15 +502,18 @@ handle_sync_io(int fd, short event, void *arg)
 		switch (msg.type) {
 		case VIODEV_MSG_IO_READ:
 			/* Read IO: make sure to send a reply */
-			msg.data = handle_io_read(&msg, dev, &intr);
+			msg.data = vio1_read(dev, &msg, &deassert);
 			msg.data_valid = 1;
-			msg.state = intr;
+			if (deassert) {
+				/* Inline any interrupt deassertions. */
+				msg.state = INTR_STATE_DEASSERT;
+			}
 			imsg_compose_event(iev, IMSG_DEVOP_MSG, 0, 0, -1, &msg,
 			    sizeof(msg));
 			break;
 		case VIODEV_MSG_IO_WRITE:
-			/* Write IO: no reply needed */
-			if (handle_io_write(&msg, dev) == 1)
+			/* Write IO: no reply needed, but maybe an irq assert */
+			if (vio1_write(dev, &msg))
 				virtio_assert_irq(dev, 0);
 			break;
 		case VIODEV_MSG_SHUTDOWN:
@@ -561,223 +528,106 @@ handle_sync_io(int fd, short event, void *arg)
 }

 static int
-handle_io_write(struct viodev_msg *msg, struct virtio_dev *dev)
+vio1_write(struct virtio_dev *dev, struct viodev_msg *msg)
 {
-	struct vioblk_dev *vioblk = &dev->vioblk;
 	uint32_t data = msg->data;
+	uint16_t reg = msg->reg;
+	uint8_t sz = msg->io_sz;
 	int intr = 0;

-	switch (msg->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(msg->reg));
+	switch (reg & 0xFF00) {
+	case VIO1_CFG_BAR_OFFSET:
+		(void)virtio_io_cfg(dev, VEI_DIR_OUT, (reg & 0x00FF), data, sz);
 		break;
-	case VIRTIO_CONFIG_GUEST_FEATURES:
-		vioblk->cfg.guest_feature = data;
+	case VIO1_DEV_BAR_OFFSET:
+		/* Ignore all writes to device configuration registers. */
 		break;
-	case VIRTIO_CONFIG_QUEUE_PFN:
-		vioblk->cfg.queue_pfn = data;
-		vioblk_update_qa(vioblk);
+	case VIO1_NOTIFY_BAR_OFFSET:
+		intr = vioblk_notifyq(dev, (uint16_t)(msg->data));
 		break;
-	case VIRTIO_CONFIG_QUEUE_SELECT:
-		vioblk->cfg.queue_select = data;
-		vioblk_update_qs(vioblk);
+	case VIO1_ISR_BAR_OFFSET:
+		/* Ignore writes to ISR. */
 		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))
-				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;
-			virtio_deassert_irq(dev, msg->vcpu);
-		}
-		break;
 	default:
-		break;
+		log_debug("%s: no handler for reg 0x%04x", __func__, reg);
 	}
+
 	return (intr);
 }

 static uint32_t
-handle_io_read(struct viodev_msg *msg, struct virtio_dev *dev, int8_t *intr)
+vio1_read(struct virtio_dev *dev, struct viodev_msg *msg, int *deassert)
 {
-	struct vioblk_dev *vioblk = &dev->vioblk;
+	uint32_t data = (uint32_t)(-1);
+	uint16_t reg = msg->reg;
 	uint8_t sz = msg->io_sz;
-	uint32_t data;

-	if (msg->data_valid)
-		data = msg->data;
-	else
-		data = 0;
-
-	switch (msg->reg) {
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
-		switch (sz) {
-		case 4:
-			data = (uint32_t)(vioblk->capacity);
-			break;
-		case 2:
-			data &= 0xFFFF0000;
-			data |= (uint32_t)(vioblk->capacity) & 0xFFFF;
-			break;
-		case 1:
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->capacity) & 0xFF;
-			break;
-		}
-		/* XXX handle invalid sz */
+	switch (reg & 0xFF00) {
+	case VIO1_CFG_BAR_OFFSET:
+		data = virtio_io_cfg(dev, VEI_DIR_IN, (uint8_t)reg, 0, sz);
 		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 1:
-		if (sz == 1) {
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->capacity >> 8) & 0xFF;
-		}
-		/* XXX handle invalid sz */
+	case VIO1_DEV_BAR_OFFSET:
+		data = vioblk_dev_read(dev, msg);
 		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 2:
-		if (sz == 1) {
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->capacity >> 16) & 0xFF;
-		} else if (sz == 2) {
-			data &= 0xFFFF0000;
-			data |= (uint32_t)(vioblk->capacity >> 16) & 0xFFFF;
-		}
-		/* XXX handle invalid sz */
+	case VIO1_NOTIFY_BAR_OFFSET:
+		/* Reads of notify register return all 1's. */
 		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 3:
-		if (sz == 1) {
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->capacity >> 24) & 0xFF;
-		}
-		/* XXX handle invalid sz */
+	case VIO1_ISR_BAR_OFFSET:
+		data = dev->isr;
+		dev->isr = 0;
+		*deassert = 1;
 		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4:
-		switch (sz) {
-		case 4:
-			data = (uint32_t)(vioblk->capacity >> 32);
+	default:
+		log_debug("%s: no handler for reg 0x%04x", __func__, reg);
+	}
+
+	return (data);
+}
+
+static uint32_t
+vioblk_dev_read(struct virtio_dev *dev, struct viodev_msg *msg)
+{
+	struct vioblk_dev *vioblk = (struct vioblk_dev *)&dev->vioblk;
+	uint32_t data = (uint32_t)(-1);
+	uint16_t reg = msg->reg;
+	uint8_t sz = msg->io_sz;
+
+	switch (reg & 0xFF) {
+	case VIRTIO_BLK_CONFIG_CAPACITY:
+		if (sz != 4) {
+			log_warnx("%s: unaligned read from capacity register",
+			    __func__);
 			break;
-		case 2:
-			data &= 0xFFFF0000;
-			data |= (uint32_t)(vioblk->capacity >> 32) & 0xFFFF;
-			break;
-		case 1:
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->capacity >> 32) & 0xFF;
-			break;
 		}
-		/* XXX handle invalid sz */
+		data = (uint32_t)(0xFFFFFFFF & vioblk->capacity);
 		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 5:
-		if (sz == 1) {
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->capacity >> 40) & 0xFF;
+	case VIRTIO_BLK_CONFIG_CAPACITY + 4:
+		if (sz != 4) {
+			log_warnx("%s: unaligned read from capacity register",
+			    __func__);
+			break;
 		}
-		/* XXX handle invalid sz */
+		data = (uint32_t)(vioblk->capacity >> 32);
 		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 6:
-		if (sz == 1) {
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->capacity >> 48) & 0xFF;
-		} else if (sz == 2) {
-			data &= 0xFFFF0000;
-			data |= (uint32_t)(vioblk->capacity >> 48) & 0xFFFF;
-		}
-		/* XXX handle invalid sz */
-		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 7:
-		if (sz == 1) {
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->capacity >> 56) & 0xFF;
-		}
-		/* XXX handle invalid sz */
-		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12:
-		switch (sz) {
-		case 4:
-			data = (uint32_t)(vioblk->seg_max);
+	case VIRTIO_BLK_CONFIG_SEG_MAX:
+		if (sz != 4) {
+			log_warnx("%s: unaligned read from segment max "
+			    "register", __func__);
 			break;
-		case 2:
-			data &= 0xFFFF0000;
-			data |= (uint32_t)(vioblk->seg_max) & 0xFFFF;
-			break;
-		case 1:
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->seg_max) & 0xFF;
-			break;
 		}
-		/* XXX handle invalid sz */
+		data = vioblk->seg_max;
 		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 13:
-		if (sz == 1) {
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->seg_max >> 8) & 0xFF;
-		}
-		/* XXX handle invalid sz */
+	case VIRTIO_BLK_CONFIG_GEOMETRY_C:
+	case VIRTIO_BLK_CONFIG_GEOMETRY_H:
+	case VIRTIO_BLK_CONFIG_GEOMETRY_S:
+		/*
+		 * SeaBIOS unconditionally reads without checking the
+		 * geometry feature flag.
+		 */
 		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 14:
-		if (sz == 1) {
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->seg_max >> 16) & 0xFF;
-		} else if (sz == 2) {
-			data &= 0xFFFF0000;
-			data |= (uint32_t)(vioblk->seg_max >> 16)
-			    & 0xFFFF;
-		}
-		/* XXX handle invalid sz */
-		break;
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 15:
-		if (sz == 1) {
-			data &= 0xFFFFFF00;
-			data |= (uint32_t)(vioblk->seg_max >> 24) & 0xFF;
-		}
-		/* XXX handle invalid sz */
-		break;
-	case VIRTIO_CONFIG_DEVICE_FEATURES:
-		data = vioblk->cfg.device_feature;
-		break;
-	case VIRTIO_CONFIG_GUEST_FEATURES:
-		data = vioblk->cfg.guest_feature;
-		break;
-	case VIRTIO_CONFIG_QUEUE_PFN:
-		data = vioblk->cfg.queue_pfn;
-		break;
-	case VIRTIO_CONFIG_QUEUE_SIZE:
-		data = vioblk->cfg.queue_size;
-		break;
-	case VIRTIO_CONFIG_QUEUE_SELECT:
-		data = vioblk->cfg.queue_select;
-		break;
-	case VIRTIO_CONFIG_QUEUE_NOTIFY:
-		data = vioblk->cfg.queue_notify;
-		break;
-	case VIRTIO_CONFIG_DEVICE_STATUS:
-		data = vioblk->cfg.device_status;
-		break;
-	case VIRTIO_CONFIG_ISR_STATUS:
-		data = vioblk->cfg.isr_status;
-		vioblk->cfg.isr_status = 0;
-		if (intr != NULL)
-			*intr = INTR_STATE_DEASSERT;
-		break;
 	default:
-		return (0xFFFFFFFF);
+		log_warnx("%s: invalid register 0x%04x", __func__, reg);
+		return (uint32_t)(-1);
 	}

 	return (data);
@@ -791,8 +641,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 +680,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 + 8c6406fc030a66f82ce5478e0069b28c75b6db88
--- usr.sbin/vmd/vionet.c
+++ usr.sbin/vmd/vionet.c
@@ -38,6 +38,18 @@
 #include "virtio.h"
 #include "vmd.h"

+#define VIONET_DEBUG	1
+#ifdef DPRINTF
+#undef DPRINTF
+#endif
+#if VIONET_DEBUG
+#define DPRINTF		log_debug
+#else
+#define DPRINTF(x...)	do {} while(0)
+#endif	/* VIONET_DEBUG */
+
+#define VIRTIO_NET_CONFIG_MAC		 0 /*  8 bit x 6 byte */
+
 #define VIRTIO_NET_F_MAC	(1 << 5)
 #define RXQ	0
 #define TXQ	1
@@ -52,17 +64,20 @@ 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,
     const struct iovec *, int);
 static void vionet_rx_event(int, short, void *);
-static uint32_t handle_io_read(struct viodev_msg *, struct virtio_dev *,
-    int8_t *);
-static void handle_io_write(struct viodev_msg *, struct virtio_dev *);
+static uint32_t vio1_read(struct virtio_dev *, struct viodev_msg *, int *);
+static void vio1_write(struct virtio_dev *, struct viodev_msg *);
+static uint32_t vionet_cfg_read(struct virtio_dev *, struct viodev_msg *);
+static void vionet_cfg_write(struct virtio_dev *, struct viodev_msg *);
+
 static int vionet_tx(struct virtio_dev *);
-static void vionet_notifyq(struct virtio_dev *);
+static void vionet_notifyq(struct virtio_dev *, uint16_t);
+static uint32_t vionet_dev_read(struct virtio_dev *, struct viodev_msg *);
 static void dev_dispatch_vm(int, short, void *);
 static void handle_sync_io(int, short, void *);
 static void read_pipe_main(int, short, void *);
@@ -85,8 +100,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 +302,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,21 +311,23 @@ 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;
+	struct virtio_net_hdr *hdr = NULL;
 	struct virtio_vq_info *vq_info;
 	struct iovec *iov;
 	int notify = 0;
 	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 +346,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;
@@ -407,7 +374,8 @@ vionet_rx(struct vionet_dev *dev, int fd)
 		iov->iov_base = hvaddr_mem(desc->addr, iov->iov_len);
 		if (iov->iov_base == NULL)
 			goto reset;
-		memset(iov->iov_base, 0, sizeof(struct virtio_net_hdr));
+		hdr = iov->iov_base;
+		memset(hdr, 0, sizeof(struct virtio_net_hdr));

 		/* Tweak the iovec to account for the virtio_net_hdr. */
 		iov->iov_len -= sizeof(struct virtio_net_hdr);
@@ -422,7 +390,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__);
@@ -452,15 +420,17 @@ vionet_rx(struct vionet_dev *dev, int fd)
 			goto reset;
 		}

+		hdr->num_buffers = iov_cnt;
+
 		/*
 		 * 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 +443,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 +600,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 +617,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);

@@ -661,11 +630,9 @@ vionet_rx_event(int fd, short event, void *arg)
 }

 static void
-vionet_notifyq(struct virtio_dev *dev)
+vionet_notifyq(struct virtio_dev *dev, uint16_t vq_idx)
 {
-	struct vionet_dev	*vionet = &dev->vionet;
-
-	switch (vionet->cfg.queue_notify) {
+	switch (vq_idx) {
 	case RXQ:
 		rx_enabled = 1;
 		vm_pipe_send(&pipe_rx, VIRTIO_NOTIFY);
@@ -679,7 +646,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 +669,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 +687,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;
@@ -733,22 +699,24 @@ vionet_tx(struct virtio_dev *dev)
 		chain_len = 0;

 		/*
-		 * As a legacy device, we most likely will receive a lead
-		 * descriptor sized to the virtio_net_hdr. However, the framing
-		 * is not guaranteed, so check for packet data.
+		 * We do not negotiate VIRTIO_NET_F_HASH_REPORT so we
+		 * assume the header length is fixed.
 		 */
-		iov->iov_len = desc->len;
-		if (iov->iov_len < sizeof(struct virtio_net_hdr)) {
+		if (desc->len < sizeof(struct virtio_net_hdr)) {
 			log_warnx("%s: invalid descriptor length", __func__);
 			goto reset;
-		} else if (iov->iov_len > sizeof(struct virtio_net_hdr)) {
+		}
+		iov->iov_len = desc->len;
+
+		if (iov->iov_len > sizeof(struct virtio_net_hdr)) {
 			/* Chop off the virtio header, leaving packet data. */
 			iov->iov_len -= sizeof(struct virtio_net_hdr);
-			chain_len += iov->iov_len;
 			iov->iov_base = hvaddr_mem(desc->addr +
 			    sizeof(struct virtio_net_hdr), iov->iov_len);
 			if (iov->iov_base == NULL)
 				goto reset;
+
+			chain_len += iov->iov_len;
 			iov_cnt++;
 		}

@@ -756,7 +724,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 +794,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++;
@@ -848,8 +816,6 @@ drop:
 				    __func__);
 				free(pkt.buf);
 			}
-			log_debug("%s: injected dhcp reply with %ld bytes",
-			    __func__, sz);
 		}
 	}

@@ -857,7 +823,6 @@ drop:
 	    !(avail->flags & VRING_AVAIL_F_NO_INTERRUPT))
 		notify = 1;

-
 	vq_info->last_avail = idx;
 	return (notify);
 reset:
@@ -949,7 +914,7 @@ handle_sync_io(int fd, short event, void *arg)
 	struct viodev_msg msg;
 	struct imsg imsg;
 	ssize_t n;
-	int8_t intr = INTR_STATE_NOOP;
+	int deassert = 0;

 	if (event & EV_READ) {
 		if ((n = imsgbuf_read(ibuf)) == -1)
@@ -989,15 +954,16 @@ handle_sync_io(int fd, short event, void *arg)
 		switch (msg.type) {
 		case VIODEV_MSG_IO_READ:
 			/* Read IO: make sure to send a reply */
-			msg.data = handle_io_read(&msg, dev, &intr);
+			msg.data = vio1_read(dev, &msg, &deassert);
 			msg.data_valid = 1;
-			msg.state = intr;
+			if (deassert)
+				msg.state = INTR_STATE_DEASSERT;
 			imsg_compose_event2(iev, IMSG_DEVOP_MSG, 0, 0, -1, &msg,
 			    sizeof(msg), ev_base_main);
 			break;
 		case VIODEV_MSG_IO_WRITE:
 			/* Write IO: no reply needed */
-			handle_io_write(&msg, dev);
+			vio1_write(dev, &msg);
 			break;
 		case VIODEV_MSG_SHUTDOWN:
 			event_del(&dev->sync_iev.ev);
@@ -1010,49 +976,291 @@ handle_sync_io(int fd, short event, void *arg)
 	imsg_event_add2(iev, ev_base_main);
 }

+static uint32_t
+vionet_cfg_read(struct virtio_dev *dev, struct viodev_msg *msg)
+{
+	struct virtio_pci_common_cfg *pci_cfg = &dev->pci_cfg;
+	uint32_t data = (uint32_t)(-1);
+	uint16_t reg = msg->reg & 0x00FF;
+
+	pthread_rwlock_rdlock(&lock);
+	switch (reg) {
+	case VIO1_PCI_DEVICE_FEATURE_SELECT:
+		data = pci_cfg->device_feature_select;
+		break;
+	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__);
+		}
+		break;
+	case VIO1_PCI_DRIVER_FEATURE_SELECT:
+		data = pci_cfg->driver_feature_select;
+		break;
+	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__);
+		}
+		break;
+	case VIO1_PCI_CONFIG_MSIX_VECTOR:
+		data = VIRTIO_MSI_NO_VECTOR;	/* Unsupported */
+		break;
+	case VIO1_PCI_NUM_QUEUES:
+		data = dev->num_queues;
+		break;
+	case VIO1_PCI_DEVICE_STATUS:
+		data = dev->status;
+		break;
+	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);
+	}
+	pthread_rwlock_unlock(&lock);
+
+	return (data);
+}
+
 static void
-handle_io_write(struct viodev_msg *msg, struct virtio_dev *dev)
+vionet_cfg_write(struct virtio_dev *dev, struct viodev_msg *msg)
 {
-	struct vionet_dev	*vionet = &dev->vionet;
-	uint32_t		 data = msg->data;
-	int			 pause_devices = 0;
+	struct virtio_pci_common_cfg *pci_cfg = &dev->pci_cfg;
+	uint32_t data = msg->data;
+	uint16_t reg = msg->reg & 0xFF;
+	uint8_t sz = msg->io_sz;
+	int i, 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:
-	case VIRTIO_CONFIG_ISR_STATUS:
-		log_warnx("%s: illegal write %x to %s", __progname, data,
-		    virtio_reg_name(msg->reg));
+	switch (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:
-		vionet->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:
-		vionet->cfg.queue_pfn = data;
-		vionet_update_qa(vionet);
+	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:
-		vionet->cfg.queue_select = data;
-		vionet_update_qs(vionet);
+	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:
-		vionet->cfg.queue_notify = data;
-		vionet_notifyq(dev);
+	case VIO1_PCI_CONFIG_MSIX_VECTOR:
+		/* Ignore until we support MSIX. */
 		break;
-	case VIRTIO_CONFIG_DEVICE_STATUS:
-		if (data == 0) {
-			resetting = 2;	/* Wait on two acks: rx & tx */
+	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);
+			}
+
+			resetting = 2;		/* Wait on two acks: rx & tx */
 			pause_devices = 1;
-		} else {
-			// XXX is this correct?
-			vionet->cfg.device_status = data;
 		}
+		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);
 	}
-
 	pthread_rwlock_unlock(&lock);
+
 	if (pause_devices) {
 		rx_enabled = 0;
 		vionet_deassert_pic_irq(dev);
@@ -1062,60 +1270,82 @@ handle_io_write(struct viodev_msg *msg, struct virtio_
 }

 static uint32_t
-handle_io_read(struct viodev_msg *msg, struct virtio_dev *dev, int8_t *intr)
+vio1_read(struct virtio_dev *dev, struct viodev_msg *msg, int *deassert)
 {
-	struct vionet_dev *vionet = &dev->vionet;
-	uint32_t data;
+	uint32_t data = (uint32_t)(-1);
+	uint16_t reg = msg->reg;

-	pthread_rwlock_rdlock(&lock);
-
-	switch (msg->reg) {
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 1:
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 2:
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 3:
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4:
-	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 5:
-		data = vionet->mac[msg->reg -
-		    VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI];
+	switch (reg & 0xFF00) {
+	case VIO1_CFG_BAR_OFFSET:
+		data = vionet_cfg_read(dev, msg);
 		break;
-	case VIRTIO_CONFIG_DEVICE_FEATURES:
-		data = vionet->cfg.device_feature;
+	case VIO1_DEV_BAR_OFFSET:
+		data = vionet_dev_read(dev, msg);
 		break;
-	case VIRTIO_CONFIG_GUEST_FEATURES:
-		data = vionet->cfg.guest_feature;
+	case VIO1_NOTIFY_BAR_OFFSET:
+		/* Reads of notify register return all 1's. */
 		break;
-	case VIRTIO_CONFIG_QUEUE_PFN:
-		data = vionet->cfg.queue_pfn;
-		break;
-	case VIRTIO_CONFIG_QUEUE_SIZE:
-		data = vionet->cfg.queue_size;
-		break;
-	case VIRTIO_CONFIG_QUEUE_SELECT:
-		data = vionet->cfg.queue_select;
-		break;
-	case VIRTIO_CONFIG_QUEUE_NOTIFY:
-		data = vionet->cfg.queue_notify;
-		break;
-	case VIRTIO_CONFIG_DEVICE_STATUS:
-		data = vionet->cfg.device_status;
-		break;
-	case VIRTIO_CONFIG_ISR_STATUS:
-		pthread_rwlock_unlock(&lock);
+	case VIO1_ISR_BAR_OFFSET:
 		pthread_rwlock_wrlock(&lock);
-		data = vionet->cfg.isr_status;
-		vionet->cfg.isr_status = 0;
-		if (intr != NULL)
-			*intr = INTR_STATE_DEASSERT;
+		data = dev->isr;
+		dev->isr = 0;
+		*deassert = 1;
+		pthread_rwlock_unlock(&lock);
 		break;
 	default:
-		data = 0xFFFFFFFF;
+		log_debug("%s: no handler for reg 0x%04x", __func__, reg);
 	}

-	pthread_rwlock_unlock(&lock);
 	return (data);
 }

+static void
+vio1_write(struct virtio_dev *dev, struct viodev_msg *msg)
+{
+	uint16_t reg = msg->reg;
+
+	switch (reg & 0xFF00) {
+	case VIO1_CFG_BAR_OFFSET:
+		(void)vionet_cfg_write(dev, msg);
+		break;
+	case VIO1_DEV_BAR_OFFSET:
+		/* Ignore all writes to device configuration registers. */
+		break;
+	case VIO1_NOTIFY_BAR_OFFSET:
+		vionet_notifyq(dev, (uint16_t)(msg->data));
+		break;
+	case VIO1_ISR_BAR_OFFSET:
+		/* ignore writes to ISR. */
+		break;
+	default:
+		log_debug("%s: no handler for reg 0x%04x", __func__, reg);
+	}
+}
+
+static uint32_t
+vionet_dev_read(struct virtio_dev *dev, struct viodev_msg *msg)
+{
+	struct vionet_dev *vionet = (struct vionet_dev *)&dev->vionet;
+	uint32_t data = (uint32_t)(-1);
+	uint16_t reg = msg->reg & 0xFF;
+
+	switch (reg) {
+	case VIRTIO_NET_CONFIG_MAC:
+	case VIRTIO_NET_CONFIG_MAC + 1:
+	case VIRTIO_NET_CONFIG_MAC + 2:
+	case VIRTIO_NET_CONFIG_MAC + 3:
+	case VIRTIO_NET_CONFIG_MAC + 4:
+	case VIRTIO_NET_CONFIG_MAC + 5:
+		data = (uint8_t)vionet->mac[reg - VIRTIO_NET_CONFIG_MAC];
+		break;
+	default:
+		log_warnx("%s: invalid register 0x%04x", __func__, reg);
+		return (uint32_t)(-1);
+	}
+
+	return (data);
+}
+
 /*
  * Handle the rx side processing, communicating to the main thread via pipe.
  */
@@ -1220,7 +1450,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 +1489,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 +1524,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;
@@ -1330,7 +1553,7 @@ vionet_assert_pic_irq(struct virtio_dev *dev)

 	memset(&msg, 0, sizeof(msg));
 	msg.irq = dev->irq;
-	msg.vcpu = 0; // XXX
+	msg.vcpu = 0; /* XXX: smp */
 	msg.type = VIODEV_MSG_KICK;
 	msg.state = INTR_STATE_ASSERT;

@@ -1352,7 +1575,7 @@ vionet_deassert_pic_irq(struct virtio_dev *dev)

 	memset(&msg, 0, sizeof(msg));
 	msg.irq = dev->irq;
-	msg.vcpu = 0; // XXX
+	msg.vcpu = 0; /* XXX: smp */
 	msg.type = VIODEV_MSG_KICK;
 	msg.state = INTR_STATE_DEASSERT;

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 + 99905bae1f501a28bdc9a5ab5f74c5cf71109c66
--- usr.sbin/vmd/virtio.c
+++ usr.sbin/vmd/virtio.c
@@ -43,12 +43,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 +74,53 @@ 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_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 +162,125 @@ 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;
+		}
+	} 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 +289,425 @@ 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)
 {
-	*intr = 0xFF;
+	struct virtio_dev *dev = (struct virtio_dev *)arg;
+	uint8_t actual = (uint8_t)reg;

-	if (dir == 0) {
+	switch (reg & 0xFF00) {
+	case VIO1_CFG_BAR_OFFSET:
+		*data = virtio_io_cfg(dev, dir, actual, *data, sz);
+		break;
+	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);
+}
+
+/*
+ * virtio 1.x PCI config register io. If a register is read, returns the value.
+ * Otherwise returns 0.
+ */
+uint32_t
+virtio_io_cfg(struct virtio_dev *dev, int dir, uint8_t reg, uint32_t data,
+    uint8_t sz)
+{
+	struct virtio_pci_common_cfg *pci_cfg = &dev->pci_cfg;
+	uint32_t res = 0;
+	uint16_t i;
+
+	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);
+				}
+			}
+
+			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:
+			res = 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)
+				res = dev->device_feature & (uint32_t)(-1);
+			else if (pci_cfg->device_feature_select == 1)
+				res = dev->device_feature >> 32;
+			else {
+				DPRINTF("%s: ignoring device feature read",
+				    __func__);
+			}
 			break;
-		case VIRTIO_CONFIG_QUEUE_PFN:
-			*data = viornd.cfg.queue_pfn;
+		case VIO1_PCI_DRIVER_FEATURE_SELECT:
+			res = 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)
+				res = dev->driver_feature & (uint32_t)(-1);
+			else if (pci_cfg->driver_feature_select == 1)
+				res = dev->driver_feature >> 32;
+			else {
+				DPRINTF("%s: ignoring driver feature read",
+				    __func__);
+			}
 			break;
-		case VIRTIO_CONFIG_QUEUE_SELECT:
-			*data = viornd.cfg.queue_select;
+		case VIO1_PCI_CONFIG_MSIX_VECTOR:
+			res = VIRTIO_MSI_NO_VECTOR;	/* Unsupported */
 			break;
-		case VIRTIO_CONFIG_QUEUE_NOTIFY:
-			*data = viornd.cfg.queue_notify;
+		case VIO1_PCI_NUM_QUEUES:
+			res = dev->num_queues;
 			break;
-		case VIRTIO_CONFIG_DEVICE_STATUS:
-			*data = viornd.cfg.device_status;
+		case VIO1_PCI_DEVICE_STATUS:
+			res = 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:
+			res = pci_cfg->config_generation;
 			break;
+		case VIO1_PCI_QUEUE_SELECT:
+			res = pci_cfg->queue_select;
+			break;
+		case VIO1_PCI_QUEUE_SIZE:
+			res = pci_cfg->queue_size;
+			break;
+		case VIO1_PCI_QUEUE_MSIX_VECTOR:
+			res = VIRTIO_MSI_NO_VECTOR;	/* Unsupported */
+			break;
+		case VIO1_PCI_QUEUE_ENABLE:
+			res = pci_cfg->queue_enable;
+			break;
+		case VIO1_PCI_QUEUE_NOTIFY_OFF:
+			res = pci_cfg->queue_notify_off;
+			break;
+		case VIO1_PCI_QUEUE_DESC:
+			res = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_desc);
+			break;
+		case VIO1_PCI_QUEUE_DESC + 4:
+			res = (uint32_t)(pci_cfg->queue_desc >> 32);
+			break;
+		case VIO1_PCI_QUEUE_AVAIL:
+			res = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_avail);
+			break;
+		case VIO1_PCI_QUEUE_AVAIL + 4:
+			res = (uint32_t)(pci_cfg->queue_avail >> 32);
+			break;
+		case VIO1_PCI_QUEUE_USED:
+			res = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_used);
+			break;
+		case VIO1_PCI_QUEUE_USED + 4:
+			res = (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",
+	    (dir == VEI_DIR_OUT) ? data : res);
+
+	return (res);
+}
+
+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");
+
+	/* 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");
+
+	/* Limit this handler 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 +716,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 +742,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 +752,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 +776,7 @@ vmmci_ctl(unsigned int cmd)
 	}

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

 	return (ret);
 }
@@ -352,9 +788,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 +807,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 +822,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 +842,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 +877,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 +963,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 +990,78 @@ 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 1.x 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_PRODUCT_QUMRANET_VIO1_NET, PCI_CLASS_SYSTEM,
 				PCI_SUBCLASS_SYSTEM_MISC, PCI_VENDOR_OPENBSD,
-				PCI_PRODUCT_VIRTIO_NETWORK, 1, NULL)) {
+				PCI_PRODUCT_VIRTIO_NETWORK, 1, 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);
+			virtio_dev_init(dev, id, VIONET_QUEUE_SIZE_DEFAULT,
+			    VIRTIO_NET_QUEUES,
+			    (VIRTIO_NET_F_MAC | VIRTIO_F_VERSION_1),
+			    vcp->vcp_id);

-			/* The vionet pci bar function is called by the vcpu. */
 			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;
 			}
+			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, 8);
+			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);

-			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 +1084,50 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
 		}
 	}

+	/* Virtio 1.x 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_PRODUCT_QUMRANET_VIO1_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, 1, 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_F_VERSION_1 | VIRTIO_BLK_F_SEG_MAX),
+			    vcp->vcp_id);

-			if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_pci_io,
-			    &dev->vioblk)) {
+			bar_id = pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_pci_io,
+			    dev);
+			if (bar_id == -1 || bar_id > 0xff) {
 				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;
+			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, 24);
+			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);

+			/* 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 +1151,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 +1277,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 +1345,125 @@ 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->isr = 0;
+	dev->vm_id = vm_id;
+
+	dev->device_feature = features;
+
+	dev->pci_cfg.config_generation = 0;
+	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
@@ -1207,14 +1748,13 @@ static int
 handle_dev_msg(struct viodev_msg *msg, struct virtio_dev *gdev)
 {
 	uint32_t vm_id = gdev->vm_id;
-	int irq = gdev->irq;

 	switch (msg->type) {
 	case VIODEV_MSG_KICK:
 		if (msg->state == INTR_STATE_ASSERT)
-			vcpu_assert_irq(vm_id, msg->vcpu, irq);
+			vcpu_assert_irq(vm_id, msg->vcpu, msg->irq);
 		else if (msg->state == INTR_STATE_DEASSERT)
-			vcpu_deassert_irq(vm_id, msg->vcpu, irq);
+			vcpu_deassert_irq(vm_id, msg->vcpu, msg->irq);
 		break;
 	case VIODEV_MSG_READY:
 		log_debug("%s: device reports ready", __func__);
@@ -1305,10 +1845,8 @@ virtio_pci_io(int dir, uint16_t reg, uint32_t *data, u
 		imsg_free(&imsg);

 		if (msg.type == VIODEV_MSG_IO_READ && msg.data_valid) {
-#if DEBUG
-			log_debug("%s: got sync read response (reg=%s)",
-			    __func__, virtio_reg_name(msg.reg));
-#endif /* DEBUG */
+			DPRINTF("%s: got sync read response (reg=%s)", __func__,
+			    virtio_reg_name(msg.reg));
 			*data = msg.data;
 			/*
 			 * It's possible we're asked to {de,}assert after the
blob - 4bd6f68b41fcb8ce68781903e4364e543adc0075
blob + 78c235952af5d461088f2ebdbfb3e4b6766bb12d
--- usr.sbin/vmd/virtio.h
+++ usr.sbin/vmd/virtio.h
@@ -19,11 +19,13 @@
 #include <sys/types.h>

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

 #include <event.h>

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

 #ifndef _VIRTIO_H_
 #define _VIRTIO_H_
@@ -33,20 +35,53 @@
 #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_DEFAULT	128
+#define VIOSCSI_QUEUE_SIZE_DEFAULT	128
+#define VIONET_QUEUE_SIZE_DEFAULT	256

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

-#define VIOSCSI_QUEUE_SIZE	128
-#define VIOSCSI_QUEUE_MASK	(VIOSCSI_QUEUE_SIZE - 1)
-
-#define VIONET_QUEUE_SIZE	256
-#define VIONET_QUEUE_MASK	(VIONET_QUEUE_SIZE - 1)
-
 /* Virtio network device is backed by tap(4), so inherit limits */
 #define VIONET_HARD_MTU		TUNMRU
 #define VIONET_MIN_TXLEN	ETHER_HDR_LEN
@@ -56,12 +91,15 @@
 #define VMMCI_TIMEOUT_SHORT	3
 #define VMMCI_TIMEOUT_LONG	120

-/* All the devices we support have either 1, 2 or 3 queues */
-/* viornd - 1 queue
- * vioblk - 1 queue
- * vionet - 2 queues
- * vioscsi - 3 queues
+/*
+ * All the devices we support have either 1, 2 or 3 queues.
+ * No devices currently support VIRTIO_*_F_MQ multi-queue features.
  */
+#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 +112,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 +150,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 +186,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 +212,8 @@ struct virtio_vq_info {
 	 * driver notified to the host.
 	 */
 	uint16_t notified_avail;
+
+	uint8_t vq_enabled;
 };

 /*
@@ -202,22 +250,10 @@ struct virtio_vq_acct {
 	struct vring_used *used;
 };

-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;
-};
-
 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 +268,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 +290,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;
@@ -298,13 +297,17 @@ struct virtio_net_hdr {
 	uint16_t gso_size;
 	uint16_t csum_start;
 	uint16_t csum_offset;
+	uint16_t num_buffers;

 	/*
-	 * num_buffers is only used if VIRTIO_NET_F_MRG_RXBUF is negotiated.
-	 * vmd(8) doesn't negotiate that, but the field is listed here
-	 * for completeness sake.
+	 * The following fields exist only if VIRTIO_NET_F_HASH_REPORT
+	 * is negotiated.
 	 */
-/*	uint16_t num_buffers; */
+	/*
+	uint32_t hash_value;
+	uint16_t hash_report;
+	uint16_t padding_reserved;
+	*/
 };

 enum vmmci_cmd {
@@ -315,13 +318,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 +333,52 @@ 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 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 *);
@@ -347,11 +390,10 @@ int vm_device_pipe(struct virtio_dev *, void (*)(int,
 int virtio_pci_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
 void virtio_assert_irq(struct virtio_dev *, int);
 void virtio_deassert_irq(struct virtio_dev *, int);
+uint32_t virtio_io_cfg(struct virtio_dev *, int, uint8_t, uint32_t, uint8_t);

-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 +404,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 +414,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)