Created
August 31, 2012 11:45
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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