Skip to content

Instantly share code, notes, and snippets.

@enukane
Created August 31, 2012 11:45
Show Gist options
  • Save enukane/3551798 to your computer and use it in GitHub Desktop.
Save enukane/3551798 to your computer and use it in GitHub Desktop.
ethervirtio.c : virtio-net for 9front, able to send ping, receive dhcp but dead when received more than 255 frames
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
#include "../port/error.h"
#include "../port/netif.h"
#include "etherif.h"
#include "ethermii.h"
//
//
// Virtio
//
//
typedef struct Vioreqhdr Vioreqhdr;
typedef struct Vringhdr Vringhdr;
typedef struct Vdesc Vdesc;
typedef struct Vused Vused;
typedef struct Vqueue Vqueue;
typedef struct Vdev Vdev;
enum {
Acknowledge = 1,
Driver = 2,
DriverOk = 4,
Failed = 128,
};
enum {
Devfeat = 0,
Drvfeat = 4,
Qaddr = 8,
Qsize = 12,
Qselect = 14,
Qnotify = 16,
Status = 18,
Isr = 19,
Devspec = 20,
};
enum {
Next = 1,
Write = 2,
Indirect = 4,
};
enum {
VIRTIO_F_NOTIFY_ON_EMPTY = (1 << 24),
VIRTIO_F_RING_INDIRECT_DESC = (1 << 28),
VIRTIO_F_RING_EVENT_IDX = (1 << 29),
};
enum {
VRING_AVAIL_F_NO_INTERRUPT = (1 << 0),
};
struct Vioreqhdr
{
u32int typ;
u32int prio;
u64int lba;
};
struct Vringhdr
{
u16int flags;
u16int idx;
};
struct Vdesc
{
u64int addr;
u32int len;
u16int flags;
u16int next;
};
struct Vused
{
u32int id;
u32int len;
};
struct Key
{
void* kaddr;
ulong paddr;
};
typedef struct Key Key;
struct Vqueue
{
int size;
int free;
int nfree;
Vdesc *desc;
Vringhdr *avail;
u16int *availent;
u16int *availevent;
Vringhdr *used;
Vused *usedent;
u16int *usedevent;
u16int lastused;
Key *key;
Rendez;
QLock;
Lock;
};
struct Vdev
{
int typ;
Pcidev *pci;
ulong port;
ulong features;
int nqueue;
Vqueue *queue[16];
int active;
Vdev *next;
};
static Vqueue*
mkvqueue(int size)
{
Vqueue *q;
uchar *p;
int i;
q = malloc(sizeof(*q));
p = mallocalign(
PGROUND(sizeof(Vdesc)*size +
sizeof(Vringhdr) +
sizeof(u16int)*size +
sizeof(u16int)) +
PGROUND(sizeof(Vringhdr) +
sizeof(Vused)*size +
sizeof(u16int)),
BY2PG, 0, 0);
if(p == nil || q == nil){
print("mkvqueue: no memory for Vqueue\n");
free(p);
free(q);
return nil;
}
q->desc = (void*)p;
p += sizeof(Vdesc)*size;
q->avail = (void*)p;
p += sizeof(Vringhdr);
q->availent = (void*)p;
p += sizeof(u16int)*size;
q->availevent = (void*)p;
p += sizeof(u16int);
p = (uchar*)PGROUND((ulong)p);
q->used = (void*)p;
p += sizeof(Vringhdr);
q->usedent = (void*)p;
p += sizeof(Vused)*size;
q->usedevent = (void*)p;
q->key = malloc(sizeof(Key) * size);
q->free = -1;
q->nfree = q->size = size;
for(i=0; i<size; i++){
q->desc[i].next = q->free;
q->free = i;
}
return q;
}
//
//
// Virtio-Net specific
//
//
typedef struct Vnetcfg Vnetcfg;
typedef struct Vnetctrl Vnetctrl;
/* Virtio-net queue number */
enum {
VIRTIO_NET_Q_RX = 0,
VIRTIO_NET_Q_TX = 1,
VIRTIO_NET_Q_CTLR = 2,
};
/* Virtio-net specific Device features */
enum {
VIRTIO_NET_F_CSUM = (1 << 0),
VIRTIO_NET_F_GUEST_CSUM = (1 << 1),
VIRTIO_NET_F_MAC = (1 << 5),
VIRTIO_NET_F_GSO = (1 << 6),
VIRTIO_NET_F_GUEST_TSO4 = (1 << 7),
VIRTIO_NET_F_GUEST_TSO6 = (1 << 8),
VIRTIO_NET_F_GUEST_ECN = (1 << 9),
VIRTIO_NET_F_GUEST_UFO = (1 << 10),
VIRTIO_NET_F_HOST_TSO4 = (1 << 11),
VIRTIO_NET_F_HOST_TSO6 = (1 << 12),
VIRTIO_NET_F_HOST_ECN = (1 << 13),
VIRTIO_NET_F_HOST_UFO = (1 << 14),
VIRTIO_NET_F_MRG_RXBUF = (1 << 15),
VIRTIO_NET_F_STATUS = (1 << 16),
VIRTIO_NET_F_CTRL_VQ = (1 << 17),
VIRTIO_NET_F_CTRL_RX = (1 << 18),
VIRTIO_NET_F_CTRL_VLAN = (1 << 19),
VIRTIO_NET_F_GUEST_ANNOUNCE = (1 << 21),
};
/* Virtio-net Device Status */
enum {
VIRTIO_NET_S_LINK_UP = 1,
VIRTIO_NET_S_ANNOUNCE = 2,
};
/* Not used : Virtio-net Device Specific Region */
struct Vnetcfg {
u8int mac[6];
u16int status;
};
/* Vnetreqhdr, flags */
enum {
VIRTIO_NET_HDR_F_NEEDS_CSUM =~1,
};
/* Vnetreqhdr, gso_type */
enum {
VIRTIO_NET_HDR_GSO_NONE = 0,
VIRTIO_NET_HDR_GSO_TCPV4 = 1,
VIRTIO_NET_HDR_GSO_UDP = 3,
VIRTIO_NET_HDR_GSO_TCPV6 = 4,
VIRTIO_NET_HDR_GSO_ECN = 0x80,
};
/* Virtio-net request header */
#pragma pack on
typedef struct Vnetreqhdr Vnetreqhdr;
struct Vnetreqhdr {
u8int flags;
u8int gso_type;
u16int hdr_len;
u16int gso_size;
u16int csum_start;
u16int csum_offset;
};
#pragma pack off
int Vnetreqhdrsize = 10;
/* Virtio-Net Control Requext Class */
enum {
VIRTIO_NET_CTRL_RX_CLASS = 0,
};
/* Virtio-net Control Request Command */
enum {
VIRTIO_NET_CTRL_RX_CMD_PROMISC = 0,
VIRTIO_NET_CTRL_RX_CMD_ALLMULTI = 1,
};
/* on off */
enum {
VIRTIO_NET_CTRL_RX_OFF = 0,
VIRTIO_NET_CTRL_RX_ON = 1,
};
/* ack value */
enum {
VIRTIO_NET_CTRL_ACK_OK = 0,
VIRTIO_NET_CTRL_ACK_ERR = 1,
};
/* Virtio-net control request */
struct Vnetctrl {
u8int class;
u8int command;
};
/* followed by u8int onoff, u8int ack */
Vdev *vionethead = nil;
Vdev *vionettail = nil;
/////////////////////////////////////////////////////////////////////
// Virtio-net helper funcs
/////////////////////////////////////////////////////////////////////
/*********** Interrupt ***********/
/*
* Free descriptors used for this transmit
*/
static void
vionettxfree(Vqueue* q, int head)
{
Vdesc *d;
int bufid;
Vdesc *bufd;
//ulong paddr;
//void* kaddr;
Block *bp;
Vnetreqhdr *vnetreqhdr;
print("txfree q %p head %d\n", q, head);
/* get Vnetreqhdr */
d = &q->desc[head];
/* Vnetreqhdr is followed by real buffer descriptor */
bufid = d->next;
bufd = &q->desc[bufid];
/* free header */
vnetreqhdr = q->key[head].kaddr;
free(vnetreqhdr);
d->len = d->flags = 0;
q->key[head].kaddr = nil;
/* calculate original Block address and free */
bp = q->key[bufid].kaddr;
freeb(bp);
q->key[bufid].kaddr = nil;
/* put vdesc for header back */
d->next = q->free;
q->free = head;
q->nfree++;
/* pub vdesc for buffer back */
bufd->next = q->free;
q->free = bufid;
q->nfree++;
}
/*
* Interrupt on Tx vq.
* responsible for checking vq change and cleanup
*/
static void
vionettxinterrupt(Vdev* vd)
{
Vqueue* q;
int id, m;
print("txinterrupt : in\n");
q = vd->queue[VIRTIO_NET_Q_TX];
print("txinterrupt : vd = %p\n", vd);
print("txinterrupt : q= %p\n", q);
m = (q->size-1);
//lock(q);
print("txinterrupt: %d ?? %d\n",
(q->lastused % m), (q->used->idx &m));
while((q->lastused % m) != (q->used->idx % m)) {
id = q->usedent[q->lastused++ % (q->size-1)].id;
vionettxfree(q, id);
}
//unlock(q);
print("txinterrupt : out\n");
}
static void
vionetrxproc(Ether* edev, Vqueue* q, int id)
{
Vdesc* d;
Vdesc* bufd;
void* buf;
Block *bp;
Vnetreqhdr* vnetreqhdr;
d = &q->desc[id];
bufd = &q->desc[d->next];
vnetreqhdr = (Vnetreqhdr*)q->key[id].kaddr;
print("rxproc : d %p len %d\n", d, d->len);
print("rxporc : vnetreqhdr %p flags %d len %d\n",
vnetreqhdr, vnetreqhdr->flags, vnetreqhdr->hdr_len);
print("rxproc : bufd %p size %d\n", bufd, bufd->len);
buf = (void*)q->key[d->next].kaddr;
bp = iallocb(ETHERMAXTU); // XXX
memmove(bp->rp, buf, ETHERMAXTU); // XXX
bp->wp += ETHERMAXTU;
bp->lim = bp->wp; /* lie like a dog */
/* go up */
etheriq(edev, bp, 1);
/* clean current buffer : XXX */
d->len = sizeof(Vnetreqhdr);
d->flags = Next|Write;
vnetreqhdr->hdr_len = ETHERMAXTU;
bufd->len = ETHERMAXTU;
bufd->flags = Write;
}
static void
vionetrxinterrupt(Ether* edev)
{
Vqueue* q;
int id, m;
Vdev* vd;
int i, head;
print("vionetrxinterrupt: in, edev %p\n", edev);
vd = edev->ctlr;
print("vionetrxinterrupt: in, vd %p\n", vd);
q = vd->queue[VIRTIO_NET_Q_RX];
print("vionetrxinterrupt: in, q %p\n", q);
m = (q->size-1);
//lock(q);
print("vionetrxinterrupt: forward and process lastused %d to used->idx %d\n",
q->lastused, q->used->idx);
i = 0;
while((q->lastused % m) != (q->used->idx % m)) {
id = q->usedent[q->lastused++ % m].id;
vionetrxproc(edev, q, id);
/*
* here id is free. we do push back to free list?
* NO. push back to avail ring as is.
*/
coherence();
i++;
q->availent[(q->avail->idx++) % m] = id;
}
coherence();
if (i) {
coherence();
outs(vd->port+Qnotify, VIRTIO_NET_Q_RX);
}
print("vionetrxinterrupt: fixedup receive buffer\n");
print("vionetrxinterrupt: out\n");
}
/************ Initialize ****************/
static void
vionettxinit(Vdev* vd)
{
print("vionettxinit: in\n");
if (vd->nqueue < 2) {
return;
}
/* no interrupt */
print("vionettxinit: in\n");
// vd->queue[VIRTIO_NET_Q_RX]->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
print("vionettxinit: in\n");
vd->queue[VIRTIO_NET_Q_RX]->lastused = 0;
print("vionettxinit: out\n");
}
static int
vionetrxinithead(Vqueue* q)
{
Vdesc* d;
int head, free;
Vnetreqhdr* vnetreqhdr;
void* buf;
if (q->nfree < 2)
return -1;
head = free = q->free;
d = &q->desc[free];
/* setup Vnethdr */
vnetreqhdr = malloc(sizeof(Vnetreqhdr));
vnetreqhdr->hdr_len = ETHERMAXTU;//sizeof(Vnetreqhdr);
vnetreqhdr->flags = 0; // XXX
// vnetreqhdr->num_buffers = 1;
/* setup vdesc for Vnetreqhdr */
d->addr = PADDR(vnetreqhdr);
d->len = Vnetreqhdrsize;//sizeof(Vnetreqhdr);
d->flags = Next|Write ;
/* save key */
q->key[head].paddr = d->addr;
q->key[head].kaddr = vnetreqhdr;
free = d->next;
/* get vdesc for data buffer : a real frame */
d = &q->desc[free];
/* setup vdesc */
buf = malloc(ETHERMAXTU);
d->addr = PADDR(buf);
d->len = ETHERMAXTU;
d->flags = Write;
/* save key */
q->key[free].paddr = d->addr;
q->key[free].kaddr = buf;
free = d->next;
q->nfree -= 2;
q->free = free;
return head;
}
static void
vionetrxinit(Vdev* vd)
{
Vqueue *q;
Vdesc *d;
int nadded;
int head, free;
Vnetreqhdr* vnetreqhdr;
void* buf;
print("vionetrxinit: in\n");
if (vd->nqueue < 1) {
return;
}
print("vionetrxinit: fetch queuen\n");
q = vd->queue[VIRTIO_NET_Q_RX];
print("vionetrxinit: each vdesc has %d byte buffer\n", ETHERMAXTU);
/*
q->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
*/
print("vionetrxinit: allocating buffer\n");
/* allocate receive buffer */
nadded = 0;
while(1) {
head = vionetrxinithead(q);
if (head < 0)
break;
coherence();
/* put head's number into avail ring */
nadded++;
q->availent[(q->avail->idx++) % (q->size-1)] = head;
coherence();
}
if (nadded > 0) {
coherence();
/* update avail idx */
coherence();
outs(vd->port+Qnotify, VIRTIO_NET_Q_RX);
}
print("vionetrxinit: allocated %d buffers\n", nadded);
q->lastused = 0;
print("vionettxinit: out\n");
}
static void
vionetvqinit(Vdev* vd)
{
int i;
int size;
u32int a;
for (i = 0; i < nelem(vd->queue) ; i++) {
/* select operating queue */
outs(vd->port + Qselect, i);
/* get operating queue size (0 : disabled) */
if ((size = ins(vd->port + Qsize)) == 0)
break;
if ((vd->queue[i] = mkvqueue(size)) == nil)
break;
coherence();
a = PADDR(vd->queue[i]->desc)/BY2PG;
/* write addr of operating queue at Qaddr */
outl(vd->port + Qaddr, a);
}
vd->nqueue = i;
/* setup rx */
vionettxinit(vd);
/* setup tx */
vionetrxinit(vd);
}
/*
* Negotiates Device features and given Driver features
*/
static ulong
vio_negotiate_feature(Vdev *vd, ulong drvfeat)
{
ulong features;
print("vio neto fet: in %ld + %d\n", vd->port, Devfeat);
/* read device features */
features = inl(vd->port + Devfeat);
print("vio neto fet: in sec\n");
features &= drvfeat;
print("vio neto fet: out %ld %d\n", vd->port, Drvfeat);
/* write back features */
outl(vd->port + Drvfeat, features);
print("vio neto fet: out\n");
return features;
}
static void
vionetinit1(Ether* edev, Vdev* vd)
{
int i;
print("vionetinit1: in\n");
/* check feature bit : F_MAC */
if (vd->features & VIRTIO_NET_F_MAC) {
for (i = 0; i < 6; i++) {
edev->ea[i] = inb(vd->port + Devspec + i);
}
} else {
for (i = 0; i < 6; i++) {
edev->ea[i] = 0xff;
outb(vd->port + Devspec + i, edev->ea[i]);
}
}
/* check feature bit : F_STATUS */
if (vd->features & VIRTIO_NET_F_STATUS) {
/* check and up the link */
edev->link = (0x01 & ins(vd->port + Devspec + Eaddrlen));
print("vionetinit1: link is %d\n", edev->link);
edev->link &= 0x01;
print("vionetinit1: link is %d\n", edev->link);
outs(vd->port + Devspec + Eaddrlen, edev->link);
} else {
/* spec says assume active */
edev->link = 0x01;
}
print("vionetinit1: out\n");
}
/*
* Initialize device&driver
*/
static void
vionetinit(Vdev* vd)
{
int is_ctrl_vq = 0;
print("vionetinit: in\n");
/* negotiate features */
vd->features = vio_negotiate_feature(vd,
VIRTIO_F_NOTIFY_ON_EMPTY |
VIRTIO_NET_F_MAC |
VIRTIO_NET_F_STATUS |
// VIRTIO_NET_F_CTRL_VQ |
VIRTIO_NET_F_CTRL_RX |
0
);
print("vionetinit: negotiated\n");
/* negotiate features */
/* check feature bit : F_CTRL_VQ */
if (vd->features & VIRTIO_NET_F_CTRL_VQ) {
is_ctrl_vq = 1;
}
/* setup virtqueue */
print("vionetinit: call vionetvqinit\n");
vionetvqinit(vd);
print("vionetinit: called vionetvqinit\n");
/* check if ctrlvq is valid */
if (is_ctrl_vq && vd->nqueue != 3) {
print("vionetinit(): ctrl queue is invalid\n");
}
print("vionetinit: out\n");
return;
}
/////////////////////////////////////////////////////////////////////
// Generic Network driver funcs
/////////////////////////////////////////////////////////////////////
static void
vionetattach(Ether*)
{
/* nothing to do */
}
void
vionettransmit(Ether* edev)
{
Block* bp; /* bp->rp has address */
Vdev* vd;
int head, free;
Vdesc *d;
Vqueue *q;
int nadded = 0;
Vnetreqhdr* vnetreqhdr;
vd = edev->ctlr;
q = vd->queue[VIRTIO_NET_Q_TX];
print("vionettransmit: in\n");
//lock(q);
for (;;) {
/* needs 2 entries for transmit */
if (q->nfree < 2) {
//unlock(q);
//error("out of virtio descriptors");
print("vionettransmit: run out of queue : break;\n");
break;
}
/* get next frame to send */
if ((bp = qget(edev->oq)) == nil) {
print("vionettransmit: run out of oq : break;\n");
break;
}
head = free = q->free;
print("vionettransmit: head is %d\n", head);
/* get vdesc for Vnethdr */
d = &q->desc[free]; free = d->next;
/* setup Vnethdr */
vnetreqhdr = malloc(Vnetreqhdrsize/*sizeof(Vnetreqhdr)*/);
// vnetreqhdr->hdr_len = Vnetreqhdrsize;//sizeof(Vnetreqhdr);
vnetreqhdr->flags = 0; // XXX
// vnetreqhdr->num_buffers = 1;
/* setup vdesc for Vnethdr */
d->addr = PADDR(vnetreqhdr);
d->len = Vnetreqhdrsize;//sizeof(Vnetreqhdr);
d->flags = Next;
/* save paddr and kaddr */
q->key[head].paddr = d->addr;
q->key[head].kaddr = vnetreqhdr;
/* get vdesc for data buffer : a real frame */
print("vionettransmit: buffer is %d\n", free);
d = &q->desc[free]; free = d->next;
/* setup vdesc for data buffer */
d->addr = PADDR(bp->rp);
d->len = BLEN(bp);
d->flags = 0;
/* save paddr and kaddr , paddr */
q->key[free].paddr = d->addr;
q->key[free].kaddr = bp;
print("vionettransmit: q->free %d, q->nfree %d\n", q->free, q->nfree);
q->nfree -= 2;
q->free = free;
coherence();
/* put head's number into avail ring */
print("vionettransmit: set head %d at idx %d + nadded %d q->size %d\n",
head, q->avail->idx, nadded, q->size);
nadded++;
/* update avail idx */
q->availent[(q->avail->idx++) & (q->size-1)] = head;
coherence();
print("vionettransmit: nadded is %d q->free %d, q->nfree %d\n",
nadded, q->free, q->nfree);
}
coherence();
//unlock(q);
if (nadded > 0) {
print("vionettransmit: updated idx to %d\n", q->avail->idx);
/* notify */
outs(vd->port+Qnotify, VIRTIO_NET_Q_TX);
}
print("vionettransmit: done\n");
print("vionettransmit: out\n");
}
static void
vionetinterrupt(Ureg*, void* arg)
{
Vdev *vd;
Ether *edev;
edev = arg;
vd = edev->ctlr;
if (edev == nil || vd == nil)
return;
print("vionetinterrupt: in edev %p vd %p\n", edev, vd);
if (inb(vd->port + Isr) & 1) {
print("vionetinterrupt: in Isr\n");
/* wakeup ctrl op */
// if (vd->nqueue > VIRTIO_NET_Q_CTLR) {
// wakeup(vd->queue[VIRTIO_NET_Q_CTLR]);
// }
/* control vq */
// vionetctrlinterrupt(vd);
/* transmit vq */
print("vionetinterrupt: in tx\n");
vionettxinterrupt(vd);
/* receive vq */
print("vionetinterrupt: in rx\n");
vionetrxinterrupt(edev);
print("vionetinterrupt: in done\n");
}
if (inb(vd->port + Isr) & 2) {
/* config space has changed */
print("vionetinterrupt: config changed\n");
}
print("vionetinterrupt: out\n");
}
static long
vionetifstat(Ether*, void*, long, ulong)
{
return 0;
}
static void
vionetshutdown()
{
/* do cleanup */
return;
}
static long
vionetctl(Ether*, void*, long)
{
/* do nothing */
return 0;
}
/*
static void
vionetdone(void *arg)
{
struct Rock *r;
Vqueue *q;
u16int i;
r = arg;
q = r->q;
for (i = q->lastused; i != q->used->idx; i++) {
if (q->usedent[i % q->size].id == r->id) {
if (i == q->lastused)
q->lastused++;
r->done =1;
break;
}
}
return r->done;
}
*/
/*
static void
vionetwait(Vqueue *q, int id)
{
struct Rock r;
r.q = q;
r.id = id;
r.done = 0;
do {
//qlock(q);
while(waserror());
sleep(q, vionetdone, &r);
poperror();
//qunlock(q);
} while(!r.done);
}
*/
/*
* Send Control request to device.
*/
static void
vionetctrl(Ether* edev, u8int cmd, u8int on)
{
Vnetctrl vnetctrl;
Vdev* vd;
Vqueue* q;
int free, head;
Vdesc *d;
u8int ack;
u8int onoff;
vd = edev->ctlr;
onoff = on ? VIRTIO_NET_CTRL_RX_ON : VIRTIO_NET_CTRL_RX_OFF;
ack = VIRTIO_NET_CTRL_ACK_ERR;
q = vd->queue[VIRTIO_NET_Q_CTLR];
//lock(q);
/* get idx of head of free entry */
head = free = q->free;
/* allocate Vnetctrl entry */
vnetctrl.class = VIRTIO_NET_CTRL_RX_CLASS;
vnetctrl.command = cmd;
/* push vnetctrl */
d = &q->desc[free]; free = d->next;
d->addr = PADDR(&vnetctrl);
d->len = sizeof(vnetctrl);
d->flags = Next;
/* push onoff */
d = &q->desc[free]; free = d->next;
d->addr = PADDR(&onoff);
d->len = sizeof(u8int);
d->flags = Next;
/* push ack */
d = &q->desc[free]; free = d->next;
d->addr = PADDR(&ack);
d->len = sizeof(u8int);
d->flags = Write;
d->next = -1;
q->free = free;
q->nfree -= 3; /* pushed 3 */
coherence(); /* sync */
q->availent[q->avail->idx++ & q->size] = head;
//unlock(q);
coherence();
/* commit notify */
outs(vd->port + Qnotify, VIRTIO_NET_Q_CTLR);
/* blocking */
//vionetwait(q, head);
if (ack != VIRTIO_NET_CTRL_RX_ON) {
print("vionetctrl: cmd = %d ack is ERR\n", cmd);
error(Eio);
}
return;
}
static void
vionetpromiscuous(void* arg, int on)
{
Ether* edev;
edev = arg;
vionetctrl(edev, VIRTIO_NET_CTRL_RX_CMD_PROMISC, on);
return;
}
static void
vionetmulticast(void* arg, uchar*, int on)
{
Ether* edev;
edev = arg;
vionetctrl(edev, VIRTIO_NET_CTRL_RX_CMD_ALLMULTI, on);
return;
}
/*
* Finds all virtio-net devices and list up in vionethead to vionettail.
* Do virtio-dev init and mkvqueue
*/
static void
vionetpci(void)
{
Pcidev* p;
Vdev *vd;
print("vionetpci: in %d %d \n",
sizeof(Vnetreqhdr), sizeof(Vnetctrl));
for (p = nil; p = pcimatch(p, 0, 0);) {
if (p->vid != 0x1af4)
continue;
if ((p->did < 0x1000) || (p->did >= 0x1040))
continue;
if (p->rid != 0)
continue;
if (pcicfgr16(p, 0x2e) != 1 /* virtio-net subid */)
continue;
print("vionetpci: found device\n");
if ((vd = malloc(sizeof(*vd))) == nil) {
print("vionetpci(): cannnot allocate Vdev\n");
break;
}
print("vionetpci: vd alloced\n");
vd->port = p->mem[0].bar & ~0x1;
if (ioalloc(vd->port, p->mem[0].size, 0, "virtio") < 0) {
print("virtio: port %lux in use\n", vd->port);
free(vd);
continue;
}
vd->typ = 1 /* nic */;
vd->pci = p;
/* reset */
outb(vd->port + Status, 0);
/* set Acknowkedge and Driver */
print("vionetpci: Acknowledge and Driver to device\n");
outl(vd->port+Status, inb(vd->port+Status)|Acknowledge|Driver);
print("vionetpci: sent Acknowledge and Driver to device\n");
/* prepare vionet device */
print("vionetpci: call vionetinit()\n");
vionetinit(vd);
print("vionetpci: called vionetinit()\n");
print("vionetpnp: enable interrupt¥n");
//intrenable(vd->pci->intl, vionetinterrupt, vd, vd->pci->tbdf, "ethervirtio");
print("vionetpnp: enabled interrupt¥n");
/* done initialization */
print("vionetpnp: set status DriverOk\n");
outb(vd->port + Status, inb(vd->port + Status) | DriverOk);
print("vionetpnp: seted status DriverOk\n");
vd->next = nil;
if (vionethead == nil)
vionethead = vd;
else
vionettail->next = vd;
vionettail = vd;
print("inited : vd %p\n", vd);
print("inited : vd q0 %p\n", vd->queue[0]);
print("inited : vd q1 %p\n", vd->queue[1]);
}
print("vionetpci: out\n");
}
static int
vionetpnp(Ether* edev)
{
Vdev* vd;
static int done;
if (!done) {
vionetpci(); /* do initialize each device */
done = 1;
}
for (vd = vionethead; ; vd = vd->next) {
if (vd == nil)
return -1;
if (vd->active)
continue;
if (edev->port == 0 || edev->port == vd->port) {
vd->active = 1;
break;
}
}
if (vd == nil)
return -1;
edev->ctlr = vd;
edev->port = vd->port;
edev->irq = vd->pci->intl;
edev->tbdf = vd->pci->tbdf;
edev->mbps = 1000;
print("vionetpnp: edev %p\n", edev);
print("vionetpnp: vd %p\n", vd);
print("vionetpnp: call vionetinit1\n");
vionetinit1(edev, vd);
print("vionetpnp: called vionetinit1\n");
/*
* set handler functions
*/
edev->attach = vionetattach;
edev->transmit = vionettransmit;
edev->interrupt = vionetinterrupt;
edev->ifstat = vionetifstat;
edev->shutdown = vionetshutdown;
edev->ctl = vionetctl;
edev->arg = edev;
/*
edev->promiscuous = vionetpromiscuous;
edev->multicast = vionetmulticast;
*/
print("vionetpnp: out\n");
return 0;
}
void
ethervirtiolink(void)
{
addethercard("virtio", vionetpnp);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment