void cons_transmitq_fn(void *_vq) // guest -> host
{
	struct virtio_vq *vq = _vq;
	uint32_t head;
	uint32_t olen, ilen;
	uint32_t i, j;
	struct iovec *iov;
	struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;

	if (!vq)
		errx(1,
			"\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__);

	// NOTE: The virtio_next_avail_vq_desc will not write more than
	//       vq->vring.num entries to iov, and the device implementation
	//       (virtio_mmio.c) will not allow the driver to set vq->vring.num to a
	//       value greater than QueueNumMax (vq->qnum_max), so you are safe as
	//       long as your iov is at least vq->qnum_max iovecs in size.
	iov = malloc(vq->qnum_max * sizeof(struct iovec));

	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);

	while (1) {
		// Get the buffers:
		head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);

		if (ilen) {
			// virtio-v1.0-cs04 s5.3.6.1 Device Operation (console section)
			VIRTIO_DRI_ERRX(vq->vqdev,
				"The driver placed a device-writeable buffer in the console device's transmitq.\n"
				"  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
		}
		// Process the buffers:
		for (i = 0; i < olen; ++i) {
			for (j = 0; j < iov[i].iov_len; ++j)
				printf("%c", ((char *)iov[i].iov_base)[j]);
		}
		fflush(stdout);

		// Add all the buffers to the used ring.
		// Pass 0 because we wrote nothing.
		virtio_add_used_desc(vq, head, 0);

		// Poke the guest however the mmio transport prefers
		// NOTE: assuming that the mmio transport was used for now
		virtio_mmio_set_vring_irq(dev);
		if (dev->poke_guest)
			dev->poke_guest(dev->vec);
		else
			VIRTIO_DEV_ERRX(vq->vqdev,
				"The host MUST provide a way for device interrupts to be sent to the guest. The 'poke_guest' function pointer on the vq->vqdev->transport_dev (assumed to be a struct virtio_mmio_dev) was not set.");
	}
	free(iov);
}
Exemple #2
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;
	int ret;
	int fd = open(data_path, O_RDWR);

	if (fd == -1)
		VIRTIO_DEV_ERRX(vq->vqdev, "Could not open data file for ether1.");

	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.
		 */
		for (int i = 0; i < olen; i++) {
			stripped = iov[i].iov_base + VIRTIO_HEADER_SIZE;
			ret = write(fd, stripped, iov[i].iov_len - VIRTIO_HEADER_SIZE);
			assert(ret == iov[i].iov_len - VIRTIO_HEADER_SIZE);
		}

		virtio_add_used_desc(vq, head, 0);

		virtio_mmio_set_vring_irq(dev);
		dev->poke_guest(dev->vec);
	}
}
Exemple #3
0
void *consin(void *arg)
{
	struct virtio_threadarg *a = arg;
	char *line, *outline;
	static char consline[128];
	static struct scatterlist iov[32];
	static struct scatterlist out[] = { {NULL, sizeof(outline)}, };
	static struct scatterlist in[] = { {NULL, sizeof(line)}, };

	static unsigned int inlen, outlen, conslen;
	struct virtqueue *v = a->arg->virtio;
	fprintf(stderr, "consin thread ..\n");
	uint16_t head, gaveit = 0, gotitback = 0;
	uint32_t vv;
	int i;
	int num;
	//char c[1];

	if (debug) fprintf(stderr, "Spin on console being read, print num queues, halt\n");

	for(num = 0;! quit;num++) {
		//int debug = 1;
		/* host: use any buffers we should have been sent. */
		head = wait_for_vq_desc(v, iov, &outlen, &inlen);
		if (debug)
			fprintf(stderr, "vq desc head %d, gaveit %d gotitback %d\n", head, gaveit, gotitback);
		for(i = 0; debug && i < outlen + inlen; i++)
			fprintf(stderr, "v[%d/%d] v %p len %d\n", i, outlen + inlen, iov[i].v, iov[i].length);
		if (debug)
			fprintf(stderr, "outlen is %d; inlen is %d\n", outlen, inlen);
		/* host: fill in the writeable buffers. */
		for (i = outlen; i < outlen + inlen; i++) {
			/* host: read a line. */
			memset(consline, 0, 128);
			if (read(0, consline, 1) < 0) {
				exit(0);
			}
			if (debug) fprintf(stderr, "CONSIN: GOT A LINE:%s:\n", consline);
			if (debug) fprintf(stderr, "CONSIN: OUTLEN:%d:\n", outlen);
			if (strlen(consline) < 3 && consline[0] == 'q' ) {
				quit = 1;
				break;
			}

			memmove(iov[i].v, consline, strlen(consline)+ 1);
			iov[i].length = strlen(consline) + 1;
		}
		if (debug) fprintf(stderr, "call add_used\n");
		/* host: now ack that we used them all. */
		add_used(v, head, outlen+inlen);
		/* turn off consdata - the IRQ injection isn't right */
		//consdata = 1;
		if (debug) fprintf(stderr, "DONE call add_used\n");

		// Send spurious for testing (Gan)
		set_posted_interrupt(0xE5);
		virtio_mmio_set_vring_irq();

		ros_syscall(SYS_vmm_poke_guest, 0, 0, 0, 0, 0, 0);
	}
	fprintf(stderr, "All done\n");
	return NULL;
}
Exemple #4
0
void blk_request(void *_vq)
{
	struct virtio_vq *vq = _vq;

	assert(vq != NULL);

	struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
	struct iovec *iov;
	uint32_t head;
	uint32_t olen, ilen;
	struct virtio_blk_outhdr *out;
	uint64_t offset;
	int64_t ret;
	size_t wlen;
	uint8_t *status;
	struct virtio_blk_config *cfg = vq->vqdev->cfg;

	if (vq->qready != 0x1)
		VIRTIO_DEV_ERRX(vq->vqdev,
		                "The service function for queue '%s' was launched before the driver set QueueReady to 0x1.",
		                 vq->name);

	if (!dev->poke_guest)
		VIRTIO_DEV_ERRX(vq->vqdev,
		                "The 'poke_guest' function pointer was not set.");

	iov = malloc(vq->qnum_max * sizeof(struct iovec));
	if (iov == NULL)
		VIRTIO_DEV_ERRX(vq->vqdev,
		                "malloc returned null trying to allocate iov.\n");

	for (;;) {
		head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
		/* There are always three iovecs.
		 * The first is the header.
		 * The second is the actual data.
		 * The third contains just the status byte.
		 */

		status = iov[2].iov_base;
		if (!status)
			VIRTIO_DEV_ERRX(vq->vqdev, "no room for status\n");

		out = iov[0].iov_base;
		if (out->type & VIRTIO_BLK_T_FLUSH)
			VIRTIO_DEV_ERRX(vq->vqdev, "Flush not supported.\n");

		offset = out->sector * 512;
		if (lseek64(diskfd, offset, SEEK_SET) != offset)
			VIRTIO_DEV_ERRX(vq->vqdev, "Bad seek at sector %llu\n",
			                out->sector);

		if (out->type & VIRTIO_BLK_T_OUT) {

			if ((offset + iov[1].iov_len) > (cfg->capacity * 512))
				VIRTIO_DEV_ERRX(vq->vqdev, "write past end of file!\n");

			ret = writev(diskfd, &iov[1], 1);

			if (ret >= 0 && ret == iov[1].iov_len)
				*status = VIRTIO_BLK_S_OK;
			else
				*status = VIRTIO_BLK_S_IOERR;
			wlen = sizeof(*status);
		} else {
			ret = readv(diskfd, &iov[1], 1);
			if (ret >= 0) {
				wlen = sizeof(*status) + ret;
				*status = VIRTIO_BLK_S_OK;
			} else {
				wlen = sizeof(*status);
				*status = VIRTIO_BLK_S_IOERR;
			}

			// Hexdump for debugging.
			if (debug_virtio_blk && ret >= 0) {
				char *pf = "";

				for (int i = 0; i < iov[olen].iov_len; i += 2) {
					uint8_t *p = (uint8_t *)iov[olen].iov_base + i;

					fprintf(stderr, "%s%02x", pf, *(p + 1));
					fprintf(stderr, "%02x", *p);
					fprintf(stderr, " ");
					pf = ((i + 2) % 16) ? " " : "\n";
				}
			}
		}

		virtio_add_used_desc(vq, head, wlen);
		virtio_mmio_set_vring_irq(dev);
		dev->poke_guest(dev->vec);
	}
}
void cons_receiveq_fn(void *_vq) // host -> guest
{
	struct virtio_vq *vq = _vq;
	uint32_t head;
	uint32_t olen, ilen;
	uint32_t i, j;
	int num_read;
	struct iovec *iov;
	struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;

	if (!vq)
		errx(1,
			"\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__);

	// NOTE: The virtio_next_avail_vq_desc will not write more than
	//       vq->vring.num entries to iov, and the device implementation
	//       (virtio_mmio.c) will not allow the driver to set vq->vring.num to a
	//       value greater than QueueNumMax (vq->qnum_max), so you are safe as
	//       long as your iov is at least vq->qnum_max iovecs in size.
	iov = malloc(vq->qnum_max * sizeof(struct iovec));

	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);

	// NOTE: This will block in 2 places:
	//       - reading from stdin
	//       - reading from eventfd in virtio_next_avail_vq_desc
	while (1) {
		head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);

		if (olen) {
			// virtio-v1.0-cs04 s5.3.6.1 Device Operation (console section)
			VIRTIO_DRI_ERRX(vq->vqdev,
				"The driver placed a device-readable buffer in the console device's receiveq.\n"
				"  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
		}

		// TODO: We may want to add some sort of console abort
		//       (e.g. type q and enter to quit)
		// readv from stdin as much as we can (to end of bufs or end of input)
		num_read = readv(0, iov, ilen);
		if (num_read < 0)
			VIRTIO_DEV_ERRX(vq->vqdev,
				"Encountered an error trying to read input from stdin (fd 0).");

		// You pass the number of bytes written to virtio_add_used_desc
		virtio_add_used_desc(vq, head, num_read);

		// Poke the guest however the mmio transport prefers
		// NOTE: assuming that the mmio transport was used for now.
		virtio_mmio_set_vring_irq(dev);
		if (dev->poke_guest)
			dev->poke_guest(dev->vec);
		else
			VIRTIO_DEV_ERRX(vq->vqdev,
				"The host MUST provide a way for device interrupts to be sent to the guest. The 'poke_guest' function pointer on the vq->vqdev->transport_dev (assumed to be a struct virtio_mmio_dev) was not set.");
	}
	free(iov);
}
Exemple #6
0
/* 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;
	int fd;
	struct virtio_net_hdr_v1 *net_header;

	fd = open(data_path, O_RDWR);
	if (fd == -1)
		VIRTIO_DEV_ERRX(vq->vqdev, "Could not open data file for ether1.");

	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");
		}

		/* For receive the virtio header is in iov[0], so we only want
		 * the packet to be read into iov[1] and above.
		 */
		num_read = readv(fd, iov + 1, ilen - 1);
		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 = iov[0].iov_base;
		net_header->num_buffers = 1;
		virtio_add_used_desc(vq, head, num_read + VIRTIO_HEADER_SIZE);

		virtio_mmio_set_vring_irq(dev);
		dev->poke_guest(dev->vec);
	}
}