symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/virtio-net.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * Virtio Network Device
       
     3  *
       
     4  * Copyright IBM, Corp. 2007
       
     5  *
       
     6  * Authors:
       
     7  *  Anthony Liguori   <aliguori@us.ibm.com>
       
     8  *
       
     9  * This work is licensed under the terms of the GNU GPL, version 2.  See
       
    10  * the COPYING file in the top-level directory.
       
    11  *
       
    12  */
       
    13 
       
    14 #include "virtio.h"
       
    15 #include "net.h"
       
    16 #include "qemu-timer.h"
       
    17 #include "virtio-net.h"
       
    18 
       
    19 typedef struct VirtIONet
       
    20 {
       
    21     VirtIODevice vdev;
       
    22     uint8_t mac[6];
       
    23     VirtQueue *rx_vq;
       
    24     VirtQueue *tx_vq;
       
    25     VLANClientState *vc;
       
    26     QEMUTimer *tx_timer;
       
    27     int tx_timer_active;
       
    28     int mergeable_rx_bufs;
       
    29 } VirtIONet;
       
    30 
       
    31 /* TODO
       
    32  * - we could suppress RX interrupt if we were so inclined.
       
    33  */
       
    34 
       
    35 static VirtIONet *to_virtio_net(VirtIODevice *vdev)
       
    36 {
       
    37     return (VirtIONet *)vdev;
       
    38 }
       
    39 
       
    40 static void virtio_net_update_config(VirtIODevice *vdev, uint8_t *config)
       
    41 {
       
    42     VirtIONet *n = to_virtio_net(vdev);
       
    43     struct virtio_net_config netcfg;
       
    44 
       
    45     memcpy(netcfg.mac, n->mac, 6);
       
    46     memcpy(config, &netcfg, sizeof(netcfg));
       
    47 }
       
    48 
       
    49 static uint32_t virtio_net_get_features(VirtIODevice *vdev)
       
    50 {
       
    51     uint32_t features = (1 << VIRTIO_NET_F_MAC);
       
    52 
       
    53     return features;
       
    54 }
       
    55 
       
    56 static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
       
    57 {
       
    58     VirtIONet *n = to_virtio_net(vdev);
       
    59 
       
    60     n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF));
       
    61 }
       
    62 
       
    63 /* RX */
       
    64 
       
    65 static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
       
    66 {
       
    67 }
       
    68 
       
    69 static int do_virtio_net_can_receive(VirtIONet *n, int bufsize)
       
    70 {
       
    71     if (!virtio_queue_ready(n->rx_vq) ||
       
    72         !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
       
    73         return 0;
       
    74 
       
    75     if (virtio_queue_empty(n->rx_vq) ||
       
    76         (n->mergeable_rx_bufs &&
       
    77          !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) {
       
    78         virtio_queue_set_notification(n->rx_vq, 1);
       
    79         return 0;
       
    80     }
       
    81 
       
    82     virtio_queue_set_notification(n->rx_vq, 0);
       
    83     return 1;
       
    84 }
       
    85 
       
    86 static int virtio_net_can_receive(void *opaque)
       
    87 {
       
    88     VirtIONet *n = opaque;
       
    89 
       
    90     return do_virtio_net_can_receive(n, VIRTIO_NET_MAX_BUFSIZE);
       
    91 }
       
    92 
       
    93 static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)
       
    94 {
       
    95     int offset, i;
       
    96 
       
    97     offset = i = 0;
       
    98     while (offset < count && i < iovcnt) {
       
    99         int len = MIN(iov[i].iov_len, count - offset);
       
   100         memcpy(iov[i].iov_base, buf + offset, len);
       
   101         offset += len;
       
   102         i++;
       
   103     }
       
   104 
       
   105     return offset;
       
   106 }
       
   107 
       
   108 static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
       
   109                           const void *buf, size_t size, size_t hdr_len)
       
   110 {
       
   111     struct virtio_net_hdr *hdr = iov[0].iov_base;
       
   112     int offset = 0;
       
   113 
       
   114     hdr->flags = 0;
       
   115     hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
       
   116 
       
   117     /* We only ever receive a struct virtio_net_hdr from the tapfd,
       
   118      * but we may be passing along a larger header to the guest.
       
   119      */
       
   120     iov[0].iov_base += hdr_len;
       
   121     iov[0].iov_len  -= hdr_len;
       
   122 
       
   123     return offset;
       
   124 }
       
   125 
       
   126 static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
       
   127 {
       
   128     VirtIONet *n = opaque;
       
   129     struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL;
       
   130     size_t hdr_len, offset, i;
       
   131 
       
   132     if (!do_virtio_net_can_receive(n, size))
       
   133         return;
       
   134 
       
   135     /* hdr_len refers to the header we supply to the guest */
       
   136     hdr_len = n->mergeable_rx_bufs ?
       
   137         sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
       
   138 
       
   139     offset = i = 0;
       
   140 
       
   141     while (offset < size) {
       
   142         VirtQueueElement elem;
       
   143         int len, total;
       
   144         struct iovec sg[VIRTQUEUE_MAX_SIZE];
       
   145 
       
   146         len = total = 0;
       
   147 
       
   148         if ((i != 0 && !n->mergeable_rx_bufs) ||
       
   149             virtqueue_pop(n->rx_vq, &elem) == 0) {
       
   150             if (i == 0)
       
   151                 return;
       
   152             fprintf(stderr, "virtio-net truncating packet\n");
       
   153             exit(1);
       
   154         }
       
   155 
       
   156         if (elem.in_num < 1) {
       
   157             fprintf(stderr, "virtio-net receive queue contains no in buffers\n");
       
   158             exit(1);
       
   159         }
       
   160 
       
   161         if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != hdr_len) {
       
   162             fprintf(stderr, "virtio-net header not in first element\n");
       
   163             exit(1);
       
   164         }
       
   165 
       
   166         memcpy(&sg, &elem.in_sg[0], sizeof(sg[0]) * elem.in_num);
       
   167 
       
   168         if (i == 0) {
       
   169             if (n->mergeable_rx_bufs)
       
   170                 mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base;
       
   171 
       
   172             offset += receive_header(n, sg, elem.in_num,
       
   173                                      buf + offset, size - offset, hdr_len);
       
   174             total += hdr_len;
       
   175         }
       
   176 
       
   177         /* copy in packet.  ugh */
       
   178         len = iov_fill(sg, elem.in_num,
       
   179                        buf + offset, size - offset);
       
   180         total += len;
       
   181 
       
   182         /* signal other side */
       
   183         virtqueue_fill(n->rx_vq, &elem, total, i++);
       
   184 
       
   185         offset += len;
       
   186     }
       
   187 
       
   188     if (mhdr)
       
   189         mhdr->num_buffers = i;
       
   190 
       
   191     virtqueue_flush(n->rx_vq, i);
       
   192     virtio_notify(&n->vdev, n->rx_vq);
       
   193 }
       
   194 
       
   195 /* TX */
       
   196 static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
       
   197 {
       
   198     VirtQueueElement elem;
       
   199     int has_vnet_hdr = 0;
       
   200 
       
   201     if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
       
   202         return;
       
   203 
       
   204     while (virtqueue_pop(vq, &elem)) {
       
   205         ssize_t len = 0;
       
   206         unsigned int out_num = elem.out_num;
       
   207         struct iovec *out_sg = &elem.out_sg[0];
       
   208         unsigned hdr_len;
       
   209 
       
   210         /* hdr_len refers to the header received from the guest */
       
   211         hdr_len = n->mergeable_rx_bufs ?
       
   212             sizeof(struct virtio_net_hdr_mrg_rxbuf) :
       
   213             sizeof(struct virtio_net_hdr);
       
   214 
       
   215         if (out_num < 1 || out_sg->iov_len != hdr_len) {
       
   216             fprintf(stderr, "virtio-net header not in first element\n");
       
   217             exit(1);
       
   218         }
       
   219 
       
   220         /* ignore the header if GSO is not supported */
       
   221         if (!has_vnet_hdr) {
       
   222             out_num--;
       
   223             out_sg++;
       
   224             len += hdr_len;
       
   225         } else if (n->mergeable_rx_bufs) {
       
   226             /* tapfd expects a struct virtio_net_hdr */
       
   227             hdr_len -= sizeof(struct virtio_net_hdr);
       
   228             out_sg->iov_len -= hdr_len;
       
   229             len += hdr_len;
       
   230         }
       
   231 
       
   232         len += qemu_sendv_packet(n->vc, out_sg, out_num);
       
   233 
       
   234         virtqueue_push(vq, &elem, len);
       
   235         virtio_notify(&n->vdev, vq);
       
   236     }
       
   237 }
       
   238 
       
   239 static void virtio_net_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
       
   240 {
       
   241     VirtIONet *n = to_virtio_net(vdev);
       
   242 
       
   243     if (n->tx_timer_active) {
       
   244         virtio_queue_set_notification(vq, 1);
       
   245         qemu_del_timer(n->tx_timer);
       
   246         n->tx_timer_active = 0;
       
   247         virtio_net_flush_tx(n, vq);
       
   248     } else {
       
   249         qemu_mod_timer(n->tx_timer,
       
   250                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
       
   251         n->tx_timer_active = 1;
       
   252         virtio_queue_set_notification(vq, 0);
       
   253     }
       
   254 }
       
   255 
       
   256 static void virtio_net_tx_timer(void *opaque)
       
   257 {
       
   258     VirtIONet *n = opaque;
       
   259 
       
   260     n->tx_timer_active = 0;
       
   261 
       
   262     /* Just in case the driver is not ready on more */
       
   263     if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
       
   264         return;
       
   265 
       
   266     virtio_queue_set_notification(n->tx_vq, 1);
       
   267     virtio_net_flush_tx(n, n->tx_vq);
       
   268 }
       
   269 
       
   270 static void virtio_net_save(QEMUFile *f, void *opaque)
       
   271 {
       
   272     VirtIONet *n = opaque;
       
   273 
       
   274     virtio_save(&n->vdev, f);
       
   275 
       
   276     qemu_put_buffer(f, n->mac, 6);
       
   277     qemu_put_be32(f, n->tx_timer_active);
       
   278 }
       
   279 
       
   280 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
       
   281 {
       
   282     VirtIONet *n = opaque;
       
   283 
       
   284     if (version_id != 1)
       
   285         return -EINVAL;
       
   286 
       
   287     virtio_load(&n->vdev, f);
       
   288 
       
   289     qemu_get_buffer(f, n->mac, 6);
       
   290     n->tx_timer_active = qemu_get_be32(f);
       
   291 
       
   292     if (n->tx_timer_active) {
       
   293         qemu_mod_timer(n->tx_timer,
       
   294                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
       
   295     }
       
   296 
       
   297     return 0;
       
   298 }
       
   299 
       
   300 void virtio_net_init(VirtIOBindFn bind, void *bind_arg, NICInfo *nd)
       
   301 {
       
   302     VirtIONet *n;
       
   303     static int virtio_net_id;
       
   304 
       
   305     n = (VirtIONet *)bind(bind_arg, "virtio-net", 0, VIRTIO_ID_NET,
       
   306                           6, sizeof(VirtIONet));
       
   307     if (!n)
       
   308         return;
       
   309 
       
   310     n->vdev.get_config = virtio_net_update_config;
       
   311     n->vdev.get_features = virtio_net_get_features;
       
   312     n->vdev.set_features = virtio_net_set_features;
       
   313     n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
       
   314     n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);
       
   315     memcpy(n->mac, nd->macaddr, 6);
       
   316     n->vc = qemu_new_vlan_client(nd->vlan, virtio_net_receive,
       
   317                                  virtio_net_can_receive, n);
       
   318 
       
   319     n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
       
   320     n->tx_timer_active = 0;
       
   321     n->mergeable_rx_bufs = 0;
       
   322 
       
   323     register_savevm("virtio-net", virtio_net_id++, 1,
       
   324                     virtio_net_save, virtio_net_load, n);
       
   325 }