|
@@ -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;
|