#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "../port/netif.h"#include "etherif.h" /* * virtio ethernet driver * http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html */ typedef struct Vring Vring;typedef struct Vdesc Vdesc;typedef struct Vused Vused;typedef struct Vheader Vheader;typedef struct Vqueue Vqueue;typedef struct Ctlr Ctlr; enum { /* §2.1 Device Status Field */ Sacknowledge = 1, Sdriver = 2, Sdriverok = 4, Sfeatureok = 8, Sfailed = 128, /* §4.1.4.8 Legacy Interfaces: A Note on PCI Device Layout */ Qdevfeat = 0, Qdrvfeat = 4, Qaddr = 8, Qsize = 12, Qselect = 14, Qnotify = 16, Qstatus = 18, Qisr = 19, Qmac = 20, Qnetstatus = 26, /* flags in Qnetstatus */ Nlinkup = (1<<0), Nannounce = (1<<1), /* feature bits */ Fmac = (1<<5), Fstatus = (1<<16), Fctrlvq = (1<<17), Fctrlrx = (1<<18), /* vring used flags */ Unonotify = 1, /* vring avail flags */ Rnointerrupt = 1, /* descriptor flags */ Dnext = 1, Dwrite = 2, Dindirect = 4, /* struct sizes */ VringSize = 4, VdescSize = 16, VusedSize = 8, VheaderSize = 10, /* §4.1.5.1.4.1 says pages are 4096 bytes * for the purposes of the driver. */ VBY2PG = 4096,#define VPGROUND(s) ROUND(s, VBY2PG) Vrxq = 0, Vtxq = 1, Vctlq = 2, /* class/cmd for Vctlq */ CtrlRx = 0x00, CmdPromisc = 0x00, CmdAllmulti = 0x01, CtrlMac = 0x01, CmdMacTableSet = 0x00, CtrlVlan= 0x02, CmdVlanAdd = 0x00, CmdVlanDel = 0x01,}; 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; u8int segtype; u16int hlen; u16int seglen; u16int csumstart; u16int csumend;}; /* §2.4 Virtqueues */struct Vqueue{ Rendez; uint qsize; uint qmask; Vdesc *desc; Vring *avail; u16int *availent; u16int *availevent; Vring *used; Vused *usedent; u16int *usedevent; u16int lastused; uint nintr; uint nnote;}; struct Ctlr { Lock; QLock ctllock; int attached; int port; Pcidev *pcidev; Ctlr *next; int active; int id; int typ; ulong feat; int nqueue; /* virtioether has 3 queues: rx, tx and ctl */ Vqueue queue[3];}; static Ctlr *ctlrhead; static intvhasroom(void *v){ Vqueue *q = v; return q->lastused != q->used->idx;} static voidvqnotify(Ctlr *ctlr, int x){ Vqueue *q; coherence(); q = &ctlr->queue[x]; if(q->used->flags & Unonotify) return; q->nnote++; outs(ctlr->port+Qnotify, x);} static voidtxproc(void *v){ Vheader *header; Block **blocks; Ether *edev; Ctlr *ctlr; Vqueue *q; Vused *u; Block *b; int i, j; edev = v; ctlr = edev->ctlr; q = &ctlr->queue[Vtxq]; header = smalloc(VheaderSize); blocks = smalloc(sizeof(Block*) * (q->qsize/2)); for(i = 0; i < q->qsize/2; i++){ j = i << 1; q->desc[j].addr = PADDR(header); q->desc[j].len = VheaderSize; q->desc[j].next = j | 1; q->desc[j].flags = Dnext; q->availent[i] = q->availent[i + q->qsize/2] = j; j |= 1; q->desc[j].next = 0; q->desc[j].flags = 0; } q->avail->flags &= ~Rnointerrupt; while(waserror()) ; while((b = qbread(edev->oq, 1000000)) != nil){ for(;;){ /* retire completed packets */ while((i = q->lastused) != q->used->idx){ u = &q->usedent[i & q->qmask]; i = (u->id & q->qmask) >> 1; if(blocks[i] == nil) break; freeb(blocks[i]); blocks[i] = nil; q->lastused++; } /* have free slot? */ i = q->avail->idx & (q->qmask >> 1); if(blocks[i] == nil) break; /* ring full, wait and retry */ if(!vhasroom(q)) sleep(q, vhasroom, q); } /* slot is free, fill in descriptor */ blocks[i] = b; j = (i << 1) | 1; q->desc[j].addr = PADDR(b->rp); q->desc[j].len = BLEN(b); coherence(); q->avail->idx++; vqnotify(ctlr, Vtxq); } pexit("ether out queue closed", 1);} static voidrxproc(void *v){ Vheader *header; Block **blocks; Ether *edev; Ctlr *ctlr; Vqueue *q; Vused *u; Block *b; int i, j; edev = v; ctlr = edev->ctlr; q = &ctlr->queue[Vrxq]; header = smalloc(VheaderSize); blocks = smalloc(sizeof(Block*) * (q->qsize/2)); for(i = 0; i < q->qsize/2; i++){ j = i << 1; q->desc[j].addr = PADDR(header); q->desc[j].len = VheaderSize; q->desc[j].next = j | 1; q->desc[j].flags = Dwrite|Dnext; q->availent[i] = q->availent[i + q->qsize/2] = j; j |= 1; q->desc[j].next = 0; q->desc[j].flags = Dwrite; } q->avail->flags &= ~Rnointerrupt; while(waserror()) ; for(;;){ /* replenish receive ring */ do { i = q->avail->idx & (q->qmask >> 1); if(blocks[i] != nil) break; if((b = iallocb(ETHERMAXTU)) == nil) break; blocks[i] = b; j = (i << 1) | 1; q->desc[j].addr = PADDR(b->rp); q->desc[j].len = BALLOC(b); coherence(); q->avail->idx++; } while(q->avail->idx != q->used->idx); vqnotify(ctlr, Vrxq); /* wait for any packets to complete */ if(!vhasroom(q)) sleep(q, vhasroom, q); /* retire completed packets */ while((i = q->lastused) != q->used->idx) { u = &q->usedent[i & q->qmask]; i = (u->id & q->qmask) >> 1; if((b = blocks[i]) == nil) break; blocks[i] = nil; b->wp = b->rp + u->len; etheriq(edev, b, 1); q->lastused++; } }} static intvctlcmd(Ether *edev, uchar class, uchar cmd, uchar *data, int ndata){ uchar hdr[2], ack[1]; Ctlr *ctlr; Vqueue *q; Vdesc *d; int i; ctlr = edev->ctlr; q = &ctlr->queue[Vctlq]; if(q->qsize < 3) return -1; qlock(&ctlr->ctllock); while(waserror()) ; ack[0] = 0x55; hdr[0] = class; hdr[1] = cmd; d = &q->desc[0]; d->addr = PADDR(hdr); d->len = sizeof(hdr); d->next = 1; d->flags = Dnext; d++; d->addr = PADDR(data); d->len = ndata; d->next = 2; d->flags = Dnext; d++; d->addr = PADDR(ack); d->len = sizeof(ack); d->next = 0; d->flags = Dwrite; i = q->avail->idx & q->qmask; q->availent[i] = 0; coherence(); q->avail->flags &= ~Rnointerrupt; q->avail->idx++; vqnotify(ctlr, Vctlq); while(!vhasroom(q)) sleep(q, vhasroom, q); q->lastused = q->used->idx; q->avail->flags |= Rnointerrupt; qunlock(&ctlr->ctllock); poperror(); if(ack[0] != 0) print("#l%d: vctlcmd: %ux.%ux -> %ux\n", edev->ctlrno, class, cmd, ack[0]); return ack[0];} static voidinterrupt(Ureg*, void* arg){ Ether *edev; Ctlr *ctlr; Vqueue *q; int i; edev = arg; ctlr = edev->ctlr; if(inb(ctlr->port+Qisr) & 1){ for(i = 0; i < ctlr->nqueue; i++){ q = &ctlr->queue[i]; if(vhasroom(q)){ q->nintr++; wakeup(q); } } }} static voidattach(Ether* edev){ char name[KNAMELEN]; Ctlr* ctlr; ctlr = edev->ctlr; lock(ctlr); if(!ctlr->attached){ ctlr->attached = 1; /* ready to go */ outb(ctlr->port+Qstatus, inb(ctlr->port+Qstatus) | Sdriverok); /* start kprocs */ snprint(name, sizeof name, "#l%drx", edev->ctlrno); kproc(name, rxproc, edev); snprint(name, sizeof name, "#l%dtx", edev->ctlrno); kproc(name, txproc, edev); } unlock(ctlr);} static longifstat(Ether *edev, void *a, long n, ulong offset){ int i, l; char *p; Ctlr *ctlr; Vqueue *q; ctlr = edev->ctlr; p = smalloc(READSTR); l = snprint(p, READSTR, "devfeat %32.32lub\n", ctlr->feat); l += snprint(p+l, READSTR-l, "drvfeat %32.32lub\n", inl(ctlr->port+Qdrvfeat)); l += snprint(p+l, READSTR-l, "devstatus %8.8ub\n", inb(ctlr->port+Qstatus)); if(ctlr->feat & Fstatus) l += snprint(p+l, READSTR-l, "netstatus %8.8ub\n", inb(ctlr->port+Qnetstatus)); for(i = 0; i < ctlr->nqueue; i++){ q = &ctlr->queue[i]; l += snprint(p+l, READSTR-l, "vq%d %#p size %d avail->idx %d used->idx %d lastused %hud nintr %ud nnote %ud\n", i, q, q->qsize, q->avail->idx, q->used->idx, q->lastused, q->nintr, q->nnote); } n = readstr(offset, a, n, p); free(p); return n;} static voidshutdown(Ether* edev){ Ctlr *ctlr = edev->ctlr; outb(ctlr->port+Qstatus, 0);} static voidpromiscuous(void *arg, int on){ Ether *edev = arg; uchar b[1]; b[0] = on != 0; vctlcmd(edev, CtrlRx, CmdPromisc, b, sizeof(b));} static voidmulticast(void *arg, uchar*, int){ Ether *edev = arg; uchar b[1]; b[0] = edev->nmaddr > 0; vctlcmd(edev, CtrlRx, CmdAllmulti, b, sizeof(b));} /* §2.4.2 Legacy Interfaces: A Note on Virtqueue Layout */static ulongqueuesize(ulong size){ return VPGROUND(VdescSize*size + sizeof(u16int)*(3+size)) + VPGROUND(sizeof(u16int)*3 + VusedSize*size);Lines 1–500 of 672. Load more
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "../port/netif.h" #include "etherif.h" /* * virtio ethernet driver * http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html */ typedef struct Vring Vring; typedef struct Vdesc Vdesc; typedef struct Vused Vused; typedef struct Vheader Vheader; typedef struct Vqueue Vqueue; typedef struct Ctlr Ctlr; enum { /* §2.1 Device Status Field */ Sacknowledge = 1, Sdriver = 2, Sdriverok = 4, Sfeatureok = 8, Sfailed = 128, /* §4.1.4.8 Legacy Interfaces: A Note on PCI Device Layout */ Qdevfeat = 0, Qdrvfeat = 4, Qaddr = 8, Qsize = 12, Qselect = 14, Qnotify = 16, Qstatus = 18, Qisr = 19, Qmac = 20, Qnetstatus = 26, /* flags in Qnetstatus */ Nlinkup = (1<<0), Nannounce = (1<<1), /* feature bits */ Fmac = (1<<5), Fstatus = (1<<16), Fctrlvq = (1<<17), Fctrlrx = (1<<18), /* vring used flags */ Unonotify = 1, /* vring avail flags */ Rnointerrupt = 1, /* descriptor flags */ Dnext = 1, Dwrite = 2, Dindirect = 4, /* struct sizes */ VringSize = 4, VdescSize = 16, VusedSize = 8, VheaderSize = 10, /* §4.1.5.1.4.1 says pages are 4096 bytes * for the purposes of the driver. */ VBY2PG = 4096, #define VPGROUND(s) ROUND(s, VBY2PG) Vrxq = 0, Vtxq = 1, Vctlq = 2, /* class/cmd for Vctlq */ CtrlRx = 0x00, CmdPromisc = 0x00, CmdAllmulti = 0x01, CtrlMac = 0x01, CmdMacTableSet = 0x00, CtrlVlan= 0x02, CmdVlanAdd = 0x00, CmdVlanDel = 0x01, }; 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; u8int segtype; u16int hlen; u16int seglen; u16int csumstart; u16int csumend; }; /* §2.4 Virtqueues */ struct Vqueue { Rendez; uint qsize; uint qmask; Vdesc *desc; Vring *avail; u16int *availent; u16int *availevent; Vring *used; Vused *usedent; u16int *usedevent; u16int lastused; uint nintr; uint nnote; }; struct Ctlr { Lock; QLock ctllock; int attached; int port; Pcidev *pcidev; Ctlr *next; int active; int id; int typ; ulong feat; int nqueue; /* virtioether has 3 queues: rx, tx and ctl */ Vqueue queue[3]; }; static Ctlr *ctlrhead; static int vhasroom(void *v) { Vqueue *q = v; return q->lastused != q->used->idx; } static void vqnotify(Ctlr *ctlr, int x) { Vqueue *q; coherence(); q = &ctlr->queue[x]; if(q->used->flags & Unonotify) return; q->nnote++; outs(ctlr->port+Qnotify, x); } static void txproc(void *v) { Vheader *header; Block **blocks; Ether *edev; Ctlr *ctlr; Vqueue *q; Vused *u; Block *b; int i, j; edev = v; ctlr = edev->ctlr; q = &ctlr->queue[Vtxq]; header = smalloc(VheaderSize); blocks = smalloc(sizeof(Block*) * (q->qsize/2)); for(i = 0; i < q->qsize/2; i++){ j = i << 1; q->desc[j].addr = PADDR(header); q->desc[j].len = VheaderSize; q->desc[j].next = j | 1; q->desc[j].flags = Dnext; q->availent[i] = q->availent[i + q->qsize/2] = j; j |= 1; q->desc[j].next = 0; q->desc[j].flags = 0; } q->avail->flags &= ~Rnointerrupt; while(waserror()) ; while((b = qbread(edev->oq, 1000000)) != nil){ for(;;){ /* retire completed packets */ while((i = q->lastused) != q->used->idx){ u = &q->usedent[i & q->qmask]; i = (u->id & q->qmask) >> 1; if(blocks[i] == nil) break; freeb(blocks[i]); blocks[i] = nil; q->lastused++; } /* have free slot? */ i = q->avail->idx & (q->qmask >> 1); if(blocks[i] == nil) break; /* ring full, wait and retry */ if(!vhasroom(q)) sleep(q, vhasroom, q); } /* slot is free, fill in descriptor */ blocks[i] = b; j = (i << 1) | 1; q->desc[j].addr = PADDR(b->rp); q->desc[j].len = BLEN(b); coherence(); q->avail->idx++; vqnotify(ctlr, Vtxq); } pexit("ether out queue closed", 1); } static void rxproc(void *v) { Vheader *header; Block **blocks; Ether *edev; Ctlr *ctlr; Vqueue *q; Vused *u; Block *b; int i, j; edev = v; ctlr = edev->ctlr; q = &ctlr->queue[Vrxq]; header = smalloc(VheaderSize); blocks = smalloc(sizeof(Block*) * (q->qsize/2)); for(i = 0; i < q->qsize/2; i++){ j = i << 1; q->desc[j].addr = PADDR(header); q->desc[j].len = VheaderSize; q->desc[j].next = j | 1; q->desc[j].flags = Dwrite|Dnext; q->availent[i] = q->availent[i + q->qsize/2] = j; j |= 1; q->desc[j].next = 0; q->desc[j].flags = Dwrite; } q->avail->flags &= ~Rnointerrupt; while(waserror()) ; for(;;){ /* replenish receive ring */ do { i = q->avail->idx & (q->qmask >> 1); if(blocks[i] != nil) break; if((b = iallocb(ETHERMAXTU)) == nil) break; blocks[i] = b; j = (i << 1) | 1; q->desc[j].addr = PADDR(b->rp); q->desc[j].len = BALLOC(b); coherence(); q->avail->idx++; } while(q->avail->idx != q->used->idx); vqnotify(ctlr, Vrxq); /* wait for any packets to complete */ if(!vhasroom(q)) sleep(q, vhasroom, q); /* retire completed packets */ while((i = q->lastused) != q->used->idx) { u = &q->usedent[i & q->qmask]; i = (u->id & q->qmask) >> 1; if((b = blocks[i]) == nil) break; blocks[i] = nil; b->wp = b->rp + u->len; etheriq(edev, b, 1); q->lastused++; } } } static int vctlcmd(Ether *edev, uchar class, uchar cmd, uchar *data, int ndata) { uchar hdr[2], ack[1]; Ctlr *ctlr; Vqueue *q; Vdesc *d; int i; ctlr = edev->ctlr; q = &ctlr->queue[Vctlq]; if(q->qsize < 3) return -1; qlock(&ctlr->ctllock); while(waserror()) ; ack[0] = 0x55; hdr[0] = class; hdr[1] = cmd; d = &q->desc[0]; d->addr = PADDR(hdr); d->len = sizeof(hdr); d->next = 1; d->flags = Dnext; d++; d->addr = PADDR(data); d->len = ndata; d->next = 2; d->flags = Dnext; d++; d->addr = PADDR(ack); d->len = sizeof(ack); d->next = 0; d->flags = Dwrite; i = q->avail->idx & q->qmask; q->availent[i] = 0; coherence(); q->avail->flags &= ~Rnointerrupt; q->avail->idx++; vqnotify(ctlr, Vctlq); while(!vhasroom(q)) sleep(q, vhasroom, q); q->lastused = q->used->idx; q->avail->flags |= Rnointerrupt; qunlock(&ctlr->ctllock); poperror(); if(ack[0] != 0) print("#l%d: vctlcmd: %ux.%ux -> %ux\n", edev->ctlrno, class, cmd, ack[0]); return ack[0]; } static void interrupt(Ureg*, void* arg) { Ether *edev; Ctlr *ctlr; Vqueue *q; int i; edev = arg; ctlr = edev->ctlr; if(inb(ctlr->port+Qisr) & 1){ for(i = 0; i < ctlr->nqueue; i++){ q = &ctlr->queue[i]; if(vhasroom(q)){ q->nintr++; wakeup(q); } } } } static void attach(Ether* edev) { char name[KNAMELEN]; Ctlr* ctlr; ctlr = edev->ctlr; lock(ctlr); if(!ctlr->attached){ ctlr->attached = 1; /* ready to go */ outb(ctlr->port+Qstatus, inb(ctlr->port+Qstatus) | Sdriverok); /* start kprocs */ snprint(name, sizeof name, "#l%drx", edev->ctlrno); kproc(name, rxproc, edev); snprint(name, sizeof name, "#l%dtx", edev->ctlrno); kproc(name, txproc, edev); } unlock(ctlr); } static long ifstat(Ether *edev, void *a, long n, ulong offset) { int i, l; char *p; Ctlr *ctlr; Vqueue *q; ctlr = edev->ctlr; p = smalloc(READSTR); l = snprint(p, READSTR, "devfeat %32.32lub\n", ctlr->feat); l += snprint(p+l, READSTR-l, "drvfeat %32.32lub\n", inl(ctlr->port+Qdrvfeat)); l += snprint(p+l, READSTR-l, "devstatus %8.8ub\n", inb(ctlr->port+Qstatus)); if(ctlr->feat & Fstatus) l += snprint(p+l, READSTR-l, "netstatus %8.8ub\n", inb(ctlr->port+Qnetstatus)); for(i = 0; i < ctlr->nqueue; i++){ q = &ctlr->queue[i]; l += snprint(p+l, READSTR-l, "vq%d %#p size %d avail->idx %d used->idx %d lastused %hud nintr %ud nnote %ud\n", i, q, q->qsize, q->avail->idx, q->used->idx, q->lastused, q->nintr, q->nnote); } n = readstr(offset, a, n, p); free(p); return n; } static void shutdown(Ether* edev) { Ctlr *ctlr = edev->ctlr; outb(ctlr->port+Qstatus, 0); } static void promiscuous(void *arg, int on) { Ether *edev = arg; uchar b[1]; b[0] = on != 0; vctlcmd(edev, CtrlRx, CmdPromisc, b, sizeof(b)); } static void multicast(void *arg, uchar*, int) { Ether *edev = arg; uchar b[1]; b[0] = edev->nmaddr > 0; vctlcmd(edev, CtrlRx, CmdAllmulti, b, sizeof(b)); } /* §2.4.2 Legacy Interfaces: A Note on Virtqueue Layout */ static ulong queuesize(ulong size) { return VPGROUND(VdescSize*size + sizeof(u16int)*(3+size)) + VPGROUND(sizeof(u16int)*3 + VusedSize*size);