|
@@ -34,6 +34,8 @@
|
|
|
#include <zlib.h>
|
|
|
#include <assert.h>
|
|
|
#include <sched.h>
|
|
|
+#include <limits.h>
|
|
|
+#include <stddef.h>
|
|
|
#include "linux/lguest_launcher.h"
|
|
|
#include "linux/virtio_config.h"
|
|
|
#include "linux/virtio_net.h"
|
|
@@ -99,13 +101,11 @@ struct device_list
|
|
|
/* The descriptor page for the devices. */
|
|
|
u8 *descpage;
|
|
|
|
|
|
- /* The tail of the last descriptor. */
|
|
|
- unsigned int desc_used;
|
|
|
-
|
|
|
/* A single linked list of devices. */
|
|
|
struct device *dev;
|
|
|
- /* ... And an end pointer so we can easily append new devices */
|
|
|
- struct device **lastdev;
|
|
|
+ /* And a pointer to the last device for easy append and also for
|
|
|
+ * configuration appending. */
|
|
|
+ struct device *lastdev;
|
|
|
};
|
|
|
|
|
|
/* The list of Guest devices, based on command line arguments. */
|
|
@@ -191,7 +191,14 @@ static void *_convert(struct iovec *iov, size_t size, size_t align,
|
|
|
#define cpu_to_le64(v64) (v64)
|
|
|
#define le16_to_cpu(v16) (v16)
|
|
|
#define le32_to_cpu(v32) (v32)
|
|
|
-#define le64_to_cpu(v32) (v64)
|
|
|
+#define le64_to_cpu(v64) (v64)
|
|
|
+
|
|
|
+/* The device virtqueue descriptors are followed by feature bitmasks. */
|
|
|
+static u8 *get_feature_bits(struct device *dev)
|
|
|
+{
|
|
|
+ return (u8 *)(dev->desc + 1)
|
|
|
+ + dev->desc->num_vq * sizeof(struct lguest_vqconfig);
|
|
|
+}
|
|
|
|
|
|
/*L:100 The Launcher code itself takes us out into userspace, that scary place
|
|
|
* where pointers run wild and free! Unfortunately, like most userspace
|
|
@@ -914,21 +921,58 @@ static void enable_fd(int fd, struct virtqueue *vq)
|
|
|
write(waker_fd, &vq->dev->fd, sizeof(vq->dev->fd));
|
|
|
}
|
|
|
|
|
|
+/* Resetting a device is fairly easy. */
|
|
|
+static void reset_device(struct device *dev)
|
|
|
+{
|
|
|
+ struct virtqueue *vq;
|
|
|
+
|
|
|
+ verbose("Resetting device %s\n", dev->name);
|
|
|
+ /* Clear the status. */
|
|
|
+ dev->desc->status = 0;
|
|
|
+
|
|
|
+ /* Clear any features they've acked. */
|
|
|
+ memset(get_feature_bits(dev) + dev->desc->feature_len, 0,
|
|
|
+ dev->desc->feature_len);
|
|
|
+
|
|
|
+ /* Zero out the virtqueues. */
|
|
|
+ for (vq = dev->vq; vq; vq = vq->next) {
|
|
|
+ memset(vq->vring.desc, 0,
|
|
|
+ vring_size(vq->config.num, getpagesize()));
|
|
|
+ vq->last_avail_idx = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */
|
|
|
static void handle_output(int fd, unsigned long addr)
|
|
|
{
|
|
|
struct device *i;
|
|
|
struct virtqueue *vq;
|
|
|
|
|
|
- /* Check each virtqueue. */
|
|
|
+ /* Check each device and virtqueue. */
|
|
|
for (i = devices.dev; i; i = i->next) {
|
|
|
+ /* Notifications to device descriptors reset the device. */
|
|
|
+ if (from_guest_phys(addr) == i->desc) {
|
|
|
+ reset_device(i);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Notifications to virtqueues mean output has occurred. */
|
|
|
for (vq = i->vq; vq; vq = vq->next) {
|
|
|
- if (vq->config.pfn == addr/getpagesize()
|
|
|
- && vq->handle_output) {
|
|
|
- verbose("Output to %s\n", vq->dev->name);
|
|
|
- vq->handle_output(fd, vq);
|
|
|
+ if (vq->config.pfn != addr/getpagesize())
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Guest should acknowledge (and set features!) before
|
|
|
+ * using the device. */
|
|
|
+ if (i->desc->status == 0) {
|
|
|
+ warnx("%s gave early output", i->name);
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
+ if (strcmp(vq->dev->name, "console") != 0)
|
|
|
+ verbose("Output to %s\n", vq->dev->name);
|
|
|
+ if (vq->handle_output)
|
|
|
+ vq->handle_output(fd, vq);
|
|
|
+ return;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -986,54 +1030,44 @@ static void handle_input(int fd)
|
|
|
*
|
|
|
* All devices need a descriptor so the Guest knows it exists, and a "struct
|
|
|
* device" so the Launcher can keep track of it. We have common helper
|
|
|
- * routines to allocate them.
|
|
|
- *
|
|
|
- * This routine allocates a new "struct lguest_device_desc" from descriptor
|
|
|
- * table just above the Guest's normal memory. It returns a pointer to that
|
|
|
- * descriptor. */
|
|
|
-static struct lguest_device_desc *new_dev_desc(u16 type)
|
|
|
-{
|
|
|
- struct lguest_device_desc *d;
|
|
|
+ * routines to allocate and manage them. */
|
|
|
|
|
|
- /* We only have one page for all the descriptors. */
|
|
|
- if (devices.desc_used + sizeof(*d) > getpagesize())
|
|
|
- errx(1, "Too many devices");
|
|
|
-
|
|
|
- /* We don't need to set config_len or status: page is 0 already. */
|
|
|
- d = (void *)devices.descpage + devices.desc_used;
|
|
|
- d->type = type;
|
|
|
- devices.desc_used += sizeof(*d);
|
|
|
-
|
|
|
- return d;
|
|
|
+/* The layout of the device page is a "struct lguest_device_desc" followed by a
|
|
|
+ * number of virtqueue descriptors, then two sets of feature bits, then an
|
|
|
+ * array of configuration bytes. This routine returns the configuration
|
|
|
+ * pointer. */
|
|
|
+static u8 *device_config(const struct device *dev)
|
|
|
+{
|
|
|
+ return (void *)(dev->desc + 1)
|
|
|
+ + dev->desc->num_vq * sizeof(struct lguest_vqconfig)
|
|
|
+ + dev->desc->feature_len * 2;
|
|
|
}
|
|
|
|
|
|
-/* Each device descriptor is followed by some configuration information.
|
|
|
- * Each configuration field looks like: u8 type, u8 len, [... len bytes...].
|
|
|
- *
|
|
|
- * This routine adds a new field to an existing device's descriptor. It only
|
|
|
- * works for the last device, but that's OK because that's how we use it. */
|
|
|
-static void add_desc_field(struct device *dev, u8 type, u8 len, const void *c)
|
|
|
+/* This routine allocates a new "struct lguest_device_desc" from descriptor
|
|
|
+ * table page just above the Guest's normal memory. It returns a pointer to
|
|
|
+ * that descriptor. */
|
|
|
+static struct lguest_device_desc *new_dev_desc(u16 type)
|
|
|
{
|
|
|
- /* This is the last descriptor, right? */
|
|
|
- assert(devices.descpage + devices.desc_used
|
|
|
- == (u8 *)(dev->desc + 1) + dev->desc->config_len);
|
|
|
+ struct lguest_device_desc d = { .type = type };
|
|
|
+ void *p;
|
|
|
|
|
|
- /* We only have one page of device descriptions. */
|
|
|
- if (devices.desc_used + 2 + len > getpagesize())
|
|
|
- errx(1, "Too many devices");
|
|
|
+ /* Figure out where the next device config is, based on the last one. */
|
|
|
+ if (devices.lastdev)
|
|
|
+ p = device_config(devices.lastdev)
|
|
|
+ + devices.lastdev->desc->config_len;
|
|
|
+ else
|
|
|
+ p = devices.descpage;
|
|
|
|
|
|
- /* Copy in the new config header: type then length. */
|
|
|
- devices.descpage[devices.desc_used++] = type;
|
|
|
- devices.descpage[devices.desc_used++] = len;
|
|
|
- memcpy(devices.descpage + devices.desc_used, c, len);
|
|
|
- devices.desc_used += len;
|
|
|
+ /* We only have one page for all the descriptors. */
|
|
|
+ if (p + sizeof(d) > (void *)devices.descpage + getpagesize())
|
|
|
+ errx(1, "Too many devices");
|
|
|
|
|
|
- /* Update the device descriptor length: two byte head then data. */
|
|
|
- dev->desc->config_len += 2 + len;
|
|
|
+ /* p might not be aligned, so we memcpy in. */
|
|
|
+ return memcpy(p, &d, sizeof(d));
|
|
|
}
|
|
|
|
|
|
-/* This routine adds a virtqueue to a device. We specify how many descriptors
|
|
|
- * the virtqueue is to have. */
|
|
|
+/* Each device descriptor is followed by the description of its virtqueues. We
|
|
|
+ * specify how many descriptors the virtqueue is to have. */
|
|
|
static void add_virtqueue(struct device *dev, unsigned int num_descs,
|
|
|
void (*handle_output)(int fd, struct virtqueue *me))
|
|
|
{
|
|
@@ -1059,9 +1093,15 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
|
|
|
/* Initialize the vring. */
|
|
|
vring_init(&vq->vring, num_descs, p, getpagesize());
|
|
|
|
|
|
- /* Add the configuration information to this device's descriptor. */
|
|
|
- add_desc_field(dev, VIRTIO_CONFIG_F_VIRTQUEUE,
|
|
|
- sizeof(vq->config), &vq->config);
|
|
|
+ /* Append virtqueue to this device's descriptor. We use
|
|
|
+ * device_config() to get the end of the device's current virtqueues;
|
|
|
+ * we check that we haven't added any config or feature information
|
|
|
+ * yet, otherwise we'd be overwriting them. */
|
|
|
+ assert(dev->desc->config_len == 0 && dev->desc->feature_len == 0);
|
|
|
+ memcpy(device_config(dev), &vq->config, sizeof(vq->config));
|
|
|
+ dev->desc->num_vq++;
|
|
|
+
|
|
|
+ verbose("Virtqueue page %#lx\n", to_guest_phys(p));
|
|
|
|
|
|
/* Add to tail of list, so dev->vq is first vq, dev->vq->next is
|
|
|
* second. */
|
|
@@ -1072,11 +1112,41 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
|
|
|
* virtqueue. */
|
|
|
vq->handle_output = handle_output;
|
|
|
|
|
|
- /* Set the "Don't Notify Me" flag if we don't have a handler */
|
|
|
+ /* As an optimization, set the advisory "Don't Notify Me" flag if we
|
|
|
+ * don't have a handler */
|
|
|
if (!handle_output)
|
|
|
vq->vring.used->flags = VRING_USED_F_NO_NOTIFY;
|
|
|
}
|
|
|
|
|
|
+/* The first half of the feature bitmask is for us to advertise features. The
|
|
|
+ * second half if for the Guest to accept features. */
|
|
|
+static void add_feature(struct device *dev, unsigned bit)
|
|
|
+{
|
|
|
+ u8 *features = get_feature_bits(dev);
|
|
|
+
|
|
|
+ /* We can't extend the feature bits once we've added config bytes */
|
|
|
+ if (dev->desc->feature_len <= bit / CHAR_BIT) {
|
|
|
+ assert(dev->desc->config_len == 0);
|
|
|
+ dev->desc->feature_len = (bit / CHAR_BIT) + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ features[bit / CHAR_BIT] |= (1 << (bit % CHAR_BIT));
|
|
|
+}
|
|
|
+
|
|
|
+/* This routine sets the configuration fields for an existing device's
|
|
|
+ * descriptor. It only works for the last device, but that's OK because that's
|
|
|
+ * how we use it. */
|
|
|
+static void set_config(struct device *dev, unsigned len, const void *conf)
|
|
|
+{
|
|
|
+ /* Check we haven't overflowed our single page. */
|
|
|
+ if (device_config(dev) + len > devices.descpage + getpagesize())
|
|
|
+ errx(1, "Too many devices");
|
|
|
+
|
|
|
+ /* Copy in the config information, and store the length. */
|
|
|
+ memcpy(device_config(dev), conf, len);
|
|
|
+ dev->desc->config_len = len;
|
|
|
+}
|
|
|
+
|
|
|
/* This routine does all the creation and setup of a new device, including
|
|
|
* calling new_dev_desc() to allocate the descriptor and device memory. */
|
|
|
static struct device *new_device(const char *name, u16 type, int fd,
|
|
@@ -1084,14 +1154,6 @@ static struct device *new_device(const char *name, u16 type, int fd,
|
|
|
{
|
|
|
struct device *dev = malloc(sizeof(*dev));
|
|
|
|
|
|
- /* Append to device list. Prepending to a single-linked list is
|
|
|
- * easier, but the user expects the devices to be arranged on the bus
|
|
|
- * in command-line order. The first network device on the command line
|
|
|
- * is eth0, the first block device /dev/vda, etc. */
|
|
|
- *devices.lastdev = dev;
|
|
|
- dev->next = NULL;
|
|
|
- devices.lastdev = &dev->next;
|
|
|
-
|
|
|
/* Now we populate the fields one at a time. */
|
|
|
dev->fd = fd;
|
|
|
/* If we have an input handler for this file descriptor, then we add it
|
|
@@ -1102,6 +1164,17 @@ static struct device *new_device(const char *name, u16 type, int fd,
|
|
|
dev->handle_input = handle_input;
|
|
|
dev->name = name;
|
|
|
dev->vq = NULL;
|
|
|
+
|
|
|
+ /* Append to device list. Prepending to a single-linked list is
|
|
|
+ * easier, but the user expects the devices to be arranged on the bus
|
|
|
+ * in command-line order. The first network device on the command line
|
|
|
+ * is eth0, the first block device /dev/vda, etc. */
|
|
|
+ if (devices.lastdev)
|
|
|
+ devices.lastdev->next = dev;
|
|
|
+ else
|
|
|
+ devices.dev = dev;
|
|
|
+ devices.lastdev = dev;
|
|
|
+
|
|
|
return dev;
|
|
|
}
|
|
|
|
|
@@ -1226,7 +1299,7 @@ static void setup_tun_net(const char *arg)
|
|
|
int netfd, ipfd;
|
|
|
u32 ip;
|
|
|
const char *br_name = NULL;
|
|
|
- u8 hwaddr[6];
|
|
|
+ struct virtio_net_config conf;
|
|
|
|
|
|
/* We open the /dev/net/tun device and tell it we want a tap device. A
|
|
|
* tap device is like a tun device, only somehow different. To tell
|
|
@@ -1265,12 +1338,13 @@ static void setup_tun_net(const char *arg)
|
|
|
ip = str2ip(arg);
|
|
|
|
|
|
/* Set up the tun device, and get the mac address for the interface. */
|
|
|
- configure_device(ipfd, ifr.ifr_name, ip, hwaddr);
|
|
|
+ configure_device(ipfd, ifr.ifr_name, ip, conf.mac);
|
|
|
|
|
|
/* Tell Guest what MAC address to use. */
|
|
|
- add_desc_field(dev, VIRTIO_CONFIG_NET_MAC_F, sizeof(hwaddr), hwaddr);
|
|
|
+ add_feature(dev, VIRTIO_NET_F_MAC);
|
|
|
+ set_config(dev, sizeof(conf), &conf);
|
|
|
|
|
|
- /* We don't seed the socket any more; setup is done. */
|
|
|
+ /* We don't need the socket any more; setup is done. */
|
|
|
close(ipfd);
|
|
|
|
|
|
verbose("device %u: tun net %u.%u.%u.%u\n",
|
|
@@ -1458,8 +1532,7 @@ static void setup_block_file(const char *filename)
|
|
|
struct device *dev;
|
|
|
struct vblk_info *vblk;
|
|
|
void *stack;
|
|
|
- u64 cap;
|
|
|
- unsigned int val;
|
|
|
+ struct virtio_blk_config conf;
|
|
|
|
|
|
/* This is the pipe the I/O thread will use to tell us I/O is done. */
|
|
|
pipe(p);
|
|
@@ -1477,14 +1550,18 @@ static void setup_block_file(const char *filename)
|
|
|
vblk->fd = open_or_die(filename, O_RDWR|O_LARGEFILE);
|
|
|
vblk->len = lseek64(vblk->fd, 0, SEEK_END);
|
|
|
|
|
|
+ /* We support barriers. */
|
|
|
+ add_feature(dev, VIRTIO_BLK_F_BARRIER);
|
|
|
+
|
|
|
/* Tell Guest how many sectors this device has. */
|
|
|
- cap = cpu_to_le64(vblk->len / 512);
|
|
|
- add_desc_field(dev, VIRTIO_CONFIG_BLK_F_CAPACITY, sizeof(cap), &cap);
|
|
|
+ conf.capacity = cpu_to_le64(vblk->len / 512);
|
|
|
|
|
|
/* Tell Guest not to put in too many descriptors at once: two are used
|
|
|
* for the in and out elements. */
|
|
|
- val = cpu_to_le32(VIRTQUEUE_NUM - 2);
|
|
|
- add_desc_field(dev, VIRTIO_CONFIG_BLK_F_SEG_MAX, sizeof(val), &val);
|
|
|
+ add_feature(dev, VIRTIO_BLK_F_SEG_MAX);
|
|
|
+ conf.seg_max = cpu_to_le32(VIRTQUEUE_NUM - 2);
|
|
|
+
|
|
|
+ set_config(dev, sizeof(conf), &conf);
|
|
|
|
|
|
/* The I/O thread writes to this end of the pipe when done. */
|
|
|
vblk->done_fd = p[1];
|
|
@@ -1505,7 +1582,7 @@ static void setup_block_file(const char *filename)
|
|
|
close(vblk->workpipe[0]);
|
|
|
|
|
|
verbose("device %u: virtblock %llu sectors\n",
|
|
|
- devices.device_num, cap);
|
|
|
+ devices.device_num, le64_to_cpu(conf.capacity));
|
|
|
}
|
|
|
/* That's the end of device setup. :*/
|
|
|
|
|
@@ -1610,12 +1687,12 @@ int main(int argc, char *argv[])
|
|
|
/* First we initialize the device list. Since console and network
|
|
|
* device receive input from a file descriptor, we keep an fdset
|
|
|
* (infds) and the maximum fd number (max_infd) with the head of the
|
|
|
- * list. We also keep a pointer to the last device, for easy appending
|
|
|
- * to the list. Finally, we keep the next interrupt number to hand out
|
|
|
- * (1: remember that 0 is used by the timer). */
|
|
|
+ * list. We also keep a pointer to the last device. Finally, we keep
|
|
|
+ * the next interrupt number to hand out (1: remember that 0 is used by
|
|
|
+ * the timer). */
|
|
|
FD_ZERO(&devices.infds);
|
|
|
devices.max_infd = -1;
|
|
|
- devices.lastdev = &devices.dev;
|
|
|
+ devices.lastdev = NULL;
|
|
|
devices.next_irq = 1;
|
|
|
|
|
|
cpu_id = 0;
|