From db24e8c2ef7eceb46818a8c22fc38dea733fe159 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 25 Oct 2007 14:09:25 +1000 Subject: lguest: example launcher header cleanup. Now the kernel headers are clean for userspace export, we don't need to typedef kernel types before including them. We also don't need pci_ids.h (that was from an earlier virtio draft). Signed-off-by: Rusty Russell diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index 5bdc37f..c91c28a 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c @@ -34,25 +34,24 @@ #include #include #include -/*L:110 We can ignore the 30 include files we need for this program, but I do - * want to draw attention to the use of kernel-style types. - * - * As Linus said, "C is a Spartan language, and so should your naming be." I - * like these abbreviations and the header we need uses them, so we define them - * here. - */ -typedef unsigned long long u64; -typedef uint32_t u32; -typedef uint16_t u16; -typedef uint8_t u8; #include "linux/lguest_launcher.h" -#include "linux/pci_ids.h" #include "linux/virtio_config.h" #include "linux/virtio_net.h" #include "linux/virtio_blk.h" #include "linux/virtio_console.h" #include "linux/virtio_ring.h" #include "asm-x86/bootparam.h" +/*L:110 We can ignore the 38 include files we need for this program, but I do + * want to draw attention to the use of kernel-style types. + * + * As Linus said, "C is a Spartan language, and so should your naming be." I + * like these abbreviations, so we define them here. Note that u64 is always + * unsigned long long, which works on all Linux systems: this means that we can + * use %llu in printf for any u64. */ +typedef unsigned long long u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; /*:*/ #define PAGE_PRESENT 0x7 /* Present, RW, Execute */ -- cgit v0.10.2 From 25c47bb35355c1e1b2ae325b49441b2c8b201ece Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 25 Oct 2007 14:09:53 +1000 Subject: lguest: use defines from x86 headers instead of magic numbers Signed-off-by: Rusty Russell diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index d2235db..136d9df 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -371,8 +371,7 @@ static void lguest_cpuid(unsigned int *eax, unsigned int *ebx, static unsigned long current_cr0, current_cr3; static void lguest_write_cr0(unsigned long val) { - /* 8 == TS bit. */ - lazy_hcall(LHCALL_TS, val & 8, 0, 0); + lazy_hcall(LHCALL_TS, val & X86_CR0_TS, 0, 0); current_cr0 = val; } @@ -387,7 +386,7 @@ static unsigned long lguest_read_cr0(void) static void lguest_clts(void) { lazy_hcall(LHCALL_TS, 0, 0, 0); - current_cr0 &= ~8U; + current_cr0 &= ~X86_CR0_TS; } /* CR2 is the virtual address of the last page fault, which the Guest only ever diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 9eed12d..09d9207 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -562,7 +562,7 @@ void lguest_arch_setup_regs(struct lguest *lg, unsigned long start) * is supposed to always be "1". Bit 9 (0x200) controls whether * interrupts are enabled. We always leave interrupts enabled while * running the Guest. */ - regs->eflags = 0x202; + regs->eflags = X86_EFLAGS_IF | 0x2; /* The "Extended Instruction Pointer" register says where the Guest is * running. */ -- cgit v0.10.2 From 197bff630ae586252d24b3595c54f8f9b712610a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 25 Oct 2007 14:10:30 +1000 Subject: lguest: remove unused "wake" element from struct lguest Signed-off-by: Rusty Russell diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index d9144be..0c74ac4 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -74,9 +74,6 @@ struct lguest u32 pgdidx; struct pgdir pgdirs[4]; - /* Cached wakeup: we hold a reference to this task. */ - struct task_struct *wake; - unsigned long noirq_start, noirq_end; unsigned long pending_notify; /* pfn from LHCALL_NOTIFY */ -- cgit v0.10.2 From 7334492b53706964eb055ed8e25e2e3315d7222a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 25 Oct 2007 14:12:20 +1000 Subject: lguest: clean up lguest_launcher.h Remove now-unused defines. Fix old idempotent #ifndef _ASM_LGUEST_USER name. Fix comment on use of lguest_req. Signed-off-by: Rusty Russell diff --git a/include/linux/lguest_launcher.h b/include/linux/lguest_launcher.h index 61e1e3e..c41fd48 100644 --- a/include/linux/lguest_launcher.h +++ b/include/linux/lguest_launcher.h @@ -1,17 +1,7 @@ -#ifndef _ASM_LGUEST_USER -#define _ASM_LGUEST_USER +#ifndef _LINUX_LGUEST_LAUNCHER +#define _LINUX_LGUEST_LAUNCHER /* Everything the "lguest" userspace program needs to know. */ #include -/* They can register up to 32 arrays of lguest_dma. */ -#define LGUEST_MAX_DMA 32 -/* At most we can dma 16 lguest_dma in one op. */ -#define LGUEST_MAX_DMA_SECTIONS 16 - -/* How many devices? Assume each one wants up to two dma arrays per device. */ -#define LGUEST_MAX_DEVICES (LGUEST_MAX_DMA/2) - -/* Where the Host expects the Guest to SEND_DMA console output to. */ -#define LGUEST_CONSOLE_DMA_KEY 0 /*D:010 * Drivers @@ -51,9 +41,9 @@ struct lguest_vqconfig { /* Write command first word is a request. */ enum lguest_req { - LHREQ_INITIALIZE, /* + pfnlimit, pgdir, start, pageoffset */ + LHREQ_INITIALIZE, /* + base, pfnlimit, pgdir, start */ LHREQ_GETDMA, /* No longer used */ LHREQ_IRQ, /* + irq */ LHREQ_BREAK, /* + on/off flag (on blocks until someone does off) */ }; -#endif /* _ASM_LGUEST_USER */ +#endif /* _LINUX_LGUEST_LAUNCHER */ -- cgit v0.10.2 From 4cfe6c3c1c301d3d7a36c9d579597b75ebb8ea13 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Thu, 25 Oct 2007 14:15:09 +1000 Subject: lguest: build fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix this error (i386 !SMP build) arch/x86/lguest/boot.c: In function ‘lguest_init’: arch/x86/lguest/boot.c:1059: error: ‘pm_power_off’ undeclared (first use in this function) by including linux/pm.h. Signed-off-by: Jeff Garzik Signed-off-by: Rusty Russell diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 136d9df..a0179fc 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 568a17ffce2eeceae0cd9fc37e97cbad12f70278 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 25 Oct 2007 14:12:24 +1000 Subject: lguest: Add to maintainers file. Signed-off-by: Rusty Russell diff --git a/MAINTAINERS b/MAINTAINERS index 76b8571..5862b78 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2259,6 +2259,13 @@ L: legousb-devel@lists.sourceforge.net W: http://legousb.sourceforge.net/ S: Maintained +LGUEST +P: Rusty Russell +M: rusty@rustcorp.com.au +L: lguest@ozlabs.org +W: http://lguest.ozlabs.org/ +S: Maintained + LINUX FOR IBM pSERIES (RS/6000) P: Paul Mackerras M: paulus@au.ibm.com -- cgit v0.10.2 From e1e72965ec2c02db99b415cd06c17ea90767e3a4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 25 Oct 2007 15:02:50 +1000 Subject: lguest: documentation update Went through the documentation doing typo and content fixes. This patch contains only comment and whitespace changes. Signed-off-by: Rusty Russell diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index c91c28a..f266839 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c @@ -360,8 +360,8 @@ static unsigned long load_bzimage(int fd) } /*L:140 Loading the kernel is easy when it's a "vmlinux", but most kernels - * come wrapped up in the self-decompressing "bzImage" format. With some funky - * coding, we can load those, too. */ + * come wrapped up in the self-decompressing "bzImage" format. With a little + * work, we can load those, too. */ static unsigned long load_kernel(int fd) { Elf32_Ehdr hdr; @@ -464,6 +464,7 @@ static unsigned long setup_pagetables(unsigned long mem, * to know where it is. */ return to_guest_phys(pgdir); } +/*:*/ /* Simple routine to roll all the commandline arguments together with spaces * between them. */ @@ -480,9 +481,9 @@ static void concat(char *dst, char *args[]) dst[len] = '\0'; } -/* This is where we actually tell the kernel to initialize the Guest. We saw - * the arguments it expects when we looked at initialize() in lguest_user.c: - * the base of guest "physical" memory, the top physical page to allow, the +/*L:185 This is where we actually tell the kernel to initialize the Guest. We + * saw the arguments it expects when we looked at initialize() in lguest_user.c: + * the base of Guest "physical" memory, the top physical page to allow, the * top level pagetable and the entry point for the Guest. */ static int tell_kernel(unsigned long pgdir, unsigned long start) { @@ -512,13 +513,14 @@ static void add_device_fd(int fd) /*L:200 * The Waker. * - * With a console and network devices, we can have lots of input which we need - * to process. We could try to tell the kernel what file descriptors to watch, - * but handing a file descriptor mask through to the kernel is fairly icky. + * With console, block and network devices, we can have lots of input which we + * need to process. We could try to tell the kernel what file descriptors to + * watch, but handing a file descriptor mask through to the kernel is fairly + * icky. * * Instead, we fork off a process which watches the file descriptors and writes - * the LHREQ_BREAK command to the /dev/lguest filedescriptor to tell the Host - * loop to stop running the Guest. This causes it to return from the + * 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 * /dev/lguest read with -EAGAIN, where it will write to /dev/lguest to reset * the LHREQ_BREAK and wake us up again. * @@ -544,7 +546,9 @@ static void wake_parent(int pipefd, int lguest_fd) if (read(pipefd, &fd, sizeof(fd)) == 0) exit(0); /* Otherwise it's telling us to change what file - * descriptors we're to listen to. */ + * descriptors we're to listen to. Positive means + * listen to a new one, negative means stop + * listening. */ if (fd >= 0) FD_SET(fd, &devices.infds); else @@ -559,7 +563,7 @@ static int setup_waker(int lguest_fd) { int pipefd[2], child; - /* We create a pipe to talk to the waker, and also so it knows when the + /* We create a pipe to talk to the Waker, and also so it knows when the * Launcher dies (and closes pipe). */ pipe(pipefd); child = fork(); @@ -567,7 +571,8 @@ static int setup_waker(int lguest_fd) err(1, "forking"); if (child == 0) { - /* Close the "writing" end of our copy of the pipe */ + /* 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); } @@ -578,12 +583,12 @@ static int setup_waker(int lguest_fd) return pipefd[1]; } -/*L:210 +/* * Device Handling. * - * When the Guest sends DMA to us, it sends us an array of addresses and sizes. + * When the Guest gives us a buffer, it sends an array of addresses and sizes. * We need to make sure it's not trying to reach into the Launcher itself, so - * we have a convenient routine which check it and exits with an error message + * we have a convenient routine which checks it and exits with an error message * if something funny is going on: */ static void *_check_pointer(unsigned long addr, unsigned int size, @@ -600,7 +605,9 @@ static void *_check_pointer(unsigned long addr, unsigned int size, /* A macro which transparently hands the line number to the real function. */ #define check_pointer(addr,size) _check_pointer(addr, size, __LINE__) -/* This function returns the next descriptor in the chain, or vq->vring.num. */ +/* Each buffer in the virtqueues is actually a chain of descriptors. This + * function returns the next descriptor in the chain, or vq->vring.num if we're + * at the end. */ static unsigned next_desc(struct virtqueue *vq, unsigned int i) { unsigned int next; @@ -679,13 +686,14 @@ static unsigned get_vq_desc(struct virtqueue *vq, return head; } -/* Once we've used one of their buffers, we tell them about it. We'll then +/* After we've used one of their buffers, we tell them about it. We'll then * want to send them an interrupt, using trigger_irq(). */ static void add_used(struct virtqueue *vq, unsigned int head, int len) { struct vring_used_elem *used; - /* Get a pointer to the next entry in the used ring. */ + /* The virtqueue contains a ring of used buffers. Get a pointer to the + * next entry in that used ring. */ used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num]; used->id = head; used->len = len; @@ -699,6 +707,7 @@ static void trigger_irq(int fd, struct virtqueue *vq) { unsigned long buf[] = { LHREQ_IRQ, vq->config.irq }; + /* If they don't want an interrupt, don't send one. */ if (vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) return; @@ -715,8 +724,11 @@ static void add_used_and_trigger(int fd, struct virtqueue *vq, trigger_irq(fd, vq); } -/* Here is the input terminal setting we save, and the routine to restore them - * on exit so the user can see what they type next. */ +/* + * The Console + * + * Here is the input terminal setting we save, and the routine to restore them + * on exit so the user gets their terminal back. */ static struct termios orig_term; static void restore_term(void) { @@ -817,7 +829,10 @@ static void handle_console_output(int fd, struct virtqueue *vq) } } -/* Handling output for network is also simple: we get all the output buffers +/* + * The Network + * + * Handling output for network is also simple: we get all the output buffers * and write them (ignoring the first element) to this device's file descriptor * (stdout). */ static void handle_net_output(int fd, struct virtqueue *vq) @@ -830,8 +845,9 @@ static void handle_net_output(int fd, struct virtqueue *vq) while ((head = get_vq_desc(vq, iov, &out, &in)) != vq->vring.num) { if (in) errx(1, "Input buffers in output queue?"); - /* Check header, but otherwise ignore it (we said we supported - * no features). */ + /* Check header, but otherwise ignore it (we told the Guest we + * supported no features, so it shouldn't have anything + * interesting). */ (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); @@ -882,7 +898,8 @@ static bool handle_tun_input(int fd, struct device *dev) return true; } -/* This callback ensures we try again, in case we stopped console or net +/*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 * delivery because Guest didn't have any buffers. */ static void enable_fd(int fd, struct virtqueue *vq) { @@ -918,7 +935,7 @@ static void handle_output(int fd, unsigned long addr) strnlen(from_guest_phys(addr), guest_limit - addr)); } -/* 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. */ static void handle_input(int fd) { @@ -985,8 +1002,7 @@ static struct lguest_device_desc *new_dev_desc(u16 type) } /* Each device descriptor is followed by some configuration information. - * The first byte is a "status" byte for the Guest to report what's happening. - * After that are fields: u8 type, u8 len, [... len bytes...]. + * 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. */ @@ -1043,14 +1059,17 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs, /* Link virtqueue back to device. */ vq->dev = dev; - /* Set up handler. */ + /* Set the routine to call when the Guest does something to this + * virtqueue. */ vq->handle_output = handle_output; + + /* Set the "Don't Notify Me" flag if we don't have a handler */ if (!handle_output) vq->vring.used->flags = VRING_USED_F_NO_NOTIFY; } /* This routine does all the creation and setup of a new device, including - * caling new_dev_desc() to allocate the descriptor and device memory. */ + * calling new_dev_desc() to allocate the descriptor and device memory. */ static struct device *new_device(const char *name, u16 type, int fd, bool (*handle_input)(int, struct device *)) { @@ -1059,7 +1078,7 @@ static struct device *new_device(const char *name, u16 type, int fd, /* 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/lgba, etc. */ + * is eth0, the first block device /dev/vda, etc. */ *devices.lastdev = dev; dev->next = NULL; devices.lastdev = &dev->next; @@ -1103,7 +1122,7 @@ static void setup_console(void) /* The console needs two virtqueues: the input then the output. When * they put something the input queue, we make sure we're listening to * stdin. When they put something in the output queue, we write it to - * stdout. */ + * stdout. */ add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd); add_virtqueue(dev, VIRTQUEUE_NUM, handle_console_output); @@ -1251,21 +1270,17 @@ static void setup_tun_net(const char *arg) verbose("attached to bridge: %s\n", br_name); } - -/* - * Block device. +/* Our block (disk) device should be really simple: the Guest asks for a block + * number and we read or write that position in the file. Unfortunately, that + * was amazingly slow: the Guest waits until the read is finished before + * running anything else, even if it could have been doing useful work. * - * Serving a block device is really easy: the Guest asks for a block number and - * we read or write that position in the file. - * - * Unfortunately, this is amazingly slow: the Guest waits until the read is - * finished before running anything else, even if it could be doing useful - * work. We could use async I/O, except it's reputed to suck so hard that - * characters actually go missing from your code when you try to use it. + * We could use async I/O, except it's reputed to suck so hard that characters + * actually go missing from your code when you try to use it. * * So we farm the I/O out to thread, and communicate with it via a pipe. */ -/* This hangs off device->priv, with the data. */ +/* This hangs off device->priv. */ struct vblk_info { /* The size of the file. */ @@ -1281,8 +1296,14 @@ struct vblk_info * Launcher triggers interrupt to Guest. */ int done_fd; }; +/*:*/ -/* This is the core of the I/O thread. It returns true if it did something. */ +/*L:210 + * The Disk + * + * Remember that the block device is handled by a separate I/O thread. We head + * straight into the core of that thread here: + */ static bool service_io(struct device *dev) { struct vblk_info *vblk = dev->priv; @@ -1293,10 +1314,14 @@ static bool service_io(struct device *dev) struct iovec iov[dev->vq->vring.num]; off64_t off; + /* See if there's a request waiting. If not, nothing to do. */ head = get_vq_desc(dev->vq, iov, &out_num, &in_num); if (head == dev->vq->vring.num) return false; + /* Every block request should contain at least one output buffer + * (detailing the location on disk and the type of request) and one + * input buffer (to hold the result). */ if (out_num == 0 || in_num == 0) errx(1, "Bad virtblk cmd %u out=%u in=%u", head, out_num, in_num); @@ -1305,10 +1330,15 @@ static bool service_io(struct device *dev) in = convert(&iov[out_num+in_num-1], struct virtio_blk_inhdr); off = out->sector * 512; - /* This is how we implement barriers. Pretty poor, no? */ + /* The block device implements "barriers", where the Guest indicates + * that it wants all previous writes to occur before this write. We + * don't have a way of asking our kernel to do a barrier, so we just + * synchronize all the data in the file. Pretty poor, no? */ if (out->type & VIRTIO_BLK_T_BARRIER) fdatasync(vblk->fd); + /* In general the virtio block driver is allowed to try SCSI commands. + * It'd be nice if we supported eject, for example, but we don't. */ if (out->type & VIRTIO_BLK_T_SCSI_CMD) { fprintf(stderr, "Scsi commands unsupported\n"); in->status = VIRTIO_BLK_S_UNSUPP; @@ -1374,7 +1404,7 @@ static int io_thread(void *_dev) /* When this read fails, it means Launcher died, so we follow. */ while (read(vblk->workpipe[0], &c, 1) == 1) { - /* We acknowledge each request immediately, to reduce latency, + /* We acknowledge each request immediately to reduce latency, * rather than waiting until we've done them all. I haven't * measured to see if it makes any difference. */ while (service_io(dev)) @@ -1383,12 +1413,14 @@ static int io_thread(void *_dev) return 0; } -/* When the thread says some I/O is done, we interrupt the Guest. */ +/* Now we've seen the I/O thread, we return to the Launcher to see what happens + * when the thread tells us it's completed some I/O. */ static bool handle_io_finish(int fd, struct device *dev) { char c; - /* If child died, presumably it printed message. */ + /* If the I/O thread died, presumably it printed the error, so we + * simply exit. */ if (read(dev->fd, &c, 1) != 1) exit(1); @@ -1397,7 +1429,7 @@ static bool handle_io_finish(int fd, struct device *dev) return true; } -/* When the Guest submits some I/O, we 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) { struct vblk_info *vblk = vq->dev->priv; @@ -1409,7 +1441,7 @@ static void handle_virtblk_output(int fd, struct virtqueue *vq) exit(1); } -/* This creates a virtual block device. */ +/*L:198 This actually sets up a virtual block device. */ static void setup_block_file(const char *filename) { int p[2]; @@ -1425,7 +1457,7 @@ static void setup_block_file(const char *filename) /* The device responds to return from I/O thread. */ dev = new_device("block", VIRTIO_ID_BLOCK, p[0], handle_io_finish); - /* The device has a virtqueue. */ + /* The device has one virtqueue, where the Guest places requests. */ add_virtqueue(dev, VIRTQUEUE_NUM, handle_virtblk_output); /* Allocate the room for our own bookkeeping */ @@ -1447,7 +1479,8 @@ static void setup_block_file(const char *filename) /* The I/O thread writes to this end of the pipe when done. */ vblk->done_fd = p[1]; - /* This is how we tell the I/O thread about more work. */ + /* This is the second pipe, which is how we tell the I/O thread about + * more work. */ pipe(vblk->workpipe); /* Create stack for thread and run it */ @@ -1486,24 +1519,25 @@ static void __attribute__((noreturn)) run_guest(int lguest_fd) char reason[1024] = { 0 }; read(lguest_fd, reason, sizeof(reason)-1); errx(1, "%s", reason); - /* EAGAIN means the waker wanted us to look at some input. + /* EAGAIN means the Waker wanted us to look at some input. * Anything else means a bug or incompatible change. */ } else if (errno != EAGAIN) err(1, "Running guest failed"); - /* Service input, then unset the BREAK which releases - * the Waker. */ + /* Service input, then unset the BREAK to release the Waker. */ handle_input(lguest_fd); if (write(lguest_fd, args, sizeof(args)) < 0) err(1, "Resetting break"); } } /* - * This is the end of the Launcher. + * This is the end of the Launcher. The good news: we are over halfway + * through! The bad news: the most fiendish part of the code still lies ahead + * of us. * - * But wait! We've seen I/O from the Launcher, and we've seen I/O from the - * Drivers. If we were to see the Host kernel I/O code, our understanding - * would be complete... :*/ + * Are you ready? Take a deep breath and join me in the core of the Host, in + * "make Host". + :*/ static struct option opts[] = { { "verbose", 0, NULL, 'v' }, @@ -1526,7 +1560,7 @@ int main(int argc, char *argv[]) /* Memory, top-level pagetable, code startpoint and size of the * (optional) initrd. */ unsigned long mem = 0, pgdir, start, initrd_size = 0; - /* A temporary and the /dev/lguest file descriptor. */ + /* Two temporaries and the /dev/lguest file descriptor. */ int i, c, lguest_fd; /* The boot information for the Guest. */ struct boot_params *boot; @@ -1621,6 +1655,7 @@ int main(int argc, char *argv[]) /* The boot header contains a command line pointer: we put the command * line after the boot header. */ boot->hdr.cmd_line_ptr = to_guest_phys(boot + 1); + /* We use a simple helper to copy the arguments separated by spaces. */ concat((char *)(boot + 1), argv+optind+2); /* Boot protocol version: 2.07 supports the fields for lguest. */ diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index a0179fc..a55b090 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -99,7 +99,7 @@ static cycle_t clock_base; * When lazy_mode is set, it means we're allowed to defer all hypercalls and do * them as a batch when lazy_mode is eventually turned off. Because hypercalls * are reasonably expensive, batching them up makes sense. For example, a - * large mmap might update dozens of page table entries: that code calls + * large munmap might update dozens of page table entries: that code calls * paravirt_enter_lazy_mmu(), does the dozen updates, then calls * lguest_leave_lazy_mode(). * @@ -164,8 +164,8 @@ void async_hcall(unsigned long call, /*:*/ /*G:033 - * Here are our first native-instruction replacements: four functions for - * interrupt control. + * After that diversion we return to our first native-instruction + * replacements: four functions for interrupt control. * * The simplest way of implementing these would be to have "turn interrupts * off" and "turn interrupts on" hypercalls. Unfortunately, this is too slow: @@ -184,7 +184,7 @@ static unsigned long save_fl(void) return lguest_data.irq_enabled; } -/* "restore_flags" just sets the flags back to the value given. */ +/* restore_flags() just sets the flags back to the value given. */ static void restore_fl(unsigned long flags) { lguest_data.irq_enabled = flags; @@ -357,7 +357,7 @@ static void lguest_cpuid(unsigned int *eax, unsigned int *ebx, * it. The Host needs to know when the Guest wants to change them, so we have * a whole series of functions like read_cr0() and write_cr0(). * - * We start with CR0. CR0 allows you to turn on and off all kinds of basic + * We start with cr0. cr0 allows you to turn on and off all kinds of basic * features, but Linux only really cares about one: the horrifically-named Task * Switched (TS) bit at bit 3 (ie. 8) * @@ -390,7 +390,7 @@ static void lguest_clts(void) current_cr0 &= ~X86_CR0_TS; } -/* CR2 is the virtual address of the last page fault, which the Guest only ever +/* cr2 is the virtual address of the last page fault, which the Guest only ever * reads. The Host kindly writes this into our "struct lguest_data", so we * just read it out of there. */ static unsigned long lguest_read_cr2(void) @@ -398,7 +398,7 @@ static unsigned long lguest_read_cr2(void) return lguest_data.cr2; } -/* CR3 is the current toplevel pagetable page: the principle is the same as +/* cr3 is the current toplevel pagetable page: the principle is the same as * cr0. Keep a local copy, and tell the Host when it changes. */ static void lguest_write_cr3(unsigned long cr3) { @@ -411,7 +411,7 @@ static unsigned long lguest_read_cr3(void) return current_cr3; } -/* CR4 is used to enable and disable PGE, but we don't care. */ +/* cr4 is used to enable and disable PGE, but we don't care. */ static unsigned long lguest_read_cr4(void) { return 0; @@ -432,7 +432,7 @@ static void lguest_write_cr4(unsigned long val) * maps virtual addresses to physical addresses using "page tables". We could * use one huge index of 1 million entries: each address is 4 bytes, so that's * 1024 pages just to hold the page tables. But since most virtual addresses - * are unused, we use a two level index which saves space. The CR3 register + * are unused, we use a two level index which saves space. The cr3 register * contains the physical address of the top level "page directory" page, which * contains physical addresses of up to 1024 second-level pages. Each of these * second level pages contains up to 1024 physical addresses of actual pages, @@ -440,7 +440,7 @@ static void lguest_write_cr4(unsigned long val) * * Here's a diagram, where arrows indicate physical addresses: * - * CR3 ---> +---------+ + * cr3 ---> +---------+ * | --------->+---------+ * | | | PADDR1 | * Top-level | | PADDR2 | @@ -498,8 +498,7 @@ static void lguest_set_pmd(pmd_t *pmdp, pmd_t pmdval) * * ... except in early boot when the kernel sets up the initial pagetables, * which makes booting astonishingly slow. So we don't even tell the Host - * anything changed until we've done the first page table switch. - */ + * anything changed until we've done the first page table switch. */ static void lguest_set_pte(pte_t *ptep, pte_t pteval) { *ptep = pteval; @@ -720,10 +719,10 @@ static void lguest_time_init(void) /* Set up the timer interrupt (0) to go to our simple timer routine */ set_irq_handler(0, lguest_time_irq); - /* Our clock structure look like arch/i386/kernel/tsc.c if we can use - * the TSC, otherwise it's a dumb nanosecond-resolution clock. Either - * way, the "rating" is initialized so high that it's always chosen - * over any other clocksource. */ + /* Our clock structure looks like arch/x86/kernel/tsc_32.c if we can + * use the TSC, otherwise it's a dumb nanosecond-resolution clock. + * Either way, the "rating" is set so high that it's always chosen over + * any other clocksource. */ if (lguest_data.tsc_khz) lguest_clock.mult = clocksource_khz2mult(lguest_data.tsc_khz, lguest_clock.shift); @@ -749,7 +748,7 @@ static void lguest_time_init(void) * to work. They're pretty simple. */ -/* The Guest needs to tell the host what stack it expects traps to use. For +/* The Guest needs to tell the Host what stack it expects traps to use. For * native hardware, this is part of the Task State Segment mentioned above in * lguest_load_tr_desc(), but to help hypervisors there's this special call. * @@ -850,13 +849,16 @@ static __init char *lguest_memory_setup(void) return "LGUEST"; } -/* Before virtqueues are set up, we use LHCALL_NOTIFY on normal memory to - * produce console output. */ +/* We will eventually use the virtio console device to produce console output, + * but before that is set up we use LHCALL_NOTIFY on normal memory to produce + * console output. */ static __init int early_put_chars(u32 vtermno, const char *buf, int count) { char scratch[17]; unsigned int len = count; + /* We use a nul-terminated string, so we have to make a copy. Icky, + * huh? */ if (len > sizeof(scratch) - 1) len = sizeof(scratch) - 1; scratch[len] = '\0'; @@ -883,7 +885,7 @@ static __init int early_put_chars(u32 vtermno, const char *buf, int count) * Our current solution is to allow the paravirt back end to optionally patch * over the indirect calls to replace them with something more efficient. We * patch the four most commonly called functions: disable interrupts, enable - * interrupts, restore interrupts and save interrupts. We usually have 10 + * interrupts, restore interrupts and save interrupts. We usually have 6 or 10 * bytes to patch into: the Guest versions of these operations are small enough * that we can fit comfortably. * @@ -1015,7 +1017,7 @@ __init void lguest_init(void) asm volatile ("mov %0, %%fs" : : "r" (__KERNEL_DS) : "memory"); /* The Host uses the top of the Guest's virtual address space for the - * Host<->Guest Switcher, and it tells us how much it needs in + * Host<->Guest Switcher, and it tells us how big that is in * lguest_data.reserve_mem, set up on the LGUEST_INIT hypercall. */ reserve_top_address(lguest_data.reserve_mem); @@ -1065,6 +1067,6 @@ __init void lguest_init(void) /* * This marks the end of stage II of our journey, The Guest. * - * It is now time for us to explore the nooks and crannies of the three Guest - * devices and complete our understanding of the Guest in "make Drivers". + * It is now time for us to explore the layer of virtual drivers and complete + * our understanding of the Guest in "make Drivers". */ diff --git a/arch/x86/lguest/i386_head.S b/arch/x86/lguest/i386_head.S index ebc6ac7..95b6fbc 100644 --- a/arch/x86/lguest/i386_head.S +++ b/arch/x86/lguest/i386_head.S @@ -6,7 +6,7 @@ #include /*G:020 This is where we begin: head.S notes that the boot header's platform - * type field is "1" (lguest), so calls us here. The boot header is in %esi. + * type field is "1" (lguest), so calls us here. * * WARNING: be very careful here! We're running at addresses equal to physical * addesses (around 0), not above PAGE_OFFSET as most code expectes @@ -17,13 +17,15 @@ * boot. */ .section .init.text, "ax", @progbits ENTRY(lguest_entry) - /* Make initial hypercall now, so we can set up the pagetables. */ + /* We make the "initialization" hypercall now to tell the Host about + * us, and also find out where it put our page tables. */ movl $LHCALL_LGUEST_INIT, %eax movl $lguest_data - __PAGE_OFFSET, %edx int $LGUEST_TRAP_ENTRY /* The Host put the toplevel pagetable in lguest_data.pgdir. The movsl - * instruction uses %esi implicitly. */ + * instruction uses %esi implicitly as the source for the copy we' + * about to do. */ movl lguest_data - __PAGE_OFFSET + LGUEST_DATA_pgdir, %esi /* Copy first 32 entries of page directory to __PAGE_OFFSET entries. diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 35d19ae..cb4c670 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -128,9 +128,12 @@ static void unmap_switcher(void) __free_pages(switcher_page[i], 0); } -/*L:305 +/*H:032 * Dealing With Guest Memory. * + * Before we go too much further into the Host, we need to grok the routines + * we use to deal with Guest memory. + * * When the Guest gives us (what it thinks is) a physical address, we can use * the normal copy_from_user() & copy_to_user() on the corresponding place in * the memory region allocated by the Launcher. diff --git a/drivers/lguest/hypercalls.c b/drivers/lguest/hypercalls.c index 9d5184c..b478aff 100644 --- a/drivers/lguest/hypercalls.c +++ b/drivers/lguest/hypercalls.c @@ -90,6 +90,7 @@ static void do_hcall(struct lguest *lg, struct hcall_args *args) lg->pending_notify = args->arg1; break; default: + /* It should be an architecture-specific hypercall. */ if (lguest_arch_do_hcall(lg, args)) kill_guest(lg, "Bad hypercall %li\n", args->arg0); } @@ -157,7 +158,6 @@ static void do_async_hcalls(struct lguest *lg) * Guest makes a hypercall, we end up here to set things up: */ static void initialize(struct lguest *lg) { - /* You can't do anything until you're initialized. The Guest knows the * rules, so we're unforgiving here. */ if (lg->hcall->arg0 != LHCALL_LGUEST_INIT) { @@ -174,7 +174,8 @@ static void initialize(struct lguest *lg) || get_user(lg->noirq_end, &lg->lguest_data->noirq_end)) kill_guest(lg, "bad guest page %p", lg->lguest_data); - /* We write the current time into the Guest's data page once now. */ + /* We write the current time into the Guest's data page once so it can + * set its clock. */ write_timestamp(lg); /* page_tables.c will also do some setup. */ @@ -182,8 +183,8 @@ static void initialize(struct lguest *lg) /* This is the one case where the above accesses might have been the * first write to a Guest page. This may have caused a copy-on-write - * fault, but the Guest might be referring to the old (read-only) - * page. */ + * fault, but the old page might be (read-only) in the Guest + * pagetable. */ guest_pagetable_clear_all(lg); } @@ -220,7 +221,7 @@ void do_hypercalls(struct lguest *lg) * Normally it doesn't matter: the Guest will run again and * update the trap number before we come back here. * - * However, if we are signalled or the Guest sends DMA to the + * However, if we are signalled or the Guest sends I/O to the * Launcher, the run_guest() loop will exit without running the * Guest. When it comes back it would try to re-run the * hypercall. */ diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 8296698..2b66f79 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -92,8 +92,8 @@ static void set_guest_interrupt(struct lguest *lg, u32 lo, u32 hi, int has_err) /* Remember that we never let the Guest actually disable interrupts, so * the "Interrupt Flag" bit is always set. We copy that bit from the - * Guest's "irq_enabled" field into the eflags word: the Guest copies - * it back in "lguest_iret". */ + * Guest's "irq_enabled" field into the eflags word: we saw the Guest + * copy it back in "lguest_iret". */ eflags = lg->regs->eflags; if (get_user(irq_enable, &lg->lguest_data->irq_enabled) == 0 && !(irq_enable & X86_EFLAGS_IF)) @@ -124,7 +124,7 @@ static void set_guest_interrupt(struct lguest *lg, u32 lo, u32 hi, int has_err) kill_guest(lg, "Disabling interrupts"); } -/*H:200 +/*H:205 * Virtual Interrupts. * * maybe_do_interrupt() gets called before every entry to the Guest, to see if @@ -256,19 +256,21 @@ int deliver_trap(struct lguest *lg, unsigned int num) * bogus one in): if we fail here, the Guest will be killed. */ if (!idt_present(lg->arch.idt[num].a, lg->arch.idt[num].b)) return 0; - set_guest_interrupt(lg, lg->arch.idt[num].a, lg->arch.idt[num].b, has_err(num)); + set_guest_interrupt(lg, lg->arch.idt[num].a, lg->arch.idt[num].b, + has_err(num)); return 1; } /*H:250 Here's the hard part: returning to the Host every time a trap happens * and then calling deliver_trap() and re-entering the Guest is slow. - * Particularly because Guest userspace system calls are traps (trap 128). + * Particularly because Guest userspace system calls are traps (usually trap + * 128). * * So we'd like to set up the IDT to tell the CPU to deliver traps directly * into the Guest. This is possible, but the complexities cause the size of * this file to double! However, 150 lines of code is worth writing for taking * system calls down from 1750ns to 270ns. Plus, if lguest didn't do it, all - * the other hypervisors would tease it. + * the other hypervisors would beat it up at lunchtime. * * This routine indicates if a particular trap number could be delivered * directly. */ @@ -331,7 +333,7 @@ void pin_stack_pages(struct lguest *lg) * change stacks on each context switch. */ void guest_set_stack(struct lguest *lg, u32 seg, u32 esp, unsigned int pages) { - /* You are not allowd have a stack segment with privilege level 0: bad + /* You are not allowed have a stack segment with privilege level 0: bad * Guest! */ if ((seg & 0x3) != GUEST_PL) kill_guest(lg, "bad stack segment %i", seg); @@ -350,7 +352,7 @@ void guest_set_stack(struct lguest *lg, u32 seg, u32 esp, unsigned int pages) * part of the Host: page table handling. */ /*H:235 This is the routine which actually checks the Guest's IDT entry and - * transfers it into our entry in "struct lguest": */ + * transfers it into the entry in "struct lguest": */ static void set_trap(struct lguest *lg, struct desc_struct *trap, unsigned int num, u32 lo, u32 hi) { @@ -456,6 +458,18 @@ void copy_traps(const struct lguest *lg, struct desc_struct *idt, } } +/*H:200 + * The Guest Clock. + * + * There are two sources of virtual interrupts. We saw one in lguest_user.c: + * the Launcher sending interrupts for virtual devices. The other is the Guest + * timer interrupt. + * + * The Guest uses the LHCALL_SET_CLOCKEVENT hypercall to tell us how long to + * the next timer interrupt (in nanoseconds). We use the high-resolution timer + * infrastructure to set a callback at that time. + * + * 0 means "turn off the clock". */ void guest_set_clockevent(struct lguest *lg, unsigned long delta) { ktime_t expires; @@ -466,20 +480,27 @@ void guest_set_clockevent(struct lguest *lg, unsigned long delta) return; } + /* We use wallclock time here, so the Guest might not be running for + * all the time between now and the timer interrupt it asked for. This + * is almost always the right thing to do. */ expires = ktime_add_ns(ktime_get_real(), delta); hrtimer_start(&lg->hrt, expires, HRTIMER_MODE_ABS); } +/* This is the function called when the Guest's timer expires. */ static enum hrtimer_restart clockdev_fn(struct hrtimer *timer) { struct lguest *lg = container_of(timer, struct lguest, hrt); + /* Remember the first interrupt is the timer interrupt. */ set_bit(0, lg->irqs_pending); + /* If the Guest is actually stopped, we need to wake it up. */ if (lg->halted) wake_up_process(lg->tsk); return HRTIMER_NORESTART; } +/* This sets up the timer for this Guest. */ void init_clockdev(struct lguest *lg) { hrtimer_init(&lg->hrt, CLOCK_REALTIME, HRTIMER_MODE_ABS); diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 0c74ac4..8692489 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -100,7 +100,7 @@ int lguest_address_ok(const struct lguest *lg, void __lgread(struct lguest *, void *, unsigned long, unsigned); void __lgwrite(struct lguest *, unsigned long, const void *, unsigned); -/*L:306 Using memory-copy operations like that is usually inconvient, so we +/*H:035 Using memory-copy operations like that is usually inconvient, so we * have the following helper macros which read and write a specific type (often * an unsigned long). * @@ -188,7 +188,7 @@ void write_timestamp(struct lguest *lg); * Let's step aside for the moment, to study one important routine that's used * widely in the Host code. * - * There are many cases where the Guest does something invalid, like pass crap + * There are many cases where the Guest can do something invalid, like pass crap * to a hypercall. Since only the Guest kernel can make hypercalls, it's quite * acceptable to simply terminate the Guest and give the Launcher a nicely * formatted reason. It's also simpler for the Guest itself, which doesn't diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 71c6483..8904f72 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -53,7 +53,8 @@ struct lguest_device { * Device configurations * * The configuration information for a device consists of a series of fields. - * The device will look for these fields during setup. + * We don't really care what they are: the Launcher set them up, and the driver + * will look at them during setup. * * For us these fields come immediately after that device's descriptor in the * lguest_devices page. @@ -122,8 +123,8 @@ static void lg_set_status(struct virtio_device *vdev, u8 status) * The other piece of infrastructure virtio needs is a "virtqueue": a way of * the Guest device registering buffers for the other side to read from or * write into (ie. send and receive buffers). Each device can have multiple - * virtqueues: for example the console has one queue for sending and one for - * receiving. + * virtqueues: for example the console driver uses one queue for sending and + * another for receiving. * * Fortunately for us, a very fast shared-memory-plus-descriptors virtqueue * already exists in virtio_ring.c. We just need to connect it up. @@ -158,7 +159,7 @@ static void lg_notify(struct virtqueue *vq) * * This is kind of an ugly duckling. It'd be nicer to have a standard * representation of a virtqueue in the configuration space, but it seems that - * everyone wants to do it differently. The KVM guys want the Guest to + * everyone wants to do it differently. The KVM coders want the Guest to * allocate its own pages and tell the Host where they are, but for lguest it's * simpler for the Host to simply tell us where the pages are. * @@ -284,6 +285,8 @@ static void add_lguest_device(struct lguest_device_desc *d) { struct lguest_device *ldev; + /* Start with zeroed memory; Linux's device layer seems to count on + * it. */ ldev = kzalloc(sizeof(*ldev), GFP_KERNEL); if (!ldev) { printk(KERN_EMERG "Cannot allocate lguest dev %u\n", diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index ee405b38..9d716fa 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -8,20 +8,22 @@ #include #include "lg.h" -/*L:315 To force the Guest to stop running and return to the Launcher, the - * Waker sets writes LHREQ_BREAK and the value "1" to /dev/lguest. The - * Launcher then writes LHREQ_BREAK and "0" to release the Waker. */ +/*L:055 When something happens, the Waker process needs a way to stop the + * kernel running the Guest and return to the Launcher. So the Waker writes + * LHREQ_BREAK and the value "1" to /dev/lguest to do this. Once the Launcher + * has done whatever needs attention, it writes LHREQ_BREAK and "0" to release + * the Waker. */ static int break_guest_out(struct lguest *lg, const unsigned long __user *input) { unsigned long on; - /* Fetch whether they're turning break on or off.. */ + /* Fetch whether they're turning break on or off. */ if (get_user(on, input) != 0) return -EFAULT; if (on) { lg->break_out = 1; - /* Pop it out (may be running on different CPU) */ + /* Pop it out of the Guest (may be running on different CPU) */ wake_up_process(lg->tsk); /* Wait for them to reset it */ return wait_event_interruptible(lg->break_wq, !lg->break_out); @@ -58,7 +60,7 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o) if (!lg) return -EINVAL; - /* If you're not the task which owns the guest, go away. */ + /* If you're not the task which owns the Guest, go away. */ if (current != lg->tsk) return -EPERM; @@ -92,8 +94,8 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o) * base: The start of the Guest-physical memory inside the Launcher memory. * * pfnlimit: The highest (Guest-physical) page number the Guest should be - * allowed to access. The Launcher has to live in Guest memory, so it sets - * this to ensure the Guest can't reach it. + * allowed to access. The Guest memory lives inside the Launcher, so it sets + * this to ensure the Guest can only reach its own memory. * * pgdir: The (Guest-physical) address of the top of the initial Guest * pagetables (which are set up by the Launcher). @@ -189,7 +191,7 @@ unlock: } /*L:010 The first operation the Launcher does must be a write. All writes - * start with a 32 bit number: for the first write this must be + * start with an unsigned long number: for the first write this must be * LHREQ_INITIALIZE to set up the Guest. After that the Launcher can use * writes of other values to send interrupts. */ static ssize_t write(struct file *file, const char __user *in, @@ -275,8 +277,7 @@ static int close(struct inode *inode, struct file *file) * The Launcher is the Host userspace program which sets up, runs and services * the Guest. In fact, many comments in the Drivers which refer to "the Host" * doing things are inaccurate: the Launcher does all the device handling for - * the Guest. The Guest can't tell what's done by the the Launcher and what by - * the Host. + * the Guest, but the Guest can't know that. * * Just to confuse you: to the Host kernel, the Launcher *is* the Guest and we * shall see more of that later. diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index 2a45f06..fffabb3 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -26,7 +26,8 @@ * * We use two-level page tables for the Guest. If you're not entirely * comfortable with virtual addresses, physical addresses and page tables then - * I recommend you review lguest.c's "Page Table Handling" (with diagrams!). + * I recommend you review arch/x86/lguest/boot.c's "Page Table Handling" (with + * diagrams!). * * The Guest keeps page tables, but we maintain the actual ones here: these are * called "shadow" page tables. Which is a very Guest-centric name: these are @@ -36,11 +37,11 @@ * * Anyway, this is the most complicated part of the Host code. There are seven * parts to this: - * (i) Setting up a page table entry for the Guest when it faults, - * (ii) Setting up the page table entry for the Guest stack, - * (iii) Setting up a page table entry when the Guest tells us it has changed, + * (i) Looking up a page table entry when the Guest faults, + * (ii) Making sure the Guest stack is mapped, + * (iii) Setting up a page table entry when the Guest tells us one has changed, * (iv) Switching page tables, - * (v) Flushing (thowing away) page tables, + * (v) Flushing (throwing away) page tables, * (vi) Mapping the Switcher when the Guest is about to run, * (vii) Setting up the page tables initially. :*/ @@ -57,16 +58,15 @@ static DEFINE_PER_CPU(pte_t *, switcher_pte_pages); #define switcher_pte_page(cpu) per_cpu(switcher_pte_pages, cpu) -/*H:320 With our shadow and Guest types established, we need to deal with - * them: the page table code is curly enough to need helper functions to keep - * it clear and clean. +/*H:320 The page table code is curly enough to need helper functions to keep it + * clear and clean. * * There are two functions which return pointers to the shadow (aka "real") * page tables. * * spgd_addr() takes the virtual address and returns a pointer to the top-level - * page directory entry for that address. Since we keep track of several page - * tables, the "i" argument tells us which one we're interested in (it's + * page directory entry (PGD) for that address. Since we keep track of several + * page tables, the "i" argument tells us which one we're interested in (it's * usually the current one). */ static pgd_t *spgd_addr(struct lguest *lg, u32 i, unsigned long vaddr) { @@ -81,9 +81,9 @@ static pgd_t *spgd_addr(struct lguest *lg, u32 i, unsigned long vaddr) return &lg->pgdirs[i].pgdir[index]; } -/* This routine then takes the PGD entry given above, which contains the - * address of the PTE page. It then returns a pointer to the PTE entry for the - * given address. */ +/* This routine then takes the page directory entry returned above, which + * contains the address of the page table entry (PTE) page. It then returns a + * pointer to the PTE entry for the given address. */ static pte_t *spte_addr(struct lguest *lg, pgd_t spgd, unsigned long vaddr) { pte_t *page = __va(pgd_pfn(spgd) << PAGE_SHIFT); @@ -191,7 +191,7 @@ static void check_gpgd(struct lguest *lg, pgd_t gpgd) } /*H:330 - * (i) Setting up a page table entry for the Guest when it faults + * (i) Looking up a page table entry when the Guest faults. * * We saw this call in run_guest(): when we see a page fault in the Guest, we * come here. That's because we only set up the shadow page tables lazily as @@ -199,7 +199,7 @@ static void check_gpgd(struct lguest *lg, pgd_t gpgd) * and return to the Guest without it knowing. * * If we fixed up the fault (ie. we mapped the address), this routine returns - * true. */ + * true. Otherwise, it was a real fault and we need to tell the Guest. */ int demand_page(struct lguest *lg, unsigned long vaddr, int errcode) { pgd_t gpgd; @@ -246,16 +246,16 @@ int demand_page(struct lguest *lg, unsigned long vaddr, int errcode) if ((errcode & 2) && !(pte_flags(gpte) & _PAGE_RW)) return 0; - /* User access to a kernel page? (bit 3 == user access) */ + /* User access to a kernel-only page? (bit 3 == user access) */ if ((errcode & 4) && !(pte_flags(gpte) & _PAGE_USER)) return 0; /* Check that the Guest PTE flags are OK, and the page number is below * the pfn_limit (ie. not mapping the Launcher binary). */ check_gpte(lg, gpte); + /* Add the _PAGE_ACCESSED and (for a write) _PAGE_DIRTY flag */ gpte = pte_mkyoung(gpte); - if (errcode & 2) gpte = pte_mkdirty(gpte); @@ -272,23 +272,28 @@ int demand_page(struct lguest *lg, unsigned long vaddr, int errcode) else /* If this is a read, don't set the "writable" bit in the page * table entry, even if the Guest says it's writable. That way - * we come back here when a write does actually ocur, so we can - * update the Guest's _PAGE_DIRTY flag. */ + * we will come back here when a write does actually occur, so + * we can update the Guest's _PAGE_DIRTY flag. */ *spte = gpte_to_spte(lg, pte_wrprotect(gpte), 0); /* Finally, we write the Guest PTE entry back: we've set the * _PAGE_ACCESSED and maybe the _PAGE_DIRTY flags. */ lgwrite(lg, gpte_ptr, pte_t, gpte); - /* We succeeded in mapping the page! */ + /* The fault is fixed, the page table is populated, the mapping + * manipulated, the result returned and the code complete. A small + * delay and a trace of alliteration are the only indications the Guest + * has that a page fault occurred at all. */ return 1; } -/*H:360 (ii) Setting up the page table entry for the Guest stack. +/*H:360 + * (ii) Making sure the Guest stack is mapped. * - * Remember pin_stack_pages() which makes sure the stack is mapped? It could - * simply call demand_page(), but as we've seen that logic is quite long, and - * usually the stack pages are already mapped anyway, so it's not required. + * Remember that direct traps into the Guest need a mapped Guest kernel stack. + * pin_stack_pages() calls us here: we could simply call demand_page(), but as + * we've seen that logic is quite long, and usually the stack pages are already + * mapped, so it's overkill. * * This is a quick version which answers the question: is this virtual address * mapped by the shadow page tables, and is it writable? */ @@ -297,7 +302,7 @@ static int page_writable(struct lguest *lg, unsigned long vaddr) pgd_t *spgd; unsigned long flags; - /* Look at the top level entry: is it present? */ + /* Look at the current top level entry: is it present? */ spgd = spgd_addr(lg, lg->pgdidx, vaddr); if (!(pgd_flags(*spgd) & _PAGE_PRESENT)) return 0; @@ -333,15 +338,14 @@ static void release_pgd(struct lguest *lg, pgd_t *spgd) release_pte(ptepage[i]); /* Now we can free the page of PTEs */ free_page((long)ptepage); - /* And zero out the PGD entry we we never release it twice. */ + /* And zero out the PGD entry so we never release it twice. */ *spgd = __pgd(0); } } -/*H:440 (v) Flushing (thowing away) page tables, - * - * We saw flush_user_mappings() called when we re-used a top-level pgdir page. - * It simply releases every PTE page from 0 up to the kernel address. */ +/*H:445 We saw flush_user_mappings() twice: once from the flush_user_mappings() + * hypercall and once in new_pgdir() when we re-used a top-level pgdir page. + * It simply releases every PTE page from 0 up to the Guest's kernel address. */ static void flush_user_mappings(struct lguest *lg, int idx) { unsigned int i; @@ -350,8 +354,10 @@ static void flush_user_mappings(struct lguest *lg, int idx) release_pgd(lg, lg->pgdirs[idx].pgdir + i); } -/* The Guest also has a hypercall to do this manually: it's used when a large - * number of mappings have been changed. */ +/*H:440 (v) Flushing (throwing away) page tables, + * + * The Guest has a hypercall to throw away the page tables: it's used when a + * large number of mappings have been changed. */ void guest_pagetable_flush_user(struct lguest *lg) { /* Drop the userspace part of the current page table. */ @@ -423,8 +429,9 @@ static unsigned int new_pgdir(struct lguest *lg, /*H:430 (iv) Switching page tables * - * This is what happens when the Guest changes page tables (ie. changes the - * top-level pgdir). This happens on almost every context switch. */ + * Now we've seen all the page table setting and manipulation, let's see what + * what happens when the Guest changes page tables (ie. changes the top-level + * pgdir). This occurs on almost every context switch. */ void guest_new_pagetable(struct lguest *lg, unsigned long pgtable) { int newpgdir, repin = 0; @@ -443,7 +450,8 @@ void guest_new_pagetable(struct lguest *lg, unsigned long pgtable) } /*H:470 Finally, a routine which throws away everything: all PGD entries in all - * the shadow page tables. This is used when we destroy the Guest. */ + * the shadow page tables, including the Guest's kernel mappings. This is used + * when we destroy the Guest. */ static void release_all_pagetables(struct lguest *lg) { unsigned int i, j; @@ -458,13 +466,22 @@ static void release_all_pagetables(struct lguest *lg) /* We also throw away everything when a Guest tells us it's changed a kernel * mapping. Since kernel mappings are in every page table, it's easiest to - * throw them all away. This is amazingly slow, but thankfully rare. */ + * throw them all away. This traps the Guest in amber for a while as + * everything faults back in, but it's rare. */ void guest_pagetable_clear_all(struct lguest *lg) { release_all_pagetables(lg); /* We need the Guest kernel stack mapped again. */ pin_stack_pages(lg); } +/*:*/ +/*M:009 Since we throw away all mappings when a kernel mapping changes, our + * performance sucks for guests using highmem. In fact, a guest with + * PAGE_OFFSET 0xc0000000 (the default) and more than about 700MB of RAM is + * usually slower than a Guest with less memory. + * + * This, of course, cannot be fixed. It would take some kind of... well, I + * don't know, but the term "puissant code-fu" comes to mind. :*/ /*H:420 This is the routine which actually sets the page table entry for then * "idx"'th shadow page table. @@ -483,7 +500,7 @@ void guest_pagetable_clear_all(struct lguest *lg) static void do_set_pte(struct lguest *lg, int idx, unsigned long vaddr, pte_t gpte) { - /* Look up the matching shadow page directot entry. */ + /* Look up the matching shadow page directory entry. */ pgd_t *spgd = spgd_addr(lg, idx, vaddr); /* If the top level isn't present, there's no entry to update. */ @@ -500,7 +517,8 @@ static void do_set_pte(struct lguest *lg, int idx, *spte = gpte_to_spte(lg, gpte, pte_flags(gpte) & _PAGE_DIRTY); } else - /* Otherwise we can demand_page() it in later. */ + /* Otherwise kill it and we can demand_page() it in + * later. */ *spte = __pte(0); } } @@ -535,7 +553,7 @@ void guest_set_pte(struct lguest *lg, } /*H:400 - * (iii) Setting up a page table entry when the Guest tells us it has changed. + * (iii) Setting up a page table entry when the Guest tells us one has changed. * * Just like we did in interrupts_and_traps.c, it makes sense for us to deal * with the other side of page tables while we're here: what happens when the @@ -612,9 +630,10 @@ void free_guest_pagetable(struct lguest *lg) /*H:480 (vi) Mapping the Switcher when the Guest is about to run. * - * The Switcher and the two pages for this CPU need to be available to the + * The Switcher and the two pages for this CPU need to be visible in the * Guest (and not the pages for other CPUs). We have the appropriate PTE pages - * for each CPU already set up, we just need to hook them in. */ + * for each CPU already set up, we just need to hook them in now we know which + * Guest is about to run on this CPU. */ void map_switcher_in_guest(struct lguest *lg, struct lguest_pages *pages) { pte_t *switcher_pte_page = __get_cpu_var(switcher_pte_pages); @@ -677,6 +696,18 @@ static __init void populate_switcher_pte_page(unsigned int cpu, __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED)); } +/* We've made it through the page table code. Perhaps our tired brains are + * still processing the details, or perhaps we're simply glad it's over. + * + * If nothing else, note that all this complexity in juggling shadow page + * tables in sync with the Guest's page tables is for one reason: for most + * Guests this page table dance determines how bad performance will be. This + * is why Xen uses exotic direct Guest pagetable manipulation, and why both + * Intel and AMD have implemented shadow page table support directly into + * hardware. + * + * There is just one file remaining in the Host. */ + /*H:510 At boot or module load time, init_pagetables() allocates and populates * the Switcher PTE page for each CPU. */ __init int init_pagetables(struct page **switcher_page, unsigned int pages) diff --git a/drivers/lguest/segments.c b/drivers/lguest/segments.c index c2434ec..9e189cb 100644 --- a/drivers/lguest/segments.c +++ b/drivers/lguest/segments.c @@ -12,8 +12,6 @@ #include "lg.h" /*H:600 - * We've almost completed the Host; there's just one file to go! - * * Segments & The Global Descriptor Table * * (That title sounds like a bad Nerdcore group. Not to suggest that there are @@ -55,7 +53,7 @@ static int ignored_gdt(unsigned int num) || num == GDT_ENTRY_DOUBLEFAULT_TSS); } -/*H:610 Once the GDT has been changed, we fix the new entries up a little. We +/*H:630 Once the Guest gave us new GDT entries, we fix them up a little. We * don't care if they're invalid: the worst that can happen is a General * Protection Fault in the Switcher when it restores a Guest segment register * which tries to use that entry. Then we kill the Guest for causing such a @@ -84,25 +82,33 @@ static void fixup_gdt_table(struct lguest *lg, unsigned start, unsigned end) } } -/* This routine is called at boot or modprobe time for each CPU to set up the - * "constant" GDT entries for Guests running on that CPU. */ +/*H:610 Like the IDT, we never simply use the GDT the Guest gives us. We keep + * a GDT for each CPU, and copy across the Guest's entries each time we want to + * run the Guest on that CPU. + * + * This routine is called at boot or modprobe time for each CPU to set up the + * constant GDT entries: the ones which are the same no matter what Guest we're + * running. */ void setup_default_gdt_entries(struct lguest_ro_state *state) { struct desc_struct *gdt = state->guest_gdt; unsigned long tss = (unsigned long)&state->guest_tss; - /* The hypervisor segments are full 0-4G segments, privilege level 0 */ + /* The Switcher segments are full 0-4G segments, privilege level 0 */ gdt[GDT_ENTRY_LGUEST_CS] = FULL_EXEC_SEGMENT; gdt[GDT_ENTRY_LGUEST_DS] = FULL_SEGMENT; - /* The TSS segment refers to the TSS entry for this CPU, so we cannot - * copy it from the Guest. Forgive the magic flags */ + /* The TSS segment refers to the TSS entry for this particular CPU. + * Forgive the magic flags: the 0x8900 means the entry is Present, it's + * privilege level 0 Available 386 TSS system segment, and the 0x67 + * means Saturn is eclipsed by Mercury in the twelfth house. */ gdt[GDT_ENTRY_TSS].a = 0x00000067 | (tss << 16); gdt[GDT_ENTRY_TSS].b = 0x00008900 | (tss & 0xFF000000) | ((tss >> 16) & 0x000000FF); } -/* This routine is called before the Guest is run for the first time. */ +/* This routine sets up the initial Guest GDT for booting. All entries start + * as 0 (unusable). */ void setup_guest_gdt(struct lguest *lg) { /* Start with full 0-4G segments... */ @@ -114,13 +120,8 @@ void setup_guest_gdt(struct lguest *lg) lg->arch.gdt[GDT_ENTRY_KERNEL_DS].b |= (GUEST_PL << 13); } -/* Like the IDT, we never simply use the GDT the Guest gives us. We set up the - * GDTs for each CPU, then we copy across the entries each time we want to run - * a different Guest on that CPU. */ - -/* A partial GDT load, for the three "thead-local storage" entries. Otherwise - * it's just like load_guest_gdt(). So much, in fact, it would probably be - * neater to have a single hypercall to cover both. */ +/*H:650 An optimization of copy_gdt(), for just the three "thead-local storage" + * entries. */ void copy_gdt_tls(const struct lguest *lg, struct desc_struct *gdt) { unsigned int i; @@ -129,7 +130,9 @@ void copy_gdt_tls(const struct lguest *lg, struct desc_struct *gdt) gdt[i] = lg->arch.gdt[i]; } -/* This is the full version */ +/*H:640 When the Guest is run on a different CPU, or the GDT entries have + * changed, copy_gdt() is called to copy the Guest's GDT entries across to this + * CPU's GDT. */ void copy_gdt(const struct lguest *lg, struct desc_struct *gdt) { unsigned int i; @@ -141,7 +144,8 @@ void copy_gdt(const struct lguest *lg, struct desc_struct *gdt) gdt[i] = lg->arch.gdt[i]; } -/* This is where the Guest asks us to load a new GDT (LHCALL_LOAD_GDT). */ +/*H:620 This is where the Guest asks us to load a new GDT (LHCALL_LOAD_GDT). + * We copy it from the Guest and tweak the entries. */ void load_guest_gdt(struct lguest *lg, unsigned long table, u32 num) { /* We assume the Guest has the same number of GDT entries as the @@ -157,16 +161,22 @@ void load_guest_gdt(struct lguest *lg, unsigned long table, u32 num) lg->changed |= CHANGED_GDT; } +/* This is the fast-track version for just changing the three TLS entries. + * Remember that this happens on every context switch, so it's worth + * optimizing. But wouldn't it be neater to have a single hypercall to cover + * both cases? */ void guest_load_tls(struct lguest *lg, unsigned long gtls) { struct desc_struct *tls = &lg->arch.gdt[GDT_ENTRY_TLS_MIN]; __lgread(lg, tls, gtls, sizeof(*tls)*GDT_ENTRY_TLS_ENTRIES); fixup_gdt_table(lg, GDT_ENTRY_TLS_MIN, GDT_ENTRY_TLS_MAX+1); + /* Note that just the TLS entries have changed. */ lg->changed |= CHANGED_GDT_TLS; } +/*:*/ -/* +/*H:660 * With this, we have finished the Host. * * Five of the seven parts of our task are complete. You have made it through diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 09d9207..482aec2 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -63,7 +63,7 @@ static struct lguest_pages *lguest_pages(unsigned int cpu) static DEFINE_PER_CPU(struct lguest *, last_guest); /*S:010 - * We are getting close to the Switcher. + * We approach the Switcher. * * Remember that each CPU has two pages which are visible to the Guest when it * runs on that CPU. This has to contain the state for that Guest: we copy the @@ -134,7 +134,7 @@ static void run_guest_once(struct lguest *lg, struct lguest_pages *pages) * * The lcall also pushes the old code segment (KERNEL_CS) onto the * stack, then the address of this call. This stack layout happens to - * exactly match the stack of an interrupt... */ + * exactly match the stack layout created by an interrupt... */ asm volatile("pushf; lcall *lguest_entry" /* This is how we tell GCC that %eax ("a") and %ebx ("b") * are changed by this routine. The "=" means output. */ @@ -151,40 +151,46 @@ static void run_guest_once(struct lguest *lg, struct lguest_pages *pages) } /*:*/ +/*M:002 There are hooks in the scheduler which we can register to tell when we + * get kicked off the CPU (preempt_notifier_register()). This would allow us + * to lazily disable SYSENTER which would regain some performance, and should + * also simplify copy_in_guest_info(). Note that we'd still need to restore + * things when we exit to Launcher userspace, but that's fairly easy. + * + * The hooks were designed for KVM, but we can also put them to good use. :*/ + /*H:040 This is the i386-specific code to setup and run the Guest. Interrupts * are disabled: we own the CPU. */ void lguest_arch_run_guest(struct lguest *lg) { - /* Remember the awfully-named TS bit? If the Guest has asked - * to set it we set it now, so we can trap and pass that trap - * to the Guest if it uses the FPU. */ + /* Remember the awfully-named TS bit? If the Guest has asked to set it + * we set it now, so we can trap and pass that trap to the Guest if it + * uses the FPU. */ if (lg->ts) lguest_set_ts(); - /* SYSENTER is an optimized way of doing system calls. We - * can't allow it because it always jumps to privilege level 0. - * A normal Guest won't try it because we don't advertise it in - * CPUID, but a malicious Guest (or malicious Guest userspace - * program) could, so we tell the CPU to disable it before - * running the Guest. */ + /* SYSENTER is an optimized way of doing system calls. We can't allow + * it because it always jumps to privilege level 0. A normal Guest + * won't try it because we don't advertise it in CPUID, but a malicious + * Guest (or malicious Guest userspace program) could, so we tell the + * CPU to disable it before running the Guest. */ if (boot_cpu_has(X86_FEATURE_SEP)) wrmsr(MSR_IA32_SYSENTER_CS, 0, 0); - /* Now we actually run the Guest. It will pop back out when - * something interesting happens, and we can examine its - * registers to see what it was doing. */ + /* Now we actually run the Guest. It will return when something + * interesting happens, and we can examine its registers to see what it + * was doing. */ run_guest_once(lg, lguest_pages(raw_smp_processor_id())); - /* The "regs" pointer contains two extra entries which are not - * really registers: a trap number which says what interrupt or - * trap made the switcher code come back, and an error code - * which some traps set. */ + /* Note that the "regs" pointer contains two extra entries which are + * not really registers: a trap number which says what interrupt or + * trap made the switcher code come back, and an error code which some + * traps set. */ - /* If the Guest page faulted, then the cr2 register will tell - * us the bad virtual address. We have to grab this now, - * because once we re-enable interrupts an interrupt could - * fault and thus overwrite cr2, or we could even move off to a - * different CPU. */ + /* If the Guest page faulted, then the cr2 register will tell us the + * bad virtual address. We have to grab this now, because once we + * re-enable interrupts an interrupt could fault and thus overwrite + * cr2, or we could even move off to a different CPU. */ if (lg->regs->trapnum == 14) lg->arch.last_pagefault = read_cr2(); /* Similarly, if we took a trap because the Guest used the FPU, @@ -197,14 +203,15 @@ void lguest_arch_run_guest(struct lguest *lg) wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0); } -/*H:130 Our Guest is usually so well behaved; it never tries to do things it - * isn't allowed to. Unfortunately, Linux's paravirtual infrastructure isn't - * quite complete, because it doesn't contain replacements for the Intel I/O - * instructions. As a result, the Guest sometimes fumbles across one during - * the boot process as it probes for various things which are usually attached - * to a PC. +/*H:130 Now we've examined the hypercall code; our Guest can make requests. + * Our Guest is usually so well behaved; it never tries to do things it isn't + * allowed to, and uses hypercalls instead. Unfortunately, Linux's paravirtual + * infrastructure isn't quite complete, because it doesn't contain replacements + * for the Intel I/O instructions. As a result, the Guest sometimes fumbles + * across one during the boot process as it probes for various things which are + * usually attached to a PC. * - * When the Guest uses one of these instructions, we get trap #13 (General + * When the Guest uses one of these instructions, we get a trap (General * Protection Fault) and come here. We see if it's one of those troublesome * instructions and skip over it. We return true if we did. */ static int emulate_insn(struct lguest *lg) @@ -275,43 +282,43 @@ static int emulate_insn(struct lguest *lg) void lguest_arch_handle_trap(struct lguest *lg) { switch (lg->regs->trapnum) { - case 13: /* We've intercepted a GPF. */ - /* Check if this was one of those annoying IN or OUT - * instructions which we need to emulate. If so, we - * just go back into the Guest after we've done it. */ + case 13: /* We've intercepted a General Protection Fault. */ + /* Check if this was one of those annoying IN or OUT + * instructions which we need to emulate. If so, we just go + * back into the Guest after we've done it. */ if (lg->regs->errcode == 0) { if (emulate_insn(lg)) return; } break; - case 14: /* We've intercepted a page fault. */ - /* The Guest accessed a virtual address that wasn't - * mapped. This happens a lot: we don't actually set - * up most of the page tables for the Guest at all when - * we start: as it runs it asks for more and more, and - * we set them up as required. In this case, we don't - * even tell the Guest that the fault happened. - * - * The errcode tells whether this was a read or a - * write, and whether kernel or userspace code. */ + case 14: /* We've intercepted a Page Fault. */ + /* The Guest accessed a virtual address that wasn't mapped. + * This happens a lot: we don't actually set up most of the + * page tables for the Guest at all when we start: as it runs + * it asks for more and more, and we set them up as + * required. In this case, we don't even tell the Guest that + * the fault happened. + * + * The errcode tells whether this was a read or a write, and + * whether kernel or userspace code. */ if (demand_page(lg, lg->arch.last_pagefault, lg->regs->errcode)) return; - /* OK, it's really not there (or not OK): the Guest - * needs to know. We write out the cr2 value so it - * knows where the fault occurred. - * - * Note that if the Guest were really messed up, this - * could happen before it's done the INITIALIZE - * hypercall, so lg->lguest_data will be NULL */ + /* OK, it's really not there (or not OK): the Guest needs to + * know. We write out the cr2 value so it knows where the + * fault occurred. + * + * Note that if the Guest were really messed up, this could + * happen before it's done the LHCALL_LGUEST_INIT hypercall, so + * lg->lguest_data could be NULL */ if (lg->lguest_data && put_user(lg->arch.last_pagefault, &lg->lguest_data->cr2)) kill_guest(lg, "Writing cr2"); break; case 7: /* We've intercepted a Device Not Available fault. */ - /* If the Guest doesn't want to know, we already - * restored the Floating Point Unit, so we just - * continue without telling it. */ + /* If the Guest doesn't want to know, we already restored the + * Floating Point Unit, so we just continue without telling + * it. */ if (!lg->ts) return; break; @@ -536,9 +543,6 @@ int lguest_arch_init_hypercalls(struct lguest *lg) return 0; } -/* Now we've examined the hypercall code; our Guest can make requests. There - * is one other way we can do things for the Guest, as we see in - * emulate_insn(). :*/ /*L:030 lguest_arch_setup_regs() * @@ -570,8 +574,8 @@ void lguest_arch_setup_regs(struct lguest *lg, unsigned long start) /* %esi points to our boot information, at physical address 0, so don't * touch it. */ + /* There are a couple of GDT entries the Guest expects when first * booting. */ - setup_guest_gdt(lg); } diff --git a/drivers/lguest/x86/switcher_32.S b/drivers/lguest/x86/switcher_32.S index 1010b90..0af8baa 100644 --- a/drivers/lguest/x86/switcher_32.S +++ b/drivers/lguest/x86/switcher_32.S @@ -6,6 +6,37 @@ * are feeling invigorated and refreshed then the next, more challenging stage * can be found in "make Guest". :*/ +/*M:012 Lguest is meant to be simple: my rule of thumb is that 1% more LOC must + * gain at least 1% more performance. Since neither LOC nor performance can be + * measured beforehand, it generally means implementing a feature then deciding + * if it's worth it. And once it's implemented, who can say no? + * + * This is why I haven't implemented this idea myself. I want to, but I + * haven't. You could, though. + * + * The main place where lguest performance sucks is Guest page faulting. When + * a Guest userspace process hits an unmapped page we switch back to the Host, + * walk the page tables, find it's not mapped, switch back to the Guest page + * fault handler, which calls a hypercall to set the page table entry, then + * finally returns to userspace. That's two round-trips. + * + * If we had a small walker in the Switcher, we could quickly check the Guest + * page table and if the page isn't mapped, immediately reflect the fault back + * into the Guest. This means the Switcher would have to know the top of the + * Guest page table and the page fault handler address. + * + * For simplicity, the Guest should only handle the case where the privilege + * level of the fault is 3 and probably only not present or write faults. It + * should also detect recursive faults, and hand the original fault to the + * Host (which is actually really easy). + * + * Two questions remain. Would the performance gain outweigh the complexity? + * And who would write the verse documenting it? :*/ + +/*M:011 Lguest64 handles NMI. This gave me NMI envy (until I looked at their + * code). It's worth doing though, since it would let us use oprofile in the + * Host when a Guest is running. :*/ + /*S:100 * Welcome to the Switcher itself! * @@ -88,7 +119,7 @@ ENTRY(switch_to_guest) // All saved and there's now five steps before us: // Stack, GDT, IDT, TSS - // And last of all the page tables are flipped. + // Then last of all the page tables are flipped. // Yet beware that our stack pointer must be // Always valid lest an NMI hits @@ -103,25 +134,25 @@ ENTRY(switch_to_guest) lgdt LGUEST_PAGES_guest_gdt_desc(%eax) // The Guest's IDT we did partially - // Move to the "struct lguest_pages" as well. + // Copy to "struct lguest_pages" as well. lidt LGUEST_PAGES_guest_idt_desc(%eax) // The TSS entry which controls traps // Must be loaded up with "ltr" now: + // The GDT entry that TSS uses + // Changes type when we load it: damn Intel! // For after we switch over our page tables - // It (as the rest) will be writable no more. - // (The GDT entry TSS needs - // Changes type when we load it: damn Intel!) + // That entry will be read-only: we'd crash. movl $(GDT_ENTRY_TSS*8), %edx ltr %dx // Look back now, before we take this last step! // The Host's TSS entry was also marked used; - // Let's clear it again, ere we return. + // Let's clear it again for our return. // The GDT descriptor of the Host // Points to the table after two "size" bytes movl (LGUEST_PAGES_host_gdt_desc+2)(%eax), %edx - // Clear the type field of "used" (byte 5, bit 2) + // Clear "used" from type field (byte 5, bit 2) andb $0xFD, (GDT_ENTRY_TSS*8 + 5)(%edx) // Once our page table's switched, the Guest is live! @@ -131,7 +162,7 @@ ENTRY(switch_to_guest) // The page table change did one tricky thing: // The Guest's register page has been mapped - // Writable onto our %esp (stack) -- + // Writable under our %esp (stack) -- // We can simply pop off all Guest regs. popl %eax popl %ebx @@ -152,16 +183,15 @@ ENTRY(switch_to_guest) addl $8, %esp // The last five stack slots hold return address - // And everything needed to change privilege - // Into the Guest privilege level of 1, + // And everything needed to switch privilege + // From Switcher's level 0 to Guest's 1, // And the stack where the Guest had last left it. // Interrupts are turned back on: we are Guest. iret -// There are two paths where we switch to the Host +// We treat two paths to switch back to the Host +// Yet both must save Guest state and restore Host // So we put the routine in a macro. -// We are on our way home, back to the Host -// Interrupted out of the Guest, we come here. #define SWITCH_TO_HOST \ /* We save the Guest state: all registers first \ * Laid out just as "struct lguest_regs" defines */ \ @@ -194,7 +224,7 @@ ENTRY(switch_to_guest) movl %esp, %eax; \ andl $(~(1 << PAGE_SHIFT - 1)), %eax; \ /* Save our trap number: the switch will obscure it \ - * (The Guest regs are not mapped here in the Host) \ + * (In the Host the Guest regs are not mapped here) \ * %ebx holds it safe for deliver_to_host */ \ movl LGUEST_PAGES_regs_trapnum(%eax), %ebx; \ /* The Host GDT, IDT and stack! \ @@ -210,9 +240,9 @@ ENTRY(switch_to_guest) /* Switch to Host's GDT, IDT. */ \ lgdt LGUEST_PAGES_host_gdt_desc(%eax); \ lidt LGUEST_PAGES_host_idt_desc(%eax); \ - /* Restore the Host's stack where it's saved regs lie */ \ + /* Restore the Host's stack where its saved regs lie */ \ movl LGUEST_PAGES_host_sp(%eax), %esp; \ - /* Last the TSS: our Host is complete */ \ + /* Last the TSS: our Host is returned */ \ movl $(GDT_ENTRY_TSS*8), %edx; \ ltr %dx; \ /* Restore now the regs saved right at the first. */ \ @@ -222,14 +252,15 @@ ENTRY(switch_to_guest) popl %ds; \ popl %es -// Here's where we come when the Guest has just trapped: -// (Which trap we'll see has been pushed on the stack). +// The first path is trod when the Guest has trapped: +// (Which trap it was has been pushed on the stack). // We need only switch back, and the Host will decode // Why we came home, and what needs to be done. return_to_host: SWITCH_TO_HOST iret +// We are lead to the second path like so: // An interrupt, with some cause external // Has ajerked us rudely from the Guest's code // Again we must return home to the Host @@ -238,7 +269,7 @@ deliver_to_host: // But now we must go home via that place // Where that interrupt was supposed to go // Had we not been ensconced, running the Guest. - // Here we see the cleverness of our stack: + // Here we see the trickness of run_guest_once(): // The Host stack is formed like an interrupt // With EIP, CS and EFLAGS layered. // Interrupt handlers end with "iret" @@ -263,7 +294,7 @@ deliver_to_host: xorw %ax, %ax orl %eax, %edx // Now the address of the handler's in %edx - // We call it now: its "iret" takes us home. + // We call it now: its "iret" drops us home. jmp *%edx // Every interrupt can come to us here diff --git a/include/asm-x86/lguest_hcall.h b/include/asm-x86/lguest_hcall.h index f948491..9c5092b 100644 --- a/include/asm-x86/lguest_hcall.h +++ b/include/asm-x86/lguest_hcall.h @@ -18,12 +18,17 @@ #define LHCALL_LOAD_TLS 16 #define LHCALL_NOTIFY 17 +#define LGUEST_TRAP_ENTRY 0x1F + +#ifndef __ASSEMBLY__ +#include + /*G:031 First, how does our Guest contact the Host to ask for privileged * operations? There are two ways: the direct way is to make a "hypercall", * to make requests of the Host Itself. * * Our hypercall mechanism uses the highest unused trap code (traps 32 and - * above are used by real hardware interrupts). Seventeen hypercalls are + * above are used by real hardware interrupts). Fifteen hypercalls are * available: the hypercall number is put in the %eax register, and the * arguments (when required) are placed in %edx, %ebx and %ecx. If a return * value makes sense, it's returned in %eax. @@ -31,20 +36,15 @@ * Grossly invalid calls result in Sudden Death at the hands of the vengeful * Host, rather than returning failure. This reflects Winston Churchill's * definition of a gentleman: "someone who is only rude intentionally". */ -#define LGUEST_TRAP_ENTRY 0x1F - -#ifndef __ASSEMBLY__ -#include - static inline unsigned long hcall(unsigned long call, unsigned long arg1, unsigned long arg2, unsigned long arg3) { /* "int" is the Intel instruction to trigger a trap. */ asm volatile("int $" __stringify(LGUEST_TRAP_ENTRY) - /* The call is in %eax (aka "a"), and can be replaced */ + /* The call in %eax (aka "a") might be overwritten */ : "=a"(call) - /* The other arguments are in %eax, %edx, %ebx & %ecx */ + /* The arguments are in %eax, %edx, %ebx & %ecx */ : "a"(call), "d"(arg1), "b"(arg2), "c"(arg3) /* "memory" means this might write somewhere in memory. * This isn't true for all calls, but it's safe to tell diff --git a/include/linux/lguest.h b/include/linux/lguest.h index 8beb291..175e63f 100644 --- a/include/linux/lguest.h +++ b/include/linux/lguest.h @@ -12,8 +12,8 @@ #define LG_CLOCK_MAX_DELTA ULONG_MAX /*G:032 The second method of communicating with the Host is to via "struct - * lguest_data". The Guest's very first hypercall is to tell the Host where - * this is, and then the Guest and Host both publish information in it. :*/ + * lguest_data". Once the Guest's initialization hypercall tells the Host where + * this is, the Guest and Host both publish information in it. :*/ struct lguest_data { /* 512 == enabled (same as eflags in normal hardware). The Guest diff --git a/include/linux/lguest_launcher.h b/include/linux/lguest_launcher.h index c41fd48..697104d 100644 --- a/include/linux/lguest_launcher.h +++ b/include/linux/lguest_launcher.h @@ -10,7 +10,11 @@ * real devices (think of the damage it could do!) we provide virtual devices. * We could emulate a PCI bus with various devices on it, but that is a fairly * complex burden for the Host and suboptimal for the Guest, so we have our own - * "lguest" bus and simple drivers. + * simple lguest bus and we use "virtio" drivers. These drivers need a set of + * routines from us which will actually do the virtual I/O, but they handle all + * the net/block/console stuff themselves. This means that if we want to add + * a new device, we simply need to write a new virtio driver and create support + * for it in the Launcher: this code won't need to change. * * Devices are described by a simplified ID, a status byte, and some "config" * bytes which describe this device's configuration. This is placed by the -- cgit v0.10.2