Code: Select all
#define VIONET_DESC_F_NEXT 1
#define VIONET_DESC_F_WRITE 2
#define VIONET_REG_QADDR 0x08
#define VIONET_REG_QSIZE 0x0C
#define VIONET_REG_QSEL 0x0E
#define VIONET_REG_DEVST 0x12
#define VIONET_REG_QNOTF 0x10
#define VIONET_ALIGN(x) (((x) + 4095) & ~4095)
#define VIONET_DEVST_ACK (1 << 1)
#define VIONET_DEVST_DRV (1 << 2)
#define VIONET_DEVST_OK (1 << 3)
#define VIONET_DEVST_FAIL (1 << 8)
typedef struct
{
uint64_t phaddr;
uint32_t len;
uint16_t flags;
uint16_t next;
} VionetBufferDesc;
typedef struct
{
uint32_t index;
uint32_t len;
} VionetUsedElem;
typedef struct
{
uint16_t flags;
uint16_t index;
uint16_t ring[];
/* after the ring: uint16_t intIndex */
} VionetAvail;
typedef struct
{
uint16_t flags;
uint16_t index;
VionetUsedElem ring[];
/* after the ring: uint16_t intIndex */
} VionetUsed;
typedef struct
{
uint16_t count;
VionetBufferDesc* bufDesc;
VionetAvail* avail;
VionetUsed* used;
uint16_t* availIntIndex;
uint16_t* usedIntIndex;
} VionetQueue;
typedef struct
{
uint8_t flags;
uint8_t seg;
uint16_t headlen;
uint16_t seglen;
uint16_t cstart;
uint16_t coff;
uint16_t bufcount;
} VionetPacketHeader;
static void vionet_qinit(VionetQueue *q, uint16_t count, void *p)
{
uint64_t addr = (uint64_t) p;
q->count = count;
q->bufDesc = (VionetBufferDesc*) addr;
q->avail = (VionetAvail*) (addr + sizeof(VionetBufferDesc) * count);
q->used = (VionetUsed*) VIONET_ALIGN(((uint64_t) &q->avail->ring[count]));
};
static inline size_t vionet_qdsize(uint16_t qsz)
{
return VIONET_ALIGN(sizeof(VionetBufferDesc)*qsz + sizeof(uint16_t)*(2 + qsz)) + VIONET_ALIGN(sizeof(VionetUsedElem)*qsz);
}
Code: Select all
MODULE_INIT(const char *opt)
{
kprintf("vionet: enumerating virtio-net devices\n");
pciEnumDevices(THIS_MODULE, vionet_enumerator, NULL);
kprintf("vionet: creating network interfaces\n");
VionetInterface *nif;
for (nif=interfaces; nif!=NULL; nif=nif->next)
{
NetIfConfig ifconfig;
memset(&ifconfig, 0, sizeof(NetIfConfig));
ifconfig.ethernet.type = IF_ETHERNET;
ifconfig.ethernet.send = vionet_send;
int i;
for (i=0; i<6; i++)
{
ifconfig.ethernet.mac.addr[i] = inb(nif->iobase + 0x14 + i);
};
pciSetBusMastering(nif->pcidev, 1);
outw(nif->iobase + VIONET_REG_DEVST, 0);
outw(nif->iobase + VIONET_REG_DEVST, VIONET_DEVST_ACK);
outw(nif->iobase + VIONET_REG_DEVST, VIONET_DEVST_ACK | VIONET_DEVST_DRV);
// find out the size of RX and TX queues.
outw(nif->iobase + VIONET_REG_QSEL, 0);
uint16_t rxqsize = inw(nif->iobase + VIONET_REG_QSIZE);
outw(nif->iobase + VIONET_REG_QSEL, 1);
uint16_t txqsize = inw(nif->iobase + VIONET_REG_QSIZE);
if ((rxqsize < 4) || (txqsize < 4))
{
panic("vionet: at least 4 RX and 4 TX buffers must be available!");
};
size_t rxqdsize = vionet_qdsize(rxqsize);
size_t txqdsize = vionet_qdsize(txqsize);
// allocate the queues as DMA buffers
int errnum;
if ((errnum = dmaCreateBuffer(&nif->dmaRX, rxqdsize, DMA_32BIT)) != 0)
{
panic("vionet: failed to allocate RX queue! (%d)", errnum);
};
if ((errnum = dmaCreateBuffer(&nif->dmaTX, txqdsize, DMA_32BIT)) != 0)
{
panic("vionet: failed to allocate TX queue! (%d)", errnum);
};
// zero out the queue descriptors
memset(dmaGetPtr(&nif->dmaRX), 0, rxqdsize);
memset(dmaGetPtr(&nif->dmaTX), 0, txqdsize);
// load the queue handles
vionet_qinit(&nif->rxq, rxqsize, dmaGetPtr(&nif->dmaRX));
vionet_qinit(&nif->txq, txqsize, dmaGetPtr(&nif->dmaTX));
// allocate the RX/TX space. It starts with 8 packet headers
// (4 for RX, then 4 for TX), followed by 1518-byte frame
// buffers (4 for RX and another 4 for TX). The packet headers
// shall be zeroed out and left like that.
if ((errnum = dmaCreateBuffer(&nif->dmaIO, 8*sizeof(VionetPacketHeader) + 8*1518, 0)) != 0)
{
panic("vionet: failed to allocate RX/TX space! (%d)", errnum);
};
// zero out the RX/TX space then post the 4 RX buffers to the queue.
memset(dmaGetPtr(&nif->dmaIO), 0, 8*sizeof(VionetPacketHeader) + 8*1518);
uint64_t spaceBase = dmaGetPhys(&nif->dmaIO);
for (i=0; i<4; i++)
{
// packet header buffer
nif->rxq.bufDesc[2*i].phaddr = spaceBase + i * sizeof(VionetPacketHeader);
nif->rxq.bufDesc[2*i].len = sizeof(VionetPacketHeader);
nif->rxq.bufDesc[2*i].flags = VIONET_DESC_F_NEXT;
nif->rxq.bufDesc[2*i].next = 2*i+1;
// frame buffer
nif->rxq.bufDesc[2*i+1].phaddr = spaceBase + 8*sizeof(VionetPacketHeader) + i*1518;
nif->rxq.bufDesc[2*i+1].len = 1518;
nif->rxq.bufDesc[2*i+1].flags = VIONET_DESC_F_WRITE;
nif->rxq.bufDesc[2*i+1].next = 0;
// post as available
nif->rxq.avail->ring[i] = 2*i;
};
// update the RX ring index
nif->rxq.avail->index = 4;
// make sure all memory is flushed!
__sync_synchronize();
// program in the queue addresses
uint32_t rxphys = (uint32_t) (dmaGetPhys(&nif->dmaRX) >> 12);
uint32_t txphys = (uint32_t) (dmaGetPhys(&nif->dmaTX) >> 12);
outw(nif->iobase + VIONET_REG_QSEL, 0);
outd(nif->iobase + VIONET_REG_QADDR, rxphys);
outw(nif->iobase + VIONET_REG_QSEL, 1);
outd(nif->iobase + VIONET_REG_QADDR, txphys);
nif->netif = CreateNetworkInterface(nif, &ifconfig);
if (nif->netif == NULL)
{
kprintf("vionet: CreateNetworkInterface() failed\n");
}
else
{
kprintf("vionet: created interface: %s\n", nif->netif->name);
};
nif->running = 1;
spinlockRelease(&nif->lock);
nif->txBitmap = 0;
// tell the host that we are ready to drive the device, and that we support MAC.
outw(nif->iobase + 0x04, (1 << 5));
outw(nif->iobase + VIONET_REG_DEVST, VIONET_DEVST_ACK | VIONET_DEVST_DRV | VIONET_DEVST_OK);
KernelThreadParams pars;
memset(&pars, 0, sizeof(KernelThreadParams));
pars.name = "VirtIO-net Interrupt Handler";
pars.stackSize = DEFAULT_STACK_SIZE;
nif->thread = CreateKernelThread(vionet_thread, &pars, nif);
};
if (interfaces == NULL)
{
return MODINIT_CANCEL;
};
return MODINIT_OK;
};
I transmit the frame as follows:
Code: Select all
static void vionet_send(NetIf *netif, const void *frame, size_t framelen)
{
VionetInterface *nif = (VionetInterface*) netif->drvdata;
spinlockAcquire(&nif->lock);
int txbuf;
if ((txbuf = vionet_findAvailTXBuffer(nif)) == 4)
{
// there are no available buffers, so
spinlockRelease(&nif->lock);
};
nif->txBitmap |= (1 << txbuf);
uint64_t spaceBase = dmaGetPhys(&nif->dmaIO);
// load the frame into the buffer
uint64_t fbufAddr = (uint64_t) dmaGetPtr(&nif->dmaIO) + 8*sizeof(VionetPacketHeader) + (txbuf+4)*1518;
memset((void*)fbufAddr, 0, 1518); // never send uninitialized data!
memcpy((void*)fbufAddr, frame, framelen);
// fill in the descriptors
nif->txq.bufDesc[2*txbuf].phaddr = spaceBase + (txbuf+4)*sizeof(VionetPacketHeader);
nif->txq.bufDesc[2*txbuf].len = sizeof(VionetPacketHeader);
nif->txq.bufDesc[2*txbuf].flags = VIONET_DESC_F_NEXT;
nif->txq.bufDesc[2*txbuf].next = 2*txbuf+1;
// frame buffer
nif->txq.bufDesc[2*txbuf+1].phaddr = spaceBase + 8*sizeof(VionetPacketHeader) + (txbuf+4)*1518;
nif->txq.bufDesc[2*txbuf+1].len = framelen;
nif->txq.bufDesc[2*txbuf+1].flags = 0;
nif->txq.bufDesc[2*txbuf+1].next = 0;
// flush memory
__sync_synchronize();
// post the buffer to the available ring
nif->txq.avail->ring[nif->txq.avail->index] = 2*txbuf;
__sync_synchronize();
nif->txq.avail->index++;
__sync_synchronize();
// notify the device that we have updated the TX queue.
outw(nif->iobase + VIONET_REG_QNOTF, 1);
spinlockRelease(&nif->lock);
};
Where is the problem? Am I not initializing correctly?
NOTE: The MAC address is correctly identified.