Fix 64 bit issue in slirp
[qemu/mini2440/sniper_sniper_test.git] / hw / virtio-net.c
blob5eca19cd7865128be94cbc6bc88df0952353f2b3
1 /*
2 * Virtio Network Device
4 * Copyright IBM, Corp. 2007
6 * Authors:
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
14 #include "virtio.h"
15 #include "net.h"
16 #include "qemu-timer.h"
17 #include "virtio-net.h"
19 typedef struct VirtIONet
21 VirtIODevice vdev;
22 uint8_t mac[6];
23 uint16_t status;
24 VirtQueue *rx_vq;
25 VirtQueue *tx_vq;
26 VLANClientState *vc;
27 QEMUTimer *tx_timer;
28 int tx_timer_active;
29 int mergeable_rx_bufs;
30 } VirtIONet;
32 /* TODO
33 * - we could suppress RX interrupt if we were so inclined.
36 static VirtIONet *to_virtio_net(VirtIODevice *vdev)
38 return (VirtIONet *)vdev;
41 static void virtio_net_update_config(VirtIODevice *vdev, uint8_t *config)
43 VirtIONet *n = to_virtio_net(vdev);
44 struct virtio_net_config netcfg;
46 netcfg.status = n->status;
47 memcpy(netcfg.mac, n->mac, 6);
48 memcpy(config, &netcfg, sizeof(netcfg));
51 static void virtio_net_set_link_status(VLANClientState *vc)
53 VirtIONet *n = vc->opaque;
54 uint16_t old_status = n->status;
56 if (vc->link_down)
57 n->status &= ~VIRTIO_NET_S_LINK_UP;
58 else
59 n->status |= VIRTIO_NET_S_LINK_UP;
61 if (n->status != old_status)
62 virtio_notify_config(&n->vdev);
65 static uint32_t virtio_net_get_features(VirtIODevice *vdev)
67 uint32_t features = (1 << VIRTIO_NET_F_MAC) | (1 << VIRTIO_NET_F_STATUS);
69 return features;
72 static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
74 VirtIONet *n = to_virtio_net(vdev);
76 n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF));
79 /* RX */
81 static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
85 static int do_virtio_net_can_receive(VirtIONet *n, int bufsize)
87 if (!virtio_queue_ready(n->rx_vq) ||
88 !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
89 return 0;
91 if (virtio_queue_empty(n->rx_vq) ||
92 (n->mergeable_rx_bufs &&
93 !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) {
94 virtio_queue_set_notification(n->rx_vq, 1);
95 return 0;
98 virtio_queue_set_notification(n->rx_vq, 0);
99 return 1;
102 static int virtio_net_can_receive(void *opaque)
104 VirtIONet *n = opaque;
106 return do_virtio_net_can_receive(n, VIRTIO_NET_MAX_BUFSIZE);
109 static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)
111 int offset, i;
113 offset = i = 0;
114 while (offset < count && i < iovcnt) {
115 int len = MIN(iov[i].iov_len, count - offset);
116 memcpy(iov[i].iov_base, buf + offset, len);
117 offset += len;
118 i++;
121 return offset;
124 static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
125 const void *buf, size_t size, size_t hdr_len)
127 struct virtio_net_hdr *hdr = iov[0].iov_base;
128 int offset = 0;
130 hdr->flags = 0;
131 hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
133 /* We only ever receive a struct virtio_net_hdr from the tapfd,
134 * but we may be passing along a larger header to the guest.
136 iov[0].iov_base += hdr_len;
137 iov[0].iov_len -= hdr_len;
139 return offset;
142 static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
144 VirtIONet *n = opaque;
145 struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL;
146 size_t hdr_len, offset, i;
148 if (!do_virtio_net_can_receive(n, size))
149 return;
151 /* hdr_len refers to the header we supply to the guest */
152 hdr_len = n->mergeable_rx_bufs ?
153 sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
155 offset = i = 0;
157 while (offset < size) {
158 VirtQueueElement elem;
159 int len, total;
160 struct iovec sg[VIRTQUEUE_MAX_SIZE];
162 len = total = 0;
164 if ((i != 0 && !n->mergeable_rx_bufs) ||
165 virtqueue_pop(n->rx_vq, &elem) == 0) {
166 if (i == 0)
167 return;
168 fprintf(stderr, "virtio-net truncating packet\n");
169 exit(1);
172 if (elem.in_num < 1) {
173 fprintf(stderr, "virtio-net receive queue contains no in buffers\n");
174 exit(1);
177 if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != hdr_len) {
178 fprintf(stderr, "virtio-net header not in first element\n");
179 exit(1);
182 memcpy(&sg, &elem.in_sg[0], sizeof(sg[0]) * elem.in_num);
184 if (i == 0) {
185 if (n->mergeable_rx_bufs)
186 mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base;
188 offset += receive_header(n, sg, elem.in_num,
189 buf + offset, size - offset, hdr_len);
190 total += hdr_len;
193 /* copy in packet. ugh */
194 len = iov_fill(sg, elem.in_num,
195 buf + offset, size - offset);
196 total += len;
198 /* signal other side */
199 virtqueue_fill(n->rx_vq, &elem, total, i++);
201 offset += len;
204 if (mhdr)
205 mhdr->num_buffers = i;
207 virtqueue_flush(n->rx_vq, i);
208 virtio_notify(&n->vdev, n->rx_vq);
211 /* TX */
212 static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
214 VirtQueueElement elem;
215 int has_vnet_hdr = 0;
217 if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
218 return;
220 while (virtqueue_pop(vq, &elem)) {
221 ssize_t len = 0;
222 unsigned int out_num = elem.out_num;
223 struct iovec *out_sg = &elem.out_sg[0];
224 unsigned hdr_len;
226 /* hdr_len refers to the header received from the guest */
227 hdr_len = n->mergeable_rx_bufs ?
228 sizeof(struct virtio_net_hdr_mrg_rxbuf) :
229 sizeof(struct virtio_net_hdr);
231 if (out_num < 1 || out_sg->iov_len != hdr_len) {
232 fprintf(stderr, "virtio-net header not in first element\n");
233 exit(1);
236 /* ignore the header if GSO is not supported */
237 if (!has_vnet_hdr) {
238 out_num--;
239 out_sg++;
240 len += hdr_len;
241 } else if (n->mergeable_rx_bufs) {
242 /* tapfd expects a struct virtio_net_hdr */
243 hdr_len -= sizeof(struct virtio_net_hdr);
244 out_sg->iov_len -= hdr_len;
245 len += hdr_len;
248 len += qemu_sendv_packet(n->vc, out_sg, out_num);
250 virtqueue_push(vq, &elem, len);
251 virtio_notify(&n->vdev, vq);
255 static void virtio_net_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
257 VirtIONet *n = to_virtio_net(vdev);
259 if (n->tx_timer_active) {
260 virtio_queue_set_notification(vq, 1);
261 qemu_del_timer(n->tx_timer);
262 n->tx_timer_active = 0;
263 virtio_net_flush_tx(n, vq);
264 } else {
265 qemu_mod_timer(n->tx_timer,
266 qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
267 n->tx_timer_active = 1;
268 virtio_queue_set_notification(vq, 0);
272 static void virtio_net_tx_timer(void *opaque)
274 VirtIONet *n = opaque;
276 n->tx_timer_active = 0;
278 /* Just in case the driver is not ready on more */
279 if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
280 return;
282 virtio_queue_set_notification(n->tx_vq, 1);
283 virtio_net_flush_tx(n, n->tx_vq);
286 static void virtio_net_save(QEMUFile *f, void *opaque)
288 VirtIONet *n = opaque;
290 virtio_save(&n->vdev, f);
292 qemu_put_buffer(f, n->mac, 6);
293 qemu_put_be32(f, n->tx_timer_active);
294 qemu_put_be32(f, n->mergeable_rx_bufs);
297 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
299 VirtIONet *n = opaque;
301 if (version_id != 2)
302 return -EINVAL;
304 virtio_load(&n->vdev, f);
306 qemu_get_buffer(f, n->mac, 6);
307 n->tx_timer_active = qemu_get_be32(f);
308 n->mergeable_rx_bufs = qemu_get_be32(f);
310 if (n->tx_timer_active) {
311 qemu_mod_timer(n->tx_timer,
312 qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
315 return 0;
318 PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
320 VirtIONet *n;
321 static int virtio_net_id;
323 n = (VirtIONet *)virtio_init_pci(bus, "virtio-net", 6900, 0x1000,
324 0, VIRTIO_ID_NET,
325 0x02, 0x00, 0x00,
326 sizeof(struct virtio_net_config),
327 sizeof(VirtIONet));
328 if (!n)
329 return NULL;
331 n->vdev.get_config = virtio_net_update_config;
332 n->vdev.get_features = virtio_net_get_features;
333 n->vdev.set_features = virtio_net_set_features;
334 n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
335 n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);
336 memcpy(n->mac, nd->macaddr, 6);
337 n->status = VIRTIO_NET_S_LINK_UP;
338 n->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
339 virtio_net_receive, virtio_net_can_receive, n);
340 n->vc->link_status_changed = virtio_net_set_link_status;
342 qemu_format_nic_info_str(n->vc, n->mac);
344 n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
345 n->tx_timer_active = 0;
346 n->mergeable_rx_bufs = 0;
348 register_savevm("virtio-net", virtio_net_id++, 2,
349 virtio_net_save, virtio_net_load, n);
351 return (PCIDevice *)n;