/* To notify on status updates, we (ab)use the NOTIFY hypercall, with the
 * descriptor address of the device.  A zero status means "reset". */
static void set_status(struct virtio_device *vdev, u8 status)
{
	unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;

	/* We set the status. */
	to_lgdev(vdev)->desc->status = status;
	hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0);
}
Ejemplo n.º 2
0
/*
 * The virtio core takes the features the Host offers, and copies the ones
 * supported by the driver into the vdev->features array.  Once that's all
 * sorted out, this routine is called so we can tell the Host which features we
 * understand and accept.
 */
static void lg_finalize_features(struct virtio_device *vdev)
{
	unsigned int i, bits;
	struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
	/* Second half of bitmap is features we accept. */
	u8 *out_features = lg_features(desc) + desc->feature_len;

	/* Give virtio_ring a chance to accept features. */
	vring_transport_features(vdev);

	/*
	 * The vdev->feature array is a Linux bitmask: this isn't the same as a
	 * the simple array of bits used by lguest devices for features.  So we
	 * do this slow, manual conversion which is completely general.
	 */
	memset(out_features, 0, desc->feature_len);
	bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8;
	for (i = 0; i < bits; i++) {
		if (test_bit(i, vdev->features))
			out_features[i / 8] |= (1 << (i % 8));
	}

	/* Tell Host we've finished with this device's feature negotiation */
	status_notify(vdev);
}
Ejemplo n.º 3
0
static void lg_set_status(struct virtio_device *vdev, u8 status)
{
	BUG_ON(!status);
	to_lgdev(vdev)->desc->status = status;

	/* Tell Host immediately if we failed. */
	if (status & VIRTIO_CONFIG_S_FAILED)
		status_notify(vdev);
}
Ejemplo n.º 4
0
/* Setting the contents is also trivial. */
static void lg_set(struct virtio_device *vdev, unsigned int offset,
		   const void *buf, unsigned len)
{
	struct lguest_device_desc *desc = to_lgdev(vdev)->desc;

	/* Check they didn't ask for more than the length of the config! */
	BUG_ON(offset + len > desc->config_len);
	memcpy(lg_config(desc) + offset, buf, len);
}
Ejemplo n.º 5
0
/* This gets the device's feature bits. */
static u32 lg_get_features(struct virtio_device *vdev)
{
	unsigned int i;
	u32 features = 0;
	struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
	u8 *in_features = lg_features(desc);

	/* We do this the slow but generic way. */
	for (i = 0; i < min(desc->feature_len * 8, 32); i++)
		if (in_features[i / 8] & (1 << (i % 8)))
			features |= (1 << i);

	return features;
}
Ejemplo n.º 6
0
/* This tests (and acknowleges) a feature bit. */
static bool lg_feature(struct virtio_device *vdev, unsigned fbit)
{
	struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
	u8 *features;

	/* Obviously if they ask for a feature off the end of our feature
	 * bitmap, it's not set. */
	if (fbit / 8 > desc->feature_len)
		return false;

	/* The feature bitmap comes after the virtqueues. */
	features = lg_features(desc);
	if (!(features[fbit / 8] & (1 << (fbit % 8))))
		return false;

	/* We set the matching bit in the other half of the bitmap to tell the
	 * Host we want to use this feature.  We don't use this yet, but we
	 * could in future. */
	features[desc->feature_len + fbit / 8] |= (1 << (fbit % 8));
	return true;
}
Ejemplo n.º 7
0
static int lg_find_vqs(struct virtio_device *vdev, unsigned nvqs,
		       struct virtqueue *vqs[],
		       vq_callback_t *callbacks[],
		       const char *names[])
{
	struct lguest_device *ldev = to_lgdev(vdev);
	int i;

	/* We must have this many virtqueues. */
	if (nvqs > ldev->desc->num_vq)
		return -ENOENT;

	for (i = 0; i < nvqs; ++i) {
		vqs[i] = lg_find_vq(vdev, i, callbacks[i], names[i]);
		if (IS_ERR(vqs[i]))
			goto error;
	}
	return 0;

error:
	lg_del_vqs(vdev);
	return PTR_ERR(vqs[i]);
}
Ejemplo n.º 8
0
/*
 * This routine finds the Nth virtqueue described in the configuration of
 * this device and sets it up.
 *
 * This is kind of an ugly duckling.  It'd be nicer to have a standard
 * representation of a virtqueue in the configuration space, but it seems that
 * everyone wants to do it differently.  The KVM coders want the Guest to
 * allocate its own pages and tell the Host where they are, but for lguest it's
 * simpler for the Host to simply tell us where the pages are.
 */
