blob: 96ef5e644765755d935fa29372f139d8ad596a31 [file] [log] [blame]
/* Virtio helper functions from linux/tools/lguest/lguest.c
*
* Copyright (C) 1991-2016, the Linux Kernel authors
* Copyright (c) 2016 Google Inc.
*
* Author:
* Rusty Russell <rusty@rustcorp.com.au>
* Kyle Milka <kmilka@google.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* The code from lguest.c has been modified for Akaros.
*
* Original linux/tools/lguest/lguest.c:
* https://github.com/torvalds/linux/blob/v4.5/tools/lguest/lguest.c
* most recent hash on the file as of v4.5 tag:
* e523caa601f4a7c2fa1ecd040db921baf7453798
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <vmm/virtio.h>
#include <vmm/virtio_mmio.h>
#include <vmm/virtio_net.h>
#include <vmm/net.h>
#include <parlib/iovec.h>
#include <iplib/iplib.h>
#define VIRTIO_HEADER_SIZE 12
void virtio_net_set_mac(struct virtio_vq_dev *vqdev, uint8_t *guest_mac)
{
memcpy(((struct virtio_net_config*)(vqdev->cfg))->mac, guest_mac,
ETH_ADDR_LEN);
memcpy(((struct virtio_net_config*)(vqdev->cfg_d))->mac, guest_mac,
ETH_ADDR_LEN);
}
/* net_receiveq_fn receives packets for the guest through the virtio networking
* device and the _vq virtio queue.
*/
void *net_receiveq_fn(void *_vq)
{
struct virtio_vq *vq = _vq;
uint32_t head;
uint32_t olen, ilen;
int num_read;
struct iovec *iov;
struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
struct virtio_net_hdr_v1 *net_header;
if (!vq)
VIRTIO_DEV_ERRX(vq->vqdev,
"\n %s:%d\n"
" Virtio device: (not sure which one): Error, device behavior.\n"
" The device must provide a valid virtio_vq as an argument to %s."
, __FILE__, __LINE__, __func__);
if (vq->qready == 0x0)
VIRTIO_DEV_ERRX(vq->vqdev,
"The service function for queue '%s' was launched before the driver set QueueReady to 0x1.",
vq->name);
iov = malloc(vq->qnum_max * sizeof(struct iovec));
assert(iov != NULL);
if (!dev->poke_guest) {
free(iov);
VIRTIO_DEV_ERRX(vq->vqdev,
"The 'poke_guest' function pointer was not set.");
}
for (;;) {
head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
if (olen) {
free(iov);
VIRTIO_DRI_ERRX(vq->vqdev,
"The driver placed a device-readable buffer in the net device's receiveq.\n"
" See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
}
/* The virtio_net header comes first. We'll assume they didn't
* break the header across IOVs, but earlier versions of Linux
* did break out the payload into the second IOV. */
net_header = iov[0].iov_base;
assert(iov[0].iov_len >= VIRTIO_HEADER_SIZE);
iov_strip_bytes(iov, ilen, VIRTIO_HEADER_SIZE);
num_read = vnet_receive_packet(iov, ilen);
if (num_read < 0) {
free(iov);
VIRTIO_DEV_ERRX(vq->vqdev,
"Encountered an error trying to read input from the ethernet device.");
}
/* See virtio spec virtio-v1.0-cs04 s5.1.6.3.2 Device
* Requirements: Setting Up Receive Buffers
*
* VIRTIO_NET_F_MRG_RXBUF is not currently negotiated.
* num_buffers will always be 1 if VIRTIO_NET_F_MRG_RXBUF is not
* negotiated.
*/
net_header->num_buffers = 1;
net_header->flags = 0;
net_header->gso_type = VIRTIO_NET_HDR_GSO_NONE;
virtio_add_used_desc(vq, head, num_read + VIRTIO_HEADER_SIZE);
virtio_mmio_set_vring_irq(dev);
dev->poke_guest(dev->vec, dev->dest);
}
return 0;
}
/* net_transmitq_fn transmits packets from the guest through the virtio
* networking device through the _vq virtio queue.
*/
void *net_transmitq_fn(void *_vq)
{
struct virtio_vq *vq = _vq;
uint32_t head;
uint32_t olen, ilen;
struct iovec *iov;
struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
void *stripped;
iov = malloc(vq->qnum_max * sizeof(struct iovec));
assert(iov != NULL);
if (!dev->poke_guest) {
free(iov);
VIRTIO_DEV_ERRX(vq->vqdev,
"The 'poke_guest' function pointer was not set.");
}
for (;;) {
head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
if (ilen) {
free(iov);
VIRTIO_DRI_ERRX(vq->vqdev,
"The driver placed a device-writeable buffer in the network device's transmitq.\n"
" See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
}
/* Strip off the virtio header (the first 12 bytes), as it is
* not a part of the actual ethernet frame. */
iov_strip_bytes(iov, olen, VIRTIO_HEADER_SIZE);
vnet_transmit_packet(iov, olen);
virtio_add_used_desc(vq, head, 0);
virtio_mmio_set_vring_irq(dev);
dev->poke_guest(dev->vec, dev->dest);
}
return 0;
}