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>
···4141#include "linux/virtio_net.h"4242#include "linux/virtio_blk.h"4343#include "linux/virtio_console.h"4444+#include "linux/virtio_rng.h"4445#include "linux/virtio_ring.h"4546#include "asm-x86/bootparam.h"4647/*L:110 We can ignore the 39 include files we need for this program, but I do···199198#define le16_to_cpu(v16) (v16)200199#define le32_to_cpu(v32) (v32)201200#define le64_to_cpu(v64) (v64)201201+202202+/* Is this iovec empty? */203203+static bool iov_empty(const struct iovec iov[], unsigned int num_iov)204204+{205205+ unsigned int i;206206+207207+ for (i = 0; i < num_iov; i++)208208+ if (iov[i].iov_len)209209+ return false;210210+ return true;211211+}212212+213213+/* Take len bytes from the front of this iovec. */214214+static void iov_consume(struct iovec iov[], unsigned num_iov, unsigned len)215215+{216216+ unsigned int i;217217+218218+ for (i = 0; i < num_iov; i++) {219219+ unsigned int used;220220+221221+ used = iov[i].iov_len < len ? iov[i].iov_len : len;222222+ iov[i].iov_base += used;223223+ iov[i].iov_len -= used;224224+ len -= used;225225+ }226226+ assert(len == 0);227227+}202228203229/* The device virtqueue descriptors are followed by feature bitmasks. */204230static u8 *get_feature_bits(struct device *dev)···17061678 verbose("device %u: virtblock %llu sectors\n",17071679 devices.device_num, le64_to_cpu(conf.capacity));17081680}16811681+16821682+/* Our random number generator device reads from /dev/random into the Guest's16831683+ * input buffers. The usual case is that the Guest doesn't want random numbers16841684+ * and so has no buffers although /dev/random is still readable, whereas16851685+ * console is the reverse.16861686+ *16871687+ * The same logic applies, however. */16881688+static bool handle_rng_input(int fd, struct device *dev)16891689+{16901690+ int len;16911691+ unsigned int head, in_num, out_num, totlen = 0;16921692+ struct iovec iov[dev->vq->vring.num];16931693+16941694+ /* First we need a buffer from the Guests's virtqueue. */16951695+ head = get_vq_desc(dev->vq, iov, &out_num, &in_num);16961696+16971697+ /* If they're not ready for input, stop listening to this file16981698+ * descriptor. We'll start again once they add an input buffer. */16991699+ if (head == dev->vq->vring.num)17001700+ return false;17011701+17021702+ if (out_num)17031703+ errx(1, "Output buffers in rng?");17041704+17051705+ /* This is why we convert to iovecs: the readv() call uses them, and so17061706+ * it reads straight into the Guest's buffer. We loop to make sure we17071707+ * fill it. */17081708+ while (!iov_empty(iov, in_num)) {17091709+ len = readv(dev->fd, iov, in_num);17101710+ if (len <= 0)17111711+ err(1, "Read from /dev/random gave %i", len);17121712+ iov_consume(iov, in_num, len);17131713+ totlen += len;17141714+ }17151715+17161716+ /* Tell the Guest about the new input. */17171717+ add_used_and_trigger(fd, dev->vq, head, totlen);17181718+17191719+ /* Everything went OK! */17201720+ return true;17211721+}17221722+17231723+/* And this creates a "hardware" random number device for the Guest. */17241724+static void setup_rng(void)17251725+{17261726+ struct device *dev;17271727+ int fd;17281728+17291729+ fd = open_or_die("/dev/random", O_RDONLY);17301730+17311731+ /* The device responds to return from I/O thread. */17321732+ dev = new_device("rng", VIRTIO_ID_RNG, fd, handle_rng_input);17331733+17341734+ /* The device has one virtqueue, where the Guest places inbufs. */17351735+ add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd);17361736+17371737+ verbose("device %u: rng\n", devices.device_num++);17381738+}17091739/* That's the end of device setup. */1710174017111741/*L:230 Reboot is pretty easy: clean up and exec() the Launcher afresh. */···18341748 { "verbose", 0, NULL, 'v' },18351749 { "tunnet", 1, NULL, 't' },18361750 { "block", 1, NULL, 'b' },17511751+ { "rng", 0, NULL, 'r' },18371752 { "initrd", 1, NULL, 'i' },18381753 { NULL },18391754};···19081821 break;19091822 case 'b':19101823 setup_block_file(optarg);18241824+ break;18251825+ case 'r':18261826+ setup_rng();19111827 break;19121828 case 'i':19131829 initrd_name = optarg;