Index: arch/i386/conf/GENERIC
===================================================================
--- arch/i386/conf/GENERIC	(.../vendor/openbsd/head/sys)	(revision 135021)
+++ arch/i386/conf/GENERIC	(.../user/yasuoka/virtio-work)	(revision 135372)
@@ -775,6 +775,11 @@ owsbm*	at onewire?		# Smart Battery Monitor
 owtemp* at onewire?		# Temperature
 owctr*	at onewire?		# Counter device
 
+# virtio
+virtio*	at pci? dev ? function ?
+viomb*	at virtio?
+vioif*	at virtio?
+
 pseudo-device	pctr		1
 pseudo-device	mtrr		1	# Memory range attributes control
 pseudo-device	nvram		1
Index: arch/amd64/conf/GENERIC
===================================================================
--- arch/amd64/conf/GENERIC	(.../vendor/openbsd/head/sys)	(revision 135021)
+++ arch/amd64/conf/GENERIC	(.../user/yasuoka/virtio-work)	(revision 135372)
@@ -586,6 +586,11 @@ owsbm*	at onewire?		# Smart Battery Monitor
 owtemp* at onewire?		# Temperature
 owctr*	at onewire?		# Counter device
 
+# virtio
+virtio*	at pci? dev ? function ?
+viomb*	at virtio?
+vioif*	at virtio?
+
 pseudo-device	pctr		1
 pseudo-device	nvram		1
 pseudo-device	sequencer	1
Index: dev/pci/virtioreg.h
===================================================================
--- dev/pci/virtioreg.h	(.../vendor/openbsd/head/sys)	(revision 0)
+++ dev/pci/virtioreg.h	(.../user/yasuoka/virtio-work)	(revision 135372)
@@ -0,0 +1,152 @@
+/*	$NetBSD$	*/
+
+/*
+ * 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'' 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 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 _DEV_PCI_VIRTIOREG_H_
+#define	_DEV_PCI_VIRTIOREG_H_
+
+#include <sys/types.h>
+
+/* 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_DEVICE_CONFIG_NOMSI	20
+#define VIRTIO_CONFIG_DEVICE_CONFIG_MSI		22
+
+/* Virtqueue */
+/* This marks a buffer as continuing via the next field. */
+#define VRING_DESC_F_NEXT       1
+/* This marks a buffer as write-only (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;
+} __packed;
+
+struct vring_avail {
+        uint16_t flags;
+        uint16_t idx;
+        uint16_t ring[0];
+} __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;
+} __packed;
+
+struct vring_used {
+        uint16_t flags;
+        uint16_t idx;
+        struct vring_used_elem ring[0];
+} __packed;
+
+#define VIRTIO_PAGE_SIZE	(4096)
+
+#endif /* _DEV_PCI_VIRTIOREG_H_ */
Index: dev/pci/files.pci
===================================================================
--- dev/pci/files.pci	(.../vendor/openbsd/head/sys)	(revision 135021)
+++ dev/pci/files.pci	(.../user/yasuoka/virtio-work)	(revision 135372)
@@ -806,3 +806,19 @@ file	dev/pci/itherm.c		itherm
 device	glxpcib: isabus, gpiobus
 attach	glxpcib at pci
 file	dev/pci/glxpcib.c		glxpcib
