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 (at) bull.net> 6 * 7 * some parts from Linux Virtio PCI driver 8 * 9 * Copyright IBM Corp. 2007 10 * Authors: Anthony Liguori <aliguori (at) 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 37 struct eth_hdr { 38 unsigned char dst_addr[ETH_ALEN]; 39 unsigned char src_addr[ETH_ALEN]; 40 unsigned short type; 41 }; 42 43 struct eth_frame { 44 struct eth_hdr hdr; 45 unsigned char data[ETH_FRAME_LEN]; 46 }; 47 48 /* TX: virtio header and eth buffer */ 49 50 static struct virtio_net_hdr tx_virtio_hdr; 51 static struct eth_frame tx_eth_frame; 52 53 /* RX: virtio headers and buffers */ 54 55 #define RX_BUF_NB 6 56 static struct virtio_net_hdr rx_hdr[RX_BUF_NB]; 57 static unsigned char rx_buffer[RX_BUF_NB][ETH_FRAME_LEN]; 58 59 /* virtio queues and vrings */ 60 61 enum { 62 RX_INDEX = 0, 63 TX_INDEX, 64 QUEUE_NB 65 }; 66 67 static struct vring_virtqueue virtqueue[QUEUE_NB]; 68 69 /* 70 * virtnet_disable 71 * 72 * Turn off ethernet interface 73 * 74 */ 75 76 static 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 */ 98 static 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 142 static 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 196 static 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 212 static 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 230 static 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 244 static 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 300 static struct pci_device_id virtnet_nics[] = { 301 PCI_ROM(0x1af4, 0x1000, "virtio-net", "Virtio Network Interface", 0), 302 }; 303 304 PCI_DRIVER ( virtnet_driver, virtnet_nics, PCI_NO_CLASS ); 305 306 DRIVER ( "VIRTIO-NET", nic_driver, pci_driver, virtnet_driver, 307 virtnet_probe, virtnet_disable ); 308