git: 9front

Download patch

ref: 2c8287a448ac858cdb90d1fcd86113b56550ae0b
parent: 391658524ca1119cb9f502651fee9eb42173ed22
author: Arne <cgnarne@netcologne.de>
date: Sun Sep 21 14:55:04 EDT 2025

virtio: support pci i/o bars for virtio10 devices

The virtio 1.x spec allows for i/o bars in addition to
mem bars and at least OpenBSD vmd uses i/o bars exclusively
since release 7.8.

Since we only have to deal with i/o on x86 there is support in
pc and pc64 to deal with both i/o and mem. the code in port is
still mem bar only.

--- a/sys/src/9/arm64/qemu
+++ b/sys/src/9/arm64/qemu
@@ -23,7 +23,7 @@
 
 link
 	usbxhcipci	pci usbxhci
-	ethervirtio10	pci
+	ethervirtio10	pci virtio10mem
 	ethersink
 	ethermedium
 	loopbackmedium
@@ -42,7 +42,7 @@
 misc
 	gic
 	uartqemu
-	sdvirtio10	pci sdscsi
+	sdvirtio10	pci sdscsi virtio10mem
 port
 	int cpuserver = 0;
 bootdir
--- a/sys/src/9/pc/mkfile
+++ b/sys/src/9/pc/mkfile
@@ -126,6 +126,7 @@
 sd53c8xx.$O:			sd53c8xx.i
 sdiahci.$O:			ahci.h ../port/led.h
 devaoe.$O sdaoe.$O:		../port/aoe.h
+sdvirtio10.$O:			../port/virtio10.h
 main.$O:			rebootcode.i
 wavelan.$O:			wavelan.c ../pc/wavelan.c ../pc/wavelan.h ../port/etherif.h ../port/netif.h
 etherwavelan.$O:		etherwavelan.c ../pc/wavelan.h
@@ -143,8 +144,10 @@
 etherm10g.$O:			etherm10g2k.i etherm10g4k.i
 etherwpi.$O:			../port/wifi.h
 etherrt2860.$O: 		../port/wifi.h
+ethervirtio10.$O:		../port/virtio10.h
 l.$O rebootcode.$O apbootstrap.$O:	mem.h
 pcipc.$O pcibios.$O:		../port/pci.h io.h
+virtio10pc.$O:			../port/virtio10.h
 
 initcode.out:		init9.$O initcode.$O /$objtype/lib/libc.a
 	$LD -l -R1 -s -o $target $prereq
--- a/sys/src/9/pc/pc
+++ b/sys/src/9/pc/pc
@@ -79,7 +79,7 @@
 	etherwpi	pci wifi
 	etherrt2860	pci wifi
 	ethervirtio	pci
-	ethervirtio10	pci
+	ethervirtio10	pci virtio10pc
 	ethermedium
 	loopbackmedium
 	netdevmedium
@@ -110,7 +110,7 @@
 	sdiahci		pci sdscsi led
 	sdodin		pci sdscsi led
 	sdvirtio	pci sdscsi
-	sdvirtio10	pci sdscsi
+	sdvirtio10	pci sdscsi virtio10pc
 	sdmmc		pci pmmc
 	sdnvme		pci
 	sdloop
--- /dev/null
+++ b/sys/src/9/pc/virtio10pc.c
@@ -1,0 +1,134 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "../port/pci.h"
+#include "../port/error.h"
+#include "../port/virtio10.h"
+
+u8int
+vin8(Vio *r, int p)
+{
+	if(r->type == Vio_port)
+		return inb(r->port+p);
+	else
+		return *(u8int *)(r->mem+p);
+}
+
+
+u16int
+vin16(Vio *r, int p)
+{
+	if(r->type == Vio_port)
+		return ins(r->port+p);
+	else 
+		return *(u16int *)(r->mem+p);
+}
+
+u32int
+vin32(Vio *r, int p)
+{
+	if(r->type == Vio_port)
+		return inl(r->port+p);
+	else
+		return *(u32int *)((uchar *)r->mem+p);
+}
+
+u64int
+vin64(Vio *r, int p)
+{
+	u64int v;
+	if(r->type == Vio_port){
+		v = inl(r->port+p);
+		v |= (u64int)inl(r->port+p+4) << 32;
+	}
+	else
+		v = *(u64int*)(r->mem+p);
+	return v;
+}
+
+void
+vout8(Vio *r, int p, u8int v)
+{
+	if(r->type == Vio_port)
+		outb(r->port+p, v);
+	else
+		*(u8int *)(r->mem+p) = v;
+}
+
+void
+vout16(Vio *r, int p, u16int v)
+{
+	if(r->type == Vio_port)
+		outs(r->port+p, v);
+	else
+		*(u16int *)(r->mem+p) = v;
+}
+
+void
+vout32(Vio *r, int p, u32int v)
+{
+	if(r->type == Vio_port)
+		outl(r->port+p, v);
+	else
+		*(u32int *)(r->mem+p) = v;
+}
+
+void
+vout64(Vio *r, int p, u64int v)
+{
+	if(r->type == Vio_port){
+		outl(r->port+p, v);
+		outl(r->port+p+4, v >> 32);
+	}
+	else
+		*(u64int *)(r->mem+p) = v;
+}
+
+void
+virtiounmap(Vio *r, usize sz)
+{
+	if(r->type == Vio_port)
+		iofree(r->port);
+	else
+		vunmap(r->mem, sz);	
+}
+
+Vio*
+virtiomapregs(Pcidev *p, int cap, int size, Vio *v)
+{
+	int bar, len;
+	uvlong addr;
+
+	if(cap < 0)
+		return nil;
+	bar = pcicfgr8(p, cap+4) % nelem(p->mem);
+	addr = pcicfgr32(p, cap+8);
+	len = pcicfgr32(p, cap+12);
+	if(size <= 0)
+		size = len;
+	else if(len < size)
+		return nil;
+	if(addr+len > p->mem[bar].size)
+		return nil;
+
+	if(p->mem[bar].bar & 1){
+		addr += p->mem[bar].bar & ~3;
+		v->type = Vio_port;
+		v->port = addr;
+		ioalloc(v->port, size, 0, "ethervirtio10");
+	}
+	else{
+		addr += p->mem[bar].bar & ~0xFULL;
+		v->type = Vio_mem;
+		v->mem = vmap(addr, size);
+		if(v->mem == nil)
+			return nil;
+	}
+	
+
+	return v;	
+}
--- a/sys/src/9/pc64/mkfile
+++ b/sys/src/9/pc64/mkfile
@@ -133,6 +133,7 @@
 $SDEV pmmc.$O:			../port/sd.h
 sdiahci.$O:			ahci.h
 devaoe.$O sdaoe.$O:		../port/aoe.h