+
+# VirtIO
+device	virtio {}
+attach	virtio at pci
+file	dev/pci/virtio.c	virtio
+
+#attach	sd at virtio with ld_virtio
+#file	dev/pci/ld_virtio.c	ld_virtio
+
+device	viomb
+attach	viomb at virtio
+file	dev/pci/viomb.c		viomb
+
+device	vioif
+attach	vioif at virtio
+file	dev/pci/if_vioif.c	vioif
Index: dev/pci/ld_virtio.c
===================================================================
--- dev/pci/ld_virtio.c	(.../vendor/openbsd/head/sys)	(revision 0)
+++ dev/pci/ld_virtio.c	(.../user/yasuoka/virtio-work)	(revision 135372)
@@ -0,0 +1,559 @@
+/*	$NetBSD$	*/
+
+/*
+ * 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/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include "rnd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/buf.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/disk.h>
+#include <sys/mutex.h>
+#if NRND > 0
+#include <sys/rnd.h>
+#endif
+
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <dev/ldvar.h>
+#include <dev/pci/virtioreg.h>
+#include <dev/pci/virtiovar.h>
+
+#include <uvm/uvm_extern.h>
+
+/*
+ * ld_virtioreg:
+ */
+/* Configuration registers */
+#define VIRTIO_BLK_CONFIG_CAPACITY	0 /* 64bit */
+#define VIRTIO_BLK_CONFIG_SIZE_MAX	8 /* 32bit */
+#define VIRTIO_BLK_CONFIG_SEG_MAX	12 /* 32bit */
+#define VIRTIO_BLK_CONFIG_GEOMETRY_C	16 /* 16bit */
+#define VIRTIO_BLK_CONFIG_GEOMETRY_H	18 /* 8bit */
+#define VIRTIO_BLK_CONFIG_GEOMETRY_S	19 /* 8bit */
+#define VIRTIO_BLK_CONFIG_BLK_SIZE	20 /* 32bit */
+#define VIRTIO_BLK_CONFIG_SECTORS_MAX	24 /* 32bit */
+
+/* Feature bits */
+#define VIRTIO_BLK_F_BARRIER	(1<<0)
+#define VIRTIO_BLK_F_SIZE_MAX	(1<<1)
+#define VIRTIO_BLK_F_SEG_MAX	(1<<2)
+#define VIRTIO_BLK_F_GEOMETRY	(1<<4)
+#define VIRTIO_BLK_F_RO		(1<<5)
+#define VIRTIO_BLK_F_BLK_SIZE	(1<<6)
+#define VIRTIO_BLK_F_SCSI	(1<<7)
+#define VIRTIO_BLK_F_FLUSH	(1<<9)
+#define VIRTIO_BLK_F_SECTOR_MAX	(1<<10)
+
+/* Command */
+#define VIRTIO_BLK_T_IN		0
+#define VIRTIO_BLK_T_OUT	1
+#define VIRTIO_BLK_T_BARRIER	0x80000000
+
+/* Status */
+#define VIRTIO_BLK_S_OK		0
+#define VIRTIO_BLK_S_IOERR	1
+
+/* Request header structure */
+struct virtio_blk_req_hdr {
+	uint32_t	type;	/* VIRTIO_BLK_T_* */
+	uint32_t	ioprio;
+	uint64_t	sector;
+} __packed;
+/* 512*virtio_blk_req_hdr.sector byte payload and 1 byte status follows */
+
+
+/*
+ * ld_virtiovar:
+ */
+struct virtio_blk_req {
+	struct virtio_blk_req_hdr	vr_hdr;
+	uint8_t				vr_status;
+	struct buf			*vr_bp;
+	bus_dmamap_t			vr_cmdsts;
+	bus_dmamap_t			vr_payload;
+};
+
+struct ld_virtio_softc {
+	struct ld_softc		sc_ld;
+	device_t		sc_dev;
+
+	struct virtio_softc	*sc_virtio;
+	struct virtqueue	sc_vq[1];
+
+	struct virtio_blk_req	*sc_reqs;
+	bus_dma_segment_t	sc_reqs_segs[1];
+
+	kmutex_t		sc_lock;
+
+	int			sc_readonly;
+};
+
+static int	ld_virtio_match(device_t, cfdata_t, void *);
+static void	ld_virtio_attach(device_t, device_t, void *);
+static int	ld_virtio_detach(device_t, int);
+
+CFATTACH_DECL_NEW(ld_virtio, sizeof(struct ld_virtio_softc),
+    ld_virtio_match, ld_virtio_attach, ld_virtio_detach, NULL);
+
+static int
+ld_virtio_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct virtio_softc *va = aux;
+
+	if (va->sc_childdevid == PCI_PRODUCT_VIRTIO_BLOCK)
+		return 1;
+
+	return 0;
+}
+
+static int ld_virtio_vq_done(struct virtqueue *);
+static int ld_virtio_dump(struct ld_softc *, void *, int, int);
+static int ld_virtio_start(struct ld_softc *, struct buf *);
+
+static int
+ld_virtio_alloc_reqs(struct ld_virtio_softc *sc, int qsize)
+{
+	int allocsize, r, rsegs, i;
+	struct ld_softc *ld = &sc->sc_ld;
+	void *vaddr;
+
+	allocsize = sizeof(struct virtio_blk_req) * qsize;
+	r = bus_dmamem_alloc(sc->sc_virtio->sc_dmat, allocsize, 0, 0,
+			     &sc->sc_reqs_segs[0], 1, &rsegs, BUS_DMA_NOWAIT);
+	if (r != 0) {
+		aprint_error_dev(sc->sc_dev,
+				 "DMA memory allocation failed, size %d, "
+				 "error code %d\n", allocsize, r);
+		goto err_none;
+	}
+	r = bus_dmamem_map(sc->sc_virtio->sc_dmat,
+			   &sc->sc_reqs_segs[0], 1, allocsize,
+			   &vaddr, BUS_DMA_NOWAIT);
+	if (r != 0) {
+		aprint_error_dev(sc->sc_dev,
+				 "DMA memory map failed, "
+				 "error code %d\n", r);
+		goto err_dmamem_alloc;
+	}
+	sc->sc_reqs = vaddr;
+	memset(vaddr, 0, allocsize);
+	for (i = 0; i < qsize; i++) {
+		struct virtio_blk_req *vr = &sc->sc_reqs[i];
+		r = bus_dmamap_create(sc->sc_virtio->sc_dmat,
+				      offsetof(struct virtio_blk_req, vr_bp),
+				      1,
+				      offsetof(struct virtio_blk_req, vr_bp),
+				      0,
+				      BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW,
+				      &vr->vr_cmdsts);
+		if (r != 0) {
+			aprint_error_dev(sc->sc_dev,
+					 "command dmamap creation failed, "
+					 "error code %d\n", r);
+			goto err_reqs;
+		}
+		r = bus_dmamap_load(sc->sc_virtio->sc_dmat, vr->vr_cmdsts,
+				    &vr->vr_hdr,
+				    offsetof(struct virtio_blk_req, vr_bp),
+				    NULL, BUS_DMA_NOWAIT);
+		if (r != 0) {
+			aprint_error_dev(sc->sc_dev,
+					 "command dmamap load failed, "
+					 "error code %d\n", r);
+			goto err_reqs;
+		}
+		r = bus_dmamap_create(sc->sc_virtio->sc_dmat,
+				      ld->sc_maxxfer,
+				      ld->sc_maxxfer / NBPG, ld->sc_maxxfer,
+				      0,
+				      BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW,
+				      &vr->vr_payload);
+		if (r != 0) {
+			aprint_error_dev(sc->sc_dev,
+					 "payload dmamap creation failed, "
+					 "error code %d\n", r);
+			goto err_reqs;
+		}
+	}
+	return 0;
+
+err_reqs:
+	for (i = 0; i < qsize; i++) {
+		struct virtio_blk_req *vr = &sc->sc_reqs[i];
+		if (vr->vr_cmdsts) {
+			bus_dmamap_destroy(sc->sc_virtio->sc_dmat,
+					   vr->vr_cmdsts);
+			vr->vr_cmdsts = 0;
+		}
+		if (vr->vr_payload) {
+			bus_dmamap_destroy(sc->sc_virtio->sc_dmat,
+					   vr->vr_payload);
+			vr->vr_payload = 0;
+		}
+	}
+	bus_dmamem_unmap(sc->sc_virtio->sc_dmat, sc->sc_reqs, allocsize);
+err_dmamem_alloc:
+	bus_dmamem_free(sc->sc_virtio->sc_dmat, &sc->sc_reqs_segs[0], 1);
+err_none:
+	return -1;
+}
+
+static void
+ld_virtio_attach(device_t parent, device_t self, void *aux)
+{
+	struct ld_virtio_softc *sc = device_private(self);
+	struct ld_softc *ld = &sc->sc_ld;
+	struct virtio_softc *vsc = device_private(parent);
+	uint32_t features;
+	int qsize, maxxfersize;
+
+	if (vsc->sc_child != NULL) {
+		aprint_normal(": child already attached for %s; "
+			      "something wrong...\n",
+			      device_xname(parent));
+		return;
+	}
+	aprint_normal("\n");
+	aprint_naive("\n");
+
+	sc->sc_dev = self;
+	sc->sc_virtio = vsc;
+
+	vsc->sc_child = self;
+	vsc->sc_ipl = IPL_BIO;
+	vsc->sc_vqs = &sc->sc_vq[0];
+	vsc->sc_nvqs = 1;
+	vsc->sc_config_change = 0;
+	vsc->sc_intrhand = virtio_vq_intr;
+
+	features = virtio_negotiate_features(vsc,
+					     (VIRTIO_BLK_F_SIZE_MAX |
+					      VIRTIO_BLK_F_SEG_MAX |
+					      VIRTIO_BLK_F_GEOMETRY |
+					      VIRTIO_BLK_F_RO |
+					      VIRTIO_BLK_F_BLK_SIZE |
+					      VIRTIO_BLK_F_SECTOR_MAX));
+	if (features & VIRTIO_BLK_F_RO)
+		sc->sc_readonly = 1;
+	else
+		sc->sc_readonly = 0;
+
+	maxxfersize = MAXPHYS;
+	if (features & VIRTIO_BLK_F_SECTOR_MAX) {
+		maxxfersize = virtio_read_device_config_4(vsc,
+					VIRTIO_BLK_CONFIG_SECTORS_MAX)
+				* ld->sc_secsize;
+		if (maxxfersize > MAXPHYS)
+			maxxfersize = MAXPHYS;
+	}
+
+	if (virtio_alloc_vq(vsc, &sc->sc_vq[0], 0,
+			    maxxfersize, maxxfersize / NBPG + 2,
+			    "I/O request") != 0) {
+		goto err;
+	}
+	qsize = sc->sc_vq[0].vq_num;
+	sc->sc_vq[0].vq_done = ld_virtio_vq_done;
+
+	ld->sc_dv = self;
+	ld->sc_secperunit = virtio_read_device_config_8(vsc,
+				VIRTIO_BLK_CONFIG_CAPACITY);
+	ld->sc_secsize = 512;
+	if (features & VIRTIO_BLK_F_BLK_SIZE) {
+		ld->sc_secsize = virtio_read_device_config_4(vsc,
+					VIRTIO_BLK_CONFIG_BLK_SIZE);
+	}
+	ld->sc_maxxfer = maxxfersize;
+	if (features & VIRTIO_BLK_F_GEOMETRY) {
+		ld->sc_ncylinders = virtio_read_device_config_2(vsc,
+					VIRTIO_BLK_CONFIG_GEOMETRY_C);
+		ld->sc_nheads     = virtio_read_device_config_1(vsc,
+					VIRTIO_BLK_CONFIG_GEOMETRY_H);
+		ld->sc_nsectors   = virtio_read_device_config_1(vsc,
+					VIRTIO_BLK_CONFIG_GEOMETRY_S);
+	}
+	ld->sc_maxqueuecnt = qsize;
+
+	if (ld_virtio_alloc_reqs(sc, qsize) < 0)
+		goto err;
+
+	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_BIO);
+
+	ld->sc_dump = ld_virtio_dump;
+	ld->sc_flush = NULL;
+	ld->sc_start = ld_virtio_start;
+
+	ld->sc_flags = LDF_ENABLED;
+	ldattach(ld);
+
+	return;
+
+err:
+	vsc->sc_child = (void*)1;
+	return;
+}
+
+static int
+ld_virtio_start(struct ld_softc *ld, struct buf *bp)
+{
+	/* splbio */
+	struct ld_virtio_softc *sc = device_private(ld->sc_dv);
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtqueue *vq = &sc->sc_vq[0];
+	struct virtio_blk_req *vr;
+	int r;
+	int isread = (bp->b_flags & B_READ);
+	int slot;
+
+	if (sc->sc_readonly && !isread)
+		return EIO;
+
+	r = virtio_enqueue_prep(vsc, vq, &slot);
+	if (r != 0)
+		return r;
+	vr = &sc->sc_reqs[slot];
+	r = bus_dmamap_load(vsc->sc_dmat, vr->vr_payload,
+			    bp->b_data, bp->b_bcount, NULL,
+			    ((isread?BUS_DMA_READ:BUS_DMA_WRITE)
+			     |BUS_DMA_NOWAIT));
+	if (r != 0)
+		return r;
+
+	r = virtio_enqueue_reserve(vsc, vq, slot, vr->vr_payload->dm_nsegs + 2);
+	if (r != 0) {
+		bus_dmamap_unload(vsc->sc_dmat, vr->vr_payload);
+		return r;
+	}
+
+	vr->vr_bp = bp;
+	vr->vr_hdr.type = isread?VIRTIO_BLK_T_IN:VIRTIO_BLK_T_OUT;
+	vr->vr_hdr.ioprio = 0;
+	vr->vr_hdr.sector = bp->b_rawblkno * sc->sc_ld.sc_secsize / 512;
+
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts,
+			0, sizeof(struct virtio_blk_req_hdr),
+			BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_payload,
+			0, bp->b_bcount,
+			isread?BUS_DMASYNC_PREREAD:BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts,
+			offsetof(struct virtio_blk_req, vr_status),
+			sizeof(uint8_t),
+			BUS_DMASYNC_PREREAD);
+
+	virtio_enqueue_p(vsc, vq, slot, vr->vr_cmdsts,
+			 0, sizeof(struct virtio_blk_req_hdr),
+			 true);
+	virtio_enqueue(vsc, vq, slot, vr->vr_payload, !isread);
+	virtio_enqueue_p(vsc, vq, slot, vr->vr_cmdsts,
+			 offsetof(struct virtio_blk_req, vr_status),
+			 sizeof(uint8_t),
+			 false);
+	virtio_enqueue_commit(vsc, vq, slot, true);
+
+	return 0;
+}
+
+static void
+ld_virtio_vq_done1(struct ld_virtio_softc *sc, struct virtio_softc *vsc,
+		   struct virtqueue *vq, int slot)
+{
+	struct virtio_blk_req *vr = &sc->sc_reqs[slot];
+	struct buf *bp = vr->vr_bp;
+
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts,
+			0, sizeof(struct virtio_blk_req_hdr),
+			BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_payload,
+			0, bp->b_bcount,
+			(bp->b_flags & B_READ)?BUS_DMASYNC_POSTREAD
+					      :BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts,
+			sizeof(struct virtio_blk_req_hdr), sizeof(uint8_t),
+			BUS_DMASYNC_POSTREAD);
+
+	if (vr->vr_status != VIRTIO_BLK_S_OK) {
+		bp->b_error = EIO;
+		bp->b_resid = bp->b_bcount;
+	} else {
+		bp->b_error = 0;
+		bp->b_resid = 0;
+	}
+
+	virtio_dequeue_commit(vsc, vq, slot);
+
+	lddone(&sc->sc_ld, bp);
+}
+
+static int
+ld_virtio_vq_done(struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct ld_virtio_softc *sc = device_private(vsc->sc_child);
+	int r = 0;
+	int slot;
+
+again:
+	if (virtio_dequeue(vsc, vq, &slot, NULL))
+		return r;
+	r = 1;
+
+	ld_virtio_vq_done1(sc, vsc, vq, slot);
+	goto again;
+}
+
+static int
+ld_virtio_dump(struct ld_softc *ld, void *data, int blkno, int blkcnt)
+{
+	struct ld_virtio_softc *sc = device_private(ld->sc_dv);
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtqueue *vq = &sc->sc_vq[0];
+	struct virtio_blk_req *vr;
+	int slot, r;
+
+	if (sc->sc_readonly)
+		return EIO;
+
+	r = virtio_enqueue_prep(vsc, vq, &slot);
+	if (r != 0) {
+		if (r == EAGAIN) { /* no free slot; dequeue first */
+			delay(100);
+			ld_virtio_vq_done(vq);
+			r = virtio_enqueue_prep(vsc, vq, &slot);
+			if (r != 0)
+				return r;
+		}
+		return r;
+	}
+	vr = &sc->sc_reqs[slot];
+	r = bus_dmamap_load(vsc->sc_dmat, vr->vr_payload,
+			    data, blkcnt*ld->sc_secsize, NULL,
+			    BUS_DMA_WRITE|BUS_DMA_NOWAIT);
+	if (r != 0)
+		return r;
+
+	r = virtio_enqueue_reserve(vsc, vq, slot, vr->vr_payload->dm_nsegs + 2);
+	if (r != 0) {
+		bus_dmamap_unload(vsc->sc_dmat, vr->vr_payload);
+		return r;
+	}
+
+	vr->vr_bp = (void*)0xdeadbeef;
+	vr->vr_hdr.type = VIRTIO_BLK_T_OUT;
+	vr->vr_hdr.ioprio = 0;
+	vr->vr_hdr.sector = (daddr_t) blkno * ld->sc_secsize / 512;
+
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts,
+			0, sizeof(struct virtio_blk_req_hdr),
+			BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_payload,
+			0, blkcnt*ld->sc_secsize,
+			BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts,
+			offsetof(struct virtio_blk_req, vr_status),
+			sizeof(uint8_t),
+			BUS_DMASYNC_PREREAD);
+
+	virtio_enqueue_p(vsc, vq, slot, vr->vr_cmdsts,
+			 0, sizeof(struct virtio_blk_req_hdr),
+			 true);
+	virtio_enqueue(vsc, vq, slot, vr->vr_payload, true);
+	virtio_enqueue_p(vsc, vq, slot, vr->vr_cmdsts,
+			 offsetof(struct virtio_blk_req, vr_status),
+			 sizeof(uint8_t),
+			 false);
+	virtio_enqueue_commit(vsc, vq, slot, true);
+
+	for ( ; ; ) {
+		int dslot;
+
+		r = virtio_dequeue(vsc, vq, &dslot, NULL);
+		if (r != 0)
+			continue;
+		if (dslot != slot) {
+			ld_virtio_vq_done1(sc, vsc, vq, dslot);
+			continue;
+		} else
+			break;
+	}
+		
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts,
+			0, sizeof(struct virtio_blk_req_hdr),
+			BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_payload,
+			0, blkcnt*ld->sc_secsize,
+			BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts,
+			offsetof(struct virtio_blk_req, vr_status),
+			sizeof(uint8_t),
+			BUS_DMASYNC_POSTREAD);
+	if (vr->vr_status == VIRTIO_BLK_S_OK)
+		r = 0;
+	else
+		r = EIO;
+	virtio_dequeue_commit(vsc, vq, slot);
+
+	return r;
+}
+
+static int
+ld_virtio_detach(device_t self, int flags)
+{
+	struct ld_virtio_softc *sc = device_private(self);
+	struct ld_softc *ld = &sc->sc_ld;
+	bus_dma_tag_t dmat = sc->sc_virtio->sc_dmat;
+	int r, i, qsize;
+
+	qsize = sc->sc_vq[0].vq_num;
+	r = ldbegindetach(ld, flags);
+	if (r != 0)
+		return r;
+	virtio_reset(sc->sc_virtio);
+	virtio_free_vq(sc->sc_virtio, &sc->sc_vq[0]);
+
+	for (i = 0; i < qsize; i++) {
+		bus_dmamap_destroy(dmat,
+				   sc->sc_reqs[i].vr_cmdsts);
+		bus_dmamap_destroy(dmat,
+				   sc->sc_reqs[i].vr_payload);
+	}
+	bus_dmamem_unmap(dmat, sc->sc_reqs,
+			 sizeof(struct virtio_blk_req) * qsize);
+	bus_dmamem_free(dmat, &sc->sc_reqs_segs[0], 1);
+
+	ldenddetach(ld);
+
+	return 0;
+}
Index: dev/pci/virtio.c
===================================================================
--- dev/pci/virtio.c	(.../vendor/openbsd/head/sys)	(revision 0)
+++ dev/pci/virtio.c	(.../user/yasuoka/virtio-work)	(revision 135372)
@@ -0,0 +1,1004 @@
+/*	$OpenBSD$	*/
+/*	$NetBSD$	*/
+
+/*
+ * 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/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+#include <machine/bus.h>
+#include <sys/device.h>
+
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <dev/pci/virtioreg.h>
+#include <dev/pci/virtiovar.h>
+
+#define	DPRINTF(x)		/* XX */
+#define MINSEG_INDIRECT		2 /* use indirect if nsegs >= this value */
+
+static int	virtio_match(struct device *, void *, void *);
+static void	virtio_attach(struct device *, struct device *, void *);
+static int	virtio_detach(struct device *, int);
+static int	virtio_intr(void *arg);
+static void	virtio_init_vq(struct virtio_softc *, struct virtqueue *);
+
+struct cfattach virtio_ca = {
+	sizeof(struct virtio_softc), virtio_match, virtio_attach,
+	virtio_detach
+};
+struct cfdriver virtio_cd = {
+	NULL, "virtio", DV_DULL
+};
+
+static void
+virtio_set_status(struct virtio_softc *sc, int status)
+{
+	int old = 0;
+
+	if (status != 0)
+		old = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+				       VIRTIO_CONFIG_DEVICE_STATUS);
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIRTIO_CONFIG_DEVICE_STATUS,
+			  status|old);
+}
+
+#define virtio_device_reset(sc)	virtio_set_status((sc), 0)
+
+static int
+virtio_match(struct device * parent, void *match, void *aux)
+{
+	struct pci_attach_args *pa;
+
+	pa = (struct pci_attach_args *)aux;
+	switch (PCI_VENDOR(pa->pa_id)) {
+	case PCI_VENDOR_QUMRANET:
+		if ((0x1000 <= PCI_PRODUCT(pa->pa_id)) &&
+		    (PCI_PRODUCT(pa->pa_id) <= 0x103f))
+			return 1;
+		break;
+	}
+
+	return 0;
+}
+
+static const char *virtio_device_name[] = {
+	"Unknown (0)",		/* 0 */
+	"Network",		/* 1 */
+	"Block",		/* 2 */
+	"Console",		/* 3 */
+	"Entropy",		/* 4 */
+	"Memory Balloon",	/* 5 */
+	"Unknown (6)",		/* 6 */
+	"Unknown (7)",		/* 7 */
+	"Unknown (8)",		/* 8 */
+	"9P Transport"		/* 9 */
+};
+#define NDEVNAMES	(sizeof(virtio_device_name)/sizeof(char*))
+
+static void
+virtio_attach(struct device * parent, struct device * self, void *aux)
+{
+	struct virtio_softc *sc = (struct virtio_softc *)self;
+	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+	pci_chipset_tag_t pc = pa->pa_pc;
+	pcitag_t tag = pa->pa_tag;
+	int revision;
+	pcireg_t id;
+	char const *intrstr;
+	pci_intr_handle_t ih;
+
+	revision = PCI_REVISION(pa->pa_class);
+	if (revision != 0) {
+		printf(": unknown revision 0x%02x; giving up\n", revision);
+		return;
+	}
+	printf("\n");
+
+	/* subsystem ID shows what I am */
+	id = pci_conf_read(pc, tag, PCI_SUBSYS_ID_REG);
+	printf("%s: %s Virtio %s Device (rev. 0x%02x)\n",
+	    sc->sc_dev.dv_xname, pci_findvendor(id),
+	    (PCI_PRODUCT(id)<NDEVNAMES?
+	    virtio_device_name[PCI_PRODUCT(id)]:"Unknown"), revision);
+
+	sc->sc_pc = pc;
+	sc->sc_tag = tag;
+	sc->sc_iot = pa->pa_iot;
+	sc->sc_dmat = pa->pa_dmat;
+	sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI;
+
+	if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_IO, 0,
+			   &sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_iosize, 0)) {
+		printf("%s: can't map i/o space\n", sc->sc_dev.dv_xname);
+		return;
+	}
+
+	virtio_device_reset(sc);
+	virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_ACK);
+	virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER);
+
+	/* XXX: use softc as aux... */
+	sc->sc_childdevid = PCI_PRODUCT(id);
+	sc->sc_child = NULL;
+	config_found(self, sc, NULL);
+	if (sc->sc_child == NULL) {
+		printf("%s: no matching child driver; not configured\n",
+		    sc->sc_dev.dv_xname);
+		return;
+	}
+	if (sc->sc_child == (void*)1) { /* this shows error */
+		printf("%s: virtio configuration failed\n",
+		    sc->sc_dev.dv_xname);
+		virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
+		return;
+	}
+
+	if (pci_intr_map(pa, &ih)) {
+		printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname);
+		virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
+		return;
+	}
+	intrstr = pci_intr_string(pc, ih);
+	sc->sc_ih = pci_intr_establish(pc, ih, sc->sc_ipl, virtio_intr, sc, 
+	    sc->sc_dev.dv_xname);
+	if (sc->sc_ih == NULL) {
+		printf("%s: couldn't establish interrup", sc->sc_dev.dv_xname);
+		if (intrstr != NULL)
+			printf(" at %s", intrstr);
+		printf("\n");
+		virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
+		return;
+	}
+	printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr);
+
+	virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK);
+
+	return;
+}
+
+static int
+virtio_detach(struct device * self, int flags)
+{
+	struct virtio_softc *sc = (struct virtio_softc *)self;
+	int r;
+
+	if (sc->sc_child != 0 && sc->sc_child != (void*)1) {
+		r = config_detach(sc->sc_child, flags);
+		if (r)
+			return r;
+	}
+	KASSERT(sc->sc_child == 0 || sc->sc_child == (void*)1);
+	KASSERT(sc->sc_vqs == 0);
+	pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
+	sc->sc_ih = 0;
+	if (sc->sc_iosize)
+		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
+	sc->sc_iosize = 0;
+
+	return 0;
+}
+
+/*
+ * Reset the device.
+ */
+/*
+ * To reset the device to a known state, do following:
+ *	virtio_reset(sc);	     // this will stop the device activity
+ *	<dequeue finished requests>; // virtio_dequeue() still can be called
+ *	<revoke pending requests in the vqs if any>;
+ *	virtio_reinit_begin(sc);     // dequeue prohibitted
+ *	newfeatures = virtio_negotiate_features(sc, requestedfeatures);
+ *	<some other initialization>;
+ *	virtio_reinit_end(sc);	     // device activated; enqueue allowed
+ * Once attached, feature negotiation can only be allowed after virtio_reset.
+ */
+void
+virtio_reset(struct virtio_softc *sc)
+{
+	virtio_device_reset(sc);
+}
+
+void
+virtio_reinit_start(struct virtio_softc *sc)
+{
+	int i;
+
+	virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_ACK);
+	virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER);
+	for (i = 0; i < sc->sc_nvqs; i++) {
+		int n;
+		struct virtqueue *vq = &sc->sc_vqs[i];
+		bus_space_write_2(sc->sc_iot, sc->sc_ioh,
+				  VIRTIO_CONFIG_QUEUE_SELECT,
+				  vq->vq_index);
+		n = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
+				     VIRTIO_CONFIG_QUEUE_SIZE);
+		if (n == 0)	/* vq disappeared */
+			continue;
+		if (n != vq->vq_num) {
+			panic("%s: virtqueue size changed, vq index %d\n",
+			      sc->sc_dev.dv_xname,
+			      vq->vq_index);
+		}
+		virtio_init_vq(sc, vq);
+		bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+				  VIRTIO_CONFIG_QUEUE_ADDRESS,
+				  (vq->vq_dmamap->dm_segs[0].ds_addr
+				   / VIRTIO_PAGE_SIZE));
+	}
+}
+
+void
+virtio_reinit_end(struct virtio_softc *sc)
+{
+	virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK);
+}
+
+/*
+ * Feature negotiation.
+ */
+uint32_t
+virtio_negotiate_features(struct virtio_softc *sc, uint32_t guest_features)
+{
+	uint32_t r;
+
+	if (!(sc->sc_dev.dv_cfdata->cf_flags & 1) &&
+	    !(sc->sc_child->dv_cfdata->cf_flags & 1)) /* XXX */
+		guest_features |= VIRTIO_F_RING_INDIRECT_DESC;
+	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+			     VIRTIO_CONFIG_DEVICE_FEATURES);
+	r &= guest_features;
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+			  VIRTIO_CONFIG_GUEST_FEATURES, r);
+	sc->sc_features = r;
+	if (r & VIRTIO_F_RING_INDIRECT_DESC)
+		sc->sc_indirect = 1;
+	else
+		sc->sc_indirect = 0;
+
+	return r;
+}
+
+/*
+ * Device configuration registers.
+ */
+uint8_t
+virtio_read_device_config_1(struct virtio_softc *sc, int index)
+{
+	return bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+				sc->sc_config_offset + index);
+}
+
+uint16_t
+virtio_read_device_config_2(struct virtio_softc *sc, int index)
+{
+	return bus_space_read_2(sc->sc_iot, sc->sc_ioh,
+				sc->sc_config_offset + index);
+}
+
+uint32_t
+virtio_read_device_config_4(struct virtio_softc *sc, int index)
+{
+	return bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+				sc->sc_config_offset + index);
+}
+
+uint64_t
+virtio_read_device_config_8(struct virtio_softc *sc, int index)
+{
+	uint64_t r;
+
+	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+			     sc->sc_config_offset + index + sizeof(uint32_t));
+	r <<= 32;
+	r += bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+			      sc->sc_config_offset + index);
+	return r;
+}
+
+void
+virtio_write_device_config_1(struct virtio_softc *sc,
+			     int index, uint8_t value)
+{
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+			  sc->sc_config_offset + index, value);
+}
+
+void
+virtio_write_device_config_2(struct virtio_softc *sc,
+			     int index, uint16_t value)
+{
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
+			  sc->sc_config_offset + index, value);
+}
+
+void
+virtio_write_device_config_4(struct virtio_softc *sc,
+			     int index, uint32_t value)
+{
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+			  sc->sc_config_offset + index, value);
+}
+
+void
+virtio_write_device_config_8(struct virtio_softc *sc,
+			     int index, uint64_t value)
+{
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+			  sc->sc_config_offset + index,
+			  value & 0xffffffff);
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+			  sc->sc_config_offset + index + sizeof(uint32_t),
+			  value >> 32);
+}
+
+/*
+ * Interrupt handler.
+ */
+static int
+virtio_intr(void *arg)
+{
+	struct virtio_softc *sc = arg;
+	int isr, r = 0;
+
+	/* check and ack the interrupt */
+	isr = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+			       VIRTIO_CONFIG_ISR_STATUS);
+	if (isr == 0)
+		return 0;
+	if ((isr & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) &&
+	    (sc->sc_config_change != NULL))
+		r = (sc->sc_config_change)(sc);
+	if (sc->sc_intrhand != NULL)
+		r |= (sc->sc_intrhand)(sc);
+
+	return r;
+}
+
+/*
+ * dmamap sync operations for a virtqueue.
+ */
+static inline void
+vq_sync_descs(struct virtio_softc *sc, struct virtqueue *vq, int ops)
+{
+	/* availoffset == sizeof(vring_desc)*vq_num */
+	bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, 0, vq->vq_availoffset,
+			ops);
+}
+
+static inline void
+vq_sync_aring(struct virtio_softc *sc, struct virtqueue *vq, int ops)
+{
+	bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap,
+			vq->vq_availoffset,
+			offsetof(struct vring_avail, ring)
+			 + vq->vq_num * sizeof(uint16_t),
+			ops);
+}
+
+static inline void
+vq_sync_uring(struct virtio_softc *sc, struct virtqueue *vq, int ops)
+{
+	bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap,
+			vq->vq_usedoffset,
+			offsetof(struct vring_used, ring)
+			 + vq->vq_num * sizeof(struct vring_used_elem),
+			ops);
+}
+
+static inline void
+vq_sync_indirect(struct virtio_softc *sc, struct virtqueue *vq, int slot,
+		     int ops)
+{
+	int offset = vq->vq_indirectoffset
+		      + sizeof(struct vring_desc) * vq->vq_maxnsegs * slot;
+
+	bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap,
+			offset, sizeof(struct vring_desc) * vq->vq_maxnsegs,
+			ops);
+}
+
+/*
+ * Can be used as sc_intrhand.
+ */
+/*
+ * Scan vq, bus_dmamap_sync for the vqs (not for the payload),
+ * and calls (*vq_done)() if some entries are consumed.
+ */
+int
+virtio_vq_intr(struct virtio_softc *sc)
+{
+	struct virtqueue *vq;
+	int i, r = 0;
+
+	for (i = 0; i < sc->sc_nvqs; i++) {
+		vq = &sc->sc_vqs[i];
+		if (vq->vq_queued) {
+			vq->vq_queued = 0;
+			vq_sync_aring(sc, vq, BUS_DMASYNC_POSTWRITE);
+		}
+		vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD);
+// XXX
+//		membar_consumer();
+		if (vq->vq_used_idx != vq->vq_used->idx) {
+			if (vq->vq_done)
+				r |= (vq->vq_done)(vq);
+		}
+	}
+		
+
+	return r;
+}
+
+/*
+ * Start/stop vq interrupt.  No guarantee.
+ */
+void
+virtio_stop_vq_intr(struct virtio_softc *sc, struct virtqueue *vq)
+{
+	vq->vq_avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
+	vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
+	vq->vq_queued++;
+}
+
+void
+virtio_start_vq_intr(struct virtio_softc *sc, struct virtqueue *vq)
+{
+	vq->vq_avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
+	vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
+	vq->vq_queued++;
+}
+
+/*
+ * Initialize vq structure.
+ */
+static void
+virtio_init_vq(struct virtio_softc *sc, struct virtqueue *vq)
+{
+	int i, j;
+	int vq_size = vq->vq_num;
+
+	memset(vq->vq_vaddr, 0, vq->vq_bytesize);
+
+	/* build the indirect descriptor chain */
+	if (vq->vq_indirect != NULL) {
+		struct vring_desc *vd;
+
+		for (i = 0; i < vq_size; i++) {
+			vd = vq->vq_indirect;
+			vd += vq->vq_maxnsegs * i;
+			for (j = 0; j < vq->vq_maxnsegs-1; j++)
+				vd[j].next = j + 1;
+		}
+	}
+
+	/* free slot management */
+	SIMPLEQ_INIT(&vq->vq_freelist);
+	for (i = 0; i < vq_size; i++) {
+		SIMPLEQ_INSERT_TAIL(&vq->vq_freelist,
+				    &vq->vq_entries[i], qe_list);
+		vq->vq_entries[i].qe_index = i;
+	}
+	mtx_init(&vq->vq_freelist_lock, sc->sc_ipl);
+
+	/* enqueue/dequeue status */
+	vq->vq_avail_idx = 0;
+	vq->vq_used_idx = 0;
+	vq->vq_queued = 0;
+	mtx_init(&vq->vq_aring_lock, sc->sc_ipl);
+	mtx_init(&vq->vq_uring_lock, sc->sc_ipl);
+	vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
+	vq_sync_uring(sc, vq, BUS_DMASYNC_PREREAD);
+	vq->vq_queued++;
+}
+	       
+/*
+ * Allocate/free a vq.
+ */
+int
+virtio_alloc_vq(struct virtio_softc *sc,
+		struct virtqueue *vq, int index, int maxsegsize, int maxnsegs,
+		const char *name)
+{
+	int vq_size, allocsize1, allocsize2, allocsize3, allocsize = 0;
+	int rsegs, r;
+#define VIRTQUEUE_ALIGN(n)	(((n)+(VIRTIO_PAGE_SIZE-1))&	\
+				 ~(VIRTIO_PAGE_SIZE-1))
+
+	memset(vq, 0, sizeof(*vq));
+
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
+			  VIRTIO_CONFIG_QUEUE_SELECT, index);
+	vq_size = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
+				   VIRTIO_CONFIG_QUEUE_SIZE);
+	if (vq_size == 0) {
+		printf("%s: virtqueue not exist, index %d for %s\n",
+		    sc->sc_dev.dv_xname, index, name);
+		goto err;
+	}
+	/* allocsize1: descriptor table + avail ring + pad */
+	allocsize1 = VIRTQUEUE_ALIGN(sizeof(struct vring_desc)*vq_size
+				     + sizeof(uint16_t)*(2+vq_size));
+	/* allocsize2: used ring + pad */
+	allocsize2 = VIRTQUEUE_ALIGN(sizeof(uint16_t)*2
+				     + sizeof(struct vring_used_elem)*vq_size);
+	/* allocsize3: indirect table */
+	if (sc->sc_indirect && maxnsegs >= MINSEG_INDIRECT)
+		allocsize3 = sizeof(struct vring_desc) * maxnsegs * vq_size;
+	else
+		allocsize3 = 0;
+	allocsize = allocsize1 + allocsize2 + allocsize3;
+
+	/* alloc and map the memory */
+	r = bus_dmamem_alloc(sc->sc_dmat, allocsize, VIRTIO_PAGE_SIZE, 0,
+			     &vq->vq_segs[0], 1, &rsegs, BUS_DMA_NOWAIT);
+	if (r != 0) {
+		printf("%s: virtqueue %d for %s allocation failed, "
+		    "error code %d\n", sc->sc_dev.dv_xname, index, name, r);
+		goto err;
+	}
+	r = bus_dmamem_map(sc->sc_dmat, &vq->vq_segs[0], 1, allocsize,
+			   &vq->vq_vaddr, BUS_DMA_NOWAIT);
+	if (r != 0) {
+		printf("%s: virtqueue %d for %s map failed, error code %d\n",
+		    sc->sc_dev.dv_xname, index, name, r);
+		goto err;
+	}
+	r = bus_dmamap_create(sc->sc_dmat, allocsize, 1, allocsize, 0,
+			      BUS_DMA_NOWAIT, &vq->vq_dmamap);
+	if (r != 0) {
+		printf("%s: virtqueue %d for %s dmamap creation failed, "
+		    "error code %d\n", sc->sc_dev.dv_xname, index, name, r);
+		goto err;
+	}
+	r = bus_dmamap_load(sc->sc_dmat, vq->vq_dmamap,
+			    vq->vq_vaddr, allocsize, NULL, BUS_DMA_NOWAIT);
+	if (r != 0) {
+		printf("%s: virtqueue %d for %s dmamap load failed, "
+		    "error code %d\n", sc->sc_dev.dv_xname, index, name, r);
+		goto err;
+	}
+
+	/* set the vq address */
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+			  VIRTIO_CONFIG_QUEUE_ADDRESS,
+			  (vq->vq_dmamap->dm_segs[0].ds_addr
+			   / 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_desc = (struct vring_desc *)vq->vq_vaddr;
+	vq->vq_availoffset = sizeof(struct vring_desc)*vq_size;
+	vq->vq_avail = (void*)(((char*)vq->vq_desc) + vq->vq_availoffset);
+	vq->vq_usedoffset = allocsize1;
+	vq->vq_used = (void*)(((char*)vq->vq_desc) + vq->vq_usedoffset);
+	if (allocsize3 > 0) {
+		vq->vq_indirectoffset = allocsize1 + allocsize2;
+		vq->vq_indirect = (void*)(((char*)vq->vq_desc)
+					  + vq->vq_indirectoffset);
+	}
+	vq->vq_bytesize = allocsize;
+	vq->vq_maxsegsize = maxsegsize;
+	vq->vq_maxnsegs = maxnsegs;
+
+	/* free slot management */
+	vq->vq_entries = malloc(sizeof(struct vq_entry)*vq_size, M_DEVBUF,
+	    M_NOWAIT | M_ZERO);
+	if (vq->vq_entries == NULL) {
+		r = ENOMEM;
+		goto err;
+	}
+
+	virtio_init_vq(sc, vq);
+
+	DPRINTF(("%s: allocated %u byte for virtqueue %d for %s, size %d\n",
+	    sc->sc_dev.dv_xname, allocsize, index, name, vq_size));
+	if (allocsize3 > 0)
+		DPRINTF(("%s: using %d byte (%d entries) indirect "
+		    "descriptors\n", sc->sc_dev.dv_xname, allocsize3,
+		    maxnsegs * vq_size));
+	return 0;
+
+err:
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+			  VIRTIO_CONFIG_QUEUE_ADDRESS, 0);
+	if (vq->vq_dmamap)
+		bus_dmamap_destroy(sc->sc_dmat, vq->vq_dmamap);
+	if (vq->vq_vaddr)
+		bus_dmamem_unmap(sc->sc_dmat, vq->vq_vaddr, allocsize);
+	if (vq->vq_segs[0].ds_addr)
+		bus_dmamem_free(sc->sc_dmat, &vq->vq_segs[0], 1);
+	memset(vq, 0, sizeof(*vq));
+
+	return -1;
+}
+
+int
+virtio_free_vq(struct virtio_softc *sc, struct virtqueue *vq)
+{
+	struct vq_entry *qe;
+	int i = 0;
+
+	/* device must be already deactivated */
+	/* confirm the vq is empty */
+	SIMPLEQ_FOREACH(qe, &vq->vq_freelist, qe_list) {
+		i++;
+	}
+	if (i != vq->vq_num) {
+		printf("%s: freeing non-empty vq, index %d\n",
+		       sc->sc_dev.dv_xname, vq->vq_index);
+		return EBUSY;
+	}
+
+	/* tell device that there's no virtqueue any longer */
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
+			  VIRTIO_CONFIG_QUEUE_SELECT, vq->vq_index);
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+			  VIRTIO_CONFIG_QUEUE_ADDRESS, 0);
+
+	free(vq->vq_entries, M_DEVBUF);
+	bus_dmamap_unload(sc->sc_dmat, vq->vq_dmamap);
+	bus_dmamap_destroy(sc->sc_dmat, vq->vq_dmamap);
+	bus_dmamem_unmap(sc->sc_dmat, vq->vq_vaddr, vq->vq_bytesize);
+	bus_dmamem_free(sc->sc_dmat, &vq->vq_segs[0], 1);
+	memset(vq, 0, sizeof(*vq));
+
+	return 0;
+}
+
+/*
+ * Free descriptor management.
+ */
+static struct vq_entry *
+vq_alloc_entry(struct virtqueue *vq)
+{
+	struct vq_entry *qe;
+
+	mtx_enter(&vq->vq_freelist_lock);
+	if (SIMPLEQ_EMPTY(&vq->vq_freelist)) {
+		mtx_leave(&vq->vq_freelist_lock);
+		return NULL;
+	}
+	qe = SIMPLEQ_FIRST(&vq->vq_freelist);
+	SIMPLEQ_REMOVE_HEAD(&vq->vq_freelist, qe_list);
+	mtx_leave(&vq->vq_freelist_lock);
+
+	return qe;
+}
+
+static void
+vq_free_entry(struct virtqueue *vq, struct vq_entry *qe)
+{
+	mtx_enter(&vq->vq_freelist_lock);
+	SIMPLEQ_INSERT_TAIL(&vq->vq_freelist, qe, qe_list);
+	mtx_leave(&vq->vq_freelist_lock);
+
+	return;
+}
+
+/*
+ * Enqueue several dmamaps as a single request.
+ */
+/*
+ * Typical usage:
+ *  <queue size> number of followings are stored in arrays
+ *  - command blocks (in dmamem) should be pre-allocated and mapped
+ *  - dmamaps for command blocks should be pre-allocated and loaded
+ *  - dmamaps for payload should be pre-allocated
+ *      r = virtio_enqueue_prep(sc, vq, &slot);		// allocate a slot
+ *	if (r)		// currently 0 or EAGAIN
+ *	  return r;
+ *	r = bus_dmamap_load(dmat, dmamap_payload[slot], data, count, ..);
+ *	if (r) {
+ *	  virtio_enqueue_abort(sc, vq, slot);
+ *	  bus_dmamap_unload(dmat, dmamap_payload[slot]);
+ *	  return r;
+ *	}
+ *	r = virtio_enqueue_reserve(sc, vq, slot, 
+ *				   dmamap_payload[slot]->dm_nsegs+1);
+ *							// ^ +1 for command
+ *	if (r) {	// currently 0 or EAGAIN
+ *	  bus_dmamap_unload(dmat, dmamap_payload[slot]);
+ *	  return r;					// do not call abort()
+ *	}
+ *	<setup and prepare commands>
+ *	bus_dmamap_sync(dmat, dmamap_cmd[slot],... BUS_DMASYNC_PREWRITE);
+ *	bus_dmamap_sync(dmat, dmamap_payload[slot],...);
+ *	virtio_enqueue(sc, vq, slot, dmamap_cmd[slot], false);
+ *	virtio_enqueue(sc, vq, slot, dmamap_payload[slot], iswrite);
+ *	virtio_enqueue_commit(sc, vq, slot, 1);
+ */
+
+/*
+ * enqueue_prep: allocate a slot number
+ */
+int
+virtio_enqueue_prep(struct virtio_softc *sc, struct virtqueue *vq, int *slotp)
+{
+	struct vq_entry *qe1;
+
+	KASSERT(slotp != NULL);
+
+	qe1 = vq_alloc_entry(vq);
+	if (qe1 == NULL)
+		return EAGAIN;
+	/* next slot is not allocated yet */
+	qe1->qe_next = -1;
+	*slotp = qe1->qe_index;
+
+	return 0;
+}
+
+/*
+ * enqueue_reserve: allocate remaining slots and build the descriptor chain.
+ */
+int
+virtio_enqueue_reserve(struct virtio_softc *sc, struct virtqueue *vq,
+		       int slot, int nsegs)
+{
+	int indirect;
+	struct vq_entry *qe1 = &vq->vq_entries[slot];
+
+	KASSERT(qe1->qe_next == -1);
+	KASSERT(1 <= nsegs && nsegs <= vq->vq_num);
+
+	if ((vq->vq_indirect != NULL) &&
+	    (nsegs >= MINSEG_INDIRECT) &&
+	    (nsegs <= vq->vq_maxnsegs))
+		indirect = 1;
+	else
+		indirect = 0;
+	qe1->qe_indirect = indirect;
+
+	if (indirect) {
+		struct vring_desc *vd;
+		int i;
+
+		vd = &vq->vq_desc[qe1->qe_index];
+		vd->addr = vq->vq_dmamap->dm_segs[0].ds_addr
+			+ vq->vq_indirectoffset;
+		vd->addr += sizeof(struct vring_desc)
+			* vq->vq_maxnsegs * qe1->qe_index;
+		vd->len = sizeof(struct vring_desc) * nsegs;
+		vd->flags = VRING_DESC_F_INDIRECT;
+
+		vd = vq->vq_indirect;
+		vd += vq->vq_maxnsegs * qe1->qe_index;
+		qe1->qe_desc_base = vd;
+
+		for (i = 0; i < nsegs-1; i++) {
+			vd[i].flags = VRING_DESC_F_NEXT;
+		}
+		vd[i].flags = 0;
+		qe1->qe_next = 0;
+
+		return 0;
+	} else {
+		struct vring_desc *vd;
+		struct vq_entry *qe;
+		int i, s;
+
+		vd = &vq->vq_desc[0];
+		qe1->qe_desc_base = vd;
+		qe1->qe_next = qe1->qe_index;
+		s = slot;
+		for (i = 0; i < nsegs - 1; i++) {
+			qe = vq_alloc_entry(vq);
+			if (qe == NULL) {
+				vd[s].flags = 0;
+				virtio_enqueue_abort(sc, vq, slot);
+				return EAGAIN;
+			}
+			vd[s].flags = VRING_DESC_F_NEXT;
+			vd[s].next = qe->qe_index;
+			s = qe->qe_index;
+		}
+		vd[s].flags = 0;
+
+		return 0;
+	}
+}
+
+/*
+ * enqueue: enqueue a single dmamap.
+ */
+int
+virtio_enqueue(struct virtio_softc *sc, struct virtqueue *vq, int slot,
+	       bus_dmamap_t dmamap, int write)
+{
+	struct vq_entry *qe1 = &vq->vq_entries[slot];
+	struct vring_desc *vd = qe1->qe_desc_base;
+	int i;
+	int s = qe1->qe_next;
+
+	KASSERT(s >= 0);
+	KASSERT(dmamap->dm_nsegs > 0);
+
+	for (i = 0; i < dmamap->dm_nsegs; i++) {
+		vd[s].addr = dmamap->dm_segs[i].ds_addr;
+		vd[s].len = dmamap->dm_segs[i].ds_len;
+		if (!write)
+			vd[s].flags |= VRING_DESC_F_WRITE;
+		s = vd[s].next;
+	}
+	qe1->qe_next = s;
+
+	return 0;
+}
+
+int
+virtio_enqueue_p(struct virtio_softc *sc, struct virtqueue *vq, int slot,
+		 bus_dmamap_t dmamap, bus_addr_t start, bus_size_t len,
+		 int write)
+{
+	struct vq_entry *qe1 = &vq->vq_entries[slot];
+	struct vring_desc *vd = qe1->qe_desc_base;
+	int s = qe1->qe_next;
+
+	KASSERT(s >= 0);
+	KASSERT(dmamap->dm_nsegs == 1); /* XXX */
+	KASSERT((dmamap->dm_segs[0].ds_len > start) &&
+		(dmamap->dm_segs[0].ds_len >= start + len));
+
+	vd[s].addr = dmamap->dm_segs[0].ds_addr + start;
+	vd[s].len = len;
+	if (!write)
+		vd[s].flags |= VRING_DESC_F_WRITE;
+	qe1->qe_next = vd[s].next;
+
+	return 0;
+}
+
+/*
+ * enqueue_commit: add it to the aring.
+ */
+int
+virtio_enqueue_commit(struct virtio_softc *sc, struct virtqueue *vq, int slot,
+		      int notifynow)
+{
+	struct vq_entry *qe1;
+
+	if (slot < 0) {
+		mtx_enter(&vq->vq_aring_lock);
+		goto notify;
+	}
+	vq_sync_descs(sc, vq, BUS_DMASYNC_PREWRITE);
+	qe1 = &vq->vq_entries[slot];
+	if (qe1->qe_indirect)
+		vq_sync_indirect(sc, vq, slot, BUS_DMASYNC_PREWRITE);
+	mtx_enter(&vq->vq_aring_lock);
+	vq->vq_avail->ring[(vq->vq_avail_idx++) % vq->vq_num] = slot;
+
+notify:
+	if (notifynow) {
+		vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
+		vq_sync_uring(sc, vq, BUS_DMASYNC_PREREAD);
+// XXX
+//		membar_producer();
+		vq->vq_avail->idx = vq->vq_avail_idx;
+		vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
+// XXX
+//		membar_producer();
+		vq->vq_queued++;
+		vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD);
+// XXX
+//		membar_consumer();
+		if (!(vq->vq_used->flags & VRING_USED_F_NO_NOTIFY))
+			bus_space_write_2(sc->sc_iot, sc->sc_ioh,
+					  VIRTIO_CONFIG_QUEUE_NOTIFY,
+					  vq->vq_index);
+	}
+	mtx_leave(&vq->vq_aring_lock);
+
+	return 0;
+}
+
+/*
+ * enqueue_abort: rollback.
+ */
+int
+virtio_enqueue_abort(struct virtio_softc *sc, struct virtqueue *vq, int slot)
+{
+	struct vq_entry *qe = &vq->vq_entries[slot];
+	struct vring_desc *vd;
+	int s;
+
+	if (qe->qe_next < 0) {
+		vq_free_entry(vq, qe);
+		return 0;
+	}
+
+	s = slot;
+	vd = &vq->vq_desc[0];
+	while (vd[s].flags & VRING_DESC_F_NEXT) {
+		s = vd[s].next;
+		vq_free_entry(vq, qe);
+		qe = &vq->vq_entries[s];
+	}
+	vq_free_entry(vq, qe);
+	return 0;
+}
+
+/*
+ * Dequeue a request.
+ */
+/*
+ * dequeue: dequeue a request from uring; dmamap_sync for uring is
+ *	    already done in the interrupt handler.
+ */
+int
+virtio_dequeue(struct virtio_softc *sc, struct virtqueue *vq,
+	       int *slotp, int *lenp)
+{
+	uint16_t slot, usedidx;
+	struct vq_entry *qe;
+
+	if (vq->vq_used_idx == vq->vq_used->idx)
+		return ENOENT;
+	mtx_enter(&vq->vq_uring_lock);
+	usedidx = vq->vq_used_idx++;
+	mtx_leave(&vq->vq_uring_lock);
+	usedidx %= vq->vq_num;
+	slot = vq->vq_used->ring[usedidx].id;
+	qe = &vq->vq_entries[slot];
+
+	if (qe->qe_indirect)
+		vq_sync_indirect(sc, vq, slot, BUS_DMASYNC_POSTWRITE);
+
+	if (slotp)
+		*slotp = slot;
+	if (lenp)
+		*lenp = vq->vq_used->ring[usedidx].len;
+
+	return 0;
+}
+
+/*
+ * dequeue_commit: complete dequeue; the slot is recycled for future use.
+ *                 if you forget to call this the slot will be leaked.
+ */
+int
+virtio_dequeue_commit(struct virtio_softc *sc, struct virtqueue *vq, int slot)
+{
+	struct vq_entry *qe = &vq->vq_entries[slot];
+	struct vring_desc *vd = &vq->vq_desc[0];
+	int s = slot;
+
+	while (vd[s].flags & VRING_DESC_F_NEXT) {
+		s = vd[s].next;
+		vq_free_entry(vq, qe);
+		qe = &vq->vq_entries[s];
+	}
+	vq_free_entry(vq, qe);
+
+	return 0;
+}
Index: dev/pci/virtiovar.h
===================================================================
--- dev/pci/virtiovar.h	(.../vendor/openbsd/head/sys)	(revision 0)
+++ dev/pci/virtiovar.h	(.../user/yasuoka/virtio-work)	(revision 135372)
@@ -0,0 +1,207 @@
+/*	$NetBSD$	*/
+
+/*
+ * 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'' 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 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 _DEV_PCI_VIRTIOVAR_H_
+#define	_DEV_PCI_VIRTIOVAR_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <machine/bus.h>
+#include <dev/pci/virtioreg.h>
+
+struct vq_entry {
+	SIMPLEQ_ENTRY(vq_entry)	qe_list; /* free list */
+	uint16_t		qe_index; /* index in vq_desc array */
+	/* followings are used only when it is the `head' entry */
+	int16_t			qe_next;     /* next enq slot */
+	int			qe_indirect; /* 1 if using indirect */
+	struct vring_desc	*qe_desc_base;
+};
+
+struct virtqueue {
+	struct virtio_softc	*vq_owner;
+        unsigned int		vq_num; /* queue size (# of entries) */
+	int			vq_index; /* queue number (0, 1, ...) */
+
+	/* vring pointers (KVA) */
+        struct vring_desc	*vq_desc;
+        struct vring_avail	*vq_avail;
+        struct vring_used	*vq_used;
+	void			*vq_indirect;
+
+	/* virtqueue allocation info */
+	caddr_t			vq_vaddr;
+	int			vq_availoffset;
+	int			vq_usedoffset;
+	int			vq_indirectoffset;
+	bus_dma_segment_t	vq_segs[1];
+	unsigned int		vq_bytesize;
+	bus_dmamap_t		vq_dmamap;
+
+	int			vq_maxsegsize;
+	int			vq_maxnsegs;
+
+	/* free entry management */
+	struct vq_entry		*vq_entries;
+	SIMPLEQ_HEAD(, vq_entry) vq_freelist;
+	struct mutex		vq_freelist_lock;
+
+	/* enqueue/dequeue status */
+	uint16_t		vq_avail_idx;
+	uint16_t		vq_used_idx;
+	int			vq_queued;
+	struct mutex		vq_aring_lock;
+	struct mutex		vq_uring_lock;
+
+	/* interrupt handler */
+	int			(*vq_done)(struct virtqueue*);
+};
+
+struct virtio_softc {
+	struct device		sc_dev;
+	pci_chipset_tag_t	sc_pc;
+	pcitag_t		sc_tag;
+	bus_dma_tag_t		sc_dmat;
+
+	int			sc_ipl; /* set by child */
+	void			*sc_ih;
+
+	bus_space_tag_t		sc_iot;
+	bus_space_handle_t	sc_ioh;
+	bus_size_t		sc_iosize;
+	int			sc_config_offset;
+
+	uint32_t		sc_features;
+	int			sc_indirect;
+
+	int			sc_nvqs; /* set by child */
+	struct virtqueue	*sc_vqs; /* set by child */
+
+	int			sc_childdevid;
+	struct device *		sc_child; /* set by child */
+	int			(*sc_config_change)(struct virtio_softc*);
+					 /* set by child */
+	int			(*sc_intrhand)(struct virtio_softc*);
+					 /* set by child */
+};
+
+/* The standard layout for the ring is a continuous chunk of memory which
+ * looks like this.  We assume num is a power of 2.
+ *
+ * struct vring {
+ *      // The actual descriptors (16 bytes each)
+ *      struct vring_desc desc[num];
+ *
+ *      // A ring of available descriptor heads with free-running index.
+ *      __u16 avail_flags;
+ *      __u16 avail_idx;
+ *      __u16 available[num];
+ *
+ *      // Padding to the next align boundary.
+ *      char pad[];
+ *
+ *      // A ring of used descriptor heads with free-running index.
+ *      __u16 used_flags;
+ *      __u16 used_idx;
+ *      struct vring_used_elem used[num];
+ * };
+ * Note: for virtio PCI, align is 4096.
+ */
+
+/* public interface */
+uint32_t virtio_negotiate_features(struct virtio_softc*, uint32_t);
+
+uint8_t virtio_read_device_config_1(struct virtio_softc *, int);
+uint16_t virtio_read_device_config_2(struct virtio_softc *, int);
+uint32_t virtio_read_device_config_4(struct virtio_softc *, int);
+uint64_t virtio_read_device_config_8(struct virtio_softc *, int);
+void virtio_write_device_config_1(struct virtio_softc *, int, uint8_t);
+void virtio_write_device_config_2(struct virtio_softc *, int, uint16_t);
+void virtio_write_device_config_4(struct virtio_softc *, int, uint32_t);
+void virtio_write_device_config_8(struct virtio_softc *, int, uint64_t);
+
+int virtio_alloc_vq(struct virtio_softc*, struct virtqueue*, int, int, int,
+		    const char*);
+int virtio_free_vq(struct virtio_softc*, struct virtqueue*);
+void virtio_reset(struct virtio_softc *);
+void virtio_reinit_start(struct virtio_softc *);
+void virtio_reinit_end(struct virtio_softc *);
+
+int virtio_enqueue_prep(struct virtio_softc*, struct virtqueue*, int*);
+int virtio_enqueue_reserve(struct virtio_softc*, struct virtqueue*, int, int);
+int virtio_enqueue(struct virtio_softc*, struct virtqueue*, int,
+		   bus_dmamap_t, int);
+int virtio_enqueue_p(struct virtio_softc*, struct virtqueue*, int,
+		     bus_dmamap_t, bus_addr_t, bus_size_t, int);
+int virtio_enqueue_commit(struct virtio_softc*, struct virtqueue*, int, int);
+int virtio_enqueue_abort(struct virtio_softc*, struct virtqueue*, int);
+
+int virtio_dequeue(struct virtio_softc*, struct virtqueue*, int *, int *);
+int virtio_dequeue_commit(struct virtio_softc*, struct virtqueue*, int);
+
+int virtio_vq_intr(struct virtio_softc *);
+void virtio_stop_vq_intr(struct virtio_softc *, struct virtqueue *);
+void virtio_start_vq_intr(struct virtio_softc *, struct virtqueue *);
+
+#endif /* _DEV_PCI_VIRTIOVAR_H_ */
Index: dev/pci/if_vioif.c
===================================================================
--- dev/pci/if_vioif.c	(.../vendor/openbsd/head/sys)	(revision 0)
+++ dev/pci/if_vioif.c	(.../user/yasuoka/virtio-work)	(revision 135372)
@@ -0,0 +1,1341 @@
+/*	$OpenBSD$	*/
+/*	$NetBSD$	*/
+
+/*
+ * 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 "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/virtioreg.h>
+#include <dev/pci/virtiovar.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+
+/*
+ * if_vioifreg.h:
+ */
+/* Configuration registers */
+#define VIRTIO_NET_CONFIG_MAC		0 /* 8bit x 6byte */
+#define VIRTIO_NET_CONFIG_STATUS	6 /* 16bit */
+
+/* Feature bits */
+#define VIRTIO_NET_F_CSUM	(1<<0)
+#define VIRTIO_NET_F_GUEST_CSUM	(1<<1)
+#define VIRTIO_NET_F_MAC	(1<<5)
+#define VIRTIO_NET_F_GSO	(1<<6)
+#define VIRTIO_NET_F_GUEST_TSO4	(1<<7)
+#define VIRTIO_NET_F_GUEST_TSO6	(1<<8)
+#define VIRTIO_NET_F_GUEST_ECN	(1<<9)
+#define VIRTIO_NET_F_GUEST_UFO	(1<<10)
+#define VIRTIO_NET_F_HOST_TSO4	(1<<11)
+#define VIRTIO_NET_F_HOST_TSO6	(1<<12)
+#define VIRTIO_NET_F_HOST_ECN	(1<<13)
+#define VIRTIO_NET_F_HOST_UFO	(1<<14)
+#define VIRTIO_NET_F_MRG_RXBUF	(1<<15)
+#define VIRTIO_NET_F_STATUS	(1<<16)
+#define VIRTIO_NET_F_CTRL_VQ	(1<<17)
+#define VIRTIO_NET_F_CTRL_RX	(1<<18)
+#define VIRTIO_NET_F_CTRL_VLAN	(1<<19)
+
+/* Status */
+#define VIRTIO_NET_S_LINK_UP	1
+
+/* Packet header structure */
+struct virtio_net_hdr {
+	uint8_t		flags;
+	uint8_t		gso_type;
+	uint16_t	hdr_len;
+	uint16_t	gso_size;
+	uint16_t	csum_start;
+	uint16_t	csum_offset;
+#if 0
+	uint16_t	num_buffers; /* if VIRTIO_NET_F_MRG_RXBUF enabled */
+#endif
+} __packed;
+
+#define VIRTIO_NET_HDR_F_NEEDS_CSUM	1 /* flags */
+#define VIRTIO_NET_HDR_GSO_NONE		0 /* gso_type */
+#define VIRTIO_NET_HDR_GSO_TCPV4	1 /* gso_type */
+#define VIRTIO_NET_HDR_GSO_UDP		3 /* gso_type */
+#define VIRTIO_NET_HDR_GSO_TCPV6	4 /* gso_type */
+#define VIRTIO_NET_HDR_GSO_ECN		0x80 /* gso_type, |'ed */
+
+#define VIRTIO_NET_MAX_GSO_LEN		(65536+ETHER_HDR_LEN)
+
+/* Control virtqueue */
+struct virtio_net_ctrl_cmd {
+	uint8_t	class;
+	uint8_t	command;
+} __packed;
+#define VIRTIO_NET_CTRL_RX		0
+# define VIRTIO_NET_CTRL_RX_PROMISC	0
+# define VIRTIO_NET_CTRL_RX_ALLMULTI	1
+
+#define VIRTIO_NET_CTRL_MAC		1
+# define VIRTIO_NET_CTRL_MAC_TABLE_SET	0
+
+#define VIRTIO_NET_CTRL_VLAN		2
+# define VIRTIO_NET_CTRL_VLAN_ADD	0
+# define VIRTIO_NET_CTRL_VLAN_DEL	1
+
+struct virtio_net_ctrl_status {
+	uint8_t	ack;
+} __packed;
+#define VIRTIO_NET_OK			0
+#define VIRTIO_NET_ERR			1
+
+struct virtio_net_ctrl_rx {
+	uint8_t	onoff;
+} __packed;
+
+struct virtio_net_ctrl_mac_tbl {
+	uint32_t nentries;
+	uint8_t macs[][ETHER_ADDR_LEN];
+} __packed;
+
+struct virtio_net_ctrl_vlan {
+	uint16_t id;
+} __packed;
+
+
+/*
+ * if_vioifvar.h:
+ */
+struct vioif_softc {
+	struct device 		sc_dev;
+
+	struct virtio_softc	*sc_virtio;
+	struct virtqueue	sc_vq[3];
+
+	uint8_t			sc_mac[ETHER_ADDR_LEN];
+	struct arpcom		sc_arpcom;
+	uint32_t		sc_features;
+	short			sc_ifflags;
+
+	/* bus_dmamem */
+	bus_dma_segment_t	sc_hdr_segs[1];
+	struct virtio_net_hdr	*sc_hdrs;
+#define sc_rx_hdrs	sc_hdrs
+	struct virtio_net_hdr	*sc_tx_hdrs;
+	struct virtio_net_ctrl_cmd *sc_ctrl_cmd;
+	struct virtio_net_ctrl_status *sc_ctrl_status;
+	struct virtio_net_ctrl_rx *sc_ctrl_rx;
+	struct virtio_net_ctrl_mac_tbl *sc_ctrl_mac_tbl_uc;
+	struct virtio_net_ctrl_mac_tbl *sc_ctrl_mac_tbl_mc;
+
+	/* kmem */
+	bus_dmamap_t		*sc_arrays;
+#define sc_rxhdr_dmamaps sc_arrays
+	bus_dmamap_t		*sc_txhdr_dmamaps;
+	bus_dmamap_t		*sc_rx_dmamaps;
+	bus_dmamap_t		*sc_tx_dmamaps;
+	struct mbuf		**sc_rx_mbufs;
+	struct mbuf		**sc_tx_mbufs;
+
+	bus_dmamap_t		sc_ctrl_cmd_dmamap;
+	bus_dmamap_t		sc_ctrl_status_dmamap;
+	bus_dmamap_t		sc_ctrl_rx_dmamap;
+	bus_dmamap_t		sc_ctrl_tbl_uc_dmamap;
+	bus_dmamap_t		sc_ctrl_tbl_mc_dmamap;
+
+	void			*sc_rx_softint;
+
+	enum {
+		FREE, INUSE, DONE
+	}			sc_ctrl_inuse;
+	struct mutex		sc_ctrl_wait_lock;
+};
+#define VIRTIO_NET_TX_MAXNSEGS		(16) /* XXX */
+#define VIRTIO_NET_CTRL_MAC_MAXENTRIES	(64) /* XXX */
+
+/* cfattach interface functions */
+static int	vioif_match(struct device *, void *, void *);
+static void	vioif_attach(struct device *, struct device *, void *);
+static void	vioif_deferred_init(void *);
+
+/* ifnet interface functions */
+static int	vioif_init(struct ifnet *);
+static int 	vioif_stop(struct ifnet *, int);
+static void	vioif_start(struct ifnet *);
+static int	vioif_ioctl(struct ifnet *, u_long, caddr_t);
+static void	vioif_watchdog(struct ifnet *);
+
+/* rx */
+static int	vioif_add_rx_mbuf(struct vioif_softc *, int);
+static void	vioif_free_rx_mbuf(struct vioif_softc *, int);
+static void	vioif_populate_rx_mbufs(struct vioif_softc *);
+static int	vioif_rx_deq(struct vioif_softc *);
+static int	vioif_rx_vq_done(struct virtqueue *);
+static void	vioif_rx_softint(void *);
+static void	vioif_rx_drain(struct vioif_softc *);
+
+/* tx */
+static int	vioif_tx_vq_done(struct virtqueue *);
+static void	vioif_tx_drain(struct vioif_softc *);
+
+/* other control */
+static int	vioif_updown(struct vioif_softc *, int);
+static int	vioif_ctrl_rx(struct vioif_softc *, int, int);
+static int	vioif_set_promisc(struct vioif_softc *, int);
+static int	vioif_set_allmulti(struct vioif_softc *, int);
+static int	vioif_set_rx_filter(struct vioif_softc *);
+static int	vioif_rx_filter(struct vioif_softc *);
+static int	vioif_ctrl_vq_done(struct virtqueue *);
+
+struct cfattach vioif_ca = {
+	sizeof(struct vioif_softc), vioif_match, vioif_attach
+};
+
+struct cfdriver vioif_cd = {
+	NULL, "vioif", DV_IFNET
+};
+
+static int
+vioif_match(struct device * parent, void *match, void *aux)
+{
+	struct virtio_softc *va = aux;
+
+	if (va->sc_childdevid == PCI_PRODUCT_VIRTIO_NETWORK)
+		return 1;
+
+	return 0;
+}
+
+/* allocate memory */
+/*
+ * dma memory is used for:
+ *   sc_rx_hdrs[slot]:	 metadata array for received frames (READ)
+ *   sc_tx_hdrs[slot]:	 metadata array for frames to be sent (WRITE)
+ *   sc_ctrl_cmd:	 command to be sent via ctrl vq (WRITE)
+ *   sc_ctrl_status:	 return value for a command via ctrl vq (READ)
+ *   sc_ctrl_rx:	 parameter for a VIRTIO_NET_CTRL_RX class command
+ *			 (WRITE)
+ *   sc_ctrl_mac_tbl_uc: unicast MAC address filter for a VIRTIO_NET_CTRL_MAC
+ *			 class command (WRITE)
+ *   sc_ctrl_mac_tbl_mc: multicast MAC address filter for a VIRTIO_NET_CTRL_MAC
+ *			 class command (WRITE)
+ * sc_ctrl_* structures are allocated only one each; they are protected by
+ * sc_ctrl_inuse variable and sc_ctrl_wait condvar.
+ */
+/*
+ * dynamically allocated memory is used for:
+ *   sc_rxhdr_dmamaps[slot]:	bus_dmamap_t array for sc_rx_hdrs[slot]
+ *   sc_txhdr_dmamaps[slot]:	bus_dmamap_t array for sc_tx_hdrs[slot]
+ *   sc_rx_dmamaps[slot]:	bus_dmamap_t array for received payload
+ *   sc_tx_dmamaps[slot]:	bus_dmamap_t array for sent payload
+ *   sc_rx_mbufs[slot]:		mbuf pointer array for received frames
+ *   sc_tx_mbufs[slot]:		mbuf pointer array for sent frames
+ */
+static int
+vioif_alloc_mems(struct vioif_softc *sc)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	int allocsize, allocsize2, r, rsegs, i;
+	caddr_t vaddr;
+	caddr_t p;
+	int rxqsize, txqsize;
+
+	rxqsize = vsc->sc_vqs[0].vq_num;
+	txqsize = vsc->sc_vqs[1].vq_num;
+
+	allocsize = sizeof(struct virtio_net_hdr) * rxqsize;
+	allocsize += sizeof(struct virtio_net_hdr) * txqsize;
+	if (vsc->sc_nvqs == 3) {
+		allocsize += sizeof(struct virtio_net_ctrl_cmd) * 1;
+		allocsize += sizeof(struct virtio_net_ctrl_status) * 1;
+		allocsize += sizeof(struct virtio_net_ctrl_rx) * 1;
+		allocsize += sizeof(struct virtio_net_ctrl_mac_tbl)
+			+ sizeof(struct virtio_net_ctrl_mac_tbl)
+			+ ETHER_ADDR_LEN * VIRTIO_NET_CTRL_MAC_MAXENTRIES;
+	}
+	r = bus_dmamem_alloc(vsc->sc_dmat, allocsize, 0, 0,
+			     &sc->sc_hdr_segs[0], 1, &rsegs, BUS_DMA_NOWAIT);
+	if (r != 0) {
+		printf("%s: DMA memory allocation failed, size %d, error code "
+		    "%d\n", sc->sc_dev.dv_xname, allocsize, r);
+		goto err_none;
+	}
+	r = bus_dmamem_map(vsc->sc_dmat,
+			   &sc->sc_hdr_segs[0], 1, allocsize,
+			   &vaddr, BUS_DMA_NOWAIT);
+	if (r != 0) {
+		printf("%s: DMA memory map failed, error code %d\n",
+		    sc->sc_dev.dv_xname, r);
+		goto err_dmamem_alloc;
+	}
+	sc->sc_hdrs = (struct virtio_net_hdr *)vaddr;
+	memset(vaddr, 0, allocsize);
+	p = vaddr;
+	p += sizeof(struct virtio_net_hdr) * rxqsize;
+#define P(name,size)	do { sc->sc_ ##name = (void*) p;	\
+			     p += size; } while (0)
+	P(tx_hdrs, sizeof(struct virtio_net_hdr) * txqsize);
+	if (vsc->sc_nvqs == 3) {
+		P(ctrl_cmd, sizeof(struct virtio_net_ctrl_cmd));
+		P(ctrl_status, sizeof(struct virtio_net_ctrl_status));
+		P(ctrl_rx, sizeof(struct virtio_net_ctrl_rx));
+		P(ctrl_mac_tbl_uc, sizeof(struct virtio_net_ctrl_mac_tbl));
+		P(ctrl_mac_tbl_mc,
+		  (sizeof(struct virtio_net_ctrl_mac_tbl)
+		   + ETHER_ADDR_LEN * VIRTIO_NET_CTRL_MAC_MAXENTRIES));
+	}
+#undef P
+
+	allocsize2 = sizeof(bus_dmamap_t) * (rxqsize + txqsize);
+	allocsize2 += sizeof(bus_dmamap_t) * (rxqsize + txqsize);
+	allocsize2 += sizeof(struct mbuf*) * (rxqsize + txqsize);
+	sc->sc_arrays = malloc(allocsize2, M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (sc->sc_arrays == NULL)
+		goto err_dmamem_map;
+	sc->sc_txhdr_dmamaps = sc->sc_arrays + rxqsize;
+	sc->sc_rx_dmamaps = sc->sc_txhdr_dmamaps + txqsize;
+	sc->sc_tx_dmamaps = sc->sc_rx_dmamaps + rxqsize;
+	sc->sc_rx_mbufs = (void*) (sc->sc_tx_dmamaps + txqsize);
+	sc->sc_tx_mbufs = sc->sc_rx_mbufs + rxqsize;
+
+#define C(map, buf, size, nsegs, rw, usage)				\
+	do {								\
+		r = bus_dmamap_create(vsc->sc_dmat, size, nsegs, size, 0, \
+				      BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW,	\
+				      &sc->sc_ ##map);			\
+		if (r != 0) {						\
+			printf("%s: " usage " dmamap creation failed, " \
+			    "error code %d\n", sc->sc_dev.dv_xname, r);	\
+					 goto err_reqs;			\
+		}							\
+	} while (0)
+#define C_L1(map, buf, size, nsegs, rw, usage)				\
+	C(map, buf, size, nsegs, rw, usage);				\
+	do {								\
+		r = bus_dmamap_load(vsc->sc_dmat, sc->sc_ ##map,	\
+				    &sc->sc_ ##buf, size, NULL,		\
+				    BUS_DMA_ ##rw | BUS_DMA_NOWAIT);	\
+		if (r != 0) {						\
+			printf("%s: "usage " dmamap load failed, "	\
+			    "error code %d\n", sc->sc_dev.dv_xname, r);	\
+			goto err_reqs;					\
+		}							\
+	} while (0)
+#define C_L2(map, buf, size, nsegs, rw, usage)				\
+	C(map, buf, size, nsegs, rw, usage);				\
+	do {								\
+		r = bus_dmamap_load(vsc->sc_dmat, sc->sc_ ##map,	\
+				    sc->sc_ ##buf, size, NULL,		\
+				    BUS_DMA_ ##rw | BUS_DMA_NOWAIT);	\
+		if (r != 0) {						\
+			printf("%s: " usage " dmamap load failed, "	\
+			    "error code %d\n", sc->sc_dev.dv_xname, r);	\
+			goto err_reqs;					\
+		}							\
+	} while (0)
+	for (i = 0; i < rxqsize; i++) {
+		C_L1(rxhdr_dmamaps[i], rx_hdrs[i],
+		    sizeof(struct virtio_net_hdr), 1,
+		    READ, "rx header");
+		C(rx_dmamaps[i], NULL, MCLBYTES, 1, 0, "rx payload");
+	}
+
+	for (i = 0; i < txqsize; i++) {
+		C_L1(txhdr_dmamaps[i], rx_hdrs[i],
+		    sizeof(struct virtio_net_hdr), 1,
+		    WRITE, "tx header");
+		C(tx_dmamaps[i], NULL, ETHER_MAX_LEN, 256 /* XXX */, 0,
+		  "tx payload");
+	}
+
+	if (vsc->sc_nvqs == 3) {
+		/* control vq class & command */
+		C_L2(ctrl_cmd_dmamap, ctrl_cmd,
+		    sizeof(struct virtio_net_ctrl_cmd), 1, WRITE,
+		    "control command");
+		
+		/* control vq status */
+		C_L2(ctrl_status_dmamap, ctrl_status,
+		    sizeof(struct virtio_net_ctrl_status), 1, READ,
+		    "control status");
+
+		/* control vq rx mode command parameter */
+		C_L2(ctrl_rx_dmamap, ctrl_rx,
+		    sizeof(struct virtio_net_ctrl_rx), 1, WRITE,
+		    "rx mode control command");
+
+		/* control vq MAC filter table for unicast */
+		/* do not load now since its length is variable */
+		C(ctrl_tbl_uc_dmamap, NULL,
+		  sizeof(struct virtio_net_ctrl_mac_tbl) + 0, 1, WRITE,
+		  "unicast MAC address filter command");
+
+		/* control vq MAC filter table for multicast */
+		C(ctrl_tbl_mc_dmamap, NULL,
+		  (sizeof(struct virtio_net_ctrl_mac_tbl)
+		   + ETHER_ADDR_LEN * VIRTIO_NET_CTRL_MAC_MAXENTRIES),
+		  1, WRITE, "multicast MAC address filter command");
+	}
+#undef C_L2
+#undef C_L1
+#undef C
+
+	return 0;
+
+err_reqs:
+#define D(map)								\
+	do {								\
+		if (sc->sc_ ##map) {					\
+			bus_dmamap_destroy(vsc->sc_dmat, sc->sc_ ##map); \
+			sc->sc_ ##map = NULL;				\
+		}							\
+	} while (0)
+	D(ctrl_tbl_mc_dmamap);
+	D(ctrl_tbl_uc_dmamap);
+	D(ctrl_rx_dmamap);
+	D(ctrl_status_dmamap);
+	D(ctrl_cmd_dmamap);
+	for (i = 0; i < txqsize; i++) {
+		D(tx_dmamaps[i]);
+		D(txhdr_dmamaps[i]);
+	}
+	for (i = 0; i < rxqsize; i++) {
+		D(rx_dmamaps[i]);
+		D(rxhdr_dmamaps[i]);
+	}
+#undef D
+	if (sc->sc_arrays) {
+		free(sc->sc_arrays, M_DEVBUF);
+		sc->sc_arrays = 0;
+	}
+err_dmamem_map:
+	bus_dmamem_unmap(vsc->sc_dmat, (caddr_t)sc->sc_hdrs, allocsize);
+err_dmamem_alloc:
+	bus_dmamem_free(vsc->sc_dmat, &sc->sc_hdr_segs[0], 1);
+err_none:
+	return -1;
+}
+
+static void
+vioif_attach(struct device * parent, struct device * self, void *aux)
+{
+	struct vioif_softc *sc = (struct vioif_softc *)self;
+	struct virtio_softc *vsc = (struct virtio_softc *)parent;
+	uint32_t features;
+	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+
+	if (vsc->sc_child != NULL) {
+		printf("%s: child already attached for %s; something "
+		    "wrong...\n", self->dv_xname, parent->dv_xname);
+		return;
+	}
+
+	sc->sc_virtio = vsc;
+
+	vsc->sc_child = self;
+	vsc->sc_ipl = IPL_NET;
+	vsc->sc_vqs = &sc->sc_vq[0];
+	vsc->sc_config_change = 0;
+	vsc->sc_intrhand = virtio_vq_intr;
+
+	features = virtio_negotiate_features(vsc,
+					     (VIRTIO_NET_F_MAC |
+					      VIRTIO_NET_F_STATUS |
+					      VIRTIO_NET_F_CTRL_VQ |
+					      VIRTIO_NET_F_CTRL_RX |
+					      VIRTIO_F_NOTIFY_ON_EMPTY));
+	if (features & VIRTIO_NET_F_MAC) {
+		sc->sc_mac[0] = virtio_read_device_config_1(vsc,
+						    VIRTIO_NET_CONFIG_MAC+0);
+		sc->sc_mac[1] = virtio_read_device_config_1(vsc,
+						    VIRTIO_NET_CONFIG_MAC+1);
+		sc->sc_mac[2] = virtio_read_device_config_1(vsc,
+						    VIRTIO_NET_CONFIG_MAC+2);
+		sc->sc_mac[3] = virtio_read_device_config_1(vsc,
+						    VIRTIO_NET_CONFIG_MAC+3);
+		sc->sc_mac[4] = virtio_read_device_config_1(vsc,
+						    VIRTIO_NET_CONFIG_MAC+4);
+		sc->sc_mac[5] = virtio_read_device_config_1(vsc,
+						    VIRTIO_NET_CONFIG_MAC+5);
+	} else {
+		/* code stolen from sys/net/if_tap.c */
+		struct timeval tv;
+		uint32_t ui;
+		getmicrouptime(&tv);
+		ui = (tv.tv_sec ^ tv.tv_usec) & 0xffffff;
+		memcpy(sc->sc_mac+3, (uint8_t *)&ui, 3);
+		virtio_write_device_config_1(vsc,
+					     VIRTIO_NET_CONFIG_MAC+0,
+					     sc->sc_mac[0]);
+		virtio_write_device_config_1(vsc,
+					     VIRTIO_NET_CONFIG_MAC+1,
+					     sc->sc_mac[1]);
+		virtio_write_device_config_1(vsc,
+					     VIRTIO_NET_CONFIG_MAC+2,
+					     sc->sc_mac[2]);
+		virtio_write_device_config_1(vsc,
+					     VIRTIO_NET_CONFIG_MAC+3,
+					     sc->sc_mac[3]);
+		virtio_write_device_config_1(vsc,
+					     VIRTIO_NET_CONFIG_MAC+4,
+					     sc->sc_mac[4]);
+		virtio_write_device_config_1(vsc,
+					     VIRTIO_NET_CONFIG_MAC+5,
+					     sc->sc_mac[5]);
+	}
+	printf(", address %s\n", ether_sprintf(sc->sc_mac));
+
+	if (virtio_alloc_vq(vsc, &sc->sc_vq[0], 0,
+			    MCLBYTES+sizeof(struct virtio_net_hdr), 2,
+			    "rx") != 0) {
+		goto err;
+	}
+	vsc->sc_nvqs = 1;
+	sc->sc_vq[0].vq_done = vioif_rx_vq_done;
+	if (virtio_alloc_vq(vsc, &sc->sc_vq[1], 1,
+			    (sizeof(struct virtio_net_hdr)
+			     + (ETHER_MAX_LEN - ETHER_HDR_LEN)),
+			    VIRTIO_NET_TX_MAXNSEGS + 1,
+			    "tx") != 0) {
+		goto err;
+	}
+	vsc->sc_nvqs = 2;
+	sc->sc_vq[1].vq_done = vioif_tx_vq_done;
+	virtio_start_vq_intr(vsc, &sc->sc_vq[0]);
+	virtio_stop_vq_intr(vsc, &sc->sc_vq[1]); /* not urgent; do it later */
+	if ((features & VIRTIO_NET_F_CTRL_VQ)
+	    && (features & VIRTIO_NET_F_CTRL_RX)) {
+		if (virtio_alloc_vq(vsc, &sc->sc_vq[2], 2,
+				    NBPG, 1, "control") == 0) {
+			sc->sc_vq[2].vq_done = vioif_ctrl_vq_done;
+			mtx_init(&sc->sc_ctrl_wait_lock, IPL_NET);
+			sc->sc_ctrl_inuse = FREE;
+			virtio_start_vq_intr(vsc, &sc->sc_vq[2]);
+			vsc->sc_nvqs = 3;
+		}
+	}
+
+	sc->sc_rx_softint = softintr_establish(IPL_SOFTNET,
+					      vioif_rx_softint, sc);
+	if (sc->sc_rx_softint == NULL) {
+		printf("%s: cannot establish softint\n", self->dv_xname);
+		goto err;
+	}
+
+	if (vioif_alloc_mems(sc) < 0)
+		goto err;
+	if (vsc->sc_nvqs == 3)
+		startuphook_establish(vioif_deferred_init, self);
+	bcopy(sc->sc_mac, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+	strlcpy(ifp->if_xname, self->dv_xname, IFNAMSIZ);
+	ifp->if_softc = sc;
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+	ifp->if_start = vioif_start;
+	ifp->if_ioctl = vioif_ioctl;
+	ifp->if_stop = vioif_stop;
+	ifp->if_capabilities = 0;
+	ifp->if_watchdog = vioif_watchdog;
+
+	if_attach(ifp);
+	ether_ifattach(ifp);
+
+
+	return;
+
+err:
+	if (vsc->sc_nvqs == 3) {
+		virtio_free_vq(vsc, &sc->sc_vq[2]);
+		vsc->sc_nvqs = 2;
+	}
+	if (vsc->sc_nvqs == 2) {
+		virtio_free_vq(vsc, &sc->sc_vq[1]);
+		vsc->sc_nvqs = 1;
+	}
+	if (vsc->sc_nvqs == 1) {
+		virtio_free_vq(vsc, &sc->sc_vq[0]);
+		vsc->sc_nvqs = 0;
+	}
+	vsc->sc_child = (void*)1;
+	return;
+}
+
+/* we need interrupts to make promiscuous mode off */
+static void
+vioif_deferred_init(void *self)
+{
+	struct vioif_softc *sc = (struct vioif_softc *)self;
+	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+	int r;
+
+	r =  vioif_set_promisc(sc, 0);
+	if (r != 0)
+		printf("%s: resetting promisc mode failed, errror code %d\n",
+		    sc->sc_dev.dv_xname, r);
+	else
+		ifp->if_flags &= ~IFF_PROMISC;
+}
+
+/*
+ * Interface functions for ifnet
+ */
+static int
+vioif_init(struct ifnet *ifp)
+{
+	struct vioif_softc *sc = ifp->if_softc;
+
+	vioif_stop(ifp, 0);
+	vioif_populate_rx_mbufs(sc);
+	vioif_updown(sc, 1);
+	ifp->if_flags |= IFF_RUNNING;
+	ifp->if_flags &= ~IFF_OACTIVE;
+	vioif_rx_filter(sc);
+
+	return 0;
+}
+
+static int
+vioif_stop(struct ifnet *ifp, int disable)
+{
+	struct vioif_softc *sc = ifp->if_softc;
+	struct virtio_softc *vsc = sc->sc_virtio;
+
+	/* only way to stop I/O and DMA is resetting... */
+	virtio_reset(vsc);
+	vioif_rx_deq(sc);
+	vioif_tx_drain(sc);
+	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+
+	if (disable)
+		vioif_rx_drain(sc);
+	
+	virtio_reinit_start(vsc);
+	virtio_negotiate_features(vsc, sc->sc_features);
+	virtio_start_vq_intr(vsc, &sc->sc_vq[0]);
+	virtio_stop_vq_intr(vsc, &sc->sc_vq[1]);
+	if (vsc->sc_nvqs >= 3)
+		virtio_start_vq_intr(vsc, &sc->sc_vq[2]);
+	virtio_reinit_end(vsc);
+	vioif_updown(sc, 0);
+
+	return 0;
+}
+
+static void
+vioif_start(struct ifnet *ifp)
+{
+	struct vioif_softc *sc = ifp->if_softc;
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtqueue *vq = &sc->sc_vq[1]; /* tx vq */
+	struct mbuf *m;
+	int queued = 0, retry = 0;
+
+	if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
+		return;
+
+	for (;;) {
+		int slot, r;
+
+		IFQ_POLL(&ifp->if_snd, m);
+		if (m == NULL)
+			break;
+		r = virtio_enqueue_prep(vsc, vq, &slot);
+		if (r == EAGAIN) {
+			ifp->if_flags |= IFF_OACTIVE;
+			vioif_tx_vq_done(vq);
+			if (retry++ == 0)
+				continue;
+			else
+				break;
+		}
+		if (r != 0)
+			panic("enqueue_prep for a tx buffer");
+		r = bus_dmamap_load_mbuf(vsc->sc_dmat,
+					 sc->sc_tx_dmamaps[slot],
+					 m, BUS_DMA_WRITE|BUS_DMA_NOWAIT);
+		if (r != 0) {
+			virtio_enqueue_abort(vsc, vq, slot);
+			printf("%s: tx dmamap load failed, error code %d\n",
+			       sc->sc_dev.dv_xname, r);
+			break;
+		}
+		r = virtio_enqueue_reserve(vsc, vq, slot,
+					sc->sc_tx_dmamaps[slot]->dm_nsegs + 1);
+		if (r != 0) {
+			bus_dmamap_unload(vsc->sc_dmat,
+					  sc->sc_tx_dmamaps[slot]);
+			ifp->if_flags |= IFF_OACTIVE;
+			vioif_tx_vq_done(vq);
+			if (retry++ == 0)
+				continue;
+			else
+				break;
+		}
+		IFQ_DEQUEUE(&ifp->if_snd, m);
+		sc->sc_tx_mbufs[slot] = m;
+
+		memset(&sc->sc_tx_hdrs[slot], 0, sizeof(struct virtio_net_hdr));
+		bus_dmamap_sync(vsc->sc_dmat, sc->sc_tx_dmamaps[slot],
+				0, sc->sc_tx_dmamaps[slot]->dm_mapsize,
+				BUS_DMASYNC_PREWRITE);
+		bus_dmamap_sync(vsc->sc_dmat, sc->sc_txhdr_dmamaps[slot],
+				0, sc->sc_txhdr_dmamaps[slot]->dm_mapsize,
+				BUS_DMASYNC_PREWRITE);
+		virtio_enqueue(vsc, vq, slot, sc->sc_txhdr_dmamaps[slot], 1);
+		virtio_enqueue(vsc, vq, slot, sc->sc_tx_dmamaps[slot], 1);
+		virtio_enqueue_commit(vsc, vq, slot, 0);
+		queued++;
+#if NBPFILTER > 0
+		if (ifp->if_bpf)
+			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
+#endif
+	}
+
+	if (queued > 0) {
+		virtio_enqueue_commit(vsc, vq, -1, 1);
+		ifp->if_timer = 5;
+	}
+}
+
+static int
+vioif_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+	int s, error;
+	struct vioif_softc *sc = ifp->if_softc;
+	struct ifaddr  *ifa = (struct ifaddr *)data;
+
+	s = splnet();
+
+	error = 0;
+	switch(cmd) {
+	case SIOCSIFADDR:
+		ifp->if_flags |= IFF_UP;
+		if (!(ifp->if_flags & IFF_RUNNING))
+			vioif_init(ifp);
+#ifdef INET
+		if (ifa->ifa_addr->sa_family == AF_INET)
+			arp_ifinit(&sc->sc_arpcom, ifa);
+#endif /* INET */
+		break;
+
+	case SIOCSIFFLAGS:
+		if (ifp->if_flags & IFF_UP) {
+			if (ifp->if_flags & IFF_RUNNING)
+				error = ENETRESET;
+			else
+				vioif_init(ifp);
+		} else {
+			if (ifp->if_flags & IFF_RUNNING)
+				vioif_stop(ifp, 0);
+		}
+		break;
+
+	default:
+		error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data);
+	}
+
+	if (error == ENETRESET) {
+		if (ifp->if_flags & IFF_RUNNING)
+			error = vioif_rx_filter(ifp->if_softc);
+		else
+			error = 0;
+	}
+
+	splx(s);
+
+	return (error);
+}
+
+void
+vioif_watchdog(struct ifnet *ifp)
+{
+	struct vioif_softc *sc = ifp->if_softc;
+
+	if (ifp->if_flags & IFF_RUNNING)
+		vioif_tx_vq_done(&sc->sc_vq[1]);
+}
+
+
+/*
+ * Recieve implementation
+ */
+/* allocate and initialize a mbuf for receive */
+static int
+vioif_add_rx_mbuf(struct vioif_softc *sc, int i)
+{
+	struct mbuf *m;
+	int r;
+
+	MGETHDR(m, M_DONTWAIT, MT_DATA);
+	if (m == NULL)
+		return ENOBUFS;
+	MCLGET(m, M_DONTWAIT);
+	if ((m->m_flags & M_EXT) == 0) {
+		m_freem(m);
+		return ENOBUFS;
+	}
+	sc->sc_rx_mbufs[i] = m;
+	m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
+	r = bus_dmamap_load_mbuf(sc->sc_virtio->sc_dmat,
+				 sc->sc_rx_dmamaps[i],
+				 m, BUS_DMA_READ|BUS_DMA_NOWAIT);
+	if (r) {
+		m_freem(m);
+		sc->sc_rx_mbufs[i] = 0;
+		return r;
+	}
+
+	return 0;
+}
+
+/* free a mbuf for receive */
+static void
+vioif_free_rx_mbuf(struct vioif_softc *sc, int i)
+{
+	bus_dmamap_unload(sc->sc_virtio->sc_dmat, sc->sc_rx_dmamaps[i]);
+	m_freem(sc->sc_rx_mbufs[i]);
+	sc->sc_rx_mbufs[i] = NULL;
+}
+
+/* add mbufs for all the empty receive slots */
+static void
+vioif_populate_rx_mbufs(struct vioif_softc *sc)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	int i, r, ndone = 0;
+	struct virtqueue *vq = &sc->sc_vq[0]; /* rx vq */
+
+	for (i = 0; i < vq->vq_num; i++) {
+		int slot;
+		r = virtio_enqueue_prep(vsc, vq, &slot);
+		if (r == EAGAIN)
+			break;
+		if (r != 0)
+			panic("enqueue_prep for rx buffers");
+		if (sc->sc_rx_mbufs[slot] == NULL) {
+			r = vioif_add_rx_mbuf(sc, slot);
+			if (r != 0) {
+				printf("%s: rx mbuf allocation failed, "
+				       "error code %d\n",
+				       sc->sc_dev.dv_xname, r);
+				break;
+			}
+		}
+		r = virtio_enqueue_reserve(vsc, vq, slot,
+					sc->sc_rx_dmamaps[slot]->dm_nsegs + 1);
+		if (r != 0) {
+			vioif_free_rx_mbuf(sc, slot);
+			break;
+		}
+		bus_dmamap_sync(vsc->sc_dmat, sc->sc_rxhdr_dmamaps[slot],
+			0, sizeof(struct virtio_net_hdr), BUS_DMASYNC_PREREAD);
+		bus_dmamap_sync(vsc->sc_dmat, sc->sc_rx_dmamaps[slot],
+			0, MCLBYTES, BUS_DMASYNC_PREREAD);
+		virtio_enqueue(vsc, vq, slot, sc->sc_rxhdr_dmamaps[slot], 0);
+		virtio_enqueue(vsc, vq, slot, sc->sc_rx_dmamaps[slot], 0);
+		virtio_enqueue_commit(vsc, vq, slot, 0);
+		ndone++;
+	}
+	if (ndone > 0)
+		virtio_enqueue_commit(vsc, vq, -1, 1);
+}
+
+/* dequeue received packets */
+static int
+vioif_rx_deq(struct vioif_softc *sc)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtqueue *vq = &sc->sc_vq[0];
+	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+	struct mbuf *m;
+	int r = 0;
+	int slot, len;
+
+	splassert(IPL_NET);
+	while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
+		len -= sizeof(struct virtio_net_hdr);
+		r = 1;
+		bus_dmamap_sync(vsc->sc_dmat, sc->sc_rxhdr_dmamaps[slot],
+				0, sizeof(struct virtio_net_hdr),
+				BUS_DMASYNC_POSTREAD);
+		bus_dmamap_sync(vsc->sc_dmat, sc->sc_rx_dmamaps[slot],
+				0, MCLBYTES,
+				BUS_DMASYNC_POSTREAD);
+		m = sc->sc_rx_mbufs[slot];
+		KASSERT(m != NULL);
+		bus_dmamap_unload(vsc->sc_dmat, sc->sc_rx_dmamaps[slot]);
+		sc->sc_rx_mbufs[slot] = 0;
+		virtio_dequeue_commit(vsc, vq, slot);
+		m->m_pkthdr.rcvif = ifp;
+		m->m_len = m->m_pkthdr.len = len;
+		ifp->if_ipackets++;
+#if NBPFILTER > 0
+		if (ifp->if_bpf)
+			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
+#endif /* NBPFILTER > 0 */
+		ether_input_mbuf(ifp, m);
+	}
+	
+	return r;
+}
+
+/* rx interrupt; call _dequeue above and schedule a softint */
+static int
+vioif_rx_vq_done(struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct vioif_softc *sc = (struct vioif_softc *)vsc->sc_child;
+	int r;
+
+	r = vioif_rx_deq(sc);
+	if (r)
+		softintr_schedule(sc->sc_rx_softint);
+
+	return r;
+}
+
+/* softint: enqueue receive requests for new incoming packets */
+static void
+vioif_rx_softint(void *arg)
+{
+	struct vioif_softc *sc = arg;
+
+	vioif_populate_rx_mbufs(sc);
+}
+
+/* free all the mbufs; called from if_stop(disable) */
+static void
+vioif_rx_drain(struct vioif_softc *sc)
+{
+	struct virtqueue *vq = &sc->sc_vq[0];
+	int i;
+
+	for (i = 0; i < vq->vq_num; i++) {
+		if (sc->sc_rx_mbufs[i] == NULL)
+			continue;
+		vioif_free_rx_mbuf(sc, i);
+	}
+}
+
+
+/*
+ * Transmition implementation
+ */
+/* actual transmission is done in if_start */
+/* tx interrupt; dequeue and free mbufs */
+/*
+ * tx interrupt is actually disabled; this should be called upon
+ * tx vq full and watchdog
+ */
+static int
+vioif_tx_vq_done(struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct vioif_softc *sc = (struct vioif_softc *)vsc->sc_child;
+	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+	struct mbuf *m;
+	int r = 0;
+	int slot, len;
+
+	while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
+		r++;
+		bus_dmamap_sync(vsc->sc_dmat, sc->sc_txhdr_dmamaps[slot],
+				0, sizeof(struct virtio_net_hdr),
+				BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_sync(vsc->sc_dmat, sc->sc_tx_dmamaps[slot],
+				0, sc->sc_tx_dmamaps[slot]->dm_mapsize,
+				BUS_DMASYNC_POSTWRITE);
+		m = sc->sc_tx_mbufs[slot];
+		bus_dmamap_unload(vsc->sc_dmat, sc->sc_tx_dmamaps[slot]);
+		sc->sc_tx_mbufs[slot] = 0;
+		virtio_dequeue_commit(vsc, vq, slot);
+		ifp->if_opackets++;
+		m_freem(m);
+	}
+
+	if (r)
+		ifp->if_flags &= ~IFF_OACTIVE;
+	return r;
+}
+
+/* free all the mbufs already put on vq; called from if_stop(disable) */
+static void
+vioif_tx_drain(struct vioif_softc *sc)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtqueue *vq = &sc->sc_vq[1];
+	int i;
+
+	for (i = 0; i < vq->vq_num; i++) {
+		if (sc->sc_tx_mbufs[i] == NULL)
+			continue;
+		bus_dmamap_unload(vsc->sc_dmat, sc->sc_tx_dmamaps[i]);
+		m_freem(sc->sc_tx_mbufs[i]);
+		sc->sc_tx_mbufs[i] = NULL;
+	}
+}
+
+/*
+ * Control vq
+ */
+/* issue a VIRTIO_NET_CTRL_RX class command and wait for completion */
+static int
+vioif_ctrl_rx(struct vioif_softc *sc, int cmd, int onoff)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtqueue *vq = &sc->sc_vq[2];
+	int r, slot;
+
+	if (vsc->sc_nvqs < 3)
+		return ENOTSUP;
+
+	mtx_enter(&sc->sc_ctrl_wait_lock);
+	while (sc->sc_ctrl_inuse != FREE)
+		msleep(sc, &sc->sc_ctrl_wait_lock, PRIBIO, "ctrl_vq", 0);
+	sc->sc_ctrl_inuse = INUSE;
+	mtx_leave(&sc->sc_ctrl_wait_lock);
+
+	sc->sc_ctrl_cmd->class = VIRTIO_NET_CTRL_RX;
+	sc->sc_ctrl_cmd->command = cmd;
+	sc->sc_ctrl_rx->onoff = onoff;
+
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_cmd_dmamap,
+			0, sizeof(struct virtio_net_ctrl_cmd),
+			BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_rx_dmamap,
+			0, sizeof(struct virtio_net_ctrl_rx),
+			BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_status_dmamap,
+			0, sizeof(struct virtio_net_ctrl_status),
+			BUS_DMASYNC_PREREAD);
+
+	r = virtio_enqueue_prep(vsc, vq, &slot);
+	if (r != 0)
+		panic("%s: control vq busy!?", sc->sc_dev.dv_xname);
+	r = virtio_enqueue_reserve(vsc, vq, slot, 3);
+	if (r != 0)
+		panic("%s: control vq busy!?", sc->sc_dev.dv_xname);
+	virtio_enqueue(vsc, vq, slot, sc->sc_ctrl_cmd_dmamap, 1);
+	virtio_enqueue(vsc, vq, slot, sc->sc_ctrl_rx_dmamap, 1);
+	virtio_enqueue(vsc, vq, slot, sc->sc_ctrl_status_dmamap, 0);
+	virtio_enqueue_commit(vsc, vq, slot, 1);
+
+	/* wait for done */
+	mtx_enter(&sc->sc_ctrl_wait_lock);
+	while (sc->sc_ctrl_inuse != DONE)
+		msleep(sc, &sc->sc_ctrl_wait_lock, PRIBIO, "ctrl_vq", 0);
+	mtx_leave(&sc->sc_ctrl_wait_lock);
+	/* already dequeueued */
+
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_cmd_dmamap, 0,
+			sizeof(struct virtio_net_ctrl_cmd),
+			BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_rx_dmamap, 0,
+			sizeof(struct virtio_net_ctrl_rx),
+			BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_status_dmamap, 0,
+			sizeof(struct virtio_net_ctrl_status),
+			BUS_DMASYNC_POSTREAD);
+
+	if (sc->sc_ctrl_status->ack == VIRTIO_NET_OK)
+		r = 0;
+	else {
+		printf("%s: failed setting rx mode\n",
+		       sc->sc_dev.dv_xname);
+		r = EIO;
+	}
+
+	mtx_enter(&sc->sc_ctrl_wait_lock);
+	sc->sc_ctrl_inuse = FREE;
+	wakeup(sc);
+	mtx_leave(&sc->sc_ctrl_wait_lock);
+	
+	return r;
+}
+
+static int
+vioif_set_promisc(struct vioif_softc *sc, int onoff)
+{
+	int r;
+
+	r = vioif_ctrl_rx(sc, VIRTIO_NET_CTRL_RX_PROMISC, onoff);
+
+	return r;
+}
+
+static int
+vioif_set_allmulti(struct vioif_softc *sc, int onoff)
+{
+	int r;
+
+	r = vioif_ctrl_rx(sc, VIRTIO_NET_CTRL_RX_ALLMULTI, onoff);
+
+	return r;
+}
+
+/* issue VIRTIO_NET_CTRL_MAC_TABLE_SET command and wait for completion */
+static int
+vioif_set_rx_filter(struct vioif_softc *sc)
+{
+	/* filter already set in sc_ctrl_mac_tbl */
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtqueue *vq = &sc->sc_vq[2];
+	int r, slot;
+
+	if (vsc->sc_nvqs < 3)
+		return ENOTSUP;
+
+	mtx_enter(&sc->sc_ctrl_wait_lock);
+	while (sc->sc_ctrl_inuse != FREE)
+		msleep(sc, &sc->sc_ctrl_wait_lock, PRIBIO, "ctrl_vq", 0);
+	sc->sc_ctrl_inuse = INUSE;
+	mtx_leave(&sc->sc_ctrl_wait_lock);
+
+	sc->sc_ctrl_cmd->class = VIRTIO_NET_CTRL_MAC;
+	sc->sc_ctrl_cmd->command = VIRTIO_NET_CTRL_MAC_TABLE_SET;
+
+	r = bus_dmamap_load(vsc->sc_dmat, sc->sc_ctrl_tbl_uc_dmamap,
+			    sc->sc_ctrl_mac_tbl_uc,
+			    (sizeof(struct virtio_net_ctrl_mac_tbl)
+			  + ETHER_ADDR_LEN * sc->sc_ctrl_mac_tbl_uc->nentries),
+			    NULL, BUS_DMA_WRITE|BUS_DMA_NOWAIT);
+	if (r) {
+		printf("%s: control command dmamap load failed, "
+		       "error code %d\n", sc->sc_dev.dv_xname, r);
+		goto out;
+	}
+	r = bus_dmamap_load(vsc->sc_dmat, sc->sc_ctrl_tbl_mc_dmamap,
+			    sc->sc_ctrl_mac_tbl_mc,
+			    (sizeof(struct virtio_net_ctrl_mac_tbl)
+			  + ETHER_ADDR_LEN * sc->sc_ctrl_mac_tbl_mc->nentries),
+			    NULL, BUS_DMA_WRITE|BUS_DMA_NOWAIT);
+	if (r) {
+		printf("%s: control command dmamap load failed, "
+		       "error code %d\n", sc->sc_dev.dv_xname, r);
+		bus_dmamap_unload(vsc->sc_dmat, sc->sc_ctrl_tbl_uc_dmamap);
+		goto out;
+	}
+
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_cmd_dmamap,
+			0, sizeof(struct virtio_net_ctrl_cmd),
+			BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_tbl_uc_dmamap, 0,
+			(sizeof(struct virtio_net_ctrl_mac_tbl)
+			 + ETHER_ADDR_LEN * sc->sc_ctrl_mac_tbl_uc->nentries),
+			BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_tbl_mc_dmamap, 0,
+			(sizeof(struct virtio_net_ctrl_mac_tbl)
+			 + ETHER_ADDR_LEN * sc->sc_ctrl_mac_tbl_mc->nentries),
+			BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_status_dmamap,
+			0, sizeof(struct virtio_net_ctrl_status),
+			BUS_DMASYNC_PREREAD);
+
+	r = virtio_enqueue_prep(vsc, vq, &slot);
+	if (r != 0)
+		panic("%s: control vq busy!?", sc->sc_dev.dv_xname);
+	r = virtio_enqueue_reserve(vsc, vq, slot, 4);
+	if (r != 0)
+		panic("%s: control vq busy!?", sc->sc_dev.dv_xname);
+	virtio_enqueue(vsc, vq, slot, sc->sc_ctrl_cmd_dmamap, 1);
+	virtio_enqueue(vsc, vq, slot, sc->sc_ctrl_tbl_uc_dmamap, 1);
+	virtio_enqueue(vsc, vq, slot, sc->sc_ctrl_tbl_mc_dmamap, 1);
+	virtio_enqueue(vsc, vq, slot, sc->sc_ctrl_status_dmamap, 0);
+	virtio_enqueue_commit(vsc, vq, slot, 1);
+
+	/* wait for done */
+	mtx_enter(&sc->sc_ctrl_wait_lock);
+	while (sc->sc_ctrl_inuse != DONE)
+		msleep(sc, &sc->sc_ctrl_wait_lock, PRIBIO, "ctrl_vq", 0);
+	mtx_leave(&sc->sc_ctrl_wait_lock);
+	/* already dequeueued */
+
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_cmd_dmamap, 0,
+			sizeof(struct virtio_net_ctrl_cmd),
+			BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_tbl_uc_dmamap, 0,
+			(sizeof(struct virtio_net_ctrl_mac_tbl)
+			 + ETHER_ADDR_LEN * sc->sc_ctrl_mac_tbl_uc->nentries),
+			BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_tbl_mc_dmamap, 0,
+			(sizeof(struct virtio_net_ctrl_mac_tbl)
+			 + ETHER_ADDR_LEN * sc->sc_ctrl_mac_tbl_mc->nentries),
+			BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_sync(vsc->sc_dmat, sc->sc_ctrl_status_dmamap, 0,
+			sizeof(struct virtio_net_ctrl_status),
+			BUS_DMASYNC_POSTREAD);
+	bus_dmamap_unload(vsc->sc_dmat, sc->sc_ctrl_tbl_uc_dmamap);
+	bus_dmamap_unload(vsc->sc_dmat, sc->sc_ctrl_tbl_mc_dmamap);
+
+	if (sc->sc_ctrl_status->ack == VIRTIO_NET_OK)
+		r = 0;
+	else {
+		printf("%s: failed setting rx filter\n", sc->sc_dev.dv_xname);
+		r = EIO;
+	}
+
+out:
+	mtx_enter(&sc->sc_ctrl_wait_lock);
+	sc->sc_ctrl_inuse = FREE;
+	wakeup(sc);
+	mtx_leave(&sc->sc_ctrl_wait_lock);
+	
+	return r;
+}
+
+/* ctrl vq interrupt; wake up the command issuer */
+static int
+vioif_ctrl_vq_done(struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct vioif_softc *sc = (struct vioif_softc *)vsc->sc_child;
+	int r, slot;
+
+	r = virtio_dequeue(vsc, vq, &slot, NULL);
+	if (r == ENOENT)
+		return 0;
+	virtio_dequeue_commit(vsc, vq, slot);
+
+	mtx_enter(&sc->sc_ctrl_wait_lock);
+	sc->sc_ctrl_inuse = DONE;
+	wakeup(sc);
+	mtx_leave(&sc->sc_ctrl_wait_lock);
+
+	return 1;
+}
+
+/*
+ * If IFF_PROMISC requested,  set promiscuous
+ * If multicast filter small enough (<=MAXENTRIES) set rx filter
+ * If large multicast filter exist use ALLMULTI
+ */
+/*
+ * If setting rx filter fails fall back to ALLMULTI
+ * If ALLMULTI fails fall back to PROMISC
+ */
+static int
+vioif_rx_filter(struct vioif_softc *sc)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+	struct ether_multi *enm;
+	struct ether_multistep step;
+	int nentries;
+	int promisc = 0, allmulti = 0, rxfilter = 0;
+	int r;
+
+	if (vsc->sc_nvqs < 3) {	/* no ctrl vq; always promisc */
+		ifp->if_flags |= IFF_PROMISC;
+		return 0;
+	}
+
+	if (ifp->if_flags & IFF_PROMISC) {
+		promisc = 1;
+		goto set;
+	}
+
+	nentries = -1;
+	ETHER_FIRST_MULTI(step, &sc->sc_arpcom, enm);
+	while (nentries++, enm != NULL) {
+		if (nentries >= VIRTIO_NET_CTRL_MAC_MAXENTRIES) {
+			allmulti = 1;
+			goto set;
+		}
+		if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
+			   ETHER_ADDR_LEN)) {
+			allmulti = 1;
+			goto set;
+		}
+		memcpy(sc->sc_ctrl_mac_tbl_mc->macs[nentries],
+		       enm->enm_addrlo, ETHER_ADDR_LEN);
+		ETHER_NEXT_MULTI(step, enm);
+	}
+	rxfilter = 1;
+
+set:
+	if (rxfilter) {
+		sc->sc_ctrl_mac_tbl_uc->nentries = 0;
+		sc->sc_ctrl_mac_tbl_mc->nentries = nentries;
+		r = vioif_set_rx_filter(sc);
+		if (r != 0) {
+			rxfilter = 0;
+			allmulti = 1; /* fallback */
+		}
+	} else {
+		/* remove rx filter */
+		sc->sc_ctrl_mac_tbl_uc->nentries = 0;
+		sc->sc_ctrl_mac_tbl_mc->nentries = 0;
+		r = vioif_set_rx_filter(sc);
+		/* what to do on failure? */
+	}
+	if (allmulti) {
+		r = vioif_set_allmulti(sc, 1);
+		if (r != 0) {
+			allmulti = 0;
+			promisc = 1; /* fallback */
+		}
+	} else {
+		r = vioif_set_allmulti(sc, 0);
+		/* what to do on failure? */
+	}
+	if (promisc) {
+		r = vioif_set_promisc(sc, 1);
+	} else {
+		r = vioif_set_promisc(sc, 0);
+	}
+
+	return r;
+}
+
+/* change link status */
+static int
+vioif_updown(struct vioif_softc *sc, int isup)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+
+	if (!(vsc->sc_features & VIRTIO_NET_F_STATUS))
+		return ENODEV;
+	virtio_write_device_config_1(vsc,
+				     VIRTIO_NET_CONFIG_STATUS,
+				     isup?VIRTIO_NET_S_LINK_UP:0);
+	return 0;
+}
Index: dev/pci/viomb.c
===================================================================
--- dev/pci/viomb.c	(.../vendor/openbsd/head/sys)	(revision 0)
+++ dev/pci/viomb.c	(.../user/yasuoka/virtio-work)	(revision 135372)
@@ -0,0 +1,522 @@
+/*	$OpenBSD$	*/
+/*	$NetBSD$	*/
+
+/*
+ * 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/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/kthread.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+#include <sys/stdint.h>
+#include <machine/bus.h>
+#include <uvm/uvm_extern.h>
+
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <dev/pci/virtioreg.h>
+#include <dev/pci/virtiovar.h>
+
+/* Configuration registers */
+#define VIRTIO_BALLOON_CONFIG_NUM_PAGES	0 /* 32bit */
+#define VIRTIO_BALLOON_CONFIG_ACTUAL	4 /* 32bit */
+
+/* Feature bits */
+#define VIRTIO_BALLOON_F_MUST_TELL_HOST (1<<0)
+#define VIRTIO_BALLOON_F_STATS_VQ	(1<<1)
+
+#define PGS_PER_REQ		(256) /* 1MB, 4KB/page */
+
+#if (PAGE_SIZE != VIRTIO_PAGE_SIZE)
+#error assert PAGE_SIZE == VIRTIO_PAGE_SIZE
+#endif
+
+struct balloon_req {
+	bus_dmamap_t			bl_dmamap;
+	struct pglist			bl_pglist;
+	int				bl_nentries;
+	uint32_t			bl_pages[PGS_PER_REQ];
+};
+
+struct viomb_softc {
+	struct device		sc_dev;
+
+	struct virtio_softc	*sc_virtio;
+	struct virtqueue	sc_vq[2];
+
+	unsigned int		sc_npages;
+	unsigned int		sc_actual;
+	int			sc_inflight;
+	struct balloon_req	sc_req;
+	struct pglist		sc_balloon_pages;
+
+	int			sc_inflate_done;
+	int			sc_deflate_done;
+
+	struct mutex		sc_waitlock;
+};
+
+static int	balloon_initialized = 0; /* multiple balloon is not allowed */
+
+static int	viomb_match(struct device *, void *, void *);
+static void	viomb_attach(struct device *, struct device *, void *);
+static void	viomb_read_config(struct viomb_softc *);
+static int	viomb_config_change(struct virtio_softc *);
+static int	inflate(struct viomb_softc *);
+static int	inflateq_done(struct virtqueue *);
+static int	inflate_done(struct viomb_softc *);
+static int	deflate(struct viomb_softc *);
+static int	deflateq_done(struct virtqueue *);
+static int	deflate_done(struct viomb_softc *);
+static void	viomb_thread(void *);
+
+struct cfattach viomb_ca = {
+	sizeof(struct viomb_softc), viomb_match, viomb_attach
+};
+
+struct cfdriver viomb_cd = {
+	NULL, "viomb", DV_DISK
+};
+
+static int
+viomb_match(struct device * parent, void * match, void *aux)
+{
+	struct virtio_softc *vsc = aux;
+
+	if (vsc->sc_childdevid == PCI_PRODUCT_VIRTIO_BALLOON)
+		return 1;
+
+	return 0;
+}
+
+static void
+viomb_attach(struct device * parent, struct device * self, void *aux)
+{
+	struct viomb_softc *sc = (struct viomb_softc *)self;
+	struct virtio_softc *vsc = (struct virtio_softc *)parent;
+	//const struct sysctlnode *node;
+
+	if (vsc->sc_child != NULL) {
+		printf(": child already attached for %s; "
+			      "something wrong...\n",
+			      parent->dv_xname);
+		return;
+	}
+	if (balloon_initialized++) {
+		printf(": balloon already exists; something wrong...\n");
+		goto err_none;
+	}
+	printf("\n");
+
+	sc->sc_virtio = vsc;
+
+	vsc->sc_child = self;
+	vsc->sc_ipl = IPL_VM;
+	vsc->sc_vqs = &sc->sc_vq[0];
+	vsc->sc_nvqs = 2;
+	vsc->sc_config_change = viomb_config_change;
+	vsc->sc_intrhand = virtio_vq_intr;
+
+	virtio_negotiate_features(vsc,
+				  VIRTIO_CONFIG_DEVICE_FEATURES);
+	if ((virtio_alloc_vq(vsc, &sc->sc_vq[0], 0,
+			     sizeof(uint32_t)*PGS_PER_REQ, 1,
+			     "inflate") != 0) ||
+	    (virtio_alloc_vq(vsc, &sc->sc_vq[1], 1,
+			     sizeof(uint32_t)*PGS_PER_REQ, 1,
+			     "deflate") != 0)) {
+		goto err_none;
+	}
+	sc->sc_vq[0].vq_done = inflateq_done;
+	sc->sc_vq[1].vq_done = deflateq_done;
+
+	viomb_read_config(sc);
+	sc->sc_inflight = 0;
+	TAILQ_INIT(&sc->sc_balloon_pages);
+
+	if (bus_dmamap_create(vsc->sc_dmat, sizeof(uint32_t)*PGS_PER_REQ,
+			      1, sizeof(uint32_t)*PGS_PER_REQ, 0,
+			      BUS_DMA_NOWAIT, &sc->sc_req.bl_dmamap)) {
+		printf("%s: dmamap creation failed.\n", sc->sc_dev.dv_xname);
+		goto err_vq;
+	}
+	if (bus_dmamap_load(vsc->sc_dmat, sc->sc_req.bl_dmamap,
+			    &sc->sc_req.bl_pages[0],
+			    sizeof(uint32_t) * PGS_PER_REQ,
+			    NULL, BUS_DMA_NOWAIT)) {
+		printf("%s: dmamap load failed.\n", sc->sc_dev.dv_xname);
+		goto err_dmamap;
+	}
+
+	sc->sc_inflate_done = sc->sc_deflate_done = 0;
+	mtx_init(&sc->sc_waitlock, IPL_VM); /* spin */
+
+	if (kthread_create(viomb_thread, sc, NULL, "viomb")) {
+		printf("%s: cannot create kthread.\n", sc->sc_dev);
+		goto err_mutex;
+	}
+
+#if 0
+	/*
+	 * XXX: use these by sysctl?
+	 *	hw.viomb .......... VirtIO Balloon status
+	 *	hw.viomb.npages ... VirtIO Balloon npages value
+	 *	hw.viomb.actual ... VirtIO Balloon actual value
+	 */
+	sysctl_createv(NULL, 0, NULL, &node, 0, CTLTYPE_NODE,
+		       "viomb", SYSCTL_DESCR("VirtIO Balloon status"),
+		       NULL, 0, NULL, 0,
+		       CTL_HW, CTL_CREATE, CTL_EOL);
+	sysctl_createv(NULL, 0, NULL, NULL, 0, CTLTYPE_INT,
+		       "npages", SYSCTL_DESCR("VirtIO Balloon npages value"),
+		       NULL, 0, &sc->sc_npages, 0,
+		       CTL_HW, node->sysctl_num, CTL_CREATE, CTL_EOL);
+	sysctl_createv(NULL, 0, NULL, NULL, 0, CTLTYPE_INT,
+		       "actual", SYSCTL_DESCR("VirtIO Balloon actual value"),
+		       NULL, 0, &sc->sc_actual, 0,
+		       CTL_HW, node->sysctl_num, CTL_CREATE, CTL_EOL);
+#endif
+	return;
+
+err_mutex:
+err_dmamap:
+	bus_dmamap_destroy(vsc->sc_dmat, sc->sc_req.bl_dmamap);
+err_vq:
+	virtio_free_vq(vsc, &sc->sc_vq[1]);
+	virtio_free_vq(vsc, &sc->sc_vq[0]);
+err_none:
+	vsc->sc_child = (void*)1;
+	return;
+}
+
+static void
+viomb_read_config(struct viomb_softc *sc)
+{
+	unsigned int reg;
+
+	/* these values are explicitly specified as little-endian */
+	reg = virtio_read_device_config_4(sc->sc_virtio,
+					  VIRTIO_BALLOON_CONFIG_NUM_PAGES);
+	sc->sc_npages = letoh32(reg);
+	
+	reg = virtio_read_device_config_4(sc->sc_virtio,
+					  VIRTIO_BALLOON_CONFIG_ACTUAL);
+	sc->sc_actual = letoh32(reg);
+}
+
+/*
+ * Config change callback: wakeup the kthread.
+ */
+static int
+viomb_config_change(struct virtio_softc *vsc)
+{
+	struct viomb_softc *sc = (struct viomb_softc *)vsc->sc_child;
+	unsigned int old;
+
+	old = sc->sc_npages;
+	viomb_read_config(sc);
+	mtx_enter(&sc->sc_waitlock);
+	wakeup(sc);
+	mtx_leave(&sc->sc_waitlock);
+	if (sc->sc_npages > old)
+		printf("%s: inflating balloon from %u to %u.\n",
+		       sc->sc_dev.dv_xname, old, sc->sc_npages);
+	else if  (sc->sc_npages < old)
+		printf("%s: deflating balloon from %u to %u.\n",
+		       sc->sc_dev.dv_xname, old, sc->sc_npages);
+
+	return 1;
+}
+
+/*
+ * Inflate: consume some amount of physical memory.
+ */
+static int
+inflate(struct viomb_softc *sc)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	int i, slot;
+	uint64_t nvpages, nhpages;
+	struct balloon_req *b;
+	struct vm_page *p;
+	struct virtqueue *vq = &sc->sc_vq[0];
+
+	if (sc->sc_inflight)
+		return 0;
+	nvpages = sc->sc_npages - sc->sc_actual;
+	if (nvpages > PGS_PER_REQ)
+		nvpages = PGS_PER_REQ;
+	nhpages = nvpages * VIRTIO_PAGE_SIZE / PAGE_SIZE;
+
+	b = &sc->sc_req;
+	if (uvm_pglistalloc(nhpages*PAGE_SIZE, 0, UINT32_MAX*PAGE_SIZE,
+			    0, 0, &b->bl_pglist, nhpages, 1)) {
+		printf("%s: %llu pages of physical memory "
+		       "could not be allocated, retrying...\n",
+		       sc->sc_dev.dv_xname, nhpages);
+		return 1;	/* sleep longer */
+	}
+
+	b->bl_nentries = nvpages;
+	i = 0;
+	TAILQ_FOREACH(p, &b->bl_pglist, pageq) {
+		b->bl_pages[i++] = p->phys_addr / VIRTIO_PAGE_SIZE;
+	}
+	KASSERT(i == nvpages);
+
+	if (virtio_enqueue_prep(vsc, vq, &slot) != 0) {
+		printf("%s: inflate enqueue failed.\n",
+		       sc->sc_dev.dv_xname);
+		uvm_pglistfree(&b->bl_pglist);
+		return 0;
+	}
+	if (virtio_enqueue_reserve(vsc, vq, slot, 1)) {
+		printf("%s: inflate enqueue failed.\n",
+		       sc->sc_dev.dv_xname);
+		uvm_pglistfree(&b->bl_pglist);
+		return 0;
+	}
+	bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap, 0, sizeof(uint32_t)*nvpages,
+			BUS_DMASYNC_PREWRITE);
+	virtio_enqueue(vsc, vq, slot, b->bl_dmamap, 1);
+	virtio_enqueue_commit(vsc, vq, slot, 1);
+	sc->sc_inflight += nvpages;
+
+	return 0;
+}
+
+static int
+inflateq_done(struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct viomb_softc *sc = (struct viomb_softc *)vsc->sc_child;
+
+	mtx_enter(&sc->sc_waitlock);
+	sc->sc_inflate_done = 1;
+	wakeup(sc);
+	mtx_leave(&sc->sc_waitlock);
+
+	return 1;
+}
+
+static int
+inflate_done(struct viomb_softc *sc)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtqueue *vq = &sc->sc_vq[0];
+	struct balloon_req *b;
+	int r, slot;
+	uint64_t nvpages;
+	struct vm_page *p;
+
+	r = virtio_dequeue(vsc, vq, &slot, NULL);
+	if (r != 0) {
+		printf("%s: inflate dequeue failed, errno %d.\n",
+		       sc->sc_dev.dv_xname, r);
+		return 1;
+	}
+	virtio_dequeue_commit(vsc, vq, slot);
+
+	b = &sc->sc_req;
+	nvpages = b->bl_nentries;
+	bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap,
+			offsetof(struct balloon_req, bl_pages),
+			sizeof(uint32_t)*nvpages,
+			BUS_DMASYNC_POSTWRITE);
+	while (!TAILQ_EMPTY(&b->bl_pglist)) {
+		p = TAILQ_FIRST(&b->bl_pglist);
+		TAILQ_REMOVE(&b->bl_pglist, p, pageq);
+		TAILQ_INSERT_TAIL(&sc->sc_balloon_pages, p, pageq);
+	}
+
+	sc->sc_inflight -= nvpages;
+	virtio_write_device_config_4(vsc,
+				     VIRTIO_BALLOON_CONFIG_ACTUAL,
+				     sc->sc_actual + nvpages);
+	viomb_read_config(sc);
+
+	return 1;
+}
+	
+/*
+ * Deflate: free previously allocated memory.
+ */
+static int
+deflate(struct viomb_softc *sc)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	int i, slot;
+	uint64_t nvpages, nhpages;
+	struct balloon_req *b;
+	struct vm_page *p;
+	struct virtqueue *vq = &sc->sc_vq[1];
+
+	nvpages = (sc->sc_actual + sc->sc_inflight) - sc->sc_npages;
+	if (nvpages > PGS_PER_REQ)
+		nvpages = PGS_PER_REQ;
+	nhpages = nvpages * VIRTIO_PAGE_SIZE / PAGE_SIZE;
+
+	b = &sc->sc_req;
+
+	b->bl_nentries = nvpages;
+	TAILQ_INIT(&b->bl_pglist);
+	for (i = 0; i < nhpages; i++) {
+		p = TAILQ_FIRST(&sc->sc_balloon_pages);
+		TAILQ_REMOVE(&sc->sc_balloon_pages, p, pageq);
+		TAILQ_INSERT_TAIL(&b->bl_pglist, p, pageq);
+		b->bl_pages[i] = p->phys_addr / VIRTIO_PAGE_SIZE;
+	}
+
+	if (virtio_enqueue_prep(vsc, vq, &slot) != 0) {
+		printf("%s: deflate enqueue failed.\n",
+		       sc->sc_dev.dv_xname);
+		TAILQ_FOREACH_REVERSE(p, &b->bl_pglist, pglist, pageq) {
+			TAILQ_REMOVE(&b->bl_pglist, p, pageq);
+			TAILQ_INSERT_HEAD(&sc->sc_balloon_pages, p, pageq);
+		}
+		return 0;
+	}
+	if (virtio_enqueue_reserve(vsc, vq, slot, 1) != 0) {
+		printf("%s: deflate enqueue failed.\n",
+		       sc->sc_dev.dv_xname);
+		TAILQ_FOREACH_REVERSE(p, &b->bl_pglist, pglist, pageq) {
+			TAILQ_REMOVE(&b->bl_pglist, p, pageq);
+			TAILQ_INSERT_HEAD(&sc->sc_balloon_pages, p, pageq);
+		}
+		return 0;
+	}
+	bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap, 0, sizeof(uint32_t)*nvpages,
+			BUS_DMASYNC_PREWRITE);
+	virtio_enqueue(vsc, vq, slot, b->bl_dmamap, 1);
+	virtio_enqueue_commit(vsc, vq, slot, 1);
+	sc->sc_inflight -= nvpages;
+
+	if (!(vsc->sc_features & VIRTIO_BALLOON_F_MUST_TELL_HOST))
+		uvm_pglistfree(&b->bl_pglist);
+
+	return 0;
+}
+
+static int
+deflateq_done(struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct viomb_softc *sc = (struct viomb_softc *)vsc->sc_child;
+
+	mtx_enter(&sc->sc_waitlock);
+	sc->sc_deflate_done = 1;
+	wakeup(sc);
+	mtx_leave(&sc->sc_waitlock);
+
+	return 1;
+}
+	
+static int
+deflate_done(struct viomb_softc *sc)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtqueue *vq = &sc->sc_vq[1];
+	struct balloon_req *b;
+	int r, slot;
+	uint64_t nvpages, nhpages;
+
+	r = virtio_dequeue(vsc, vq, &slot, NULL);
+	if (r != 0) {
+		printf("%s: deflate dequeue failed, errno %d\n",
+		       sc->sc_dev.dv_xname, r);
+		return 1;
+	}
+	virtio_dequeue_commit(vsc, vq, slot);
+
+	b = &sc->sc_req;
+	nvpages = b->bl_nentries;
+	nhpages = nvpages * VIRTIO_PAGE_SIZE / PAGE_SIZE;
+	bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap,
+			offsetof(struct balloon_req, bl_pages),
+			sizeof(uint32_t)*nvpages,
+			BUS_DMASYNC_POSTWRITE);
+
+	if (vsc->sc_features & VIRTIO_BALLOON_F_MUST_TELL_HOST)
+		uvm_pglistfree(&b->bl_pglist);
+
+	sc->sc_inflight += nvpages;
+	virtio_write_device_config_4(vsc,
+				     VIRTIO_BALLOON_CONFIG_ACTUAL,
+				     sc->sc_actual - nvpages);
+	viomb_read_config(sc);
+
+	return 1;
+}
+
+/*
+ * Kthread: sleeps, eventually inflate and deflate.
+ */
+static void
+viomb_thread(void *arg)
+{
+	struct viomb_softc *sc = arg;
+	int sleeptime, r;
+
+	for ( ; ; ) {
+		sleeptime = 30000;
+		if (sc->sc_npages > sc->sc_actual + sc->sc_inflight) {
+			if (sc->sc_inflight == 0) {
+				r = inflate(sc);
+				if (r != 0)
+					sleeptime = 10000;
+				else
+					sleeptime = 1000;
+			} else
+				sleeptime = 100;
+		} else if (sc->sc_npages < sc->sc_actual + sc->sc_inflight) {
+			if (sc->sc_inflight == 0)
+				r = deflate(sc);
+			sleeptime = 100;
+		}
+
+	again:
+		mtx_enter(&sc->sc_waitlock);
+		if (sc->sc_inflate_done) {
+			sc->sc_inflate_done = 0;
+			mtx_leave(&sc->sc_waitlock);
+			inflate_done(sc);
+			goto again;
+		}
+		if (sc->sc_deflate_done) {
+			sc->sc_deflate_done = 0;
+			mtx_leave(&sc->sc_waitlock);
+			deflate_done(sc);
+			goto again;
+		}
+		msleep(sc, &sc->sc_waitlock, PRIBIO, "baloon",
+		    (sleeptime * hz) / 1000);
+		mtx_leave(&sc->sc_waitlock);
+	}
+}
