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>
···41#include "linux/virtio_net.h"42#include "linux/virtio_blk.h"43#include "linux/virtio_console.h"044#include "linux/virtio_ring.h"45#include "asm-x86/bootparam.h"46/*L:110 We can ignore the 39 include files we need for this program, but I do···199#define le16_to_cpu(v16) (v16)200#define le32_to_cpu(v32) (v32)201#define le64_to_cpu(v64) (v64)000000000000000000000000000202203/* The device virtqueue descriptors are followed by feature bitmasks. */204static u8 *get_feature_bits(struct device *dev)···1706 verbose("device %u: virtblock %llu sectors\n",1707 devices.device_num, le64_to_cpu(conf.capacity));1708}00000000000000000000000000000000000000000000000000000000001709/* That's the end of device setup. */17101711/*L:230 Reboot is pretty easy: clean up and exec() the Launcher afresh. */···1834 { "verbose", 0, NULL, 'v' },1835 { "tunnet", 1, NULL, 't' },1836 { "block", 1, NULL, 'b' },01837 { "initrd", 1, NULL, 'i' },1838 { NULL },1839};···1908 break;1909 case 'b':1910 setup_block_file(optarg);0001911 break;1912 case 'i':1913 initrd_name = optarg;
···41#include "linux/virtio_net.h"42#include "linux/virtio_blk.h"43#include "linux/virtio_console.h"44+#include "linux/virtio_rng.h"45#include "linux/virtio_ring.h"46#include "asm-x86/bootparam.h"47/*L:110 We can ignore the 39 include files we need for this program, but I do···198#define le16_to_cpu(v16) (v16)199#define le32_to_cpu(v32) (v32)200#define le64_to_cpu(v64) (v64)201+202+/* Is this iovec empty? */203+static bool iov_empty(const struct iovec iov[], unsigned int num_iov)204+{205+ unsigned int i;206+207+ for (i = 0; i < num_iov; i++)208+ if (iov[i].iov_len)209+ return false;210+ return true;211+}212+213+/* Take len bytes from the front of this iovec. */214+static void iov_consume(struct iovec iov[], unsigned num_iov, unsigned len)215+{216+ unsigned int i;217+218+ for (i = 0; i < num_iov; i++) {219+ unsigned int used;220+221+ used = iov[i].iov_len < len ? iov[i].iov_len : len;222+ iov[i].iov_base += used;223+ iov[i].iov_len -= used;224+ len -= used;225+ }226+ assert(len == 0);227+}228229/* The device virtqueue descriptors are followed by feature bitmasks. */230static u8 *get_feature_bits(struct device *dev)···1678 verbose("device %u: virtblock %llu sectors\n",1679 devices.device_num, le64_to_cpu(conf.capacity));1680}1681+1682+/* Our random number generator device reads from /dev/random into the Guest's1683+ * input buffers. The usual case is that the Guest doesn't want random numbers1684+ * and so has no buffers although /dev/random is still readable, whereas1685+ * console is the reverse.1686+ *1687+ * The same logic applies, however. */1688+static bool handle_rng_input(int fd, struct device *dev)1689+{1690+ int len;1691+ unsigned int head, in_num, out_num, totlen = 0;1692+ struct iovec iov[dev->vq->vring.num];1693+1694+ /* First we need a buffer from the Guests's virtqueue. */1695+ head = get_vq_desc(dev->vq, iov, &out_num, &in_num);1696+1697+ /* If they're not ready for input, stop listening to this file1698+ * descriptor. We'll start again once they add an input buffer. */1699+ if (head == dev->vq->vring.num)1700+ return false;1701+1702+ if (out_num)1703+ errx(1, "Output buffers in rng?");1704+1705+ /* This is why we convert to iovecs: the readv() call uses them, and so1706+ * it reads straight into the Guest's buffer. We loop to make sure we1707+ * fill it. */1708+ while (!iov_empty(iov, in_num)) {1709+ len = readv(dev->fd, iov, in_num);1710+ if (len <= 0)1711+ err(1, "Read from /dev/random gave %i", len);1712+ iov_consume(iov, in_num, len);1713+ totlen += len;1714+ }1715+1716+ /* Tell the Guest about the new input. */1717+ add_used_and_trigger(fd, dev->vq, head, totlen);1718+1719+ /* Everything went OK! */1720+ return true;1721+}1722+1723+/* And this creates a "hardware" random number device for the Guest. */1724+static void setup_rng(void)1725+{1726+ struct device *dev;1727+ int fd;1728+1729+ fd = open_or_die("/dev/random", O_RDONLY);1730+1731+ /* The device responds to return from I/O thread. */1732+ dev = new_device("rng", VIRTIO_ID_RNG, fd, handle_rng_input);1733+1734+ /* The device has one virtqueue, where the Guest places inbufs. */1735+ add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd);1736+1737+ verbose("device %u: rng\n", devices.device_num++);1738+}1739/* That's the end of device setup. */17401741/*L:230 Reboot is pretty easy: clean up and exec() the Launcher afresh. */···1748 { "verbose", 0, NULL, 'v' },1749 { "tunnet", 1, NULL, 't' },1750 { "block", 1, NULL, 'b' },1751+ { "rng", 0, NULL, 'r' },1752 { "initrd", 1, NULL, 'i' },1753 { NULL },1754};···1821 break;1822 case 'b':1823 setup_block_file(optarg);1824+ break;1825+ case 'r':1826+ setup_rng();1827 break;1828 case 'i':1829 initrd_name = optarg;