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
  23#ifndef DEBUG_VIRTIO_NET
  24#define DEBUG_VIRTIO_NET 0
  25#endif
  26
  27#define VIRTIO_NET_F_MAC_BIT  (1 << 5)
  28
  29#define VQ_RX 0         /* Receive queue */
  30#define VQ_TX 1         /* Transmit queue */
  31
  32struct VirtioNetHdr {
  33    uint8_t flags;
  34    uint8_t gso_type;
  35    uint16_t hdr_len;
  36    uint16_t gso_size;
  37    uint16_t csum_start;
  38    uint16_t csum_offset;
  39    /*uint16_t num_buffers;*/ /* Only with VIRTIO_NET_F_MRG_RXBUF or VIRTIO1 */
  40};
  41typedef struct VirtioNetHdr VirtioNetHdr;
  42
  43static uint16_t rx_last_idx;  /* Last index in receive queue "used" ring */
  44
  45int virtio_net_init(void *mac_addr)
  46{
  47    VDev *vdev = virtio_get_device();
  48    VRing *rxvq = &vdev->vrings[VQ_RX];
  49    void *buf;
  50    int i;
  51
  52    vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
  53    virtio_setup_ccw(vdev);
  54
  55    IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT,
  56               "virtio-net device does not support the MAC address feature");
  57    memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
  58
  59    for (i = 0; i < 64; i++) {
  60        buf = malloc(ETH_MTU_SIZE + sizeof(VirtioNetHdr));
  61        IPL_assert(buf != NULL, "Can not allocate memory for receive buffers");
  62        vring_send_buf(rxvq, buf, ETH_MTU_SIZE + sizeof(VirtioNetHdr),
  63                       VRING_DESC_F_WRITE);
  64    }
  65    vring_notify(rxvq);
  66
  67    return 0;
  68}
  69
  70int send(int fd, const void *buf, int len, int flags)
  71{
  72    VirtioNetHdr tx_hdr;
  73    VDev *vdev = virtio_get_device();
  74    VRing *txvq = &vdev->vrings[VQ_TX];
  75
  76    /* Set up header - we do not use anything special, so simply clear it */
  77    memset(&tx_hdr, 0, sizeof(tx_hdr));
  78
  79    vring_send_buf(txvq, &tx_hdr, sizeof(tx_hdr), VRING_DESC_F_NEXT);
  80    vring_send_buf(txvq, (void *)buf, len, VRING_HIDDEN_IS_CHAIN);
  81    while (!vr_poll(txvq)) {
  82        yield();
  83    }
  84    if (drain_irqs(txvq->schid)) {
  85        puts("send: drain irqs failed");
  86        return -1;
  87    }
  88
  89    return len;
  90}
  91
  92int recv(int fd, void *buf, int maxlen, int flags)
  93{
  94    VDev *vdev = virtio_get_device();
  95    VRing *rxvq = &vdev->vrings[VQ_RX];
  96    int len, id;
  97    uint8_t *pkt;
  98
  99    if (rx_last_idx == rxvq->used->idx) {
 100        return 0;
 101    }
 102
 103    len = rxvq->used->ring[rx_last_idx % rxvq->num].len - sizeof(VirtioNetHdr);
 104    if (len > maxlen) {
 105        puts("virtio-net: Receive buffer too small");
 106        len = maxlen;
 107    }
 108    id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num;
 109    pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr));
 110
 111#if DEBUG_VIRTIO_NET   /* Dump packet */
 112    int i;
 113    printf("\nbuf %p: len=%i\n", (void *)rxvq->desc[id].addr, len);
 114    for (i = 0; i < 64; i++) {
 115        printf(" %02x", pkt[i]);
 116        if ((i % 16) == 15) {
 117            printf("\n");
 118        }
 119    }
 120    printf("\n");
 121#endif
 122
 123    /* Copy data to destination buffer */
 124    memcpy(buf, pkt, len);
 125
 126    /* Mark buffer as available to the host again */
 127    rxvq->avail->ring[rxvq->avail->idx % rxvq->num] = id;
 128    rxvq->avail->idx = rxvq->avail->idx + 1;
 129    vring_notify(rxvq);
 130
 131    /* Move index to next entry */
 132    rx_last_idx = rx_last_idx + 1;
 133
 134    return len;
 135}
 136