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: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2008-07-29 09:58:33 -05:00
parent dec6a2be08
commit 28fd6d7f95

View File

@ -41,6 +41,7 @@
#include "linux/virtio_net.h"
#include "linux/virtio_blk.h"
#include "linux/virtio_console.h"
#include "linux/virtio_rng.h"
#include "linux/virtio_ring.h"
#include "asm-x86/bootparam.h"
/*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 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. */
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",
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. */
/*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' },
{ "tunnet", 1, NULL, 't' },
{ "block", 1, NULL, 'b' },
{ "rng", 0, NULL, 'r' },
{ "initrd", 1, NULL, 'i' },
{ NULL },
};
@ -1822,6 +1909,9 @@ int main(int argc, char *argv[])
case 'b':
setup_block_file(optarg);
break;
case 'r':
setup_rng();
break;
case 'i':
initrd_name = optarg;
break;