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;
+}
--
⑨