+sdvirtio10.$O:			../port/virtio10.h
 main.$O:			rebootcode.i
 
 devusb.$O usbuhci.$O usbohci.$O usbehci.$O usbehcipc.$O usbxhci.$O: ../port/usb.h
@@ -151,7 +152,10 @@
 etherwpi.$O:			../port/wifi.h
 etherrt2860.$O: 		../port/wifi.h
 
+ethervirtio10.$O:		../port/virtio10.h
+
 pcipc.$O:	../port/pci.h io.h
+virtio10pc.$O:	../port/virtio10.h
 
 initcode.out:		init9.$O initcode.$O /$objtype/lib/libc.a
 	$LD -l -R1 -s -o $target $prereq
--- a/sys/src/9/pc64/pc64
+++ b/sys/src/9/pc64/pc64
@@ -79,7 +79,7 @@
 	etherwpi	pci wifi
 	etherrt2860	pci wifi
 	ethervirtio	pci
-	ethervirtio10	pci
+	ethervirtio10	pci virtio10pc
 	ethermedium
 	loopbackmedium
 	netdevmedium
@@ -109,7 +109,7 @@
 	sdiahci		pci sdscsi led
 #	sdodin		pci sdscsi led
 	sdvirtio	pci sdscsi
-	sdvirtio10	pci sdscsi
+	sdvirtio10	pci sdscsi virtio10pc
 	sdmmc		pci pmmc
 	sdnvme		pci
 	sdloop
--- a/sys/src/9/port/ethervirtio10.c
+++ b/sys/src/9/port/ethervirtio10.c
@@ -23,13 +23,8 @@
 #include "../port/error.h"
 #include "../port/netif.h"
 #include "../port/etherif.h"
+#include "../port/virtio10.h"
 
-typedef struct Vconfig Vconfig;
-typedef struct Vnetcfg Vnetcfg;
-
-typedef struct Vring Vring;
-typedef struct Vdesc Vdesc;
-typedef struct Vused Vused;
 typedef struct Vheader Vheader;
 typedef struct Vqueue Vqueue;
 
@@ -36,13 +31,6 @@
 typedef struct Ctlr Ctlr;
 
 enum {
-	/* §2.1 Device Status Field */
-	Sacknowledge = 1,
-	Sdriver = 2,
-	Sdriverok = 4,
-	Sfeaturesok = 8,
-	Sfailed = 128,
-
 	/* flags in Qnetstatus */
 	Nlinkup = (1<<0),
 	Nannounce = (1<<1),
@@ -53,9 +41,6 @@
 	Fctrlvq = 1<<17,
 	Fctrlrx = 1<<18,
 
-	/* feat[1] bits */
-	Fversion1 = 1<<(32-32),
-
 	/* vring used flags */
 	Unonotify = 1,
 	/* vring avail flags */
@@ -87,60 +72,13 @@
 		CmdVlanDel	= 0x01,
 };
 
-struct Vconfig {
-	u32int	devfeatsel;
-	u32int	devfeat;
-	u32int	drvfeatsel;
-	u32int	drvfeat;
-
-	u16int	msixcfg;
-	u16int	nqueues;
-
-	u8int	status;
-	u8int	cfggen;
-	u16int	queuesel;
-
-	u16int	queuesize;
-	u16int	queuemsixvect;
-
-	u16int	queueenable;
-	u16int	queuenotifyoff;
-
-	u64int	queuedesc;
-	u64int	queueavail;
-	u64int	queueused;
-};
-
-struct Vnetcfg
+enum
 {
-	u16int	mac0;
-	u16int	mac1;
-	u16int	mac2;
-	u16int	status;
-	u16int	maxqueuepairs;
-	u16int	mtu;
+	Vnet_mac0 = 0,
+	Vnet_status = 6,
+	Vnet_sz = 8
 };
 
