Beispiel #1
0
int
vq_getchain(uint32_t offs, struct vqueue_info *vq,
	struct iovec *iov, int n_iov, uint16_t *flags)
{
	volatile struct vring_desc *vdir, *vindir, *vp;
	int idx, ndesc, n_indir;
	int head, next;
	int i;

	idx = vq->vq_last_avail;
	ndesc = (be16toh(vq->vq_avail->idx) - idx);
	if (ndesc == 0)
		return (0);

	head = be16toh(vq->vq_avail->ring[idx & (vq->vq_qsize - 1)]);
	next = head;

	for (i = 0; i < VQ_MAX_DESCRIPTORS; next = be16toh(vdir->next)) {
		vdir = &vq->vq_desc[next];
		if ((be16toh(vdir->flags) & VRING_DESC_F_INDIRECT) == 0) {
			_vq_record(offs, i, vdir, iov, n_iov, flags);
			i++;
		} else {
			n_indir = be32toh(vdir->len) / 16;
			vindir = paddr_map(offs, be64toh(vdir->addr),
					be32toh(vdir->len));
			next = 0;
			for (;;) {
				vp = &vindir[next];
				_vq_record(offs, i, vp, iov, n_iov, flags);
				i+=1;
				if ((be16toh(vp->flags) & \
					VRING_DESC_F_NEXT) == 0)
					break;
				next = be16toh(vp->next);
			}
			paddr_unmap((void *)vindir, be32toh(vdir->len));
		}

		if ((be16toh(vdir->flags) & VRING_DESC_F_NEXT) == 0)
			return (i);
	}

	return (i);
}
Beispiel #2
0
/*
 * Examine the chain of descriptors starting at the "next one" to
 * make sure that they describe a sensible request.  If so, return
 * the number of "real" descriptors that would be needed/used in
 * acting on this request.  This may be smaller than the number of
 * available descriptors, e.g., if there are two available but
 * they are two separate requests, this just returns 1.  Or, it
 * may be larger: if there are indirect descriptors involved,
 * there may only be one descriptor available but it may be an
 * indirect pointing to eight more.  We return 8 in this case,
 * i.e., we do not count the indirect descriptors, only the "real"
 * ones.
 *
 * Basically, this vets the vd_flags and vd_next field of each
 * descriptor and tells you how many are involved.  Since some may
 * be indirect, this also needs the vmctx (in the pci_devinst
 * at vs->vs_pi) so that it can find indirect descriptors.
 *
 * As we process each descriptor, we copy and adjust it (guest to
 * host address wise, also using the vmtctx) into the given iov[]
 * array (of the given size).  If the array overflows, we stop
 * placing values into the array but keep processing descriptors,
 * up to VQ_MAX_DESCRIPTORS, before giving up and returning -1.
 * So you, the caller, must not assume that iov[] is as big as the
 * return value (you can process the same thing twice to allocate
 * a larger iov array if needed, or supply a zero length to find
 * out how much space is needed).
 *
 * If you want to verify the WRITE flag on each descriptor, pass a
 * non-NULL "flags" pointer to an array of "uint16_t" of the same size
 * as n_iov and we'll copy each vd_flags field after unwinding any
 * indirects.
 *
 * If some descriptor(s) are invalid, this prints a diagnostic message
 * and returns -1.  If no descriptors are ready now it simply returns 0.
 *
 * You are assumed to have done a vq_ring_ready() if needed (note
 * that vq_has_descs() does one).
 */
int
vq_getchain(struct vqueue_info *vq,
	    struct iovec *iov, int n_iov, uint16_t *flags)
{
	int i;
	u_int ndesc, n_indir;
	u_int idx, head, next;
	volatile struct virtio_desc *vdir, *vindir, *vp;
	struct vmctx *ctx;
	struct virtio_softc *vs;
	const char *name;

	vs = vq->vq_vs;
	name = vs->vs_vc->vc_name;

