changeset 13858:1c0f1f24a6ad

1562 Integrate the virtio core module Reviewed by: Dmitry Yusupov <Dmitry.Yusupov@nexenta.com> Reviewed by: Gordon Ross <gordon.w.ross@gmail.com> Approved by: Garrett D'Amore <garrett@damore.org>
author Alexey Zaytsev <alexey.zaytsev@gmail.com>
date Tue, 16 Oct 2012 03:36:33 -0700
parents a4e0f94e01f4
children 76e0530c7265
files usr/src/pkg/manifests/driver-misc-virtio.mf usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/virtio/virtio.c usr/src/uts/common/io/virtio/virtioreg.h usr/src/uts/common/io/virtio/virtiovar.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/virtio/Makefile
diffstat 8 files changed, 1887 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkg/manifests/driver-misc-virtio.mf	Tue Oct 16 03:36:33 2012 -0700
@@ -0,0 +1,43 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
+#
+
+#
+# The default for payload-bearing actions in this package is to appear in the
+# global zone only.  See the include file for greater detail, as well as
+# information about overriding the defaults.
+#
+<include global_zone_only_component>
+set name=pkg.fmri value=pkg:/driver/misc/virtio@$(PKGVERS)
+set name=pkg.description value="Virtio core"
+set name=pkg.summary value="Virtio core"
+set name=info.classification \
+    value=org.opensolaris.category.2008:System/Hardware
+set name=variant.arch value=i386
+dir path=kernel group=sys
+dir path=kernel/misc group=sys
+dir path=kernel/misc/$(ARCH64) group=sys
+file path=kernel/misc/$(ARCH64)/virtio group=sys mode=0755
+file path=kernel/misc/virtio group=sys mode=0755
+license lic_CDDL license=lic_CDDL
--- a/usr/src/uts/common/Makefile.files	Tue Oct 16 03:33:44 2012 -0700
+++ b/usr/src/uts/common/Makefile.files	Tue Oct 16 03:36:33 2012 -0700
@@ -21,7 +21,7 @@
 
 #
 # Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+# Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
 # Copyright (c) 2012 by Delphix. All rights reserved.
 #
 
@@ -1944,6 +1944,13 @@
 		nxge_hcall.o
 
 #
+# Virtio modules
+#
+
+# Virtio core
+VIRTIO_OBJS = virtio.o
+
+#
 #	kiconv modules
 #
 KICONV_EMEA_OBJS += kiconv_emea.o
--- a/usr/src/uts/common/Makefile.rules	Tue Oct 16 03:33:44 2012 -0700
+++ b/usr/src/uts/common/Makefile.rules	Tue Oct 16 03:36:33 2012 -0700
@@ -21,10 +21,7 @@
 
 #
 # Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
-#
-
-#
-# Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+# Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
 #
 
 #
@@ -1403,6 +1400,9 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/virtio/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
 #
 # krtld must refer to its own bzero/bcopy until the kernel is fully linked
 #