-struct Vring
-{
-	u16int	flags;
-	u16int	idx;
-};
-
-struct Vdesc
-{
-	u64int	addr;
-	u32int	len;
-	u16int	flags;
-	u16int	next;
-};
-
-struct Vused
-{
-	u32int	id;
-	u32int	len;
-};
-
 struct Vheader
 {
 	u8int	flags;
@@ -173,7 +111,7 @@
 	uint	nnote;
 
 	/* notify register */
-	void	*notify;
+	Vio	notify;
 };
 
 struct Ctlr {
@@ -184,10 +122,10 @@
 	int	attached;
 
 	/* registers */
-	Vconfig	*cfg;
-	Vnetcfg *dev;
-	u8int	*isr;
-	u8int	*notify;
+	Vio	cfg;
+	Vio 	dev;
+	Vio	isr;
+	Vio	notify;
 	u32int	notifyoffmult;
 
 	uvlong	port;
@@ -222,7 +160,7 @@
 	if(q->used->flags & Unonotify)
 		return;
 	q->nnote++;
-	*((u16int*)q->notify) = x;
+	vout16(&q->notify, 0, x);
 }
 
 static void
@@ -445,7 +383,7 @@
 
 	edev = arg;
 	ctlr = edev->ctlr;
-	if(*ctlr->isr & 1){
+	if(vin8(&ctlr->isr, 0) & 1){
 		for(i = 0; i < ctlr->nqueue; i++){
 			q = &ctlr->queue[i];
 			if(vhasroom(q)){
@@ -473,12 +411,12 @@
 
 	/* enable the queues */
 	for(i = 0; i < ctlr->nqueue; i++){
-		ctlr->cfg->queuesel = i;
-		ctlr->cfg->queueenable = 1;
+		vout16(&ctlr->cfg, Vconf_queuesel, i);
+		vout16(&ctlr->cfg, Vconf_queueenable, 1);
 	}
 
 	/* driver is ready */
-	ctlr->cfg->status |= Sdriverok;
+	vout8(&ctlr->cfg, Vconf_status, vin8(&ctlr->cfg, Vconf_status) | Sdriverok);
 
 	iunlock(ctlr);
 
@@ -502,7 +440,7 @@
 	edev = a;
 	ctlr = edev->ctlr;
 	s = seprint(s, e, "devfeat %32.32lub %32.32lub\n", ctlr->feat[1], ctlr->feat[0]);
-	s = seprint(s, e, "devstatus %8.8ub\n", ctlr->cfg->status);
+	s = seprint(s, e, "devstatus %8.8ub\n", vin8(&ctlr->cfg, Vconf_status));
 	for(i = 0; i < ctlr->nqueue; i++){
 		q = &ctlr->queue[i];
 		s = seprint(s, e,
@@ -518,7 +456,7 @@
 	Ctlr *ctlr = edev->ctlr;
 
 	coherence();
-	ctlr->cfg->status = 0;
+	vout8(&ctlr->cfg, Vconf_status, 0);
 	coherence();
 
 	pciclrbme(ctlr->pcidev);
@@ -605,8 +543,7 @@
 	/* skip invalid or non memory bars */
 	bar = pcicfgr8(p, off+4);
 	if(bar < 0 || bar >= nelem(p->mem) 
-	|| p->mem[bar].size == 0
-	|| (p->mem[bar].bar & 3) != 0)
+	|| p->mem[bar].size == 0)
 		return 1;
 
 	return 0;
@@ -618,34 +555,14 @@
 	return pcienumcaps(p, matchvirtiocfgcap, typ);
 }
 
-static void*
-virtiomapregs(Pcidev *p, int cap, int size)
-{
-	int bar, len;
-	uvlong addr;
-
-	if(cap < 0)
-		return nil;
-	bar = pcicfgr8(p, cap+4) % nelem(p->mem);
-	addr = pcicfgr32(p, cap+8);
-	len = pcicfgr32(p, cap+12);
-	if(size <= 0)
-		size = len;
-	else if(len < size)
-		return nil;
-	if(addr+len > p->mem[bar].size)
-		return nil;
-	addr += p->mem[bar].bar & ~0xFULL;
-	return vmap(addr, size);
-}
-
 static Ctlr*
 pciprobe(void)
 {
 	Ctlr *c, *h, *t;
 	Pcidev *p;
-	Vconfig *cfg;
-	int bar, cap, n, i;
+	Vio cfg;
+	int bar,cap, n, i;
+	uvlong mask;
 
 	h = t = nil;
 
@@ -657,8 +574,7 @@
 		if((cap = virtiocap(p, 1)) < 0)
 			continue;
 		bar = pcicfgr8(p, cap+4) % nelem(p->mem);
-		cfg = virtiomapregs(p, cap, sizeof(Vconfig));
-		if(cfg == nil)
+		if(virtiomapregs(p, cap, Vconf_sz, &cfg) == nil)
 			continue;
 		if((c = mallocz(sizeof(Ctlr), 1)) == nil){
 			print("ethervirtio: no memory for Ctlr\n");
@@ -666,46 +582,44 @@
 		}
 		c->cfg = cfg;
 		c->pcidev = p;
-		c->port = p->mem[bar].bar & ~0xFULL;
+		mask = p->mem[bar].bar&1?~0x3ULL:~0xFULL;
+		c->port = p->mem[bar].bar & mask;
 
 		pcienable(p);
-		c->dev = virtiomapregs(p, virtiocap(p, 4), sizeof(Vnetcfg));
-		if(c->dev == nil)
+		if(virtiomapregs(p, virtiocap(p, 4), Vnet_sz, &c->dev) == nil)
 			goto Baddev;
-		c->isr = virtiomapregs(p, virtiocap(p, 3), 0);
-		if(c->isr == nil)
+		if(virtiomapregs(p, virtiocap(p, 3), 0, &c->isr) == nil)
 			goto Baddev;
 		cap = virtiocap(p, 2);
-		c->notify = virtiomapregs(p, cap, 0);
-		if(c->notify == nil)
+		if(virtiomapregs(p, cap, 0, &c->notify) == nil)
 			goto Baddev;
 		c->notifyoffmult = pcicfgr32(p, cap+16);
 
 		/* device reset */
 		coherence();
-		cfg->status = 0;
-		while(cfg->status != 0)
+		vout8(&cfg, Vconf_status, 0);
+		while(vin8(&cfg, Vconf_status) != 0)
 			delay(1);
-		cfg->status = Sacknowledge|Sdriver;
+		vout8(&cfg, Vconf_status, Sacknowledge|Sdriver);
 
 		/* negotiate feature bits */
-		cfg->devfeatsel = 1;
-		c->feat[1] = cfg->devfeat;
+		vout32(&cfg, Vconf_devfeatsel, 1);
+		c->feat[1] = vin32(&cfg, Vconf_devfeat);
 
-		cfg->devfeatsel = 0;
-		c->feat[0] = cfg->devfeat;
+		vout32(&cfg, Vconf_devfeatsel, 0);
+		c->feat[0] = vin32(&cfg, Vconf_devfeat);
 
-		cfg->drvfeatsel = 1;
-		cfg->drvfeat = c->feat[1] & Fversion1;
+		vout32(&cfg, Vconf_drvfeatsel, 1);
+		vout32(&cfg, Vconf_drvfeat, c->feat[1] & Fversion1);
 
-		cfg->drvfeatsel = 0;
-		cfg->drvfeat = c->feat[0] & (Fmac|Fctrlvq|Fctrlrx);
+		vout32(&cfg, Vconf_drvfeatsel, 0);
+		vout32(&cfg, Vconf_drvfeat, c->feat[0] & (Fmac|Fctrlvq|Fctrlrx));
 
-		cfg->status |= Sfeaturesok;
+		vout8(&cfg, Vconf_status, vin8(&cfg, Vconf_status) | Sfeaturesok);
 
 		for(i=0; i<nelem(c->queue); i++){
-			cfg->queuesel = i;
-			n = cfg->queuesize;
+			vout16(&cfg, Vconf_queuesel, i);
+			n = vin16(&cfg, Vconf_queuesize);
 			if(n == 0 || (n & (n-1)) != 0){
 				if(i < 2)
 					print("ethervirtio: queue %d has invalid size %d\n", i, n);
@@ -713,11 +627,15 @@
 			}
 			if(initqueue(&c->queue[i], n) < 0)
 				break;
-			c->queue[i].notify = c->notify + c->notifyoffmult * cfg->queuenotifyoff;
+			c->queue[i].notify = c->notify;
+			if(c->queue[i].notify.type == Vio_port)
+				c->queue[i].notify.port+= c->notifyoffmult * vin16(&cfg, Vconf_queuenotifyoff);
+			else
+				c->queue[i].notify.mem+= c->notifyoffmult * vin16(&cfg, Vconf_queuenotifyoff);
 			coherence();
-			cfg->queuedesc = PADDR(c->queue[i].desc);
-			cfg->queueavail = PADDR(c->queue[i].avail);
-			cfg->queueused = PADDR(c->queue[i].used);
+			vout64(&cfg, Vconf_queuedesc, PADDR(c->queue[i].desc));
+			vout64(&cfg, Vconf_queueavail, PADDR(c->queue[i].avail));
+			vout64(&cfg, Vconf_queueused, PADDR(c->queue[i].used));
 		}
 		if(i < 2){
 			print("ethervirtio: no queues\n");
@@ -771,10 +689,10 @@
 
 	if((ctlr->feat[0] & Fmac) != 0 && memcmp(edev->ea, zeros, Eaddrlen) == 0){
 		for(i = 0; i < Eaddrlen; i++)
-			edev->ea[i] = ((uchar*)ctlr->dev)[i];
+			edev->ea[i] = vin8(&ctlr->dev, Vnet_mac0+i);
 	} else {
-		for(i = 0; i < Eaddrlen; i++)
-			((uchar*)ctlr->dev)[i] = edev->ea[i];
+		for(i = 0; i < Eaddrlen; i++);
+			vout8(&ctlr->dev, Vnet_mac0+i, edev->ea[i]);
 	}
 
 	edev->arg = edev;
--- a/sys/src/9/port/portmkfile
+++ b/sys/src/9/port/portmkfile
@@ -82,7 +82,7 @@
 sdmmc.$O:	../port/sd.h
 sdnvme.$O:	../port/sd.h ../port/pci.h /$objtype/include/ureg.h
 sdram.$O:	../port/sd.h
-sdvirtio10.$O:	../port/sd.h ../port/pci.h /$objtype/include/ureg.h
+sdvirtio10.$O:	../port/sd.h ../port/pci.h ../port/virtio10.h /$objtype/include/ureg.h
 trap.$O:	/$objtype/include/ureg.h
 proc.$O:	/$objtype/include/ureg.h
 sysproc.$O:	/$objtype/include/ureg.h
@@ -121,3 +121,5 @@
 devether.$O ethersink.$O etheriwl.$O ethervirtio10.$O wifi.$O:	../port/netif.h ../port/etherif.h
 etheriwl.$O wifi.$O:	../port/wifi.h
 ethermii.$O devmii.$O:	../port/ethermii.h
+ethervirtio10.$O: ../port/virtio10.h
+
--- a/sys/src/9/port/sdvirtio10.c
+++ b/sys/src/9/port/sdvirtio10.c
@@ -24,14 +24,8 @@
 #include "../port/error.h"
 
 #include "../port/sd.h"
+#include "../port/virtio10.h"
 
-typedef struct Vscsidev Vscsidev;
-typedef struct Vblkdev Vblkdev;
-
-typedef struct Vconfig Vconfig;
-typedef struct Vring Vring;
-typedef struct Vdesc Vdesc;
-typedef struct Vused Vused;
 typedef struct Vqueue Vqueue;
 typedef struct Vdev Vdev;
 
@@ -68,76 +62,31 @@
 	SENSESIZE	= 96,
 };
 
-	
-struct Vscsidev
-{
-	u32int	num_queues;
-	u32int	seg_max;
-	u32int	max_sectors;
-	u32int	cmd_per_lun;
-	u32int	event_info_size;
-	u32int	sense_size;
-	u32int	cdb_size;
-	u16int	max_channel;
-	u16int	max_target;
-	u32int	max_lun;
+enum {
+	Vscsi_num_queues = 0,
+	Vscsi_seg_max = 4,
+	Vscsi_max_sectors = 8,
+	Vscsi_cmd_per_lun = 12,
+	Vscsi_event_info_size = 16,
+	Vscsi_sense_size = 20,
+	Vscsi_cdb_size = 24,
+	Vscsi_max_channel = 28,
+	Vscsi_max_target = 30,
+	Vscsi_max_lun = 32,
+	Vscsi_sz = 36
 };
 
-struct Vblkdev
-{
-	u64int	capacity;
+enum {
+	Vblk_capacity = 0,
+	Vblk_sz = 8
 };
 
-struct Vconfig {
-	u32int	devfeatsel;
-	u32int	devfeat;
-	u32int	drvfeatsel;
-	u32int	drvfeat;
-
-	u16int	msixcfg;
-	u16int	nqueues;
-
-	u8int	status;
-	u8int	cfggen;
-	u16int	queuesel;
-
-	u16int	queuesize;
-	u16int	queuemsixvect;
-
-	u16int	queueenable;
-	u16int	queuenotifyoff;
-
-	u64int	queuedesc;
-	u64int	queueavail;
-	u64int	queueused;
-};
-
-struct Vring
-{
-	u16int	flags;
-	u16int	idx;
-};
-
-struct Vdesc
-{
-	u64int	addr;
-	u32int	len;
-	u16int	flags;
-	u16int	next;
-};
-
-struct Vused
-{
-	u32int	id;
-	u32int	len;
-};
-
 struct Vqueue
 {
 	Lock;
 
 	Vdev	*dev;
-	void	*notify;
+	Vio	notify;
 	int	idx;
 
 	int	size;
@@ -171,12 +120,12 @@
 	int	nqueue;
 	Vqueue	*queue[16];
 
-	void	*dev;	/* device specific config (for scsi) */
+	Vio	dev;	/* device specific config (for scsi) */
 
 	/* registers */
-	Vconfig	*cfg;
-	u8int	*isr;
-	u8int	*notify;
+	Vio	cfg;
+	Vio	isr;
+	Vio	notify;
 	u32int	notifyoffmult;
 
 	Vdev	*next;
@@ -240,11 +189,10 @@
 	if(cap != 9 || pcicfgr8(p, off+3) != typ)
 		return 1;
 
-	/* skip invalid or non memory bars */
+	/* skip invalid bars */
 	bar = pcicfgr8(p, off+4);
 	if(bar < 0 || bar >= nelem(p->mem) 
-	|| p->mem[bar].size == 0
-	|| (p->mem[bar].bar & 3) != 0)
+	|| p->mem[bar].size == 0)
 		return 1;
 
 	return 0;
@@ -256,36 +204,16 @@
 	return pcienumcaps(p, matchvirtiocfgcap, typ);
 }
 
-static void*
-virtiomapregs(Pcidev *p, int cap, int size)
-{
-	int bar, len;
-	uvlong addr;
-
-	if(cap < 0)
-		return nil;
-	bar = pcicfgr8(p, cap+4) % nelem(p->mem);
-	addr = pcicfgr32(p, cap+8);
-	len = pcicfgr32(p, cap+12);
-	if(size <= 0)
-		size = len;
-	else if(len < size)
-		return nil;
-	if(addr+len > p->mem[bar].size)
-		return nil;
-	addr += p->mem[bar].bar & ~0xFULL;
-	return vmap(addr, size);
-}
-
 static Vdev*
 viopnpdevs(int typ)
 {
 	Vdev *vd, *h, *t;
-	Vconfig *cfg;
+	Vio cfg;
 	Vqueue *q;
 	Pcidev *p;
 	int cap, bar;
 	int n, i;
+	uvlong mask;
 
 	h = t = nil;
 	for(p = nil; p = pcimatch(p, 0x1AF4, 0x1040+typ);){
@@ -294,21 +222,20 @@
 		if((cap = virtiocap(p, 1)) < 0)
 			continue;
 		bar = pcicfgr8(p, cap+4) % nelem(p->mem);
-		cfg = virtiomapregs(p, cap, sizeof(Vconfig));
-		if(cfg == nil)
+		if(virtiomapregs(p, cap, Vconf_sz, &cfg) == 0)
 			continue;
 		if((vd = malloc(sizeof(*vd))) == nil){
 			print("virtio: no memory for Vdev\n");
 			break;
 		}
-		vd->port = p->mem[bar].bar & ~0xFULL;
+		mask = p->mem[bar].bar&1?~0x3ULL:~0xFULL;
+		vd->port = p->mem[bar].bar & mask;
 		vd->typ = typ;
 		vd->pci = p;
 		vd->cfg = cfg;
 		pcienable(p);
 
-		vd->isr = virtiomapregs(p, virtiocap(p, 3), 0);
-		if(vd->isr == nil){
+		if(virtiomapregs(p, virtiocap(p, 3), 0, &vd->isr) == nil){
 Baddev:
 			pcidisable(p);
 			/* TODO: vunmap */
@@ -316,43 +243,52 @@
 			continue;
 		}
 		cap = virtiocap(p, 2);
-		vd->notify = virtiomapregs(p, cap, 0);
-		if(vd->notify == nil)
+		if(virtiomapregs(p, cap, 0, &vd->notify) == nil)
 			goto Baddev;
 		vd->notifyoffmult = pcicfgr32(p, cap+16);
 
 		/* reset */
-		cfg->status = 0;
-		while(cfg->status != 0)
+		coherence();
+		vout8(&cfg, Vconf_status, 0);
+		while(vin8(&cfg, Vconf_status) != 0)
 			delay(1);
-		cfg->status = Acknowledge|Driver;
+		vout8(&cfg, Vconf_status, Acknowledge|Driver);
 
 		/* negotiate feature bits */
-		cfg->devfeatsel = 1;
-		vd->feat[1] = cfg->devfeat;
-		cfg->devfeatsel = 0;
-		vd->feat[0] = cfg->devfeat;
-		cfg->drvfeatsel = 1;
-		cfg->drvfeat = vd->feat[1] & 1;
-		cfg->drvfeatsel = 0;
-		cfg->drvfeat = 0;
-		cfg->status |= FeaturesOk;
+		vout32(&cfg, Vconf_devfeatsel, 1);
+		vd->feat[1] = vin32(&cfg, Vconf_devfeat);
 
+		vout32(&cfg, Vconf_devfeatsel, 0);
+		vd->feat[0] = vin32(&cfg, Vconf_devfeat);
+
+		vout32(&cfg, Vconf_drvfeatsel, 1);
+		vout32(&cfg, Vconf_drvfeat, vd->feat[1] & 1);
+
+		vout32(&cfg, Vconf_drvfeatsel, 0);
+		vout32(&cfg, Vconf_drvfeat, 0);
+
+		vout8(&cfg, Vconf_status, vin8(&cfg, Vconf_status) | FeaturesOk);
+
+
 		for(i=0; i<nelem(vd->queue); i++){
-			cfg->queuesel = i;
-			n = cfg->queuesize;
+			vout16(&cfg, Vconf_queuesel, i);
+			n = vin16(&cfg, Vconf_queuesize);
 			if(n == 0 || (n & (n-1)) != 0)
 				break;
 			if((q = mkvqueue(n)) == nil)
 				break;
-			q->notify = vd->notify + vd->notifyoffmult * cfg->queuenotifyoff;
+			q->notify = vd->notify;
+			if(q->notify.type == Vio_port)
+				q->notify.port += vd->notifyoffmult * vin16(&cfg, Vconf_queuenotifyoff);
+			else
+				q->notify.mem += vd->notifyoffmult * vin16(&cfg, Vconf_queuenotifyoff);
 			q->dev = vd;
 			q->idx = i;
 			vd->queue[i] = q;
 			coherence();
-			cfg->queuedesc = PADDR(q->desc);
-			cfg->queueavail = PADDR(q->avail);
-			cfg->queueused = PADDR(q->used);
+			vout64(&cfg, Vconf_queuedesc, PADDR(q->desc));
+			vout64(&cfg, Vconf_queueavail, PADDR(q->avail));
+			vout64(&cfg, Vconf_queueused, PADDR(q->used));
 		}
 		vd->nqueue = i;
 	
@@ -406,7 +342,7 @@
 {
 	Vdev *vd = arg;
 
-	if(vd->isr[0] & 1)
+	if(vin8(&vd->isr, 0) & 1)
 		vqinterrupt(vd->queue[vd->typ == TypSCSI ? 2 : 0]);
 }
 
@@ -429,7 +365,7 @@
 	q->avail->idx++;
 	iunlock(q);
 	if((q->used->flags & 1) == 0)
-		*((u16int*)q->notify) = q->idx;
+		vout16(&q->notify, 0, q->idx);
 	while(!rock.done){
 		while(waserror())
 			;
@@ -515,11 +451,9 @@
 	Vdesc *d;
 	Vdev *vd;
 	SDunit *u;
-	Vscsidev *scsi;
 
 	u = r->unit;
 	vd = u->dev->ctlr;
-	scsi = vd->dev;
 
 	memset(resp, 0, sizeof(resp));
 	memset(req, 0, sizeof(req));
@@ -547,7 +481,7 @@
 
 	d = &q->desc[free]; free = d->next;
 	d->addr = PADDR(req);
-	d->len = 8+8+3+scsi->cdb_size;
+	d->len = 8+8+3+vin32(&vd->dev, Vscsi_cdb_size);
 	d->flags = Next;
 
 	if(r->write && r->dlen > 0){
@@ -559,7 +493,7 @@
 
 	d = &q->desc[free]; free = d->next;
 	d->addr = PADDR(resp);
-	d->len = 4+4+2+2+scsi->sense_size;
+	d->len = 4+4+2+2+vin32(&vd->dev, Vscsi_sense_size);
 	d->flags = Write;
 
 	if(!r->write && r->dlen > 0){
@@ -656,7 +590,6 @@
 vioonline(SDunit *u)
 {
 	Vdev *vd;
-	Vblkdev *blk;
 	uvlong cap;
 
 	vd = u->dev->ctlr;
@@ -663,8 +596,7 @@
 	if(vd->typ == TypSCSI)
 		return scsionline(u);
 
-	blk = vd->dev;
-	cap = blk->capacity;
+	cap = vin64(&vd->dev, Vblk_capacity);
 	if(u->sectors != cap){
 		u->sectors = cap;
 		u->secsize = 512;
@@ -701,10 +633,10 @@
 	coherence();
 
 	for(i = 0; i < vd->nqueue; i++){
-		vd->cfg->queuesel = i;
-		vd->cfg->queueenable = 1;
+		vout16(&vd->cfg, Vconf_queuesel, i);
+		vout16(&vd->cfg, Vconf_queueenable, 1);
 	}
-	vd->cfg->status |= DriverOk;
+	vout8(&vd->cfg, Vconf_status, vin8(&vd->cfg, Vconf_status) | DriverOk);
 
 	return 1;
 }
@@ -736,7 +668,7 @@
 		if(vd->nqueue == 0)
 			continue;
 
-		if((vd->dev = virtiomapregs(vd->pci, virtiocap(vd->pci, 4), sizeof(Vblkdev))) == nil)
+		if(virtiomapregs(vd->pci, virtiocap(vd->pci, 4), Vblk_sz, &vd->dev) == nil)
 			break;
 		if((s = malloc(sizeof(*s))) == nil)
 			break;
@@ -753,24 +685,21 @@
 
 	id = '0';
 	for(vd = viopnpdevs(TypSCSI); vd; vd = vd->next){
-		Vscsidev *scsi;
-
 		if(vd->nqueue < 3)
 			continue;
 
-		if((scsi = virtiomapregs(vd->pci, virtiocap(vd->pci, 4), sizeof(Vscsidev))) == nil)
+		if(virtiomapregs(vd->pci, virtiocap(vd->pci, 4), Vscsi_sz, &vd->dev) == nil)
 			break;
-		if(scsi->max_target == 0){
-			vunmap(scsi, sizeof(Vscsidev));
+		if(vin16(&vd->dev, Vscsi_max_target) == 0){
+			virtiounmap(&vd->dev, Vscsi_sz);
 			continue;
 		}
-		if((scsi->cdb_size > CDBSIZE) || (scsi->sense_size > SENSESIZE)){
+		if((vin32(&vd->dev, Vscsi_cdb_size) > CDBSIZE) || (vin32(&vd->dev, Vscsi_sense_size) > SENSESIZE)){
 			print("sdvirtio: cdb %ud or sense size %ud too big\n",
-				scsi->cdb_size, scsi->sense_size);
-			vunmap(scsi, sizeof(Vscsidev));
+				vin32(&vd->dev, Vscsi_cdb_size), vin32(&vd->dev, Vscsi_sense_size));
+			virtiounmap(&vd->dev, Vscsi_sz);
 			continue;
 		}
-		vd->dev = scsi;
 
 		if((s = malloc(sizeof(*s))) == nil)
 			break;
@@ -777,7 +706,7 @@
 		s->ctlr = vd;
 		s->idno = id++;
 		s->ifc = &sdvirtio10ifc;
-		s->nunit = scsi->max_target;
+		s->nunit = vin16(&vd->dev, Vscsi_max_target);
 
 		if(h)
 			t->next = s;
--- /dev/null
+++ b/sys/src/9/port/virtio10.h
@@ -1,0 +1,80 @@
+enum {
+	/* §2.1 Device Status Field */
+	Sacknowledge = 1,
+	Sdriver = 2,
+	Sdriverok = 4,
+	Sfeaturesok = 8,
+	Sfailed = 128,
+
+	/* feat[1] bits */
+	Fversion1 = 1<<(32-32),
+
+	Vconf_devfeatsel = 0,
+	Vconf_devfeat = 4,
+	Vconf_drvfeatsel = 8,
+	Vconf_drvfeat = 12,
+	Vconf_msixcfg = 16,
+	Vconf_nqueues = 18,
+	Vconf_status = 20,
+	Vconf_cfggen = 21,
+	Vconf_queuesel = 22,
+	Vconf_queuesize = 24,
+	Vconf_queuemsixvect = 26,
+	Vconf_queueenable = 28,
+	Vconf_queuenotifyoff = 30,
+	Vconf_queuedesc = 32,
+	Vconf_queueavail = 40,
+	Vconf_queueused = 48,
+	Vconf_sz = 56,
+
+	Vio_port = 0,
+	Vio_mem,
+};
+
+typedef struct Vio Vio;
+struct Vio
+{
+	int type;
+	union {
+		int port;
+		uchar *mem;
+	};
+};
+
+typedef struct Vring Vring;
+struct Vring
+{
+	u16int	flags;
+	u16int	idx;
+};
+
+typedef struct Vdesc Vdesc;
+struct Vdesc
+{
+	u64int	addr;
+	u32int	len;
+	u16int	flags;
+	u16int	next;
+};
+
+typedef struct Vused Vused;
+struct Vused
+{
+	u32int	id;
+	u32int	len;
+};
+
+/* machine dependent functions
+for most archs this is provided by port/virtio10mem.c which does pci mem bar only
+except for x86 which has to deal with io space and mem space in pc/virtio10pc.c 
+*/
+u8int vin8(Vio *, int);
+u16int vin16(Vio *, int);
+u32int vin32(Vio *, int);
+u64int vin64(Vio *, int);
+void vout8(Vio *, int, u8int);
+void vout16(Vio *, int, u16int);
+void vout32(Vio *, int, u32int);
+void vout64(Vio *, int, u64int);
+void virtiounmap(Vio *, usize);
+Vio* virtiomapregs(Pcidev *, int, int, Vio *);
--- /dev/null
+++ b/sys/src/9/port/virtio10mem.c
@@ -1,0 +1,102 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "../port/pci.h"
+#include "../port/error.h"
+#include "../port/virtio10.h"
+
+u8int
+vin8(Vio *r, int p)
+{
+	assert(r->type == Vio_mem);
+	return *(u8int *)(r->mem+p);
+}
+
+
+u16int
+vin16(Vio *r, int p)
+{
+	assert(r->type == Vio_mem);
+	return *(u16int *)(r->mem+p);
+}
+
+u32int
+vin32(Vio *r, int p)
+{
+	assert(r->type == Vio_mem);
+	return *(u32int *)(r->mem+p);
+}
+
+u64int
+vin64(Vio *r, int p)
+{
+	assert(r->type == Vio_mem);
+	return *(u64int*)(r->mem+p);
+	
+}
+
+void
+vout8(Vio *r, int p, u8int v)
+{
+	assert(r->type == Vio_mem);
+	*(uchar *)(r->mem+p) = v;
+}
+
+void
+vout16(Vio *r, int p, u16int v)
+{
+	assert(r->type == Vio_mem);
+	*(u16int *)(r->mem+p) = v;
+}
+
+void
+vout32(Vio *r, int p, u32int v)
+{
+	assert(r->type == Vio_mem);
+	*(u32int *)(r->mem+p) = v;
+}
+
+void
+vout64(Vio *r, int p, u64int v)
+{
+	assert(r->type == Vio_mem);
+	*(u64int *)(r->mem+p) = v;
+}
+
+void
+virtiounmap(Vio *r, usize sz)
+{
+	assert(r->type == Vio_mem);
+	vunmap(r->mem, sz);	
+}
+
+Vio*
+virtiomapregs(Pcidev *p, int cap, int size, Vio *v)
+{
+	int bar, len;
+	uvlong addr;
+
+	if(cap < 0)
+		return nil;
+	bar = pcicfgr8(p, cap+4) % nelem(p->mem);
+	addr = pcicfgr32(p, cap+8);
+	len = pcicfgr32(p, cap+12);
+	if(size <= 0)
+		size = len;
+	else if(len < size)
+		return nil;
+	if(p->mem[bar].bar & 1 ||  addr+len > p->mem[bar].size)
+		return nil;
+
+	addr += p->mem[bar].bar & ~0xFULL;
+	v->type = Vio_mem;
+	v->mem = vmap(addr, size);
+	if(v->mem == nil)
+		return nil;
+
+	return v;	
+}
--