| /* 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; | 
 | } |