Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 28fd6d7f authored by Rusty Russell's avatar Rusty Russell
Browse files

lguest: virtio-rng support



This is a simple patch to add support for the virtio "hardware random
generator" to lguest.  It gets about 1.2 MB/sec reading from /dev/hwrng
in the guest.

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent dec6a2be
Loading
Loading
Loading
Loading
+90 −0
Original line number Original line Diff line number Diff line
@@ -41,6 +41,7 @@
#include "linux/virtio_net.h"
#include "linux/virtio_net.h"
#include "linux/virtio_blk.h"
#include "linux/virtio_blk.h"
#include "linux/virtio_console.h"
#include "linux/virtio_console.h"
#include "linux/virtio_rng.h"
#include "linux/virtio_ring.h"
#include "linux/virtio_ring.h"
#include "asm-x86/bootparam.h"
#include "asm-x86/bootparam.h"
/*L:110 We can ignore the 39 include files we need for this program, but I do
/*L:110 We can ignore the 39 include files we need for this program, but I do
@@ -199,6 +200,33 @@ static void *_convert(struct iovec *iov, size_t size, size_t align,
#define le32_to_cpu(v32) (v32)
#define le32_to_cpu(v32) (v32)
#define le64_to_cpu(v64) (v64)
#define le64_to_cpu(v64) (v64)


/* Is this iovec empty? */
static bool iov_empty(const struct iovec iov[], unsigned int num_iov)
{
	unsigned int i;

	for (i = 0; i < num_iov; i++)
		if (iov[i].iov_len)
			return false;
	return true;
}

/* Take len bytes from the front of this iovec. */
static void iov_consume(struct iovec iov[], unsigned num_iov, unsigned len)
{
	unsigned int i;

	for (i = 0; i < num_iov; i++) {
		unsigned int used;

		used = iov[i].iov_len < len ? iov[i].iov_len : len;
		iov[i].iov_base += used;
		iov[i].iov_len -= used;
		len -= used;
	}
	assert(len == 0);
}

/* The device virtqueue descriptors are followed by feature bitmasks. */
/* The device virtqueue descriptors are followed by feature bitmasks. */
static u8 *get_feature_bits(struct device *dev)
static u8 *get_feature_bits(struct device *dev)
{
{
@@ -1678,6 +1706,64 @@ static void setup_block_file(const char *filename)
	verbose("device %u: virtblock %llu sectors\n",
	verbose("device %u: virtblock %llu sectors\n",
		devices.device_num, le64_to_cpu(conf.capacity));
		devices.device_num, le64_to_cpu(conf.capacity));
}
}

/* Our random number generator device reads from /dev/random into the Guest's
 * input buffers.  The usual case is that the Guest doesn't want random numbers
 * and so has no buffers although /dev/random is still readable, whereas
 * console is the reverse.
 *
 * The same logic applies, however. */
static bool handle_rng_input(int fd, struct device *dev)
{
	int len;
	unsigned int head, in_num, out_num, totlen = 0;
	struct iovec iov[dev->vq->vring.num];

	/* First we need a buffer from the Guests's virtqueue. */
	head = get_vq_desc(dev->vq, iov, &out_num, &in_num);

	/* If they're not ready for input, stop listening to this file
	 * descriptor.  We'll start again once they add an input buffer. */
	if (head == dev->vq->vring.num)
		return false;

	if (out_num)
		errx(1, "Output buffers in rng?");

	/* This is why we convert to iovecs: the readv() call uses them, and so
	 * it reads straight into the Guest's buffer.  We loop to make sure we
	 * fill it. */
	while (!iov_empty(iov, in_num)) {
		len = readv(dev->fd, iov, in_num);
		if (len <= 0)
			err(1, "Read from /dev/random gave %i", len);
		iov_consume(iov, in_num, len);
		totlen += len;
	}

	/* Tell the Guest about the new input. */
	add_used_and_trigger(fd, dev->vq, head, totlen);

	/* Everything went OK! */
	return true;
}

/* And this creates a "hardware" random number device for the Guest. */
static void setup_rng(void)
{
	struct device *dev;
	int fd;

	fd = open_or_die("/dev/random", O_RDONLY);

	/* The device responds to return from I/O thread. */
	dev = new_device("rng", VIRTIO_ID_RNG, fd, handle_rng_input);

	/* The device has one virtqueue, where the Guest places inbufs. */
	add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd);

	verbose("device %u: rng\n", devices.device_num++);
}
/* That's the end of device setup. */
/* That's the end of device setup. */


/*L:230 Reboot is pretty easy: clean up and exec() the Launcher afresh. */
/*L:230 Reboot is pretty easy: clean up and exec() the Launcher afresh. */
@@ -1748,6 +1834,7 @@ static struct option opts[] = {
	{ "verbose", 0, NULL, 'v' },
	{ "verbose", 0, NULL, 'v' },
	{ "tunnet", 1, NULL, 't' },
	{ "tunnet", 1, NULL, 't' },
	{ "block", 1, NULL, 'b' },
	{ "block", 1, NULL, 'b' },
	{ "rng", 0, NULL, 'r' },
	{ "initrd", 1, NULL, 'i' },
	{ "initrd", 1, NULL, 'i' },
	{ NULL },
	{ NULL },
};
};
@@ -1822,6 +1909,9 @@ int main(int argc, char *argv[])
		case 'b':
		case 'b':
			setup_block_file(optarg);
			setup_block_file(optarg);
			break;
			break;
		case 'r':
			setup_rng();
			break;
		case 'i':
		case 'i':
			initrd_name = optarg;
			initrd_name = optarg;
			break;
			break;