summaryrefslogtreecommitdiff
path: root/gpxe/src/drivers/net/virtio-net.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-02-17 20:17:17 -0800
committerH. Peter Anvin <hpa@zytor.com>2009-02-17 20:17:17 -0800
commitd0c6656a62113b913948361779d6298fe76f6e61 (patch)
treeefa2541a1abae4760717c6db421ea818114ab6f7 /gpxe/src/drivers/net/virtio-net.c
parent85b92a462dab7ce36c48614ea18314f8fc83ca9c (diff)
downloadsyslinux-d0c6656a62113b913948361779d6298fe76f6e61.tar.gz
Update gPXE to version 0.9.6+ 277b84c6e7d49f3cf01c855007f591de8c7cb75f
Update gPXE to version 0.9.6+, from commit 277b84c6e7d49f3cf01c855007f591de8c7cb75f in the main gPXE repository. The only differences is src/config/general.h which has a few protocols added, and src/arch/i386/prefix/boot1a.S which was called boot1a.s in the upstream repository.
Diffstat (limited to 'gpxe/src/drivers/net/virtio-net.c')
-rw-r--r--gpxe/src/drivers/net/virtio-net.c289
1 files changed, 52 insertions, 237 deletions
diff --git a/gpxe/src/drivers/net/virtio-net.c b/gpxe/src/drivers/net/virtio-net.c
index 4ec154df..f0afd3f6 100644
--- a/gpxe/src/drivers/net/virtio-net.c
+++ b/gpxe/src/drivers/net/virtio-net.c
@@ -21,8 +21,8 @@
#include "etherboot.h"
#include "nic.h"
-#include "virtio-ring.h"
-#include "virtio-pci.h"
+#include "gpxe/virtio-ring.h"
+#include "gpxe/virtio-pci.h"
#include "virtio-net.h"
#define BUG() do { \
@@ -45,8 +45,6 @@ struct eth_frame {
unsigned char data[ETH_FRAME_LEN];
};
-typedef unsigned char virtio_queue_t[PAGE_MASK + vring_size(MAX_QUEUE_NUM)];
-
/* TX: virtio header and eth buffer */
static struct virtio_net_hdr tx_virtio_hdr;
@@ -66,209 +64,7 @@ enum {
QUEUE_NB
};
-static virtio_queue_t queue[QUEUE_NB];
-static struct vring vring[QUEUE_NB];
-static u16 free_head[QUEUE_NB];
-static u16 last_used_idx[QUEUE_NB];
-static u16 vdata[QUEUE_NB][MAX_QUEUE_NUM];
-
-/*
- * Virtio PCI interface
- *
- */
-
-static int vp_find_vq(struct nic *nic, int queue_index)
-{
- struct vring * vr = &vring[queue_index];
- u16 num;
-
- /* select the queue */
-
- outw(queue_index, nic->ioaddr + VIRTIO_PCI_QUEUE_SEL);
-
- /* check if the queue is available */
-
- num = inw(nic->ioaddr + VIRTIO_PCI_QUEUE_NUM);
- if (!num) {
- printf("ERROR: queue size is 0\n");
- return -1;
- }
-
- if (num > MAX_QUEUE_NUM) {
- printf("ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM);
- return -1;
- }
-
- /* check if the queue is already active */
-
- if (inl(nic->ioaddr + VIRTIO_PCI_QUEUE_PFN)) {
- printf("ERROR: queue already active\n");
- return -1;
- }
-
- /* initialize the queue */
-
- vring_init(vr, num, (unsigned char*)&queue[queue_index]);
-
- /* activate the queue
- *
- * NOTE: vr->desc is initialized by vring_init()
- */
-
- outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT,
- nic->ioaddr + VIRTIO_PCI_QUEUE_PFN);
-
- return num;
-}
-
-/*
- * Virtual ring management
- *
- */
-
-static void vring_enable_cb(int queue_index)
-{
- vring[queue_index].avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
-}
-
-static void vring_disable_cb(int queue_index)
-{
- vring[queue_index].avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
-}
-
-/*
- * vring_free
- *
- * put at the begin of the free list the current desc[head]
- */
-
-static void vring_detach(int queue_index, unsigned int head)
-{
- struct vring *vr = &vring[queue_index];
- unsigned int i;
-
- /* find end of given descriptor */
-
- i = head;
- while (vr->desc[i].flags & VRING_DESC_F_NEXT)
- i = vr->desc[i].next;
-
- /* link it with free list and point to it */
-
- vr->desc[i].next = free_head[queue_index];
- wmb();
- free_head[queue_index] = head;
-}
-
-/*
- * vring_more_used
- *
- * is there some used buffers ?
- *
- */
-
-static inline int vring_more_used(int queue_index)
-{
- wmb();
- return last_used_idx[queue_index] != vring[queue_index].used->idx;
-}
-
-/*
- * vring_get_buf
- *
- * get a buffer from the used list
- *
- */
-
-static int vring_get_buf(int queue_index, unsigned int *len)
-{
- struct vring *vr = &vring[queue_index];
- struct vring_used_elem *elem;
- u32 id;
- int ret;
-
- elem = &vr->used->ring[last_used_idx[queue_index] % vr->num];
- wmb();
- id = elem->id;
- if (len != NULL)
- *len = elem->len;
-
- ret = vdata[queue_index][id];
-
- vring_detach(queue_index, id);
-
- last_used_idx[queue_index]++;
-
- return ret;
-}
-
-static void vring_add_buf(int queue_index, int index, int num_added)
-{
- struct vring *vr = &vring[queue_index];
- int i, avail, head;
-
- BUG_ON(queue_index >= QUEUE_NB);
-
- head = free_head[queue_index];
- i = head;
-
- if (queue_index == TX_INDEX) {
-
- BUG_ON(index != 0);
-
- /* add header into vring */
-
- vr->desc[i].flags = VRING_DESC_F_NEXT;
- vr->desc[i].addr = (u64)virt_to_phys(&tx_virtio_hdr);
- vr->desc[i].len = sizeof(struct virtio_net_hdr);
- i = vr->desc[i].next;
-
- /* add frame buffer into vring */
-
- vr->desc[i].flags = 0;
- vr->desc[i].addr = (u64)virt_to_phys(&tx_eth_frame);
- vr->desc[i].len = ETH_FRAME_LEN;
- i = vr->desc[i].next;
-
- } else if (queue_index == RX_INDEX) {
-
- BUG_ON(index >= RX_BUF_NB);
-
- /* add header into vring */
-
- vr->desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
- vr->desc[i].addr = (u64)virt_to_phys(&rx_hdr[index]);
- vr->desc[i].len = sizeof(struct virtio_net_hdr);
- i = vr->desc[i].next;
-
- /* add frame buffer into vring */
-
- vr->desc[i].flags = VRING_DESC_F_WRITE;
- vr->desc[i].addr = (u64)virt_to_phys(&rx_buffer[index]);
- vr->desc[i].len = ETH_FRAME_LEN;
- i = vr->desc[i].next;
- }
-
- free_head[queue_index] = i;
-
- vdata[queue_index][head] = index;
-
- avail = (vr->avail->idx + num_added) % vr->num;
- vr->avail->ring[avail] = head;
- wmb();
-}
-
-static void vring_kick(struct nic *nic, int queue_index, int num_added)
-{
- struct vring *vr = &vring[queue_index];
-
- wmb();
- vr->avail->idx += num_added;
-
- mb();
- if (!(vr->used->flags & VRING_USED_F_NO_NOTIFY))
- vp_notify(nic, queue_index);
-}
+static struct vring_virtqueue virtqueue[QUEUE_NB];
/*
* virtnet_disable
@@ -282,10 +78,10 @@ static void virtnet_disable(struct nic *nic)
int i;
for (i = 0; i < QUEUE_NB; i++) {
- vring_disable_cb(i);
- vp_del_vq(nic, i);
+ vring_disable_cb(&virtqueue[i]);
+ vp_del_vq(nic->ioaddr, i);
}
- vp_reset(nic);
+ vp_reset(nic->ioaddr);
}
/*
@@ -304,27 +100,33 @@ static int virtnet_poll(struct nic *nic, int retrieve)
unsigned int len;
u16 token;
struct virtio_net_hdr *hdr;
+ struct vring_list list[2];
- if (!vring_more_used(RX_INDEX))
+ if (!vring_more_used(&virtqueue[RX_INDEX]))
return 0;
if (!retrieve)
return 1;
- token = vring_get_buf(RX_INDEX, &len);
+ token = vring_get_buf(&virtqueue[RX_INDEX], &len);
BUG_ON(len > sizeof(struct virtio_net_hdr) + ETH_FRAME_LEN);
hdr = &rx_hdr[token]; /* FIXME: check flags */
len -= sizeof(struct virtio_net_hdr);
- nic->packetlen = len;
+ nic->packetlen = len;
memcpy(nic->packet, (char *)rx_buffer[token], nic->packetlen);
/* add buffer to desc */
- vring_add_buf(RX_INDEX, token, 0);
- vring_kick(nic, RX_INDEX, 1);
+ list[0].addr = (char*)&rx_hdr[token];
+ list[0].length = sizeof(struct virtio_net_hdr);
+ list[1].addr = (char*)&rx_buffer[token];
+ list[1].length = ETH_FRAME_LEN;
+
+ vring_add_buf(&virtqueue[RX_INDEX], list, 0, 2, token, 0);
+ vring_kick(nic->ioaddr, &virtqueue[RX_INDEX], 1);
return 1;
}
@@ -340,6 +142,8 @@ static int virtnet_poll(struct nic *nic, int retrieve)
static void virtnet_transmit(struct nic *nic, const char *destaddr,
unsigned int type, unsigned int len, const char *data)
{
+ struct vring_list list[2];
+
/*
* from http://www.etherboot.org/wiki/dev/devmanual :
* "You do not need more than one transmit buffer."
@@ -363,7 +167,14 @@ static void virtnet_transmit(struct nic *nic, const char *destaddr,
tx_eth_frame.hdr.type = htons(type);
memcpy(tx_eth_frame.data, data, len);
- vring_add_buf(TX_INDEX, 0, 0);
+ list[0].addr = (char*)&tx_virtio_hdr;
+ list[0].length = sizeof(struct virtio_net_hdr);
+ list[1].addr = (char*)&tx_eth_frame;
+ list[1].length = ETH_FRAME_LEN;
+
+ vring_add_buf(&virtqueue[TX_INDEX], list, 2, 0, 0, 0);
+
+ vring_kick(nic->ioaddr, &virtqueue[TX_INDEX], 1);
/*
* http://www.etherboot.org/wiki/dev/devmanual
@@ -372,28 +183,26 @@ static void virtnet_transmit(struct nic *nic, const char *destaddr,
* before returning from this routine"
*/
- while (vring_more_used(TX_INDEX)) {
+ while (!vring_more_used(&virtqueue[TX_INDEX])) {
mb();
udelay(10);
}
- vring_kick(nic, TX_INDEX, 1);
-
/* free desc */
- (void)vring_get_buf(TX_INDEX, NULL);
+ (void)vring_get_buf(&virtqueue[TX_INDEX], NULL);
}
static void virtnet_irq(struct nic *nic __unused, irq_action_t action)
{
switch ( action ) {
case DISABLE :
- vring_disable_cb(RX_INDEX);
- vring_disable_cb(TX_INDEX);
+ vring_disable_cb(&virtqueue[RX_INDEX]);
+ vring_disable_cb(&virtqueue[TX_INDEX]);
break;
case ENABLE :
- vring_enable_cb(RX_INDEX);
- vring_enable_cb(TX_INDEX);
+ vring_enable_cb(&virtqueue[RX_INDEX]);
+ vring_enable_cb(&virtqueue[TX_INDEX]);
break;
case FORCE :
break;
@@ -403,13 +212,19 @@ static void virtnet_irq(struct nic *nic __unused, irq_action_t action)
static void provide_buffers(struct nic *nic)
{
int i;
-
- for (i = 0; i < RX_BUF_NB; i++)
- vring_add_buf(RX_INDEX, i, i);
+ struct vring_list list[2];
+
+ for (i = 0; i < RX_BUF_NB; i++) {
+ list[0].addr = (char*)&rx_hdr[i];
+ list[0].length = sizeof(struct virtio_net_hdr);
+ list[1].addr = (char*)&rx_buffer[i];
+ list[1].length = ETH_FRAME_LEN;
+ vring_add_buf(&virtqueue[RX_INDEX], list, 0, 2, i, i);
+ }
/* nofify */
- vring_kick(nic, RX_INDEX, i);
+ vring_kick(nic->ioaddr, &virtqueue[RX_INDEX], i);
}
static struct nic_operations virtnet_operations = {
@@ -443,11 +258,11 @@ static int virtnet_probe(struct nic *nic, struct pci_device *pci)
adjust_pci_device(pci);
- vp_reset(nic);
+ vp_reset(nic->ioaddr);
- features = vp_get_features(nic);
+ features = vp_get_features(nic->ioaddr);
if (features & (1 << VIRTIO_NET_F_MAC)) {
- vp_get(nic, offsetof(struct virtio_net_config, mac),
+ vp_get(nic->ioaddr, offsetof(struct virtio_net_config, mac),
nic->node_addr, ETH_ALEN);
printf("MAC address ");
for (i = 0; i < ETH_ALEN; i++) {
@@ -459,10 +274,10 @@ static int virtnet_probe(struct nic *nic, struct pci_device *pci)
/* initialize emit/receive queue */
for (i = 0; i < QUEUE_NB; i++) {
- free_head[i] = 0;
- last_used_idx[i] = 0;
- memset((char*)&queue[i], 0, sizeof(queue[i]));
- if (vp_find_vq(nic, i) == -1)
+ virtqueue[i].free_head = 0;
+ virtqueue[i].last_used_idx = 0;
+ memset((char*)&virtqueue[i].queue, 0, sizeof(virtqueue[i].queue));
+ if (vp_find_vq(nic->ioaddr, i, &virtqueue[i]) == -1)
printf("Cannot register queue #%d\n", i);
}
@@ -476,8 +291,8 @@ static int virtnet_probe(struct nic *nic, struct pci_device *pci)
/* driver is ready */
- vp_set_features(nic, features & (1 << VIRTIO_NET_F_MAC));
- vp_set_status(nic, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
+ vp_set_features(nic->ioaddr, features & (1 << VIRTIO_NET_F_MAC));
+ vp_set_status(nic->ioaddr, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
return 1;
}