qemu/pc-bios/s390-ccw/virtio-net.c
<<
>>
Prefs
   1/*
   2 * Virtio-net driver for the s390-ccw firmware
   3 *
   4 * Copyright 2017 Thomas Huth, Red Hat Inc.
   5 *
   6 * This code is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License as published by the
   8 * Free Software Foundation; either version 2 of the License, or (at your
   9 * option) any later version.
  10 */
  11
  12#include <stdint.h>
  13#include <stdbool.h>
  14#include <stdio.h>
  15#include <stdlib.h>
  16#include <string.h>
  17#include <unistd.h>
  18#include <sys/socket.h>
  19#include <ethernet.h>
  20#include "s390-ccw.h"
  21#include "virtio.h"
  22#include "s390-time.h"
  23#include "helper.h"
  24
  25#ifndef DEBUG_VIRTIO_NET
  26#define DEBUG_VIRTIO_NET 0
  27#endif
  28
  29#define VIRTIO_NET_F_MAC_BIT  (1 << 5)
  30
  31#define VQ_RX 0         /* Receive queue */
  32#define VQ_TX 1         /* Transmit queue */
  33
  34struct VirtioNetHdr {
  35    uint8_t flags;
  36    uint8_t gso_type;
  37    uint16_t hdr_len;
  38    uint16_t gso_size;
  39    uint16_t csum_start;
  40    uint16_t csum_offset;
  41    /*uint16_t num_buffers;*/ /* Only with VIRTIO_NET_F_MRG_RXBUF or VIRTIO1 */
  42};
  43typedef struct VirtioNetHdr VirtioNetHdr;
  44
  45static uint16_t rx_last_idx;  /* Last index in receive queue "used" ring */
  46
  47int virtio_net_init(void *mac_addr)
  48{
  49    VDev *vdev = virtio_get_device();
  50    VRing *rxvq = &vdev->vrings[VQ_RX];
  51    void *buf;
  52    int i;
  53
  54    vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
  55    virtio_setup_ccw(vdev);
  56
  57    IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT,
  58               "virtio-net device does not support the MAC address feature");
  59    memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
  60
  61    for (i = 0; i < 64; i++) {
  62        buf = malloc(ETH_MTU_SIZE + sizeof(VirtioNetHdr));
  63        IPL_assert(buf != NULL, "Can not allocate memory for receive buffers");
  64        vring_send_buf(rxvq, buf, ETH_MTU_SIZE + sizeof(VirtioNetHdr),
  65                       VRING_DESC_F_WRITE);
  66    }
  67    vring_notify(rxvq);
  68
  69    return 0;
  70}
  71
  72int send(int fd, const void *buf, int len, int flags)
  73{
  74    VirtioNetHdr tx_hdr;
  75    VDev *vdev = virtio_get_device();
  76    VRing *txvq = &vdev->vrings[VQ_TX];
  77
  78    /* Set up header - we do not use anything special, so simply clear it */
  79    memset(&tx_hdr, 0, sizeof(tx_hdr));
  80
  81    vring_send_buf(txvq, &tx_hdr, sizeof(tx_hdr), VRING_DESC_F_NEXT);
  82    vring_send_buf(txvq, (void *)buf, len, VRING_HIDDEN_IS_CHAIN);
  83    while (!vr_poll(txvq)) {
  84        yield();
  85    }
  86    if (drain_irqs(txvq->schid)) {
  87        puts("send: drain irqs failed");
  88        return -1;
  89    }
  90
  91    return len;
  92}
  93
  94int recv(int fd, void *buf, int maxlen, int flags)
  95{
  96    VDev *vdev = virtio_get_device();
  97    VRing *rxvq = &vdev->vrings[VQ_RX];
  98    int len, id;
  99    uint8_t *pkt;
 100
 101    if (rx_last_idx == rxvq->used->idx) {
 102        return 0;
 103    }
 104
 105    len = rxvq->used->ring[rx_last_idx % rxvq->num].len - sizeof(VirtioNetHdr);
 106    if (len > maxlen) {
 107        puts("virtio-net: Receive buffer too small");
 108        len = maxlen;
 109    }
 110    id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num;
 111    pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr));
 112
 113#if DEBUG_VIRTIO_NET   /* Dump packet */
 114    int i;
 115    printf("\nbuf %p: len=%i\n", (void *)rxvq->desc[id].addr, len);
 116    for (i = 0; i < 64; i++) {
 117        printf(" %02x", pkt[i]);
 118        if ((i % 16) == 15) {
 119            printf("\n");
 120        }
 121    }
 122    printf("\n");
 123#endif
 124
 125    /* Copy data to destination buffer */
 126    memcpy(buf, pkt, len);
 127
 128    /* Mark buffer as available to the host again */
 129    rxvq->avail->ring[rxvq->avail->idx % rxvq->num] = id;
 130    rxvq->avail->idx = rxvq->avail->idx + 1;
 131    vring_notify(rxvq);
 132
 133    /* Move index to next entry */
 134    rx_last_idx = rx_last_idx + 1;
 135
 136    return len;
 137}
 138