static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
				    unsigned index,
				    void (*callback)(struct virtqueue *vq),
				    const char *name)
{
	struct lguest_device *ldev = to_lgdev(vdev);
	struct lguest_vq_info *lvq;
	struct virtqueue *vq;
	int err;

	if (!name)
		return NULL;

	/* We must have this many virtqueues. */
	if (index >= ldev->desc->num_vq)
		return ERR_PTR(-ENOENT);

	lvq = kmalloc(sizeof(*lvq), GFP_KERNEL);
	if (!lvq)
		return ERR_PTR(-ENOMEM);

	/*
	 * Make a copy of the "struct lguest_vqconfig" entry, which sits after
	 * the descriptor.  We need a copy because the config space might not
	 * be aligned correctly.
	 */
	memcpy(&lvq->config, lg_vq(ldev->desc)+index, sizeof(lvq->config));

	printk("Mapping virtqueue %i addr %lx\n", index,
	       (unsigned long)lvq->config.pfn << PAGE_SHIFT);
	/* Figure out how many pages the ring will take, and map that memory */
	lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT,
				DIV_ROUND_UP(vring_size(lvq->config.num,
							LGUEST_VRING_ALIGN),
					     PAGE_SIZE));
	if (!lvq->pages) {
		err = -ENOMEM;
		goto free_lvq;
	}

	/*
	 * OK, tell virtio_ring.c to set up a virtqueue now we know its size
	 * and we've got a pointer to its pages.  Note that we set weak_barriers
	 * to 'true': the host just a(nother) SMP CPU, so we only need inter-cpu
	 * barriers.
	 */
	vq = vring_new_virtqueue(index, lvq->config.num, LGUEST_VRING_ALIGN, vdev,
				 true, lvq->pages, lg_notify, callback, name);
	if (!vq) {
		err = -ENOMEM;
		goto unmap;
	}

	/* Make sure the interrupt is allocated. */
	err = lguest_setup_irq(lvq->config.irq);
	if (err)
		goto destroy_vring;

	/*
	 * Tell the interrupt for this virtqueue to go to the virtio_ring
	 * interrupt handler.
	 *
	 * FIXME: We used to have a flag for the Host to tell us we could use
	 * the interrupt as a source of randomness: it'd be nice to have that
	 * back.
	 */
	err = request_irq(lvq->config.irq, vring_interrupt, IRQF_SHARED,
			  dev_name(&vdev->dev), vq);
	if (err)
		goto free_desc;

	/*
	 * Last of all we hook up our 'struct lguest_vq_info" to the
	 * virtqueue's priv pointer.
	 */
	vq->priv = lvq;
	return vq;

free_desc:
	irq_free_desc(lvq->config.irq);
destroy_vring:
	vring_del_virtqueue(vq);
unmap:
	lguest_unmap(lvq->pages);
free_lvq:
	kfree(lvq);
	return ERR_PTR(err);
}
Ejemplo n.º 9
0
static void lg_reset(struct virtio_device *vdev)
{
	/* 0 status means "reset" */
	to_lgdev(vdev)->desc->status = 0;
	status_notify(vdev);
}
Ejemplo n.º 10
0
/*
 * The operations to get and set the status word just access the status field
 * of the device descriptor.
 */
static u8 lg_get_status(struct virtio_device *vdev)
{
	return to_lgdev(vdev)->desc->status;
}
Ejemplo n.º 11
0
/*
 * To notify on reset or feature finalization, we (ab)use the NOTIFY
 * hypercall, with the descriptor address of the device.
 */
static void status_notify(struct virtio_device *vdev)
{
	unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;

	hcall(LHCALL_NOTIFY, (max_pfn << PAGE_SHIFT) + offset, 0, 0, 0);
}
Ejemplo n.º 12
0
static void lg_set_status(struct virtio_device *vdev, u8 status)
{
	BUG_ON(!status);
	to_lgdev(vdev)->desc->status = status;
}