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:
parent
dec6a2be08
commit
28fd6d7f95
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user