source: bootcd/isolinux/syslinux-6.03/gpxe/src/drivers/net/virtio-net.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 7.1 KB
Line 
1/* virtio-net.c - etherboot driver for virtio network interface
2 *
3 * (c) Copyright 2008 Bull S.A.S.
4 *
5 *  Author: Laurent Vivier <Laurent.Vivier@bull.net>
6 *
7 * some parts from Linux Virtio PCI driver
8 *
9 *  Copyright IBM Corp. 2007
10 *  Authors: Anthony Liguori  <aliguori@us.ibm.com>
11 *
12 *  some parts from Linux Virtio Ring
13 *
14 *  Copyright Rusty Russell IBM Corporation 2007
15 *
16 * This work is licensed under the terms of the GNU GPL, version 2 or later.
17 * See the COPYING file in the top-level directory.
18 *
19 *
20 */
21
22#include "etherboot.h"
23#include "nic.h"
24#include "gpxe/virtio-ring.h"
25#include "gpxe/virtio-pci.h"
26#include "virtio-net.h"
27
28#define BUG() do { \
29   printf("BUG: failure at %s:%d/%s()!\n", \
30          __FILE__, __LINE__, __FUNCTION__); \
31   while(1); \
32} while (0)
33#define BUG_ON(condition) do { if (condition) BUG(); } while (0)
34
35/* Ethernet header */
36
37struct eth_hdr {
38   unsigned char dst_addr[ETH_ALEN];
39   unsigned char src_addr[ETH_ALEN];
40   unsigned short type;
41};
42
43struct eth_frame {
44   struct eth_hdr hdr;
45   unsigned char data[ETH_FRAME_LEN];
46};
47
48/* TX: virtio header and eth buffer */
49
50static struct virtio_net_hdr tx_virtio_hdr;
51static struct eth_frame tx_eth_frame;
52
53/* RX: virtio headers and buffers */
54
55#define RX_BUF_NB  6
56static struct virtio_net_hdr rx_hdr[RX_BUF_NB];
57static unsigned char rx_buffer[RX_BUF_NB][ETH_FRAME_LEN];
58
59/* virtio queues and vrings */
60
61enum {
62   RX_INDEX = 0,
63   TX_INDEX,
64   QUEUE_NB
65};
66
67static struct vring_virtqueue virtqueue[QUEUE_NB];
68
69/*
70 * virtnet_disable
71 *
72 * Turn off ethernet interface
73 *
74 */
75
76static void virtnet_disable(struct nic *nic)
77{
78   int i;
79
80   for (i = 0; i < QUEUE_NB; i++) {
81           vring_disable_cb(&virtqueue[i]);
82           vp_del_vq(nic->ioaddr, i);
83   }
84   vp_reset(nic->ioaddr);
85}
86
87/*
88 * virtnet_poll
89 *
90 * Wait for a frame
91 *
92 * return true if there is a packet ready to read
93 *
94 * nic->packet should contain data on return
95 * nic->packetlen should contain length of data
96 *
97 */
98static int virtnet_poll(struct nic *nic, int retrieve)
99{
100   unsigned int len;
101   u16 token;
102   struct virtio_net_hdr *hdr;
103   struct vring_list list[2];
104
105   if (!vring_more_used(&virtqueue[RX_INDEX]))
106           return 0;
107
108   if (!retrieve)
109           return 1;
110
111   token = vring_get_buf(&virtqueue[RX_INDEX], &len);
112
113   BUG_ON(len > sizeof(struct virtio_net_hdr) + ETH_FRAME_LEN);
114
115   hdr = &rx_hdr[token];   /* FIXME: check flags */
116   len -= sizeof(struct virtio_net_hdr);
117
118   nic->packetlen = len;
119   memcpy(nic->packet, (char *)rx_buffer[token], nic->packetlen);
120
121   /* add buffer to desc */
122
123   list[0].addr = (char*)&rx_hdr[token];
124   list[0].length = sizeof(struct virtio_net_hdr);
125   list[1].addr = (char*)&rx_buffer[token];
126   list[1].length = ETH_FRAME_LEN;
127
128   vring_add_buf(&virtqueue[RX_INDEX], list, 0, 2, token, 0);
129   vring_kick(nic->ioaddr, &virtqueue[RX_INDEX], 1);
130
131   return 1;
132}
133
134/*
135 *
136 * virtnet_transmit
137 *
138 * Transmit a frame
139 *
140 */
141
142static void virtnet_transmit(struct nic *nic, const char *destaddr,
143        unsigned int type, unsigned int len, const char *data)
144{
145   struct vring_list list[2];
146
147   /*
148    * from http://www.etherboot.org/wiki/dev/devmanual :
149    *     "You do not need more than one transmit buffer."
150    */
151
152   /* FIXME: initialize header according to vp_get_features() */
153
154   tx_virtio_hdr.flags = 0;
155   tx_virtio_hdr.csum_offset = 0;
156   tx_virtio_hdr.csum_start = 0;
157   tx_virtio_hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE;
158   tx_virtio_hdr.gso_size = 0;
159   tx_virtio_hdr.hdr_len = 0;
160
161   /* add ethernet frame into vring */
162
163   BUG_ON(len > sizeof(tx_eth_frame.data));
164
165   memcpy(tx_eth_frame.hdr.dst_addr, destaddr, ETH_ALEN);
166   memcpy(tx_eth_frame.hdr.src_addr, nic->node_addr, ETH_ALEN);
167   tx_eth_frame.hdr.type = htons(type);
168   memcpy(tx_eth_frame.data, data, len);
169
170   list[0].addr = (char*)&tx_virtio_hdr;
171   list[0].length = sizeof(struct virtio_net_hdr);
172   list[1].addr = (char*)&tx_eth_frame;
173   list[1].length = ETH_FRAME_LEN;
174
175   vring_add_buf(&virtqueue[TX_INDEX], list, 2, 0, 0, 0);
176
177   vring_kick(nic->ioaddr, &virtqueue[TX_INDEX], 1);
178
179   /*
180    * http://www.etherboot.org/wiki/dev/devmanual
181    *
182    *   "You should ensure the packet is fully transmitted
183    *    before returning from this routine"
184    */
185
186   while (!vring_more_used(&virtqueue[TX_INDEX])) {
187           mb();
188           udelay(10);
189   }
190
191   /* free desc */
192
193   (void)vring_get_buf(&virtqueue[TX_INDEX], NULL);
194}
195
196static void virtnet_irq(struct nic *nic __unused, irq_action_t action)
197{
198   switch ( action ) {
199   case DISABLE :
200           vring_disable_cb(&virtqueue[RX_INDEX]);
201           vring_disable_cb(&virtqueue[TX_INDEX]);
202           break;
203   case ENABLE :
204           vring_enable_cb(&virtqueue[RX_INDEX]);
205           vring_enable_cb(&virtqueue[TX_INDEX]);
206           break;
207   case FORCE :
208           break;
209   }
210}
211
212static void provide_buffers(struct nic *nic)
213{
214   int i;
215   struct vring_list list[2];
216
217   for (i = 0; i < RX_BUF_NB; i++) {
218           list[0].addr = (char*)&rx_hdr[i];
219           list[0].length = sizeof(struct virtio_net_hdr);
220           list[1].addr = (char*)&rx_buffer[i];
221           list[1].length = ETH_FRAME_LEN;
222           vring_add_buf(&virtqueue[RX_INDEX], list, 0, 2, i, i);
223   }
224
225   /* nofify */
226
227   vring_kick(nic->ioaddr, &virtqueue[RX_INDEX], i);
228}
229
230static struct nic_operations virtnet_operations = {
231        .connect = dummy_connect,
232        .poll = virtnet_poll,
233        .transmit = virtnet_transmit,
234        .irq = virtnet_irq,
235};
236
237/*
238 * virtnet_probe
239 *
240 * Look for a virtio network adapter
241 *
242 */
243
244static int virtnet_probe(struct nic *nic, struct pci_device *pci)
245{
246   u32 features;
247   int i;
248
249   /* Mask the bit that says "this is an io addr" */
250
251   nic->ioaddr = pci->ioaddr & ~3;
252
253   /* Copy IRQ from PCI information */
254
255   nic->irqno = pci->irq;
256
257   printf("I/O address 0x%08x, IRQ #%d\n", nic->ioaddr, nic->irqno);
258
259   adjust_pci_device(pci);
260
261   vp_reset(nic->ioaddr);
262
263   features = vp_get_features(nic->ioaddr);
264   if (features & (1 << VIRTIO_NET_F_MAC)) {
265           vp_get(nic->ioaddr, offsetof(struct virtio_net_config, mac),
266                  nic->node_addr, ETH_ALEN);
267           printf("MAC address ");
268           for (i = 0; i < ETH_ALEN; i++) {
269                   printf("%02x%c", nic->node_addr[i],
270                          (i == ETH_ALEN - 1) ? '\n' : ':');
271           }
272   }
273
274   /* initialize emit/receive queue */
275
276   for (i = 0; i < QUEUE_NB; i++) {
277           virtqueue[i].free_head = 0;
278           virtqueue[i].last_used_idx = 0;
279           memset((char*)&virtqueue[i].queue, 0, sizeof(virtqueue[i].queue));
280           if (vp_find_vq(nic->ioaddr, i, &virtqueue[i]) == -1)
281                   printf("Cannot register queue #%d\n", i);
282   }
283
284   /* provide some receive buffers */
285
286    provide_buffers(nic);
287
288   /* define NIC interface */
289
290    nic->nic_op = &virtnet_operations;
291
292   /* driver is ready */
293
294   vp_set_features(nic->ioaddr, features & (1 << VIRTIO_NET_F_MAC));
295   vp_set_status(nic->ioaddr, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
296
297   return 1;
298}
299
300static struct pci_device_id virtnet_nics[] = {
301PCI_ROM(0x1af4, 0x1000, "virtio-net",              "Virtio Network Interface", 0),
302};
303
304PCI_DRIVER ( virtnet_driver, virtnet_nics, PCI_NO_CLASS );
305
306DRIVER ( "VIRTIO-NET", nic_driver, pci_driver, virtnet_driver,
307         virtnet_probe, virtnet_disable );
Note: See TracBrowser for help on using the repository browser.