@@ -2642,6 +2642,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/inet/kifconf/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/virtio/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 ZMODLINTFLAGS = -erroff=E_CONSTANT_CONDITION
 
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/zmod/%.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/virtio/virtio.c	Tue Oct 16 03:36:33 2012 -0700
@@ -0,0 +1,1348 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2012 Nexenta Systems, Inc.
+ * Copyright 2012 Alexey Zaytsev <alexey.zaytsev@gmail.com>
+ */
+
+/* Based on the NetBSD virtio driver by Minoura Makoto. */
+/*
+ * Copyright (c) 2010 Minoura Makoto.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/modctl.h>
+#include <sys/autoconf.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/avintr.h>
+#include <sys/spl.h>
+#include <sys/promif.h>
+#include <sys/list.h>
+#include <sys/bootconf.h>
+#include <sys/bootsvcs.h>
+#include <sys/sysmacros.h>
+#include <sys/pci.h>
+
+#include "virtiovar.h"
+#include "virtioreg.h"
+#define	NDEVNAMES	(sizeof (virtio_device_name) / sizeof (char *))
+#define	MINSEG_INDIRECT	2	/* use indirect if nsegs >= this value */
+#define	VIRTQUEUE_ALIGN(n) (((n)+(VIRTIO_PAGE_SIZE-1)) & \
+	    ~(VIRTIO_PAGE_SIZE-1))
+
+void
+virtio_set_status(struct virtio_softc *sc, unsigned int status)
+{
+	int old = 0;
+
+	if (status != 0)
+		old = ddi_get8(sc->sc_ioh,
+		    (uint8_t *)(sc->sc_io_addr +
+		    VIRTIO_CONFIG_DEVICE_STATUS));
+
+	ddi_put8(sc->sc_ioh,
+	    (uint8_t *)(sc->sc_io_addr + VIRTIO_CONFIG_DEVICE_STATUS),
+	    status | old);
+}
+
+/*
+ * Negotiate features, save the result in sc->sc_features
+ */
+uint32_t
+virtio_negotiate_features(struct virtio_softc *sc, uint32_t guest_features)
+{
+	uint32_t host_features;
+	uint32_t features;
+
+	host_features = ddi_get32(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint32_t *)(sc->sc_io_addr + VIRTIO_CONFIG_DEVICE_FEATURES));
+
+	dev_debug(sc->sc_dev, CE_NOTE,
+	    "host features: %x, guest features: %x",
+	    host_features, guest_features);
+
+	features = host_features & guest_features;
+	ddi_put32(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint32_t *)(sc->sc_io_addr + VIRTIO_CONFIG_GUEST_FEATURES),
+	    features);
+
+	sc->sc_features = features;
+
+	return (host_features);
+}
+
+size_t
+virtio_show_features(uint32_t features,
+    char *buf, size_t len)
+{
+	char *orig_buf = buf;
+	char *bufend = buf + len;
+
+	/* LINTED E_PTRDIFF_OVERFLOW */
+	buf += snprintf(buf, bufend - buf, "Generic ( ");
+	if (features & VIRTIO_F_RING_INDIRECT_DESC)
+		/* LINTED E_PTRDIFF_OVERFLOW */
+		buf += snprintf(buf, bufend - buf, "INDIRECT_DESC ");
+
+	/* LINTED E_PTRDIFF_OVERFLOW */
+	buf += snprintf(buf, bufend - buf, ") ");
+
+	/* LINTED E_PTRDIFF_OVERFLOW */
+	return (buf - orig_buf);
+}
+
+boolean_t
+virtio_has_feature(struct virtio_softc *sc, uint32_t feature)
+{
+	return (sc->sc_features & feature);
+}
+
+/*
+ * Device configuration registers.
+ */
+uint8_t
+virtio_read_device_config_1(struct virtio_softc *sc, unsigned int index)
+{
+	ASSERT(sc->sc_config_offset);
+	return ddi_get8(sc->sc_ioh,
+	    (uint8_t *)(sc->sc_io_addr + sc->sc_config_offset + index));
+}
+
+uint16_t
+virtio_read_device_config_2(struct virtio_softc *sc, unsigned int index)
+{
+	ASSERT(sc->sc_config_offset);
+	return ddi_get16(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint16_t *)(sc->sc_io_addr + sc->sc_config_offset + index));
+}
+
+uint32_t
+virtio_read_device_config_4(struct virtio_softc *sc, unsigned int index)
+{
+	ASSERT(sc->sc_config_offset);
+	return ddi_get32(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset + index));
+}
+
+uint64_t
+virtio_read_device_config_8(struct virtio_softc *sc, unsigned int index)
+{
+	uint64_t r;
+
+	ASSERT(sc->sc_config_offset);
+	r = ddi_get32(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset +
+	    index + sizeof (uint32_t)));
+
+	r <<= 32;
+
+	r += ddi_get32(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset + index));
+	return (r);
+}
+
+void
+virtio_write_device_config_1(struct virtio_softc *sc,
+    unsigned int index, uint8_t value)
+{
+	ASSERT(sc->sc_config_offset);
+	ddi_put8(sc->sc_ioh,
+	    (uint8_t *)(sc->sc_io_addr + sc->sc_config_offset + index), value);
+}
+
+void
+virtio_write_device_config_2(struct virtio_softc *sc,
+    unsigned int index, uint16_t value)
+{
+	ASSERT(sc->sc_config_offset);
+	ddi_put16(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint16_t *)(sc->sc_io_addr + sc->sc_config_offset + index), value);
+}
+
+void
+virtio_write_device_config_4(struct virtio_softc *sc,
+    unsigned int index, uint32_t value)
+{
+	ASSERT(sc->sc_config_offset);
+	ddi_put32(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset + index), value);
+}
+
+void
+virtio_write_device_config_8(struct virtio_softc *sc,
+    unsigned int index, uint64_t value)
+{
+	ASSERT(sc->sc_config_offset);
+	ddi_put32(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset + index),
+	    value & 0xFFFFFFFF);
+	ddi_put32(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset +
+	    index + sizeof (uint32_t)), value >> 32);
+}
+
+/*
+ * Start/stop vq interrupt.  No guarantee.
+ */
+void
+virtio_stop_vq_intr(struct virtqueue *vq)
+{
+	vq->vq_avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
+}
+
+void
+virtio_start_vq_intr(struct virtqueue *vq)
+{
+	vq->vq_avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
+}
+
+static ddi_dma_attr_t virtio_vq_dma_attr = {
+	DMA_ATTR_V0,	/* Version number */
+	0,		/* low address */
+	/*
+	 * high address. Has to fit into 32 bits
+	 * after page-shifting
+	 */
+	0x00000FFFFFFFFFFF,
+	0xFFFFFFFF,	/* counter register max */
+	VIRTIO_PAGE_SIZE, /* page alignment required */
+	0x3F,		/* burst sizes: 1 - 32 */
+	0x1,		/* minimum transfer size */
+	0xFFFFFFFF,	/* max transfer size */
+	0xFFFFFFFF,	/* address register max */
+	1,		/* no scatter-gather */
+	1,		/* device operates on bytes */
+	0,		/* attr flag: set to 0 */
+};
+
+static ddi_dma_attr_t virtio_vq_indirect_dma_attr = {
+	DMA_ATTR_V0,	/* Version number */
+	0,		/* low address */
+	0xFFFFFFFFFFFFFFFF, /* high address */
+	0xFFFFFFFF,	/* counter register max */
+	1,		/* No specific alignment */
+	0x3F,		/* burst sizes: 1 - 32 */
+	0x1,		/* minimum transfer size */
+	0xFFFFFFFF,	/* max transfer size */
+	0xFFFFFFFF,	/* address register max */
+	1,		/* no scatter-gather */
+	1,		/* device operates on bytes */
+	0,		/* attr flag: set to 0 */
+};
+
+/* Same for direct and indirect descriptors. */
+static ddi_device_acc_attr_t virtio_vq_devattr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_NEVERSWAP_ACC,
+	DDI_STORECACHING_OK_ACC,
+	DDI_DEFAULT_ACC
+};
+
+static void
+virtio_free_indirect(struct vq_entry *entry)
+{
+
+	(void) ddi_dma_unbind_handle(entry->qe_indirect_dma_handle);
+	ddi_dma_mem_free(&entry->qe_indirect_dma_acch);
+	ddi_dma_free_handle(&entry->qe_indirect_dma_handle);
+
+	entry->qe_indirect_descs = NULL;
+}
+
+
+static int
+virtio_alloc_indirect(struct virtio_softc *sc, struct vq_entry *entry)
+{
+	int allocsize, num;
+	size_t len;
+	unsigned int ncookies;
+	int ret;
+
+	num = entry->qe_queue->vq_indirect_num;
+	ASSERT(num > 1);
+
+	allocsize = sizeof (struct vring_desc) * num;
+
+	ret = ddi_dma_alloc_handle(sc->sc_dev, &virtio_vq_indirect_dma_attr,
+	    DDI_DMA_SLEEP, NULL, &entry->qe_indirect_dma_handle);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN,
+		    "Failed to allocate dma handle for indirect descriptors,"
+		    " entry %d, vq %d", entry->qe_index,
+		    entry->qe_queue->vq_index);
+		goto out_alloc_handle;
+	}
+
+	ret = ddi_dma_mem_alloc(entry->qe_indirect_dma_handle,
+	    allocsize, &virtio_vq_devattr,
+	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
+	    (caddr_t *)&entry->qe_indirect_descs, &len,
+	    &entry->qe_indirect_dma_acch);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN,
+		    "Failed to alocate dma memory for indirect descriptors,"
+		    " entry %d, vq %d,", entry->qe_index,
+		    entry->qe_queue->vq_index);
+		goto out_alloc;
+	}
+
+	(void) memset(entry->qe_indirect_descs, 0xff, allocsize);
+
+	ret = ddi_dma_addr_bind_handle(entry->qe_indirect_dma_handle, NULL,
+	    (caddr_t)entry->qe_indirect_descs, len,
+	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
+	    DDI_DMA_SLEEP, NULL, &entry->qe_indirect_dma_cookie, &ncookies);
+	if (ret != DDI_DMA_MAPPED) {
+		dev_err(sc->sc_dev, CE_WARN,
+		    "Failed to bind dma memory for indirect descriptors,"
+		    "entry %d, vq %d", entry->qe_index,
+		    entry->qe_queue->vq_index);
+		goto out_bind;
+	}
+
+	/* We asked for a single segment */
+	ASSERT(ncookies == 1);
+
+	return (0);
+
+out_bind:
+	ddi_dma_mem_free(&entry->qe_indirect_dma_acch);
+out_alloc:
+	ddi_dma_free_handle(&entry->qe_indirect_dma_handle);
+out_alloc_handle:
+
+	return (ret);
+}
+
+/*
+ * Initialize the vq structure.
+ */
+static int
+virtio_init_vq(struct virtio_softc *sc, struct virtqueue *vq)
+{
+	int ret;
+	uint16_t i;
+	int vq_size = vq->vq_num;
+	int indirect_num = vq->vq_indirect_num;
+
+	/* free slot management */
+	list_create(&vq->vq_freelist, sizeof (struct vq_entry),
+	    offsetof(struct vq_entry, qe_list));
+
+	for (i = 0; i < vq_size; i++) {
+		struct vq_entry *entry = &vq->vq_entries[i];
+		list_insert_tail(&vq->vq_freelist, entry);
+		entry->qe_index = i;
+		entry->qe_desc = &vq->vq_descs[i];
+		entry->qe_queue = vq;
+
+		if (indirect_num) {
+			ret = virtio_alloc_indirect(sc, entry);
+			if (ret)
+				goto out_indirect;
+		}
+	}
+
+	mutex_init(&vq->vq_freelist_lock, "virtio-freelist",
+	    MUTEX_DRIVER, DDI_INTR_PRI(sc->sc_intr_prio));
+	mutex_init(&vq->vq_avail_lock, "virtio-avail",
+	    MUTEX_DRIVER, DDI_INTR_PRI(sc->sc_intr_prio));
+	mutex_init(&vq->vq_used_lock, "virtio-used",
+	    MUTEX_DRIVER, DDI_INTR_PRI(sc->sc_intr_prio));
+
+	return (0);
+
+out_indirect:
+	for (i = 0; i < vq_size; i++) {
+		struct vq_entry *entry = &vq->vq_entries[i];
+		if (entry->qe_indirect_descs)
+			virtio_free_indirect(entry);
+	}
+
+	return (ret);
+}
+
+
+
+/*
+ * Allocate/free a vq.
+ */
+struct virtqueue *
+virtio_alloc_vq(struct virtio_softc *sc,
+    unsigned int index,
+    unsigned int size,
+    unsigned int indirect_num,
+    const char *name)
+{
+	int vq_size, allocsize1, allocsize2, allocsize = 0;
+	int ret;
+	unsigned int ncookies;
+	size_t len;
+	struct virtqueue *vq;
+
+
+	ddi_put16(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint16_t *)(sc->sc_io_addr + VIRTIO_CONFIG_QUEUE_SELECT), index);
+	vq_size = ddi_get16(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint16_t *)(sc->sc_io_addr + VIRTIO_CONFIG_QUEUE_SIZE));
+	if (vq_size == 0) {
+		dev_err(sc->sc_dev, CE_WARN,
+		    "virtqueue dest not exist, index %d for %s\n", index, name);
+		goto out;
+	}
+
+	vq = kmem_zalloc(sizeof (struct virtqueue), KM_SLEEP);
+
+	/* size 0 => use native vq size, good for receive queues. */
+	if (size)
+		vq_size = MIN(vq_size, size);
+
+	/* allocsize1: descriptor table + avail ring + pad */
+	allocsize1 = VIRTQUEUE_ALIGN(sizeof (struct vring_desc) * vq_size +
+	    sizeof (struct vring_avail) +
+	    sizeof (uint16_t) * vq_size);
+	/* allocsize2: used ring + pad */
+	allocsize2 = VIRTQUEUE_ALIGN(sizeof (struct vring_used)
+	    + sizeof (struct vring_used_elem) * vq_size);
+
+	allocsize = allocsize1 + allocsize2;
+
+	ret = ddi_dma_alloc_handle(sc->sc_dev, &virtio_vq_dma_attr,
+	    DDI_DMA_SLEEP, NULL, &vq->vq_dma_handle);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN,
+		    "Failed to allocate dma handle for vq %d", index);
+		goto out_alloc_handle;
+	}
+
+	ret = ddi_dma_mem_alloc(vq->vq_dma_handle, allocsize,
+	    &virtio_vq_devattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
+	    (caddr_t *)&vq->vq_vaddr, &len, &vq->vq_dma_acch);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN,
+		    "Failed to alocate dma memory for vq %d", index);
+		goto out_alloc;
+	}
+
+
+	ret = ddi_dma_addr_bind_handle(vq->vq_dma_handle, NULL,
+	    (caddr_t)vq->vq_vaddr, len,
+	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
+	    DDI_DMA_SLEEP, NULL, &vq->vq_dma_cookie, &ncookies);
+	if (ret != DDI_DMA_MAPPED) {
+		dev_err(sc->sc_dev, CE_WARN,
+		    "Failed to bind dma memory for vq %d", index);
+		goto out_bind;
+	}
+
+	/* We asked for a single segment */
+	ASSERT(ncookies == 1);
+	/* and page-ligned buffers. */
+	ASSERT(vq->vq_dma_cookie.dmac_laddress % VIRTIO_PAGE_SIZE == 0);
+
+	(void) memset(vq->vq_vaddr, 0, allocsize);
+
+	/* Make sure all zeros hit the buffer before we point the host to it */
+	membar_producer();
+
+	/* set the vq address */
+	ddi_put32(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint32_t *)(sc->sc_io_addr + VIRTIO_CONFIG_QUEUE_ADDRESS),
+	    (vq->vq_dma_cookie.dmac_laddress / VIRTIO_PAGE_SIZE));
+
+	/* remember addresses and offsets for later use */
+	vq->vq_owner = sc;
+	vq->vq_num = vq_size;
+	vq->vq_index = index;
+	vq->vq_descs = vq->vq_vaddr;
+	vq->vq_availoffset = sizeof (struct vring_desc)*vq_size;
+	vq->vq_avail = (void *)(((char *)vq->vq_descs) + vq->vq_availoffset);
+	vq->vq_usedoffset = allocsize1;
+	vq->vq_used = (void *)(((char *)vq->vq_descs) + vq->vq_usedoffset);
+
+	ASSERT(indirect_num == 0 ||
+	    virtio_has_feature(sc, VIRTIO_F_RING_INDIRECT_DESC));
+	vq->vq_indirect_num = indirect_num;
+
+	/* free slot management */
+	vq->vq_entries = kmem_zalloc(sizeof (struct vq_entry) * vq_size,
+	    KM_SLEEP);
+
+	ret = virtio_init_vq(sc, vq);
+	if (ret)
+		goto out_init;
+
+	dev_debug(sc->sc_dev, CE_NOTE,
+	    "Allocated %d entries for vq %d:%s (%d incdirect descs)",
+	    vq_size, index, name, indirect_num * vq_size);
+
+	return (vq);
+
+out_init:
+	kmem_free(vq->vq_entries, sizeof (struct vq_entry) * vq_size);
+	(void) ddi_dma_unbind_handle(vq->vq_dma_handle);
+out_bind:
+	ddi_dma_mem_free(&vq->vq_dma_acch);
+out_alloc:
+	ddi_dma_free_handle(&vq->vq_dma_handle);
+out_alloc_handle:
+	kmem_free(vq, sizeof (struct virtqueue));
+out:
+	return (NULL);
+}
+
+
+void
+virtio_free_vq(struct virtqueue *vq)
+{
+	struct virtio_softc *sc = vq->vq_owner;
+	int i;
+
+	/* tell device that there's no virtqueue any longer */
+	ddi_put16(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint16_t *)(sc->sc_io_addr + VIRTIO_CONFIG_QUEUE_SELECT),
+	    vq->vq_index);
+	ddi_put32(sc->sc_ioh,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (uint32_t *)(sc->sc_io_addr + VIRTIO_CONFIG_QUEUE_ADDRESS), 0);
+
+	/* Free the indirect descriptors, if any. */
+	for (i = 0; i < vq->vq_num; i++) {
+		struct vq_entry *entry = &vq->vq_entries[i];
+		if (entry->qe_indirect_descs)
+			virtio_free_indirect(entry);
+	}
+
+	kmem_free(vq->vq_entries, sizeof (struct vq_entry) * vq->vq_num);
+
+	(void) ddi_dma_unbind_handle(vq->vq_dma_handle);
+	ddi_dma_mem_free(&vq->vq_dma_acch);
+	ddi_dma_free_handle(&vq->vq_dma_handle);
+
+	mutex_destroy(&vq->vq_used_lock);
+	mutex_destroy(&vq->vq_avail_lock);
+	mutex_destroy(&vq->vq_freelist_lock);
+
+	kmem_free(vq, sizeof (struct virtqueue));
+}
+
+/*
+ * Free descriptor management.
+ */
+struct vq_entry *
+vq_alloc_entry(struct virtqueue *vq)
+{
+	struct vq_entry *qe;
+
+	mutex_enter(&vq->vq_freelist_lock);
+	if (list_is_empty(&vq->vq_freelist)) {
+		mutex_exit(&vq->vq_freelist_lock);
+		return (NULL);
+	}
+	qe = list_remove_head(&vq->vq_freelist);
+
+	ASSERT(vq->vq_used_entries >= 0);
+	vq->vq_used_entries++;
+
+	mutex_exit(&vq->vq_freelist_lock);
+
+	qe->qe_next = NULL;
+	qe->qe_indirect_next = 0;
+	(void) memset(qe->qe_desc, 0, sizeof (struct vring_desc));
+
+	return (qe);
+}
+
+void
+vq_free_entry(struct virtqueue *vq, struct vq_entry *qe)
+{
+	mutex_enter(&vq->vq_freelist_lock);
+
+	list_insert_head(&vq->vq_freelist, qe);
+	vq->vq_used_entries--;
+	ASSERT(vq->vq_used_entries >= 0);
+	mutex_exit(&vq->vq_freelist_lock);
+}
+
+/*
+ * We (intentionally) don't have a global vq mutex, so you are
+ * responsible for external locking to avoid allocting/freeing any
+ * entries before using the returned value. Have fun.
+ */
+uint_t
+vq_num_used(struct virtqueue *vq)
+{
+	/* vq->vq_freelist_lock would not help here. */
+	return (vq->vq_used_entries);
+}
+
+static inline void
+virtio_ve_set_desc(struct vring_desc *desc, uint64_t paddr, uint32_t len,
+    boolean_t write)
+{
+	desc->addr = paddr;
+	desc->len = len;
+	desc->next = 0;
+	desc->flags = 0;
+
+	/* 'write' - from the driver's point of view */
+	if (!write)
+		desc->flags = VRING_DESC_F_WRITE;
+
+
+}
+
+void
+virtio_ve_set(struct vq_entry *qe, uint64_t paddr, uint32_t len,
+    boolean_t write)
+{
+	virtio_ve_set_desc(qe->qe_desc, paddr, len, write);
+}
+
+void
+virtio_ve_add_indirect_buf(struct vq_entry *qe, uint64_t paddr, uint32_t len,
+    boolean_t write)
+{
+	struct vring_desc *indirect_desc;
+
+	ASSERT(qe->qe_queue->vq_indirect_num);
+	ASSERT(qe->qe_indirect_next < qe->qe_queue->vq_indirect_num);
+
+	indirect_desc = &qe->qe_indirect_descs[qe->qe_indirect_next];
+	virtio_ve_set_desc(indirect_desc, paddr, len, write);
+	qe->qe_indirect_next++;
+}
+
+void
+virtio_ve_add_cookie(struct vq_entry *qe, ddi_dma_handle_t dma_handle,
+    ddi_dma_cookie_t dma_cookie, unsigned int ncookies, boolean_t write)
+{
+	int i;
+
+	for (i = 0; i < ncookies; i++) {
+		virtio_ve_add_indirect_buf(qe, dma_cookie.dmac_laddress,
+		    dma_cookie.dmac_size, write);
+		ddi_dma_nextcookie(dma_handle, &dma_cookie);
+	}
+}
+
+void
+virtio_sync_vq(struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+
+	/* Make sure the avail ring update hit the buffer */
+	membar_producer();
+
+	vq->vq_avail->idx = vq->vq_avail_idx;
+
+	/* Make sure the avail idx update hits the buffer */
+	membar_producer();
+
+	/* Make sure we see the flags update */
+	membar_consumer();
+
+	if (!(vq->vq_used->flags & VRING_USED_F_NO_NOTIFY))
+		ddi_put16(vsc->sc_ioh,
+		    /* LINTED E_BAD_PTR_CAST_ALIGN */
+		    (uint16_t *)(vsc->sc_io_addr +
+		    VIRTIO_CONFIG_QUEUE_NOTIFY),
+		    vq->vq_index);
+}
+
+void
+virtio_push_chain(struct vq_entry *qe, boolean_t sync)
+{
+	struct virtqueue *vq = qe->qe_queue;
+	struct vq_entry *head = qe;
+	struct vring_desc *desc;
+	int idx;
+
+	ASSERT(qe);
+
+	/*
+	 * Bind the descs together, paddr and len should be already
+	 * set with virtio_ve_set
+	 */
+	do {
+		/* Bind the indirect descriptors */
+		if (qe->qe_indirect_next > 1) {
+			uint16_t i = 0;
+
+			/*
+			 * Set the pointer/flags to the
+			 * first indirect descriptor
+			 */
+			virtio_ve_set_desc(qe->qe_desc,
+			    qe->qe_indirect_dma_cookie.dmac_laddress,
+			    sizeof (struct vring_desc) * qe->qe_indirect_next,
+			    B_FALSE);
+			qe->qe_desc->flags |= VRING_DESC_F_INDIRECT;
+
+			/* For all but the last one, add the next index/flag */
+			do {
+				desc = &qe->qe_indirect_descs[i];
+				i++;
+
+				desc->flags |= VRING_DESC_F_NEXT;
+				desc->next = i;
+			} while (i < qe->qe_indirect_next - 1);
+
+		}
+
+		if (qe->qe_next) {
+			qe->qe_desc->flags |= VRING_DESC_F_NEXT;
+			qe->qe_desc->next = qe->qe_next->qe_index;
+		}
+
+		qe = qe->qe_next;
+	} while (qe);
+
+	mutex_enter(&vq->vq_avail_lock);
+	idx = vq->vq_avail_idx;
+	vq->vq_avail_idx++;
+
+	/* Make sure the bits hit the descriptor(s) */
+	membar_producer();
+	vq->vq_avail->ring[idx % vq->vq_num] = head->qe_index;
+
+	/* Notify the device, if needed. */
+	if (sync)
+		virtio_sync_vq(vq);
+
+	mutex_exit(&vq->vq_avail_lock);
+}
+
+/* Get a chain of descriptors from the used ring, if one is available. */
+struct vq_entry *
+virtio_pull_chain(struct virtqueue *vq, uint32_t *len)
+{
+	struct vq_entry *head;
+	int slot;
+	int usedidx;
+
+	mutex_enter(&vq->vq_used_lock);
+
+	/* No used entries? Bye. */
+	if (vq->vq_used_idx == vq->vq_used->idx) {
+		mutex_exit(&vq->vq_used_lock);
+		return (NULL);
+	}
+
+	usedidx = vq->vq_used_idx;
+	vq->vq_used_idx++;
+	mutex_exit(&vq->vq_used_lock);
+
+	usedidx %= vq->vq_num;
+
+	/* Make sure we do the next step _after_ checking the idx. */
+	membar_consumer();
+
+	slot = vq->vq_used->ring[usedidx].id;
+	*len = vq->vq_used->ring[usedidx].len;
+
+	head = &vq->vq_entries[slot];
+
+	return (head);
+}
+
+void
+virtio_free_chain(struct vq_entry *qe)
+{
+	struct vq_entry *tmp;
+	struct virtqueue *vq = qe->qe_queue;
+
+	ASSERT(qe);
+
+	do {
+		ASSERT(qe->qe_queue == vq);
+		tmp = qe->qe_next;
+		vq_free_entry(vq, qe);
+		qe = tmp;
+	} while (tmp);
+}
+
+void
+virtio_ventry_stick(struct vq_entry *first, struct vq_entry *second)
+{
+	first->qe_next = second;
+}
+
+static int
+virtio_register_msi(struct virtio_softc *sc,
+    struct virtio_int_handler *config_handler,
+    struct virtio_int_handler vq_handlers[],
+    int intr_types)
+{
+	int count, actual;
+	int int_type;
+	int i;
+	int handler_count;
+	int ret;
+
+	/* If both MSI and MSI-x are reported, prefer MSI-x. */
+	int_type = DDI_INTR_TYPE_MSI;
+	if (intr_types & DDI_INTR_TYPE_MSIX)
+		int_type = DDI_INTR_TYPE_MSIX;
+
+	/* Walk the handler table to get the number of handlers. */
+	for (handler_count = 0;
+	    vq_handlers && vq_handlers[handler_count].vh_func;
+	    handler_count++)
+		;
+
+	/* +1 if there is a config change handler. */
+	if (config_handler)
+		handler_count++;
+
+	/* Number of MSIs supported by the device. */
+	ret = ddi_intr_get_nintrs(sc->sc_dev, int_type, &count);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN, "ddi_intr_get_nintrs failed");
+		return (ret);
+	}
+
+	/*
+	 * Those who try to register more handlers then the device
+	 * supports shall suffer.
+	 */
+	ASSERT(handler_count <= count);
+
+	sc->sc_intr_htable = kmem_zalloc(
+	    sizeof (ddi_intr_handle_t) * handler_count, KM_SLEEP);
+
+	ret = ddi_intr_alloc(sc->sc_dev, sc->sc_intr_htable, int_type, 0,
+	    handler_count, &actual, DDI_INTR_ALLOC_NORMAL);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN, "Failed to allocate MSI: %d", ret);
+		goto out_msi_alloc;
+	}
+
+	if (actual != handler_count) {
+		dev_err(sc->sc_dev, CE_WARN,
+		    "Not enough MSI available: need %d, available %d",
+		    handler_count, actual);
+		goto out_msi_available;
+	}
+
+	sc->sc_intr_num = handler_count;
+	sc->sc_intr_config = B_FALSE;
+	if (config_handler) {
+		sc->sc_intr_config = B_TRUE;
+	}
+
+	/* Assume they are all same priority */
+	ret = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_prio);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN, "ddi_intr_get_pri failed");
+		goto out_msi_prio;
+	}
+
+	/* Add the vq handlers */
+	for (i = 0; vq_handlers[i].vh_func; i++) {
+		ret = ddi_intr_add_handler(sc->sc_intr_htable[i],
+		    vq_handlers[i].vh_func,
+		    sc, vq_handlers[i].vh_priv);
+		if (ret != DDI_SUCCESS) {
+			dev_err(sc->sc_dev, CE_WARN,
+			    "ddi_intr_add_handler failed");
+			/* Remove the handlers that succeeded. */
+			while (--i >= 0) {
+				(void) ddi_intr_remove_handler(
+				    sc->sc_intr_htable[i]);
+			}
+			goto out_add_handlers;
+		}
+	}
+
+	/* Don't forget the config handler */
+	if (config_handler) {
+		ret = ddi_intr_add_handler(sc->sc_intr_htable[i],
+		    config_handler->vh_func,
+		    sc, config_handler->vh_priv);
+		if (ret != DDI_SUCCESS) {
+			dev_err(sc->sc_dev, CE_WARN,
+			    "ddi_intr_add_handler failed");
+			/* Remove the handlers that succeeded. */
+			while (--i >= 0) {
+				(void) ddi_intr_remove_handler(
+				    sc->sc_intr_htable[i]);
+			}
+			goto out_add_handlers;
+		}
+	}
+
+	/* We know we are using MSI, so set the config offset. */
+	sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_MSI;
+
+	ret = ddi_intr_get_cap(sc->sc_intr_htable[0],
+	    &sc->sc_intr_cap);
+	/* Just in case. */
+	if (ret != DDI_SUCCESS)
+		sc->sc_intr_cap = 0;
+
+out_add_handlers:
+out_msi_prio:
+out_msi_available:
+	for (i = 0; i < actual; i++)
+		(void) ddi_intr_free(sc->sc_intr_htable[i]);
+out_msi_alloc:
+	kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t) * count);
+
+	return (ret);
+}
+
+struct virtio_handler_container {
+	int nhandlers;
+	struct virtio_int_handler config_handler;
+	struct virtio_int_handler vq_handlers[];
+};
+
+uint_t
+virtio_intx_dispatch(caddr_t arg1, caddr_t arg2)
+{
+	struct virtio_softc *sc = (void *)arg1;
+	struct virtio_handler_container *vhc = (void *)arg2;
+	uint8_t isr_status;
+	int i;
+
+	isr_status = ddi_get8(sc->sc_ioh, (uint8_t *)(sc->sc_io_addr +
+	    VIRTIO_CONFIG_ISR_STATUS));
+
+	if (!isr_status)
+		return (DDI_INTR_UNCLAIMED);
+
+	if ((isr_status & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) &&
+	    vhc->config_handler.vh_func) {
+		vhc->config_handler.vh_func((void *)sc,
+		    vhc->config_handler.vh_priv);
+	}
+
+	/* Notify all handlers */
+	for (i = 0; i < vhc->nhandlers; i++) {
+		vhc->vq_handlers[i].vh_func((void *)sc,
+		    vhc->vq_handlers[i].vh_priv);
+	}
+
+	return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * config_handler and vq_handlers may be allocated on stack.
+ * Take precautions not to loose them.
+ */
+static int
+virtio_register_intx(struct virtio_softc *sc,
+    struct virtio_int_handler *config_handler,
+    struct virtio_int_handler vq_handlers[])
+{
+	int vq_handler_count;
+	int config_handler_count = 0;
+	int actual;
+	struct virtio_handler_container *vhc;
+	int ret = DDI_FAILURE;
+
+	/* Walk the handler table to get the number of handlers. */
+	for (vq_handler_count = 0;
+	    vq_handlers && vq_handlers[vq_handler_count].vh_func;
+	    vq_handler_count++)
+		;
+
+	if (config_handler)
+		config_handler_count = 1;
+
+	vhc = kmem_zalloc(sizeof (struct virtio_handler_container) +
+	    sizeof (struct virtio_int_handler) * vq_handler_count,
+	    KM_SLEEP);
+
+	vhc->nhandlers = vq_handler_count;
+	(void) memcpy(vhc->vq_handlers, vq_handlers,
+	    sizeof (struct virtio_int_handler) * vq_handler_count);
+
+	if (config_handler) {
+		(void) memcpy(&vhc->config_handler, config_handler,
+		    sizeof (struct virtio_int_handler));
+	}
+
+	/* Just a single entry for a single interrupt. */
+	sc->sc_intr_htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP);
+
+	ret = ddi_intr_alloc(sc->sc_dev, sc->sc_intr_htable,
+	    DDI_INTR_TYPE_FIXED, 0, 1, &actual,
+	    DDI_INTR_ALLOC_NORMAL);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN,
+		    "Failed to allocate a fixed interrupt: %d", ret);
+		goto out_int_alloc;
+	}
+
+	ASSERT(actual == 1);
+	sc->sc_intr_num = 1;
+
+	ret = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_prio);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN, "ddi_intr_get_pri failed");
+		goto out_prio;
+	}
+
+	ret = ddi_intr_add_handler(sc->sc_intr_htable[0],
+	    virtio_intx_dispatch, sc, vhc);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN, "ddi_intr_add_handler failed");
+		goto out_add_handlers;
+	}
+
+	/* We know we are not using MSI, so set the config offset. */
+	sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI;
+
+	return (DDI_SUCCESS);
+
+out_add_handlers:
+out_prio:
+	(void) ddi_intr_free(sc->sc_intr_htable[0]);
+out_int_alloc:
+	kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t));
+	kmem_free(vhc, sizeof (struct virtio_int_handler) *
+	    (vq_handler_count + config_handler_count));
+	return (ret);
+}
+
+/*
+ * We find out if we support MSI during this, and the register layout
+ * depends on the MSI (doh). Don't acces the device specific bits in
+ * BAR 0 before calling it!
+ */
+int
+virtio_register_ints(struct virtio_softc *sc,
+    struct virtio_int_handler *config_handler,
+    struct virtio_int_handler vq_handlers[])
+{
+	int ret;
+	int intr_types;
+
+	/* Determine which types of interrupts are supported */
+	ret = ddi_intr_get_supported_types(sc->sc_dev, &intr_types);
+	if (ret != DDI_SUCCESS) {
+		dev_err(sc->sc_dev, CE_WARN, "Can't get supported int types");
+		goto out_inttype;
+	}
+
+	/* If we have msi, let's use them. */
+	if (intr_types & (DDI_INTR_TYPE_MSIX | DDI_INTR_TYPE_MSI)) {
+		ret = virtio_register_msi(sc, config_handler,
+		    vq_handlers, intr_types);
+		if (!ret)
+			return (0);
+	}
+
+	/* Fall back to old-fashioned interrupts. */
+	if (intr_types & DDI_INTR_TYPE_FIXED) {
+		dev_debug(sc->sc_dev, CE_WARN,
+		    "Using legacy interrupts");
+
+		return (virtio_register_intx(sc, config_handler, vq_handlers));
+	}
+
+	dev_err(sc->sc_dev, CE_WARN,
+	    "MSI failed and fixed interrupts not supported. Giving up.");
+	ret = DDI_FAILURE;
+
+out_inttype:
+	return (ret);
+}
+
+
+static int
+virtio_enable_msi(struct virtio_softc *sc)
+{
+	int ret, i;
+	int vq_handler_count = sc->sc_intr_num;
+
+	/* Number of handlers, not counting the counfig. */
+	if (sc->sc_intr_config)
+		vq_handler_count--;
+
+	/* Enable the iterrupts. Either the whole block, or one by one. */
+	if (sc->sc_intr_cap & DDI_INTR_FLAG_BLOCK) {
+		ret = ddi_intr_block_enable(sc->sc_intr_htable,
+		    sc->sc_intr_num);
+		if (ret != DDI_SUCCESS) {
+			dev_err(sc->sc_dev, CE_WARN,
+			    "Failed to enable MSI, falling back to INTx");
+			goto out_enable;
+		}
+	} else {
+		for (i = 0; i < sc->sc_intr_num; i++) {
+			ret = ddi_intr_enable(sc->sc_intr_htable[i]);
+			if (ret != DDI_SUCCESS) {
+				dev_err(sc->sc_dev, CE_WARN,
+				    "Failed to enable MSI %d, "
+				    "falling back to INTx", i);
+
+				while (--i >= 0) {
+					(void) ddi_intr_disable(
+					    sc->sc_intr_htable[i]);
+				}
+				goto out_enable;
+			}
+		}
+	}
+
+	/* Bind the allocated MSI to the queues and config */
+	for (i = 0; i < vq_handler_count; i++) {
+		int check;
+		ddi_put16(sc->sc_ioh,
+		    /* LINTED E_BAD_PTR_CAST_ALIGN */
+		    (uint16_t *)(sc->sc_io_addr +
+		    VIRTIO_CONFIG_QUEUE_SELECT), i);
+
+		ddi_put16(sc->sc_ioh,
+		    /* LINTED E_BAD_PTR_CAST_ALIGN */
+		    (uint16_t *)(sc->sc_io_addr +
+		    VIRTIO_CONFIG_QUEUE_VECTOR), i);
+
+		check = ddi_get16(sc->sc_ioh,
+		    /* LINTED E_BAD_PTR_CAST_ALIGN */
+		    (uint16_t *)(sc->sc_io_addr +
+		    VIRTIO_CONFIG_QUEUE_VECTOR));
+		if (check != i) {
+			dev_err(sc->sc_dev, CE_WARN, "Failed to bind handler"
+			    "for VQ %d, MSI %d. Check = %x", i, i, check);
+			ret = ENODEV;
+			goto out_bind;
+		}
+	}
+
+	if (sc->sc_intr_config) {
+		int check;
+		ddi_put16(sc->sc_ioh,
+		    /* LINTED E_BAD_PTR_CAST_ALIGN */
+		    (uint16_t *)(sc->sc_io_addr +
+		    VIRTIO_CONFIG_CONFIG_VECTOR), i);
+
+		check = ddi_get16(sc->sc_ioh,
+		    /* LINTED E_BAD_PTR_CAST_ALIGN */
+		    (uint16_t *)(sc->sc_io_addr +
+		    VIRTIO_CONFIG_CONFIG_VECTOR));
+		if (check != i) {
+			dev_err(sc->sc_dev, CE_WARN, "Failed to bind handler "
+			    "for Config updates, MSI %d", i);
+			ret = ENODEV;
+			goto out_bind;
+		}
+	}
+
+	return (DDI_SUCCESS);
+
+out_bind:
+	/* Unbind the vqs */
+	for (i = 0; i < vq_handler_count - 1; i++) {
+		ddi_put16(sc->sc_ioh,
+		    /* LINTED E_BAD_PTR_CAST_ALIGN */
+		    (uint16_t *)(sc->sc_io_addr +
+		    VIRTIO_CONFIG_QUEUE_SELECT), i);
+
+		ddi_put16(sc->sc_ioh,
+		    /* LINTED E_BAD_PTR_CAST_ALIGN */
+		    (uint16_t *)(sc->sc_io_addr +
+		    VIRTIO_CONFIG_QUEUE_VECTOR),
+		    VIRTIO_MSI_NO_VECTOR);
+	}
+	/* And the config */
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	ddi_put16(sc->sc_ioh, (uint16_t *)(sc->sc_io_addr +
+	    VIRTIO_CONFIG_CONFIG_VECTOR), VIRTIO_MSI_NO_VECTOR);
+
+	ret = DDI_FAILURE;
+
+out_enable:
+	return (ret);
+}
+
+static int virtio_enable_intx(struct virtio_softc *sc)
+{
+	int ret;
+
+	ret = ddi_intr_enable(sc->sc_intr_htable[0]);
+	if (ret != DDI_SUCCESS)
+		dev_err(sc->sc_dev, CE_WARN,
+		    "Failed to enable interrupt: %d", ret);
+	return (ret);
+}
+
+/*
+ * We can't enable/disable individual handlers in the INTx case so do
+ * the whole bunch even in the msi case.
+ */
+int
+virtio_enable_ints(struct virtio_softc *sc)
+{
+
+	/* See if we are using MSI. */
+	if (sc->sc_config_offset == VIRTIO_CONFIG_DEVICE_CONFIG_MSI)
+		return (virtio_enable_msi(sc));
+
+	ASSERT(sc->sc_config_offset == VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI);
+
+	return (virtio_enable_intx(sc));
+}
+
+void
+virtio_release_ints(struct virtio_softc *sc)
+{
+	int i;
+	int ret;
+
+	/* We were running with MSI, unbind them. */
+	if (sc->sc_config_offset == VIRTIO_CONFIG_DEVICE_CONFIG_MSI) {
+		/* Unbind all vqs */
+		for (i = 0; i < sc->sc_nvqs; i++) {
+			ddi_put16(sc->sc_ioh,
+			    /* LINTED E_BAD_PTR_CAST_ALIGN */
+			    (uint16_t *)(sc->sc_io_addr +
+			    VIRTIO_CONFIG_QUEUE_SELECT), i);
+
+			ddi_put16(sc->sc_ioh,
+			    /* LINTED E_BAD_PTR_CAST_ALIGN */
+			    (uint16_t *)(sc->sc_io_addr +
+			    VIRTIO_CONFIG_QUEUE_VECTOR),
+			    VIRTIO_MSI_NO_VECTOR);
+		}
+		/* And the config */
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		ddi_put16(sc->sc_ioh, (uint16_t *)(sc->sc_io_addr +
+		    VIRTIO_CONFIG_CONFIG_VECTOR),
+		    VIRTIO_MSI_NO_VECTOR);
+
+	}
+
+	/* Disable the iterrupts. Either the whole block, or one by one. */
+	if (sc->sc_intr_cap & DDI_INTR_FLAG_BLOCK) {
+		ret = ddi_intr_block_disable(sc->sc_intr_htable,
+		    sc->sc_intr_num);
+		if (ret != DDI_SUCCESS) {
+			dev_err(sc->sc_dev, CE_WARN,
+			    "Failed to disable MSIs, won't be able to"
+			    "reuse next time");
+		}
+	} else {
+		for (i = 0; i < sc->sc_intr_num; i++) {
+			ret = ddi_intr_disable(sc->sc_intr_htable[i]);
+			if (ret != DDI_SUCCESS) {
+				dev_err(sc->sc_dev, CE_WARN,
+				    "Failed to disable interrupt %d, "
+				    "won't be able to reuse", i);
+
+			}
+		}
+	}
+
+
+	for (i = 0; i < sc->sc_intr_num; i++) {
+		(void) ddi_intr_remove_handler(sc->sc_intr_htable[i]);
+	}
+
+	for (i = 0; i < sc->sc_intr_num; i++)
+		(void) ddi_intr_free(sc->sc_intr_htable[i]);
+
+	kmem_free(sc->sc_intr_htable,
+	    sizeof (ddi_intr_handle_t) * sc->sc_intr_num);
+
+
+	/* After disabling interrupts, the config offset is non-MSI. */
+	sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI;
+}
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modlmisc modlmisc = {
+	&mod_miscops, /* Type of module */
+	"VirtIO common library module",
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	{
+		(void *)&modlmisc,
+		NULL
+	}
+};
+
+int
+_init(void)
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_fini(void)
+{
+	return (mod_remove(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/virtio/virtioreg.h	Tue Oct 16 03:36:33 2012 -0700
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2010 Minoura Makoto.
+ * Copyright (c) 2012 Nexenta Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Part of the file derived from `Virtio PCI Card Specification v0.8.6 DRAFT'
+ * Appendix A.
+ */
+
+/*
+ * An interface for efficient virtio implementation.
+ *
+ * This header is BSD licensed so anyone can use the definitions
+ * to implement compatible drivers/servers.
+ *
+ * Copyright 2007, 2009, IBM Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' ANDANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef __VIRTIOREG_H__
+#define	__VIRTIOREG_H__
+
+#include <sys/types.h>
+
+#define	PCI_VENDOR_QUMRANET 0x1af4
+#define	PCI_DEV_VIRTIO_MIN 0x1000
+#define	PCI_DEV_VIRTIO_MAX 0x103f
+#define	VIRTIO_PCI_ABI_VERSION 0
+
+/* Virtio product id (subsystem) */
+#define	PCI_PRODUCT_VIRTIO_NETWORK	1
+#define	PCI_PRODUCT_VIRTIO_BLOCK	2
+#define	PCI_PRODUCT_VIRTIO_CONSOLE	3
+#define	PCI_PRODUCT_VIRTIO_ENTROPY	4
+#define	PCI_PRODUCT_VIRTIO_BALLOON	5
+#define	PCI_PRODUCT_VIRTIO_9P		9
+
+/* Virtio header */
+#define	VIRTIO_CONFIG_DEVICE_FEATURES		0 /* 32bit */
+#define	VIRTIO_CONFIG_GUEST_FEATURES		4 /* 32bit */
+
+#define	VIRTIO_F_NOTIFY_ON_EMPTY		(1<<24)
+#define	VIRTIO_F_RING_INDIRECT_DESC		(1<<28)
+#define	VIRTIO_F_BAD_FEATURE			(1<<30)
+
+#define	VIRTIO_CONFIG_QUEUE_ADDRESS		8 /* 32bit */
+#define	VIRTIO_CONFIG_QUEUE_SIZE		12 /* 16bit */
+#define	VIRTIO_CONFIG_QUEUE_SELECT		14 /* 16bit */
+#define	VIRTIO_CONFIG_QUEUE_NOTIFY		16 /* 16bit */
+#define	VIRTIO_CONFIG_DEVICE_STATUS		18 /* 8bit */
+
+#define	VIRTIO_CONFIG_DEVICE_STATUS_RESET	0
+#define	VIRTIO_CONFIG_DEVICE_STATUS_ACK		1
+#define	VIRTIO_CONFIG_DEVICE_STATUS_DRIVER	2
+#define	VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK	4
+#define	VIRTIO_CONFIG_DEVICE_STATUS_FAILED	128
+
+#define	VIRTIO_CONFIG_ISR_STATUS		19 /* 8bit */
+#define	VIRTIO_CONFIG_ISR_CONFIG_CHANGE		2
+
+#define	VIRTIO_CONFIG_CONFIG_VECTOR		20 /* 16bit, optional */
+#define	VIRTIO_CONFIG_QUEUE_VECTOR		22
+
+#define	VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI	20
+#define	VIRTIO_CONFIG_DEVICE_CONFIG_MSI		24
+
+#define	VIRTIO_MSI_NO_VECTOR 0xffff
+
+/* Virtqueue */
+/* This marks a buffer as continuing via the next field. */
+#define	VRING_DESC_F_NEXT	1
+/*
+ * This marks a buffer as write-only, from the devices's perspective.
+ * (otherwise read-only).
+ */
+#define	VRING_DESC_F_WRITE	2
+/* This means the buffer contains a list of buffer descriptors. */
+#define	VRING_DESC_F_INDIRECT	4
+
+/*
+ * The Host uses this in used->flags to advise the Guest: don't kick me
+ * when you add a buffer.  It's unreliable, so it's simply an
+ * optimization.  Guest will still kick if it's out of buffers.
+ */
+#define	VRING_USED_F_NO_NOTIFY	1
+/*
+ *  The Guest uses this in avail->flags to advise the Host: don't
+ * interrupt me when you consume a buffer.  It's unreliable, so it's
+ * simply an optimization.
+ */
+#define	VRING_AVAIL_F_NO_INTERRUPT	1
+
+/*
+ * Virtio ring descriptors: 16 bytes.
+ * These can chain together via "next".
+ */
+struct vring_desc {
+	/* Address (guest-physical). */
+	uint64_t addr;
+	/* Length. */
+	uint32_t len;
+	/* The flags as indicated above. */
+	uint16_t flags;
+	/* We chain unused descriptors via this, too */
+	uint16_t next;
+} __attribute__((packed));
+
+struct vring_avail {
+	uint16_t flags;
+	uint16_t idx;
+	uint16_t ring[];
+} __attribute__((packed));
+
+/* u32 is used here for ids for padding reasons. */
+struct vring_used_elem {
+	/* Index of start of used descriptor chain. */
+	uint32_t id;
+	/* Total length of the descriptor chain which was written to. */
+	uint32_t len;
+} __attribute__((packed));
+
+struct vring_used {
+	uint16_t flags;
+	uint16_t idx;
+	struct vring_used_elem ring[];
+} __attribute__((packed));
+
+
+/* Got nothing to do with the system page size, just a confusing name. */
+#define	VIRTIO_PAGE_SIZE	(4096)
+
+#endif /* __VIRTIOREG_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/virtio/virtiovar.h	Tue Oct 16 03:36:33 2012 -0700
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2010 Minoura Makoto.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Part of the file derived from `Virtio PCI Card Specification v0.8.6 DRAFT'
+ * Appendix A.
+ */
+
+/*
+ * An interface for efficient virtio implementation.
+ *
+ * This header is BSD licensed so anyone can use the definitions
+ * to implement compatible drivers/servers.
+ *
+ * Copyright 2007, 2009, IBM Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' ANDANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#ifndef __VIRTIOVAR_H__
+#define	__VIRTIOVAR_H__
+
+#include <sys/types.h>
+#include <sys/dditypes.h>
+#include <sys/cmn_err.h>
+#include <sys/list.h>
+
+#ifdef DEBUG
+#define	dev_debug(dip, fmt, arg...) \
+	dev_err(dip, fmt, ##arg)
+#else
+#define	dev_debug(dip, fmt, arg...)
+#endif
+
+struct vq_entry {
+	list_node_t		qe_list;
+	struct virtqueue	*qe_queue;
+	uint16_t		qe_index; /* index in vq_desc array */
+	/* followings are used only when it is the `head' entry */
+	struct vq_entry		*qe_next;
+	struct vring_desc	*qe_desc;
+	ddi_dma_cookie_t	qe_indirect_dma_cookie;
+	ddi_dma_handle_t	qe_indirect_dma_handle;
+	ddi_acc_handle_t	qe_indirect_dma_acch;
+	struct vring_desc	*qe_indirect_descs;
+	unsigned int 		qe_indirect_next;
+};
+
+struct virtqueue {
+	struct virtio_softc	*vq_owner;
+	unsigned int		vq_num; /* queue size (# of entries) */
+	unsigned int		vq_indirect_num;
+	int			vq_index; /* queue number (0, 1, ...) */
+
+	/* vring pointers (KVA) */
+	struct vring_desc	*vq_descs;
+	struct vring_avail	*vq_avail;
+	struct vring_used	*vq_used;
+
+	/* virtqueue allocation info */
+	void			*vq_vaddr;
+	int			vq_availoffset;
+	int			vq_usedoffset;
+	ddi_dma_cookie_t	vq_dma_cookie;
+	ddi_dma_handle_t	vq_dma_handle;
+	ddi_acc_handle_t	vq_dma_acch;
+
+	int			vq_maxsegsize;
+
+	/* free entry management */
+	struct vq_entry		*vq_entries;
+	list_t			vq_freelist;
+	kmutex_t		vq_freelist_lock;
+	int			vq_used_entries;
+
+	/* enqueue/dequeue status */
+	uint16_t		vq_avail_idx;
+	kmutex_t		vq_avail_lock;
+	uint16_t		vq_used_idx;
+	kmutex_t		vq_used_lock;
+};
+
+struct virtio_softc {
+	dev_info_t		*sc_dev;
+
+	uint_t			sc_intr_prio;
+
+	ddi_acc_handle_t	sc_ioh;
+	caddr_t			sc_io_addr;
+	int			sc_config_offset;
+
+	uint32_t		sc_features;
+
+	int			sc_nvqs; /* set by the user */
+
+	ddi_intr_handle_t	*sc_intr_htable;
+	int			sc_intr_num;
+	boolean_t		sc_intr_config;
+	int			sc_intr_cap;
+};
+
+struct virtio_int_handler {
+	ddi_intr_handler_t *vh_func;
+	void *vh_priv;
+};
+
+/* public interface */
+uint32_t virtio_negotiate_features(struct virtio_softc *, uint32_t);
+size_t virtio_show_features(uint32_t features, char *buffer, size_t len);
+boolean_t virtio_has_feature(struct virtio_softc *sc, uint32_t feature);
+void virtio_set_status(struct virtio_softc *sc, unsigned int);
+#define	virtio_device_reset(sc)	virtio_set_status((sc), 0)
+
+uint8_t virtio_read_device_config_1(struct virtio_softc *sc,
+		unsigned int index);
+uint16_t virtio_read_device_config_2(struct virtio_softc *sc,
+		unsigned int index);
+uint32_t virtio_read_device_config_4(struct virtio_softc *sc,
+		unsigned int index);
+uint64_t virtio_read_device_config_8(struct virtio_softc *sc,
+		unsigned int index);
+void virtio_write_device_config_1(struct virtio_softc *sc,
+		unsigned int index, uint8_t value);
+void virtio_write_device_config_2(struct virtio_softc *sc,
+		unsigned int index, uint16_t value);
+void virtio_write_device_config_4(struct virtio_softc *sc,
+		unsigned int index, uint32_t value);
+void virtio_write_device_config_8(struct virtio_softc *sc,
+		unsigned int index, uint64_t value);
+
+struct virtqueue *virtio_alloc_vq(struct virtio_softc *sc,
+		unsigned int index, unsigned int size,
+		unsigned int indirect_num, const char *name);
+void virtio_free_vq(struct virtqueue *);
+void virtio_reset(struct virtio_softc *);
+struct vq_entry *vq_alloc_entry(struct virtqueue *vq);
+void vq_free_entry(struct virtqueue *vq, struct vq_entry *qe);
+uint_t vq_num_used(struct virtqueue *vq);
+
+void virtio_stop_vq_intr(struct virtqueue *);
+void virtio_start_vq_intr(struct virtqueue *);
+
+void virtio_ve_add_cookie(struct vq_entry *qe, ddi_dma_handle_t dma_handle,
+    ddi_dma_cookie_t dma_cookie, unsigned int ncookies, boolean_t write);
+void virtio_ve_add_indirect_buf(struct vq_entry *qe, uint64_t paddr,
+    uint32_t len, boolean_t write);
+void virtio_ve_set(struct vq_entry *qe, uint64_t paddr, uint32_t len,
+		boolean_t write);
+
+void virtio_push_chain(struct vq_entry *qe, boolean_t sync);
+struct vq_entry *virtio_pull_chain(struct virtqueue *vq, uint32_t *len);
+void virtio_free_chain(struct vq_entry *ve);
+void virtio_sync_vq(struct virtqueue *vq);
+
+int virtio_register_ints(struct virtio_softc *sc,
+		struct virtio_int_handler *config_handler,
+		struct virtio_int_handler vq_handlers[]);
+void virtio_release_ints(struct virtio_softc *sc);
+int virtio_enable_ints(struct virtio_softc *sc);
+
+#endif /* __VIRTIOVAR_H__ */
--- a/usr/src/uts/intel/Makefile.intel.shared	Tue Oct 16 03:33:44 2012 -0700
+++ b/usr/src/uts/intel/Makefile.intel.shared	Tue Oct 16 03:36:33 2012 -0700
@@ -20,8 +20,7 @@
 #
 
 # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
-
-# Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+# Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
 
 #
 #	This makefile contains the common definitions for all intel
@@ -407,6 +406,13 @@
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= ixgb
 
 #
+# Virtio drivers
+#
+
+# Virtio core
+DRV_KMODS	+= virtio
+
+#
 #	DTrace and DTrace Providers
 #
 DRV_KMODS	+= dtrace
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/virtio/Makefile	Tue Oct 16 03:36:33 2012 -0700
@@ -0,0 +1,86 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE		= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= virtio
+OBJECTS		= $(VIRTIO_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(VIRTIO_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_MISC_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides
+#
+
+INC_PATH += -I$(UTSBASE)/common/io/virtio
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