diff options
author | H. Peter Anvin <hpa@zytor.com> | 2009-02-17 20:17:17 -0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2009-02-17 20:17:17 -0800 |
commit | d0c6656a62113b913948361779d6298fe76f6e61 (patch) | |
tree | efa2541a1abae4760717c6db421ea818114ab6f7 /gpxe/src/drivers/net/virtio-net.c | |
parent | 85b92a462dab7ce36c48614ea18314f8fc83ca9c (diff) | |
download | syslinux-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.c | 289 |
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; } |