	/*
	 * Note: it's the responsibility of the guest not to
	 * update vq->vq_avail->va_idx until all of the descriptors
         * the guest has written are valid (including all their
         * vd_next fields and vd_flags).
	 *
	 * Compute (last_avail - va_idx) in integers mod 2**16.  This is
	 * the number of descriptors the device has made available
	 * since the last time we updated vq->vq_last_avail.
	 *
	 * We just need to do the subtraction as an unsigned int,
	 * then trim off excess bits.
	 */
	idx = vq->vq_last_avail;
	ndesc = (uint16_t)((u_int)vq->vq_avail->va_idx - idx);
	if (ndesc == 0)
		return (0);
	if (ndesc > vq->vq_qsize) {
		/* XXX need better way to diagnose issues */
		fprintf(stderr,
		    "%s: ndesc (%u) out of range, driver confused?\r\n",
		    name, (u_int)ndesc);
		return (-1);
	}

	/*
	 * Now count/parse "involved" descriptors starting from
	 * the head of the chain.
	 *
	 * To prevent loops, we could be more complicated and
	 * check whether we're re-visiting a previously visited
	 * index, but we just abort if the count gets excessive.
	 */
	ctx = vs->vs_pi->pi_vmctx;
	head = vq->vq_avail->va_ring[idx & (vq->vq_qsize - 1)];
	next = head;
	for (i = 0; i < VQ_MAX_DESCRIPTORS; next = vdir->vd_next) {
		if (next >= vq->vq_qsize) {
			fprintf(stderr,
			    "%s: descriptor index %u out of range, "
			    "driver confused?\r\n",
			    name, next);
			return (-1);
		}
		vdir = &vq->vq_desc[next];
		if ((vdir->vd_flags & VRING_DESC_F_INDIRECT) == 0) {
			_vq_record(i, vdir, ctx, iov, n_iov, flags);
			i++;
		} else if ((vs->vs_negotiated_caps &
		    VIRTIO_RING_F_INDIRECT_DESC) == 0) {
			fprintf(stderr,
			    "%s: descriptor has forbidden INDIRECT flag, "
			    "driver confused?\r\n",
			    name);
			return (-1);
		} else {
			n_indir = vdir->vd_len / 16;
			if ((vdir->vd_len & 0xf) || n_indir == 0) {
				fprintf(stderr,
				    "%s: invalid indir len 0x%x, "
				    "driver confused?\r\n",
				    name, (u_int)vdir->vd_len);
				return (-1);
			}
			vindir = paddr_guest2host(ctx,
			    vdir->vd_addr, vdir->vd_len);
			/*
			 * Indirects start at the 0th, then follow
			 * their own embedded "next"s until those run
			 * out.  Each one's indirect flag must be off
			 * (we don't really have to check, could just
			 * ignore errors...).
			 */
			next = 0;
			for (;;) {
				vp = &vindir[next];
				if (vp->vd_flags & VRING_DESC_F_INDIRECT) {
					fprintf(stderr,
					    "%s: indirect desc has INDIR flag,"
					    " driver confused?\r\n",
					    name);
					return (-1);
				}
				_vq_record(i, vp, ctx, iov, n_iov, flags);
				if (++i > VQ_MAX_DESCRIPTORS)
					goto loopy;
				if ((vp->vd_flags & VRING_DESC_F_NEXT) == 0)
					break;
				next = vp->vd_next;
				if (next >= n_indir) {
					fprintf(stderr,
					    "%s: invalid next %u > %u, "
					    "driver confused?\r\n",
					    name, (u_int)next, n_indir);
					return (-1);
				}
			}
		}
		if ((vdir->vd_flags & VRING_DESC_F_NEXT) == 0)
			return (i);
	}
loopy:
	fprintf(stderr,
	    "%s: descriptor loop? count > %d - driver confused?\r\n",
	    name, i);
	return (-1);
}