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