Commit 7874d351 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-for-linus

* git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-for-linus:
  lguest: turn Waker into a thread, not a process
  lguest: Enlarge virtio rings
  lguest: Use GSO/IFF_VNET_HDR extensions on tun/tap
  lguest: Remove 'network: no dma buffer!' warning
  lguest: Adaptive timeout
  lguest: Tell Guest net not to notify us on every packet xmit
  lguest: net block unneeded receive queue update notifications
  lguest: wrap last_avail accesses.
  lguest: use cpu capability accessors
  lguest: virtio-rng support
  lguest: Support assigning a MAC address
  lguest: Don't leak /dev/zero fd
  lguest: fix verbose printing of device features.
  lguest: fix switcher_page leak on unload
  lguest: Guest int3 fix
  lguest: set max_pfn_mapped, growl loudly at Yinghai Lu
parents 5dfb66ba 8c79873d
...@@ -36,11 +36,13 @@ ...@@ -36,11 +36,13 @@
#include <sched.h> #include <sched.h>
#include <limits.h> #include <limits.h>
#include <stddef.h> #include <stddef.h>
#include <signal.h>
#include "linux/lguest_launcher.h" #include "linux/lguest_launcher.h"
#include "linux/virtio_config.h" #include "linux/virtio_config.h"
#include "linux/virtio_net.h" #include "linux/virtio_net.h"
#include "linux/virtio_blk.h" #include "linux/virtio_blk.h"
#include "linux/virtio_console.h" #include "linux/virtio_console.h"
#include "linux/virtio_rng.h"
#include "linux/virtio_ring.h" #include "linux/virtio_ring.h"
#include "asm-x86/bootparam.h" #include "asm-x86/bootparam.h"
/*L:110 We can ignore the 39 include files we need for this program, but I do /*L:110 We can ignore the 39 include files we need for this program, but I do
...@@ -64,8 +66,8 @@ typedef uint8_t u8; ...@@ -64,8 +66,8 @@ typedef uint8_t u8;
#endif #endif
/* We can have up to 256 pages for devices. */ /* We can have up to 256 pages for devices. */
#define DEVICE_PAGES 256 #define DEVICE_PAGES 256
/* This will occupy 2 pages: it must be a power of 2. */ /* This will occupy 3 pages: it must be a power of 2. */
#define VIRTQUEUE_NUM 128 #define VIRTQUEUE_NUM 256
/*L:120 verbose is both a global flag and a macro. The C preprocessor allows /*L:120 verbose is both a global flag and a macro. The C preprocessor allows
* this, and although I wouldn't recommend it, it works quite nicely here. */ * this, and although I wouldn't recommend it, it works quite nicely here. */
...@@ -74,12 +76,19 @@ static bool verbose; ...@@ -74,12 +76,19 @@ static bool verbose;
do { if (verbose) printf(args); } while(0) do { if (verbose) printf(args); } while(0)
/*:*/ /*:*/
/* The pipe to send commands to the waker process */ /* File descriptors for the Waker. */
static int waker_fd; struct {
int pipe[2];
int lguest_fd;
} waker_fds;
/* The pointer to the start of guest memory. */ /* The pointer to the start of guest memory. */
static void *guest_base; static void *guest_base;
/* The maximum guest physical address allowed, and maximum possible. */ /* The maximum guest physical address allowed, and maximum possible. */
static unsigned long guest_limit, guest_max; static unsigned long guest_limit, guest_max;
/* The pipe for signal hander to write to. */
static int timeoutpipe[2];
static unsigned int timeout_usec = 500;
/* a per-cpu variable indicating whose vcpu is currently running */ /* a per-cpu variable indicating whose vcpu is currently running */
static unsigned int __thread cpu_id; static unsigned int __thread cpu_id;
...@@ -155,11 +164,14 @@ struct virtqueue ...@@ -155,11 +164,14 @@ struct virtqueue
/* Last available index we saw. */ /* Last available index we saw. */
u16 last_avail_idx; u16 last_avail_idx;
/* The routine to call when the Guest pings us. */ /* The routine to call when the Guest pings us, or timeout. */
void (*handle_output)(int fd, struct virtqueue *me); void (*handle_output)(int fd, struct virtqueue *me, bool timeout);
/* Outstanding buffers */ /* Outstanding buffers */
unsigned int inflight; unsigned int inflight;
/* Is this blocked awaiting a timer? */
bool blocked;
}; };
/* Remember the arguments to the program so we can "reboot" */ /* Remember the arguments to the program so we can "reboot" */
...@@ -190,6 +202,9 @@ static void *_convert(struct iovec *iov, size_t size, size_t align, ...@@ -190,6 +202,9 @@ static void *_convert(struct iovec *iov, size_t size, size_t align,
return iov->iov_base; return iov->iov_base;
} }
/* Wrapper for the last available index. Makes it easier to change. */
#define lg_last_avail(vq) ((vq)->last_avail_idx)
/* The virtio configuration space is defined to be little-endian. x86 is /* The virtio configuration space is defined to be little-endian. x86 is
* little-endian too, but it's nice to be explicit so we have these helpers. */ * little-endian too, but it's nice to be explicit so we have these helpers. */
#define cpu_to_le16(v16) (v16) #define cpu_to_le16(v16) (v16)
...@@ -199,6 +214,33 @@ static void *_convert(struct iovec *iov, size_t size, size_t align, ...@@ -199,6 +214,33 @@ static void *_convert(struct iovec *iov, size_t size, size_t align,
#define le32_to_cpu(v32) (v32) #define le32_to_cpu(v32) (v32)
#define le64_to_cpu(v64) (v64) #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. */ /* The device virtqueue descriptors are followed by feature bitmasks. */
static u8 *get_feature_bits(struct device *dev) static u8 *get_feature_bits(struct device *dev)
{ {
...@@ -254,6 +296,7 @@ static void *map_zeroed_pages(unsigned int num) ...@@ -254,6 +296,7 @@ static void *map_zeroed_pages(unsigned int num)
PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0); PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) if (addr == MAP_FAILED)
err(1, "Mmaping %u pages of /dev/zero", num); err(1, "Mmaping %u pages of /dev/zero", num);
close(fd);
return addr; return addr;
} }
...@@ -540,69 +583,64 @@ static void add_device_fd(int fd) ...@@ -540,69 +583,64 @@ static void add_device_fd(int fd)
* watch, but handing a file descriptor mask through to the kernel is fairly * watch, but handing a file descriptor mask through to the kernel is fairly
* icky. * icky.
* *
* Instead, we fork off a process which watches the file descriptors and writes * Instead, we clone off a thread which watches the file descriptors and writes
* the LHREQ_BREAK command to the /dev/lguest file descriptor to tell the Host * the LHREQ_BREAK command to the /dev/lguest file descriptor to tell the Host
* stop running the Guest. This causes the Launcher to return from the * stop running the Guest. This causes the Launcher to return from the
* /dev/lguest read with -EAGAIN, where it will write to /dev/lguest to reset * /dev/lguest read with -EAGAIN, where it will write to /dev/lguest to reset
* the LHREQ_BREAK and wake us up again. * the LHREQ_BREAK and wake us up again.
* *
* This, of course, is merely a different *kind* of icky. * This, of course, is merely a different *kind* of icky.
*
* Given my well-known antipathy to threads, I'd prefer to use processes. But
* it's easier to share Guest memory with threads, and trivial to share the
* devices.infds as the Launcher changes it.
*/ */
static void wake_parent(int pipefd, int lguest_fd) static int waker(void *unused)
{ {
/* Add the pipe from the Launcher to the fdset in the device_list, so /* Close the write end of the pipe: only the Launcher has it open. */
* we watch it, too. */ close(waker_fds.pipe[1]);
add_device_fd(pipefd);
for (;;) { for (;;) {
fd_set rfds = devices.infds; fd_set rfds = devices.infds;
unsigned long args[] = { LHREQ_BREAK, 1 }; unsigned long args[] = { LHREQ_BREAK, 1 };
unsigned int maxfd = devices.max_infd;
/* We also listen to the pipe from the Launcher. */
FD_SET(waker_fds.pipe[0], &rfds);
if (waker_fds.pipe[0] > maxfd)
maxfd = waker_fds.pipe[0];
/* Wait until input is ready from one of the devices. */ /* Wait until input is ready from one of the devices. */
select(devices.max_infd+1, &rfds, NULL, NULL, NULL); select(maxfd+1, &rfds, NULL, NULL, NULL);
/* Is it a message from the Launcher? */
if (FD_ISSET(pipefd, &rfds)) { /* Message from Launcher? */
int fd; if (FD_ISSET(waker_fds.pipe[0], &rfds)) {
/* If read() returns 0, it means the Launcher has char c;
* exited. We silently follow. */ /* If this fails, then assume Launcher has exited.
if (read(pipefd, &fd, sizeof(fd)) == 0) * Don't do anything on exit: we're just a thread! */
exit(0); if (read(waker_fds.pipe[0], &c, 1) != 1)
/* Otherwise it's telling us to change what file _exit(0);
* descriptors we're to listen to. Positive means continue;
* listen to a new one, negative means stop }
* listening. */
if (fd >= 0) /* Send LHREQ_BREAK command to snap the Launcher out of it. */
FD_SET(fd, &devices.infds); pwrite(waker_fds.lguest_fd, args, sizeof(args), cpu_id);
else
FD_CLR(-fd - 1, &devices.infds);
} else /* Send LHREQ_BREAK command. */
pwrite(lguest_fd, args, sizeof(args), cpu_id);
} }
return 0;
} }
/* This routine just sets up a pipe to the Waker process. */ /* This routine just sets up a pipe to the Waker process. */
static int setup_waker(int lguest_fd) static void setup_waker(int lguest_fd)
{ {
int pipefd[2], child; /* This pipe is closed when Launcher dies, telling Waker. */
if (pipe(waker_fds.pipe) != 0)
/* We create a pipe to talk to the Waker, and also so it knows when the err(1, "Creating pipe for Waker");
* Launcher dies (and closes pipe). */
pipe(pipefd);
child = fork();
if (child == -1)
err(1, "forking");
if (child == 0) {
/* We are the Waker: close the "writing" end of our copy of the
* pipe and start waiting for input. */
close(pipefd[1]);
wake_parent(pipefd[0], lguest_fd);
}
/* Close the reading end of our copy of the pipe. */
close(pipefd[0]);
/* Here is the fd used to talk to the waker. */ /* Waker also needs to know the lguest fd */
return pipefd[1]; waker_fds.lguest_fd = lguest_fd;
if (clone(waker, malloc(4096) + 4096, CLONE_VM | SIGCHLD, NULL) == -1)
err(1, "Creating Waker");
} }
/* /*
...@@ -661,19 +699,22 @@ static unsigned get_vq_desc(struct virtqueue *vq, ...@@ -661,19 +699,22 @@ static unsigned get_vq_desc(struct virtqueue *vq,
unsigned int *out_num, unsigned int *in_num) unsigned int *out_num, unsigned int *in_num)
{ {
unsigned int i, head; unsigned int i, head;
u16 last_avail;
/* Check it isn't doing very strange things with descriptor numbers. */ /* Check it isn't doing very strange things with descriptor numbers. */
if ((u16)(vq->vring.avail->idx - vq->last_avail_idx) > vq->vring.num) last_avail = lg_last_avail(vq);
if ((u16)(vq->vring.avail->idx - last_avail) > vq->vring.num)
errx(1, "Guest moved used index from %u to %u", errx(1, "Guest moved used index from %u to %u",
vq->last_avail_idx, vq->vring.avail->idx); last_avail, vq->vring.avail->idx);
/* If there's nothing new since last we looked, return invalid. */ /* If there's nothing new since last we looked, return invalid. */
if (vq->vring.avail->idx == vq->last_avail_idx) if (vq->vring.avail->idx == last_avail)
return vq->vring.num; return vq->vring.num;
/* Grab the next descriptor number they're advertising, and increment /* Grab the next descriptor number they're advertising, and increment
* the index we've seen. */ * the index we've seen. */
head = vq->vring.avail->ring[vq->last_avail_idx++ % vq->vring.num]; head = vq->vring.avail->ring[last_avail % vq->vring.num];
lg_last_avail(vq)++;
/* If their number is silly, that's a fatal mistake. */ /* If their number is silly, that's a fatal mistake. */
if (head >= vq->vring.num) if (head >= vq->vring.num)
...@@ -821,8 +862,8 @@ static bool handle_console_input(int fd, struct device *dev) ...@@ -821,8 +862,8 @@ static bool handle_console_input(int fd, struct device *dev)
unsigned long args[] = { LHREQ_BREAK, 0 }; unsigned long args[] = { LHREQ_BREAK, 0 };
/* Close the fd so Waker will know it has to /* Close the fd so Waker will know it has to
* exit. */ * exit. */
close(waker_fd); close(waker_fds.pipe[1]);
/* Just in case waker is blocked in BREAK, send /* Just in case Waker is blocked in BREAK, send
* unbreak now. */ * unbreak now. */
write(fd, args, sizeof(args)); write(fd, args, sizeof(args));
exit(2); exit(2);
...@@ -839,7 +880,7 @@ static bool handle_console_input(int fd, struct device *dev) ...@@ -839,7 +880,7 @@ static bool handle_console_input(int fd, struct device *dev)
/* Handling output for console is simple: we just get all the output buffers /* Handling output for console is simple: we just get all the output buffers
* and write them to stdout. */ * and write them to stdout. */
static void handle_console_output(int fd, struct virtqueue *vq) static void handle_console_output(int fd, struct virtqueue *vq, bool timeout)
{ {
unsigned int head, out, in; unsigned int head, out, in;
int len; int len;
...@@ -854,6 +895,21 @@ static void handle_console_output(int fd, struct virtqueue *vq) ...@@ -854,6 +895,21 @@ static void handle_console_output(int fd, struct virtqueue *vq)
} }
} }
static void block_vq(struct virtqueue *vq)
{
struct itimerval itm;
vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
vq->blocked = true;
itm.it_interval.tv_sec = 0;
itm.it_interval.tv_usec = 0;
itm.it_value.tv_sec = 0;
itm.it_value.tv_usec = timeout_usec;
setitimer(ITIMER_REAL, &itm, NULL);
}
/* /*
* The Network * The Network
* *
...@@ -861,22 +917,34 @@ static void handle_console_output(int fd, struct virtqueue *vq) ...@@ -861,22 +917,34 @@ static void handle_console_output(int fd, struct virtqueue *vq)
* and write them (ignoring the first element) to this device's file descriptor * and write them (ignoring the first element) to this device's file descriptor
* (/dev/net/tun). * (/dev/net/tun).
*/ */
static void handle_net_output(int fd, struct virtqueue *vq) static void handle_net_output(int fd, struct virtqueue *vq, bool timeout)
{ {
unsigned int head, out, in; unsigned int head, out, in, num = 0;
int len; int len;
struct iovec iov[vq->vring.num]; struct iovec iov[vq->vring.num];
static int last_timeout_num;
/* Keep getting output buffers from the Guest until we run out. */ /* Keep getting output buffers from the Guest until we run out. */
while ((head = get_vq_desc(vq, iov, &out, &in)) != vq->vring.num) { while ((head = get_vq_desc(vq, iov, &out, &in)) != vq->vring.num) {
if (in) if (in)
errx(1, "Input buffers in output queue?"); errx(1, "Input buffers in output queue?");
/* Check header, but otherwise ignore it (we told the Guest we len = writev(vq->dev->fd, iov, out);
* supported no features, so it shouldn't have anything if (len < 0)
* interesting). */ err(1, "Writing network packet to tun");
(void)convert(&iov[0], struct virtio_net_hdr);
len = writev(vq->dev->fd, iov+1, out-1);
add_used_and_trigger(fd, vq, head, len); add_used_and_trigger(fd, vq, head, len);
num++;
}
/* Block further kicks and set up a timer if we saw anything. */
if (!timeout && num)
block_vq(vq);
if (timeout) {
if (num < last_timeout_num)
timeout_usec += 10;
else if (timeout_usec > 1)
timeout_usec--;
last_timeout_num = num;
} }
} }
...@@ -887,7 +955,6 @@ static bool handle_tun_input(int fd, struct device *dev) ...@@ -887,7 +955,6 @@ static bool handle_tun_input(int fd, struct device *dev)
unsigned int head, in_num, out_num; unsigned int head, in_num, out_num;
int len; int len;
struct iovec iov[dev->vq->vring.num]; struct iovec iov[dev->vq->vring.num];
struct virtio_net_hdr *hdr;
/* First we need a network buffer from the Guests's recv virtqueue. */ /* First we need a network buffer from the Guests's recv virtqueue. */
head = get_vq_desc(dev->vq, iov, &out_num, &in_num); head = get_vq_desc(dev->vq, iov, &out_num, &in_num);
...@@ -896,25 +963,23 @@ static bool handle_tun_input(int fd, struct device *dev) ...@@ -896,25 +963,23 @@ static bool handle_tun_input(int fd, struct device *dev)
* early, the Guest won't be ready yet. Wait until the device * early, the Guest won't be ready yet. Wait until the device
* status says it's ready. */ * status says it's ready. */
/* FIXME: Actually want DRIVER_ACTIVE here. */ /* FIXME: Actually want DRIVER_ACTIVE here. */
if (dev->desc->status & VIRTIO_CONFIG_S_DRIVER_OK)
warn("network: no dma buffer!"); /* Now tell it we want to know if new things appear. */
dev->vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
wmb();
/* We'll turn this back on if input buffers are registered. */ /* We'll turn this back on if input buffers are registered. */
return false; return false;
} else if (out_num) } else if (out_num)
errx(1, "Output buffers in network recv queue?"); errx(1, "Output buffers in network recv queue?");
/* First element is the header: we set it to 0 (no features). */
hdr = convert(&iov[0], struct virtio_net_hdr);
hdr->flags = 0;
hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
/* Read the packet from the device directly into the Guest's buffer. */ /* Read the packet from the device directly into the Guest's buffer. */
len = readv(dev->fd, iov+1, in_num-1); len = readv(dev->fd, iov, in_num);
if (len <= 0) if (len <= 0)
err(1, "reading network"); err(1, "reading network");
/* Tell the Guest about the new packet. */ /* Tell the Guest about the new packet. */
add_used_and_trigger(fd, dev->vq, head, sizeof(*hdr) + len); add_used_and_trigger(fd, dev->vq, head, len);
verbose("tun input packet len %i [%02x %02x] (%s)\n", len, verbose("tun input packet len %i [%02x %02x] (%s)\n", len,
((u8 *)iov[1].iov_base)[0], ((u8 *)iov[1].iov_base)[1], ((u8 *)iov[1].iov_base)[0], ((u8 *)iov[1].iov_base)[1],
...@@ -927,11 +992,18 @@ static bool handle_tun_input(int fd, struct device *dev) ...@@ -927,11 +992,18 @@ static bool handle_tun_input(int fd, struct device *dev)
/*L:215 This is the callback attached to the network and console input /*L:215 This is the callback attached to the network and console input
* virtqueues: it ensures we try again, in case we stopped console or net * virtqueues: it ensures we try again, in case we stopped console or net
* delivery because Guest didn't have any buffers. */ * delivery because Guest didn't have any buffers. */
static void enable_fd(int fd, struct virtqueue *vq) static void enable_fd(int fd, struct virtqueue *vq, bool timeout)
{ {
add_device_fd(vq->dev->fd); add_device_fd(vq->dev->fd);
/* Tell waker to listen to it again */ /* Snap the Waker out of its select loop. */
write(waker_fd, &vq->dev->fd, sizeof(vq->dev->fd)); write(waker_fds.pipe[1], "", 1);
}
static void net_enable_fd(int fd, struct virtqueue *vq, bool timeout)
{
/* We don't need to know again when Guest refills receive buffer. */
vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
enable_fd(fd, vq, timeout);
} }
/* When the Guest tells us they updated the status field, we handle it. */ /* When the Guest tells us they updated the status field, we handle it. */
...@@ -951,7 +1023,7 @@ static void update_device_status(struct device *dev) ...@@ -951,7 +1023,7 @@ static void update_device_status(struct device *dev)
for (vq = dev->vq; vq; vq = vq->next) { for (vq = dev->vq; vq; vq = vq->next) {
memset(vq->vring.desc, 0, memset(vq->vring.desc, 0,
vring_size(vq->config.num, getpagesize())); vring_size(vq->config.num, getpagesize()));
vq->last_avail_idx = 0; lg_last_avail(vq) = 0;
} }
} else if (dev->desc->status & VIRTIO_CONFIG_S_FAILED) { } else if (dev->desc->status & VIRTIO_CONFIG_S_FAILED) {
warnx("Device %s configuration FAILED", dev->name); warnx("Device %s configuration FAILED", dev->name);
...@@ -960,10 +1032,10 @@ static void update_device_status(struct device *dev) ...@@ -960,10 +1032,10 @@ static void update_device_status(struct device *dev)
verbose("Device %s OK: offered", dev->name); verbose("Device %s OK: offered", dev->name);
for (i = 0; i < dev->desc->feature_len; i++) for (i = 0; i < dev->desc->feature_len; i++)
verbose(" %08x", get_feature_bits(dev)[i]); verbose(" %02x", get_feature_bits(dev)[i]);
verbose(", accepted"); verbose(", accepted");
for (i = 0; i < dev->desc->feature_len; i++) for (i = 0; i < dev->desc->feature_len; i++)
verbose(" %08x", get_feature_bits(dev) verbose(" %02x", get_feature_bits(dev)
[dev->desc->feature_len+i]); [dev->desc->feature_len+i]);
if (dev->ready) if (dev->ready)
...@@ -1000,7 +1072,7 @@ static void handle_output(int fd, unsigned long addr) ...@@ -1000,7 +1072,7 @@ static void handle_output(int fd, unsigned long addr)
if (strcmp(vq->dev->name, "console") != 0) if (strcmp(vq->dev->name, "console") != 0)
verbose("Output to %s\n", vq->dev->name); verbose("Output to %s\n", vq->dev->name);
if (vq->handle_output) if (vq->handle_output)
vq->handle_output(fd, vq); vq->handle_output(fd, vq, false);
return; return;
} }
} }
...@@ -1014,6 +1086,29 @@ static void handle_output(int fd, unsigned long addr) ...@@ -1014,6 +1086,29 @@ static void handle_output(int fd, unsigned long addr)
strnlen(from_guest_phys(addr), guest_limit - addr)); strnlen(from_guest_phys(addr), guest_limit - addr));
} }
static void handle_timeout(int fd)
{
char buf[32];
struct device *i;
struct virtqueue *vq;
/* Clear the pipe */
read(timeoutpipe[0], buf, sizeof(buf));
/* Check each device and virtqueue: flush blocked ones. */
for (i = devices.dev; i; i = i->next) {
for (vq = i->vq; vq; vq = vq->next) {
if (!vq->blocked)
continue;
vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
vq->blocked = false;
if (vq->handle_output)
vq->handle_output(fd, vq, true);
}
}
}
/* This is called when the Waker wakes us up: check for incoming file /* This is called when the Waker wakes us up: check for incoming file
* descriptors. */ * descriptors. */
static void handle_input(int fd) static void handle_input(int fd)
...@@ -1024,16 +1119,20 @@ static void handle_input(int fd) ...@@ -1024,16 +1119,20 @@ static void handle_input(int fd)
for (;;) { for (;;) {
struct device *i; struct device *i;
fd_set fds = devices.infds; fd_set fds = devices.infds;
int num;
num = select(devices.max_infd+1, &fds, NULL, NULL, &poll);
/* Could get interrupted */
if (num < 0)
continue;
/* If nothing is ready, we're done. */ /* If nothing is ready, we're done. */
if (select(devices.max_infd+1, &fds, NULL, NULL, &poll) == 0) if (num == 0)
break; break;
/* Otherwise, call the device(s) which have readable file /* Otherwise, call the device(s) which have readable file
* descriptors and a method of handling them. */ * descriptors and a method of handling them. */
for (i = devices.dev; i; i = i->next) { for (i = devices.dev; i; i = i->next) {
if (i->handle_input && FD_ISSET(i->fd, &fds)) { if (i->handle_input && FD_ISSET(i->fd, &fds)) {
int dev_fd;
if (i->handle_input(fd, i)) if (i->handle_input(fd, i))
continue; continue;
...@@ -1043,13 +1142,12 @@ static void handle_input(int fd) ...@@ -1043,13 +1142,12 @@ static void handle_input(int fd)
* buffers to deliver into. Console also uses * buffers to deliver into. Console also uses
* it when it discovers that stdin is closed. */ * it when it discovers that stdin is closed. */
FD_CLR(i->fd, &devices.infds); FD_CLR(i->fd, &devices.infds);
/* Tell waker to ignore it too, by sending a
* negative fd number (-1, since 0 is a valid
* FD number). */
dev_fd = -i->fd - 1;
write(waker_fd, &dev_fd, sizeof(dev_fd));
} }
} }
/* Is this the timeout fd? */
if (FD_ISSET(timeoutpipe[0], &fds))
handle_timeout(fd);
} }
} }
...@@ -1098,7 +1196,7 @@ static struct lguest_device_desc *new_dev_desc(u16 type) ...@@ -1098,7 +1196,7 @@ static struct lguest_device_desc *new_dev_desc(u16 type)
/* Each device descriptor is followed by the description of its virtqueues. We /* Each device descriptor is followed by the description of its virtqueues. We
* specify how many descriptors the virtqueue is to have. */ * specify how many descriptors the virtqueue is to have. */
static void add_virtqueue(struct device *dev, unsigned int num_descs, static void add_virtqueue(struct device *dev, unsigned int num_descs,
void (*handle_output)(int fd, struct virtqueue *me)) void (*handle_output)(int, struct virtqueue *, bool))
{ {
unsigned int pages; unsigned int pages;
struct virtqueue **i, *vq = malloc(sizeof(*vq)); struct virtqueue **i, *vq = malloc(sizeof(*vq));
...@@ -1114,6 +1212,7 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs, ...@@ -1114,6 +1212,7 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
vq->last_avail_idx = 0; vq->last_avail_idx = 0;
vq->dev = dev; vq->dev = dev;
vq->inflight = 0; vq->inflight = 0;
vq->blocked = false;
/* Initialize the configuration. */ /* Initialize the configuration. */
vq->config.num = num_descs; vq->config.num = num_descs;
...@@ -1246,6 +1345,24 @@ static void setup_console(void) ...@@ -1246,6 +1345,24 @@ static void setup_console(void)
} }
/*:*/ /*:*/
static void timeout_alarm(int sig)
{
write(timeoutpipe[1], "", 1);
}
static void setup_timeout(void)
{
if (pipe(timeoutpipe) != 0)
err(1, "Creating timeout pipe");
if (fcntl(timeoutpipe[1], F_SETFL,
fcntl(timeoutpipe[1], F_GETFL) | O_NONBLOCK) != 0)
err(1, "Making timeout pipe nonblocking");
add_device_fd(timeoutpipe[0]);
signal(SIGALRM, timeout_alarm);
}
/*M:010 Inter-guest networking is an interesting area. Simplest is to have a /*M:010 Inter-guest networking is an interesting area. Simplest is to have a
* --sharenet=<name> option which opens or creates a named pipe. This can be * --sharenet=<name> option which opens or creates a named pipe. This can be
* used to send packets to another guest in a 1:1 manner. * used to send packets to another guest in a 1:1 manner.
...@@ -1264,10 +1381,25 @@ static void setup_console(void) ...@@ -1264,10 +1381,25 @@ static void setup_console(void)
static u32 str2ip(const char *ipaddr) static u32 str2ip(const char *ipaddr)
{ {
unsigned int byte[4]; unsigned int b[4];
sscanf(ipaddr, "%u.%u.%u.%u", &byte[0], &byte[1], &byte[2], &byte[3]); if (sscanf(ipaddr, "%u.%u.%u.%u", &b[0], &b[1], &b[2], &b[3]) != 4)
return (byte[0] << 24) | (byte[1] << 16) | (byte[2] << 8) | byte[3]; errx(1, "Failed to parse IP address '%s'", ipaddr);
return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
}
static void str2mac(const char *macaddr, unsigned char mac[6])
{
unsigned int m[6];
if (sscanf(macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
&m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) != 6)
errx(1, "Failed to parse mac address '%s'", macaddr);
mac[0] = m[0];
mac[1] = m[1];
mac[2] = m[2];
mac[3] = m[3];
mac[4] = m[4];
mac[5] = m[5];
} }
/* This code is "adapted" from libbridge: it attaches the Host end of the /* This code is "adapted" from libbridge: it attaches the Host end of the
...@@ -1288,6 +1420,7 @@ static void add_to_bridge(int fd, const char *if_name, const char *br_name) ...@@ -1288,6 +1420,7 @@ static void add_to_bridge(int fd, const char *if_name, const char *br_name)
errx(1, "interface %s does not exist!", if_name); errx(1, "interface %s does not exist!", if_name);
strncpy(ifr.ifr_name, br_name, IFNAMSIZ); strncpy(ifr.ifr_name, br_name, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
ifr.ifr_ifindex = ifidx; ifr.ifr_ifindex = ifidx;
if (ioctl(fd, SIOCBRADDIF, &ifr) < 0) if (ioctl(fd, SIOCBRADDIF, &ifr) < 0)
err(1, "can't add %s to bridge %s", if_name, br_name); err(1, "can't add %s to bridge %s", if_name, br_name);
...@@ -1296,64 +1429,90 @@ static void add_to_bridge(int fd, const char *if_name, const char *br_name) ...@@ -1296,64 +1429,90 @@ static void add_to_bridge(int fd, const char *if_name, const char *br_name)
/* This sets up the Host end of the network device with an IP address, brings /* This sets up the Host end of the network device with an IP address, brings
* it up so packets will flow, the copies the MAC address into the hwaddr * it up so packets will flow, the copies the MAC address into the hwaddr
* pointer. */ * pointer. */
static void configure_device(int fd, const char *devname, u32 ipaddr, static void configure_device(int fd, const char *tapif, u32 ipaddr)
unsigned char hwaddr[6])
{ {
struct ifreq ifr; struct ifreq ifr;
struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
/* Don't read these incantations. Just cut & paste them like I did! */
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, devname); strcpy(ifr.ifr_name, tapif);
/* Don't read these incantations. Just cut & paste them like I did! */
sin->sin_family = AF_INET; sin->sin_family = AF_INET;
sin->sin_addr.s_addr = htonl(ipaddr); sin->sin_addr.s_addr = htonl(ipaddr);
if (ioctl(fd, SIOCSIFADDR, &ifr) != 0) if (ioctl(fd, SIOCSIFADDR, &ifr) != 0)
err(1, "Setting %s interface address", devname); err(1, "Setting %s interface address", tapif);
ifr.ifr_flags = IFF_UP; ifr.ifr_flags = IFF_UP;
if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0)
err(1, "Bringing interface %s up", devname); err(1, "Bringing interface %s up", tapif);
}
static void get_mac(int fd, const char *tapif, unsigned char hwaddr[6])
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, tapif);
/* SIOC stands for Socket I/O Control. G means Get (vs S for Set /* SIOC stands for Socket I/O Control. G means Get (vs S for Set
* above). IF means Interface, and HWADDR is hardware address. * above). IF means Interface, and HWADDR is hardware address.
* Simple! */ * Simple! */
if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
err(1, "getting hw address for %s", devname); err(1, "getting hw address for %s", tapif);
memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6); memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);
} }
/*L:195 Our network is a Host<->Guest network. This can either use bridging or static int get_tun_device(char tapif[IFNAMSIZ])
* routing, but the principle is the same: it uses the "tun" device to inject
* packets into the Host as if they came in from a normal network card. We
* just shunt packets between the Guest and the tun device. */
static void setup_tun_net(const char *arg)
{ {
struct device *dev;
struct ifreq ifr; struct ifreq ifr;
int netfd, ipfd; int netfd;
u32 ip;
const char *br_name = NULL; /* Start with this zeroed. Messy but sure. */
struct virtio_net_config conf; memset(&ifr, 0, sizeof(ifr));
/* We open the /dev/net/tun device and tell it we want a tap device. A /* 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 * tap device is like a tun device, only somehow different. To tell
* the truth, I completely blundered my way through this code, but it * the truth, I completely blundered my way through this code, but it
* works now! */ * works now! */
netfd = open_or_die("/dev/net/tun", O_RDWR); netfd = open_or_die("/dev/net/tun", O_RDWR);
memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
strcpy(ifr.ifr_name, "tap%d"); strcpy(ifr.ifr_name, "tap%d");
if (ioctl(netfd, TUNSETIFF, &ifr) != 0) if (ioctl(netfd, TUNSETIFF, &ifr) != 0)
err(1, "configuring /dev/net/tun"); err(1, "configuring /dev/net/tun");
if (ioctl(netfd, TUNSETOFFLOAD,
TUN_F_CSUM|TUN_F_TSO4|TUN_F_TSO6|TUN_F_TSO_ECN) != 0)
err(1, "Could not set features for tun device");
/* We don't need checksums calculated for packets coming in this /* We don't need checksums calculated for packets coming in this
* device: trust us! */ * device: trust us! */
ioctl(netfd, TUNSETNOCSUM, 1); ioctl(netfd, TUNSETNOCSUM, 1);
memcpy(tapif, ifr.ifr_name, IFNAMSIZ);
return netfd;
}
/*L:195 Our network is a Host<->Guest network. This can either use bridging or
* routing, but the principle is the same: it uses the "tun" device to inject
* packets into the Host as if they came in from a normal network card. We
* just shunt packets between the Guest and the tun device. */
static void setup_tun_net(char *arg)
{
struct device *dev;
int netfd, ipfd;
u32 ip = INADDR_ANY;
bool bridging = false;
char tapif[IFNAMSIZ], *p;
struct virtio_net_config conf;
netfd = get_tun_device(tapif);
/* First we create a new network device. */ /* First we create a new network device. */
dev = new_device("net", VIRTIO_ID_NET, netfd, handle_tun_input); dev = new_device("net", VIRTIO_ID_NET, netfd, handle_tun_input);
/* Network devices need a receive and a send queue, just like /* Network devices need a receive and a send queue, just like
* console. */ * console. */
add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd); add_virtqueue(dev, VIRTQUEUE_NUM, net_enable_fd);
add_virtqueue(dev, VIRTQUEUE_NUM, handle_net_output); add_virtqueue(dev, VIRTQUEUE_NUM, handle_net_output);
/* We need a socket to perform the magic network ioctls to bring up the /* We need a socket to perform the magic network ioctls to bring up the
...@@ -1364,28 +1523,56 @@ static void setup_tun_net(const char *arg) ...@@ -1364,28 +1523,56 @@ static void setup_tun_net(const char *arg)
/* If the command line was --tunnet=bridge:<name> do bridging. */ /* If the command line was --tunnet=bridge:<name> do bridging. */
if (!strncmp(BRIDGE_PFX, arg, strlen(BRIDGE_PFX))) { if (!strncmp(BRIDGE_PFX, arg, strlen(BRIDGE_PFX))) {
ip = INADDR_ANY; arg += strlen(BRIDGE_PFX);
br_name = arg + strlen(BRIDGE_PFX); bridging = true;
add_to_bridge(ipfd, ifr.ifr_name, br_name); }
} else /* It is an IP address to set up the device with */
/* A mac address may follow the bridge name or IP address */
p = strchr(arg, ':');
if (p) {
str2mac(p+1, conf.mac);
*p = '\0';
} else {
p = arg + strlen(arg);
/* None supplied; query the randomly assigned mac. */
get_mac(ipfd, tapif, conf.mac);
}
/* arg is now either an IP address or a bridge name */
if (bridging)
add_to_bridge(ipfd, tapif, arg);
else
ip = str2ip(arg); ip = str2ip(arg);
/* Set up the tun device, and get the mac address for the interface. */ /* Set up the tun device. */
configure_device(ipfd, ifr.ifr_name, ip, conf.mac); configure_device(ipfd, tapif, ip);
/* Tell Guest what MAC address to use. */ /* Tell Guest what MAC address to use. */
add_feature(dev, VIRTIO_NET_F_MAC); add_feature(dev, VIRTIO_NET_F_MAC);
add_feature(dev, VIRTIO_F_NOTIFY_ON_EMPTY); add_feature(dev, VIRTIO_F_NOTIFY_ON_EMPTY);
/* Expect Guest to handle everything except UFO */
add_feature(dev, VIRTIO_NET_F_CSUM);
add_feature(dev, VIRTIO_NET_F_GUEST_CSUM);
add_feature(dev, VIRTIO_NET_F_MAC);
add_feature(dev, VIRTIO_NET_F_GUEST_TSO4);
add_feature(dev, VIRTIO_NET_F_GUEST_TSO6);
add_feature(dev, VIRTIO_NET_F_GUEST_ECN);
add_feature(dev, VIRTIO_NET_F_HOST_TSO4);
add_feature(dev, VIRTIO_NET_F_HOST_TSO6);
add_feature(dev, VIRTIO_NET_F_HOST_ECN);
set_config(dev, sizeof(conf), &conf); set_config(dev, sizeof(conf), &conf);
/* We don't need the socket any more; setup is done. */ /* We don't need the socket any more; setup is done. */
close(ipfd); close(ipfd);
verbose("device %u: tun net %u.%u.%u.%u\n", devices.device_num++;
devices.device_num++,
(u8)(ip>>24),(u8)(ip>>16),(u8)(ip>>8),(u8)ip); if (bridging)
if (br_name) verbose("device %u: tun %s attached to bridge: %s\n",
verbose("attached to bridge: %s\n", br_name); devices.device_num, tapif, arg);
else
verbose("device %u: tun %s: %s\n",
devices.device_num, tapif, arg);
} }
/* Our block (disk) device should be really simple: the Guest asks for a block /* Our block (disk) device should be really simple: the Guest asks for a block
...@@ -1550,7 +1737,7 @@ static bool handle_io_finish(int fd, struct device *dev) ...@@ -1550,7 +1737,7 @@ static bool handle_io_finish(int fd, struct device *dev)
} }
/* When the Guest submits some I/O, we just need to wake the I/O thread. */ /* When the Guest submits some I/O, we just need to wake the I/O thread. */
static void handle_virtblk_output(int fd, struct virtqueue *vq) static void handle_virtblk_output(int fd, struct virtqueue *vq, bool timeout)
{ {
struct vblk_info *vblk = vq->dev->priv; struct vblk_info *vblk = vq->dev->priv;
char c = 0; char c = 0;
...@@ -1621,6 +1808,64 @@ static void setup_block_file(const char *filename) ...@@ -1621,6 +1808,64 @@ static void setup_block_file(const char *filename)
verbose("device %u: virtblock %llu sectors\n", verbose("device %u: virtblock %llu sectors\n",
devices.device_num, le64_to_cpu(conf.capacity)); 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. */ /* That's the end of device setup. */
/*L:230 Reboot is pretty easy: clean up and exec() the Launcher afresh. */ /*L:230 Reboot is pretty easy: clean up and exec() the Launcher afresh. */
...@@ -1628,11 +1873,12 @@ static void __attribute__((noreturn)) restart_guest(void) ...@@ -1628,11 +1873,12 @@ static void __attribute__((noreturn)) restart_guest(void)
{ {
unsigned int i; unsigned int i;
/* Closing pipes causes the Waker thread and io_threads to die, and /* Since we don't track all open fds, we simply close everything beyond
* closing /dev/lguest cleans up the Guest. Since we don't track all * stderr. */
* open fds, we simply close everything beyond stderr. */
for (i = 3; i < FD_SETSIZE; i++) for (i = 3; i < FD_SETSIZE; i++)
close(i); close(i);
/* The exec automatically gets rid of the I/O and Waker threads. */
execv(main_args[0], main_args); execv(main_args[0], main_args);
err(1, "Could not exec %s", main_args[0]); err(1, "Could not exec %s", main_args[0]);
} }
...@@ -1663,7 +1909,7 @@ static void __attribute__((noreturn)) run_guest(int lguest_fd) ...@@ -1663,7 +1909,7 @@ static void __attribute__((noreturn)) run_guest(int lguest_fd)
/* ERESTART means that we need to reboot the guest */ /* ERESTART means that we need to reboot the guest */
} else if (errno == ERESTART) { } else if (errno == ERESTART) {
restart_guest(); restart_guest();
/* EAGAIN means the Waker wanted us to look at some input. /* EAGAIN means a signal (timeout).
* Anything else means a bug or incompatible change. */ * Anything else means a bug or incompatible change. */
} else if (errno != EAGAIN) } else if (errno != EAGAIN)
err(1, "Running guest failed"); err(1, "Running guest failed");
...@@ -1691,13 +1937,14 @@ static struct option opts[] = { ...@@ -1691,13 +1937,14 @@ static struct option opts[] = {
{ "verbose", 0, NULL, 'v' }, { "verbose", 0, NULL, 'v' },
{ "tunnet", 1, NULL, 't' }, { "tunnet", 1, NULL, 't' },
{ "block", 1, NULL, 'b' }, { "block", 1, NULL, 'b' },
{ "rng", 0, NULL, 'r' },
{ "initrd", 1, NULL, 'i' }, { "initrd", 1, NULL, 'i' },
{ NULL }, { NULL },
}; };
static void usage(void) static void usage(void)
{ {
errx(1, "Usage: lguest [--verbose] " errx(1, "Usage: lguest [--verbose] "
"[--tunnet=(<ipaddr>|bridge:<bridgename>)\n" "[--tunnet=(<ipaddr>:<macaddr>|bridge:<bridgename>:<macaddr>)\n"
"|--block=<filename>|--initrd=<filename>]...\n" "|--block=<filename>|--initrd=<filename>]...\n"
"<mem-in-mb> vmlinux [args...]"); "<mem-in-mb> vmlinux [args...]");
} }
...@@ -1765,6 +2012,9 @@ int main(int argc, char *argv[]) ...@@ -1765,6 +2012,9 @@ int main(int argc, char *argv[])
case 'b': case 'b':
setup_block_file(optarg); setup_block_file(optarg);
break; break;
case 'r':
setup_rng();
break;
case 'i': case 'i':
initrd_name = optarg; initrd_name = optarg;
break; break;
...@@ -1783,6 +2033,9 @@ int main(int argc, char *argv[]) ...@@ -1783,6 +2033,9 @@ int main(int argc, char *argv[])
/* We always have a console device */ /* We always have a console device */
setup_console(); setup_console();
/* We can timeout waiting for Guest network transmit. */
setup_timeout();
/* Now we load the kernel */ /* Now we load the kernel */
start = load_kernel(open_or_die(argv[optind+1], O_RDONLY)); start = load_kernel(open_or_die(argv[optind+1], O_RDONLY));
...@@ -1826,10 +2079,10 @@ int main(int argc, char *argv[]) ...@@ -1826,10 +2079,10 @@ int main(int argc, char *argv[])
* /dev/lguest file descriptor. */ * /dev/lguest file descriptor. */
lguest_fd = tell_kernel(pgdir, start); lguest_fd = tell_kernel(pgdir, start);
/* We fork off a child process, which wakes the Launcher whenever one /* We clone off a thread, which wakes the Launcher whenever one of the
* of the input file descriptors needs attention. We call this the * input file descriptors needs attention. We call this the Waker, and
* Waker, and we'll cover it in a moment. */ * we'll cover it in a moment. */
waker_fd = setup_waker(lguest_fd); setup_waker(lguest_fd);
/* Finally, run the Guest. This doesn't return. */ /* Finally, run the Guest. This doesn't return. */
run_guest(lguest_fd); run_guest(lguest_fd);
......
...@@ -1014,6 +1014,9 @@ __init void lguest_init(void) ...@@ -1014,6 +1014,9 @@ __init void lguest_init(void)
init_pg_tables_start = __pa(pg0); init_pg_tables_start = __pa(pg0);
init_pg_tables_end = __pa(pg0); init_pg_tables_end = __pa(pg0);
/* As described in head_32.S, we map the first 128M of memory. */
max_pfn_mapped = (128*1024*1024) >> PAGE_SHIFT;
/* Load the %fs segment register (the per-cpu segment register) with /* Load the %fs segment register (the per-cpu segment register) with
* the normal data segment to get through booting. */ * the normal data segment to get through booting. */
asm volatile ("mov %0, %%fs" : : "r" (__KERNEL_DS) : "memory"); asm volatile ("mov %0, %%fs" : : "r" (__KERNEL_DS) : "memory");
......
...@@ -135,6 +135,7 @@ static void unmap_switcher(void) ...@@ -135,6 +135,7 @@ static void unmap_switcher(void)
/* Now we just need to free the pages we copied the switcher into */ /* Now we just need to free the pages we copied the switcher into */
for (i = 0; i < TOTAL_SWITCHER_PAGES; i++) for (i = 0; i < TOTAL_SWITCHER_PAGES; i++)
__free_pages(switcher_page[i], 0); __free_pages(switcher_page[i], 0);
kfree(switcher_page);
} }
/*H:032 /*H:032
......
...@@ -406,7 +406,8 @@ void load_guest_idt_entry(struct lg_cpu *cpu, unsigned int num, u32 lo, u32 hi) ...@@ -406,7 +406,8 @@ void load_guest_idt_entry(struct lg_cpu *cpu, unsigned int num, u32 lo, u32 hi)
* deliver_trap() to bounce it back into the Guest. */ * deliver_trap() to bounce it back into the Guest. */
static void default_idt_entry(struct desc_struct *idt, static void default_idt_entry(struct desc_struct *idt,
int trap, int trap,
const unsigned long handler) const unsigned long handler,
const struct desc_struct *base)
{ {
/* A present interrupt gate. */ /* A present interrupt gate. */
u32 flags = 0x8e00; u32 flags = 0x8e00;
...@@ -415,6 +416,10 @@ static void default_idt_entry(struct desc_struct *idt, ...@@ -415,6 +416,10 @@ static void default_idt_entry(struct desc_struct *idt,
* the Guest to use the "int" instruction to trigger it. */ * the Guest to use the "int" instruction to trigger it. */
if (trap == LGUEST_TRAP_ENTRY) if (trap == LGUEST_TRAP_ENTRY)
flags |= (GUEST_PL << 13); flags |= (GUEST_PL << 13);
else if (base)
/* Copy priv. level from what Guest asked for. This allows
* debug (int 3) traps from Guest userspace, for example. */
flags |= (base->b & 0x6000);
/* Now pack it into the IDT entry in its weird format. */ /* Now pack it into the IDT entry in its weird format. */
idt->a = (LGUEST_CS<<16) | (handler&0x0000FFFF); idt->a = (LGUEST_CS<<16) | (handler&0x0000FFFF);
...@@ -428,7 +433,7 @@ void setup_default_idt_entries(struct lguest_ro_state *state, ...@@ -428,7 +433,7 @@ void setup_default_idt_entries(struct lguest_ro_state *state,
unsigned int i; unsigned int i;
for (i = 0; i < ARRAY_SIZE(state->guest_idt); i++) for (i = 0; i < ARRAY_SIZE(state->guest_idt); i++)
default_idt_entry(&state->guest_idt[i], i, def[i]); default_idt_entry(&state->guest_idt[i], i, def[i], NULL);
} }
/*H:240 We don't use the IDT entries in the "struct lguest" directly, instead /*H:240 We don't use the IDT entries in the "struct lguest" directly, instead
...@@ -442,6 +447,8 @@ void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt, ...@@ -442,6 +447,8 @@ void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt,
/* We can simply copy the direct traps, otherwise we use the default /* We can simply copy the direct traps, otherwise we use the default
* ones in the Switcher: they will return to the Host. */ * ones in the Switcher: they will return to the Host. */
for (i = 0; i < ARRAY_SIZE(cpu->arch.idt); i++) { for (i = 0; i < ARRAY_SIZE(cpu->arch.idt); i++) {
const struct desc_struct *gidt = &cpu->arch.idt[i];
/* If no Guest can ever override this trap, leave it alone. */ /* If no Guest can ever override this trap, leave it alone. */
if (!direct_trap(i)) if (!direct_trap(i))
continue; continue;
...@@ -449,12 +456,15 @@ void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt, ...@@ -449,12 +456,15 @@ void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt,
/* Only trap gates (type 15) can go direct to the Guest. /* Only trap gates (type 15) can go direct to the Guest.
* Interrupt gates (type 14) disable interrupts as they are * Interrupt gates (type 14) disable interrupts as they are
* entered, which we never let the Guest do. Not present * entered, which we never let the Guest do. Not present
* entries (type 0x0) also can't go direct, of course. */ * entries (type 0x0) also can't go direct, of course.
if (idt_type(cpu->arch.idt[i].a, cpu->arch.idt[i].b) == 0xF) *
idt[i] = cpu->arch.idt[i]; * If it can't go direct, we still need to copy the priv. level:
* they might want to give userspace access to a software
* interrupt. */
if (idt_type(gidt->a, gidt->b) == 0xF)
idt[i] = *gidt;
else else
/* Reset it to the default. */ default_idt_entry(&idt[i], i, def[i], gidt);
default_idt_entry(&idt[i], i, def[i]);
} }
} }
......
...@@ -480,7 +480,7 @@ void __init lguest_arch_host_init(void) ...@@ -480,7 +480,7 @@ void __init lguest_arch_host_init(void)
* bit on its CPU, depending on the argument (0 == unset). */ * bit on its CPU, depending on the argument (0 == unset). */
on_each_cpu(adjust_pge, (void *)0, 1); on_each_cpu(adjust_pge, (void *)0, 1);
/* Turn off the feature in the global feature set. */ /* Turn off the feature in the global feature set. */
clear_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability); clear_cpu_cap(&boot_cpu_data, X86_FEATURE_PGE);
} }
put_online_cpus(); put_online_cpus();
}; };
...@@ -491,7 +491,7 @@ void __exit lguest_arch_host_fini(void) ...@@ -491,7 +491,7 @@ void __exit lguest_arch_host_fini(void)
/* If we had PGE before we started, turn it back on now. */ /* If we had PGE before we started, turn it back on now. */
get_online_cpus(); get_online_cpus();
if (cpu_had_pge) { if (cpu_had_pge) {
set_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability); set_cpu_cap(&boot_cpu_data, X86_FEATURE_PGE);
/* adjust_pge's argument "1" means set PGE. */ /* adjust_pge's argument "1" means set PGE. */
on_each_cpu(adjust_pge, (void *)1, 1); on_each_cpu(adjust_pge, (void *)1, 1);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment