diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-23 09:03:07 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-23 09:03:07 -0700 |
commit | 0d6810091cdbd05efeb31654c6a41a6cbdfdd2c8 (patch) | |
tree | 44d79f8133ea6acd791fe4f32188789c2c65da93 /Documentation/lguest/lguest.c | |
parent | a98ce5c6feead6bfedefabd46cb3d7f5be148d9a (diff) | |
parent | 43d33b21a03d3abcc8cbdeb4d52bc4568f822c5e (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-lguest
* git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-lguest: (45 commits)
Use "struct boot_params" in example launcher
Loading bzImage directly.
Revert lguest magic and use hook in head.S
Update lguest documentation to reflect the new virtual block device name.
generalize lgread_u32/lgwrite_u32.
Example launcher handle guests not being ready for input
Update example launcher for virtio
Lguest support for Virtio
Remove old lguest I/O infrrasructure.
Remove old lguest bus and drivers.
Virtio helper routines for a descriptor ringbuffer implementation
Module autoprobing support for virtio drivers.
Virtio console driver
Block driver using virtio.
Net driver using virtio
Virtio interface
Boot with virtual == physical to get closer to native Linux.
Allow guest to specify syscall vector to use.
Rename "cr3" to "gpgdir" to avoid x86-specific naming.
Pagetables to use normal kernel types
...
Diffstat (limited to 'Documentation/lguest/lguest.c')
-rw-r--r-- | Documentation/lguest/lguest.c | 1629 |
1 files changed, 882 insertions, 747 deletions
diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index 103e346c8b6..5bdc37f8184 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c @@ -1,10 +1,7 @@ /*P:100 This is the Launcher code, a simple program which lays out the * "physical" memory for the new Guest by mapping the kernel image and the * virtual devices, then reads repeatedly from /dev/lguest to run the Guest. - * - * The only trick: the Makefile links it at a high address so it will be clear - * of the guest memory region. It means that each Guest cannot have more than - * about 2.5G of memory on a normally configured Host. :*/ +:*/ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE #include <stdio.h> @@ -15,6 +12,7 @@ #include <stdlib.h> #include <elf.h> #include <sys/mman.h> +#include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> @@ -34,7 +32,9 @@ #include <termios.h> #include <getopt.h> #include <zlib.h> -/*L:110 We can ignore the 28 include files we need for this program, but I do +#include <assert.h> +#include <sched.h> +/*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 @@ -45,8 +45,14 @@ typedef unsigned long long u64; typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; -#include "../../include/linux/lguest_launcher.h" -#include "../../include/asm-x86/e820_32.h" +#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" /*:*/ #define PAGE_PRESENT 0x7 /* Present, RW, Execute */ @@ -55,6 +61,10 @@ typedef uint8_t u8; #ifndef SIOCBRADDIF #define SIOCBRADDIF 0x89a2 /* add interface to bridge */ #endif +/* We can have up to 256 pages for devices. */ +#define DEVICE_PAGES 256 +/* This fits nicely in a single 4096-byte page. */ +#define VIRTQUEUE_NUM 127 /*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. */ @@ -65,8 +75,10 @@ static bool verbose; /* The pipe to send commands to the waker process */ static int waker_fd; -/* The top of guest physical memory. */ -static u32 top; +/* The pointer to the start of guest memory. */ +static void *guest_base; +/* The maximum guest physical address allowed, and maximum possible. */ +static unsigned long guest_limit, guest_max; /* This is our list of devices. */ struct device_list @@ -76,8 +88,17 @@ struct device_list fd_set infds; int max_infd; + /* Counter to assign interrupt numbers. */ + unsigned int next_irq; + + /* Counter to print out convenient device numbers. */ + unsigned int device_num; + /* The descriptor page for the devices. */ - struct lguest_device_desc *descs; + u8 *descpage; + + /* The tail of the last descriptor. */ + unsigned int desc_used; /* A single linked list of devices. */ struct device *dev; @@ -85,31 +106,111 @@ struct device_list struct device **lastdev; }; +/* The list of Guest devices, based on command line arguments. */ +static struct device_list devices; + /* The device structure describes a single device. */ struct device { /* The linked-list pointer. */ struct device *next; - /* The descriptor for this device, as mapped into the Guest. */ + + /* The this device's descriptor, as mapped into the Guest. */ struct lguest_device_desc *desc; - /* The memory page(s) of this device, if any. Also mapped in Guest. */ - void *mem; + + /* The name of this device, for --verbose. */ + const char *name; /* If handle_input is set, it wants to be called when this file * descriptor is ready. */ int fd; bool (*handle_input)(int fd, struct device *me); - /* If handle_output is set, it wants to be called when the Guest sends - * DMA to this key. */ - unsigned long watch_key; - u32 (*handle_output)(int fd, const struct iovec *iov, - unsigned int num, struct device *me); + /* Any queues attached to this device */ + struct virtqueue *vq; /* Device-specific data. */ void *priv; }; +/* The virtqueue structure describes a queue attached to a device. */ +struct virtqueue +{ + struct virtqueue *next; + + /* Which device owns me. */ + struct device *dev; + + /* The configuration for this queue. */ + struct lguest_vqconfig config; + + /* The actual ring of buffers. */ + struct vring vring; + + /* Last available index we saw. */ + u16 last_avail_idx; + + /* The routine to call when the Guest pings us. */ + void (*handle_output)(int fd, struct virtqueue *me); +}; + +/* Since guest is UP and we don't run at the same time, we don't need barriers. + * But I include them in the code in case others copy it. */ +#define wmb() + +/* Convert an iovec element to the given type. + * + * This is a fairly ugly trick: we need to know the size of the type and + * alignment requirement to check the pointer is kosher. It's also nice to + * have the name of the type in case we report failure. + * + * Typing those three things all the time is cumbersome and error prone, so we + * have a macro which sets them all up and passes to the real function. */ +#define convert(iov, type) \ + ((type *)_convert((iov), sizeof(type), __alignof__(type), #type)) + +static void *_convert(struct iovec *iov, size_t size, size_t align, + const char *name) +{ + if (iov->iov_len != size) + errx(1, "Bad iovec size %zu for %s", iov->iov_len, name); + if ((unsigned long)iov->iov_base % align != 0) + errx(1, "Bad alignment %p for %s", iov->iov_base, name); + return iov->iov_base; +} + +/* 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. */ +#define cpu_to_le16(v16) (v16) +#define cpu_to_le32(v32) (v32) +#define cpu_to_le64(v64) (v64) +#define le16_to_cpu(v16) (v16) +#define le32_to_cpu(v32) (v32) +#define le64_to_cpu(v32) (v64) + +/*L:100 The Launcher code itself takes us out into userspace, that scary place + * where pointers run wild and free! Unfortunately, like most userspace + * programs, it's quite boring (which is why everyone likes to hack on the + * kernel!). Perhaps if you make up an Lguest Drinking Game at this point, it + * will get you through this section. Or, maybe not. + * + * The Launcher sets up a big chunk of memory to be the Guest's "physical" + * memory and stores it in "guest_base". In other words, Guest physical == + * Launcher virtual with an offset. + * + * This can be tough to get your head around, but usually it just means that we + * use these trivial conversion functions when the Guest gives us it's + * "physical" addresses: */ +static void *from_guest_phys(unsigned long addr) +{ + return guest_base + addr; +} + +static unsigned long to_guest_phys(const void *addr) +{ + return (addr - guest_base); +} + /*L:130 * Loading the Kernel. * @@ -123,43 +224,55 @@ static int open_or_die(const char *name, int flags) return fd; } -/* map_zeroed_pages() takes a (page-aligned) address and a number of pages. */ -static void *map_zeroed_pages(unsigned long addr, unsigned int num) +/* map_zeroed_pages() takes a number of pages. */ +static void *map_zeroed_pages(unsigned int num) { - /* We cache the /dev/zero file-descriptor so we only open it once. */ - static int fd = -1; - - if (fd == -1) - fd = open_or_die("/dev/zero", O_RDONLY); + int fd = open_or_die("/dev/zero", O_RDONLY); + void *addr; /* We use a private mapping (ie. if we write to the page, it will be - * copied), and obviously we insist that it be mapped where we ask. */ - if (mmap((void *)addr, getpagesize() * num, - PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, fd, 0) - != (void *)addr) - err(1, "Mmaping %u pages of /dev/zero @%p", num, (void *)addr); - - /* Returning the address is just a courtesy: can simplify callers. */ - return (void *)addr; + * copied). */ + addr = mmap(NULL, getpagesize() * num, + PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0); + if (addr == MAP_FAILED) + err(1, "Mmaping %u pages of /dev/zero", num); + + return addr; } -/* To find out where to start we look for the magic Guest string, which marks - * the code we see in lguest_asm.S. This is a hack which we are currently - * plotting to replace with the normal Linux entry point. */ -static unsigned long entry_point(void *start, void *end, - unsigned long page_offset) +/* Get some more pages for a device. */ +static void *get_pages(unsigned int num) { - void *p; + void *addr = from_guest_phys(guest_limit); - /* The scan gives us the physical starting address. We want the - * virtual address in this case, and fortunately, we already figured - * out the physical-virtual difference and passed it here in - * "page_offset". */ - for (p = start; p < end; p++) - if (memcmp(p, "GenuineLguest", strlen("GenuineLguest")) == 0) - return (long)p + strlen("GenuineLguest") + page_offset; + guest_limit += num * getpagesize(); + if (guest_limit > guest_max) + errx(1, "Not enough memory for devices"); + return addr; +} - err(1, "Is this image a genuine lguest?"); +/* This routine is used to load the kernel or initrd. It tries mmap, but if + * that fails (Plan 9's kernel file isn't nicely aligned on page boundaries), + * it falls back to reading the memory in. */ +static void map_at(int fd, void *addr, unsigned long offset, unsigned long len) +{ + ssize_t r; + + /* We map writable even though for some segments are marked read-only. + * The kernel really wants to be writable: it patches its own + * instructions. + * + * MAP_PRIVATE means that the page won't be copied until a write is + * done to it. This allows us to share untouched memory between + * Guests. */ + if (mmap(addr, len, PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, fd, offset) != MAP_FAILED) + return; + + /* pread does a seek and a read in one shot: saves a few lines. */ + r = pread(fd, addr, len, offset); + if (r != len) + err(1, "Reading offset %lu len %lu gave %zi", offset, len, r); } /* This routine takes an open vmlinux image, which is in ELF, and maps it into @@ -167,19 +280,14 @@ static unsigned long entry_point(void *start, void *end, * by all modern binaries on Linux including the kernel. * * The ELF headers give *two* addresses: a physical address, and a virtual - * address. The Guest kernel expects to be placed in memory at the physical - * address, and the page tables set up so it will correspond to that virtual - * address. We return the difference between the virtual and physical - * addresses in the "page_offset" pointer. + * address. We use the physical address; the Guest will map itself to the + * virtual address. * * We return the starting address. */ -static unsigned long map_elf(int elf_fd, const Elf32_Ehdr *ehdr, - unsigned long *page_offset) +static unsigned long map_elf(int elf_fd, const Elf32_Ehdr *ehdr) { - void *addr; Elf32_Phdr phdr[ehdr->e_phnum]; unsigned int i; - unsigned long start = -1UL, end = 0; /* Sanity checks on the main ELF header: an x86 executable with a * reasonable number of correctly-sized program headers. */ @@ -199,9 +307,6 @@ static unsigned long map_elf(int elf_fd, const Elf32_Ehdr *ehdr, if (read(elf_fd, phdr, sizeof(phdr)) != sizeof(phdr)) err(1, "Reading program headers"); - /* We don't know page_offset yet. */ - *page_offset = 0; - /* Try all the headers: there are usually only three. A read-only one, * a read-write one, and a "note" section which isn't loadable. */ for (i = 0; i < ehdr->e_phnum; i++) { @@ -212,158 +317,53 @@ static unsigned long map_elf(int elf_fd, const Elf32_Ehdr *ehdr, verbose("Section %i: size %i addr %p\n", i, phdr[i].p_memsz, (void *)phdr[i].p_paddr); - /* We expect a simple linear address space: every segment must - * have the same difference between virtual (p_vaddr) and - * physical (p_paddr) address. */ - if (!*page_offset) - *page_offset = phdr[i].p_vaddr - phdr[i].p_paddr; - else if (*page_offset != phdr[i].p_vaddr - phdr[i].p_paddr) - errx(1, "Page offset of section %i different", i); - - /* We track the first and last address we mapped, so we can - * tell entry_point() where to scan. */ - if (phdr[i].p_paddr < start) - start = phdr[i].p_paddr; - if (phdr[i].p_paddr + phdr[i].p_filesz > end) - end = phdr[i].p_paddr + phdr[i].p_filesz; - - /* We map this section of the file at its physical address. We - * map it read & write even if the header says this segment is - * read-only. The kernel really wants to be writable: it - * patches its own instructions which would normally be - * read-only. - * - * MAP_PRIVATE means that the page won't be copied until a - * write is done to it. This allows us to share much of the - * kernel memory between Guests. */ - addr = mmap((void *)phdr[i].p_paddr, - phdr[i].p_filesz, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, - elf_fd, phdr[i].p_offset); - if (addr != (void *)phdr[i].p_paddr) - err(1, "Mmaping vmlinux seg %i gave %p not %p", - i, addr, (void *)phdr[i].p_paddr); + /* We map this section of the file at its physical address. */ + map_at(elf_fd, from_guest_phys(phdr[i].p_paddr), + phdr[i].p_offset, phdr[i].p_filesz); } - return entry_point((void *)start, (void *)end, *page_offset); + /* The entry point is given in the ELF header. */ + return ehdr->e_entry; } -/*L:170 Prepare to be SHOCKED and AMAZED. And possibly a trifle nauseated. - * - * We know that CONFIG_PAGE_OFFSET sets what virtual address the kernel expects - * to be. We don't know what that option was, but we can figure it out - * approximately by looking at the addresses in the code. I chose the common - * case of reading a memory location into the %eax register: - * - * movl <some-address>, %eax - * - * This gets encoded as five bytes: "0xA1 <4-byte-address>". For example, - * "0xA1 0x18 0x60 0x47 0xC0" reads the address 0xC0476018 into %eax. - * - * In this example can guess that the kernel was compiled with - * CONFIG_PAGE_OFFSET set to 0xC0000000 (it's always a round number). If the - * kernel were larger than 16MB, we might see 0xC1 addresses show up, but our - * kernel isn't that bloated yet. - * - * Unfortunately, x86 has variable-length instructions, so finding this - * particular instruction properly involves writing a disassembler. Instead, - * we rely on statistics. We look for "0xA1" and tally the different bytes - * which occur 4 bytes later (the "0xC0" in our example above). When one of - * those bytes appears three times, we can be reasonably confident that it - * forms the start of CONFIG_PAGE_OFFSET. +/*L:150 A bzImage, unlike an ELF file, is not meant to be loaded. You're + * supposed to jump into it and it will unpack itself. We used to have to + * perform some hairy magic because the unpacking code scared me. * - * This is amazingly reliable. */ -static unsigned long intuit_page_offset(unsigned char *img, unsigned long len) + * Fortunately, Jeremy Fitzhardinge convinced me it wasn't that hard and wrote + * a small patch to jump over the tricky bits in the Guest, so now we just read + * the funky header so we know where in the file to load, and away we go! */ +static unsigned long load_bzimage(int fd) { - unsigned int i, possibilities[256] = { 0 }; + struct boot_params boot; + int r; + /* Modern bzImages get loaded at 1M. */ + void *p = from_guest_phys(0x100000); - for (i = 0; i + 4 < len; i++) { - /* mov 0xXXXXXXXX,%eax */ - if (img[i] == 0xA1 && ++possibilities[img[i+4]] > 3) - return (unsigned long)img[i+4] << 24; - } - errx(1, "could not determine page offset"); -} + /* Go back to the start of the file and read the header. It should be + * a Linux boot header (see Documentation/i386/boot.txt) */ + lseek(fd, 0, SEEK_SET); + read(fd, &boot, sizeof(boot)); -/*L:160 Unfortunately the entire ELF image isn't compressed: the segments - * which need loading are extracted and compressed raw. This denies us the - * information we need to make a fully-general loader. */ -static unsigned long unpack_bzimage(int fd, unsigned long *page_offset) -{ - gzFile f; - int ret, len = 0; - /* A bzImage always gets loaded at physical address 1M. This is - * actually configurable as CONFIG_PHYSICAL_START, but as the comment - * there says, "Don't change this unless you know what you are doing". - * Indeed. */ - void *img = (void *)0x100000; - - /* gzdopen takes our file descriptor (carefully placed at the start of - * the GZIP header we found) and returns a gzFile. */ - f = gzdopen(fd, "rb"); - /* We read it into memory in 64k chunks until we hit the end. */ - while ((ret = gzread(f, img + len, 65536)) > 0) - len += ret; - if (ret < 0) - err(1, "reading image from bzImage"); - - verbose("Unpacked size %i addr %p\n", len, img); - - /* Without the ELF header, we can't tell virtual-physical gap. This is - * CONFIG_PAGE_OFFSET, and people do actually change it. Fortunately, - * I have a clever way of figuring it out from the code itself. */ - *page_offset = intuit_page_offset(img, len); - - return entry_point(img, img + len, *page_offset); -} + /* Inside the setup_hdr, we expect the magic "HdrS" */ + if (memcmp(&boot.hdr.header, "HdrS", 4) != 0) + errx(1, "This doesn't look like a bzImage to me"); -/*L:150 A bzImage, unlike an ELF file, is not meant to be loaded. You're - * supposed to jump into it and it will unpack itself. We can't do that - * because the Guest can't run the unpacking code, and adding features to - * lguest kills puppies, so we don't want to. - * - * The bzImage is formed by putting the decompressing code in front of the - * compressed kernel code. So we can simple scan through it looking for the - * first "gzip" header, and start decompressing from there. */ -static unsigned long load_bzimage(int fd, unsigned long *page_offset) -{ - unsigned char c; - int state = 0; - - /* GZIP header is 0x1F 0x8B <method> <flags>... <compressed-by>. */ - while (read(fd, &c, 1) == 1) { - switch (state) { - case 0: - if (c == 0x1F) - state++; - break; - case 1: - if (c == 0x8B) - state++; - else - state = 0; - break; - case 2 ... 8: - state++; - break; - case 9: - /* Seek back to the start of the gzip header. */ - lseek(fd, -10, SEEK_CUR); - /* One final check: "compressed under UNIX". */ - if (c != 0x03) - state = -1; - else - return unpack_bzimage(fd, page_offset); - } - } - errx(1, "Could not find kernel in bzImage"); + /* Skip over the extra sectors of the header. */ + lseek(fd, (boot.hdr.setup_sects+1) * 512, SEEK_SET); + + /* Now read everything into memory. in nice big chunks. */ + while ((r = read(fd, p, 65536)) > 0) + p += r; + + /* Finally, code32_start tells us where to enter the kernel. */ + return boot.hdr.code32_start; } /*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. */ -static unsigned long load_kernel(int fd, unsigned long *page_offset) +static unsigned long load_kernel(int fd) { Elf32_Ehdr hdr; @@ -373,10 +373,10 @@ static unsigned long load_kernel(int fd, unsigned long *page_offset) /* If it's an ELF file, it starts with "\177ELF" */ if (memcmp(hdr.e_ident, ELFMAG, SELFMAG) == 0) - return map_elf(fd, &hdr, page_offset); + return map_elf(fd, &hdr); /* Otherwise we assume it's a bzImage, and try to unpack it */ - return load_bzimage(fd, page_offset); + return load_bzimage(fd); } /* This is a trivial little helper to align pages. Andi Kleen hated it because @@ -402,59 +402,45 @@ static unsigned long load_initrd(const char *name, unsigned long mem) int ifd; struct stat st; unsigned long len; - void *iaddr; ifd = open_or_die(name, O_RDONLY); /* fstat() is needed to get the file size. */ if (fstat(ifd, &st) < 0) err(1, "fstat() on initrd '%s'", name); - /* The length needs to be rounded up to a page size: mmap needs the - * address to be page aligned. */ + /* We map the initrd at the top of memory, but mmap wants it to be + * page-aligned, so we round the size up for that. */ len = page_align(st.st_size); - /* We map the initrd at the top of memory. */ - iaddr = mmap((void *)mem - len, st.st_size, - PROT_READ|PROT_EXEC|PROT_WRITE, - MAP_FIXED|MAP_PRIVATE, ifd, 0); - if (iaddr != (void *)mem - len) - err(1, "Mmaping initrd '%s' returned %p not %p", - name, iaddr, (void *)mem - len); + map_at(ifd, from_guest_phys(mem - len), 0, st.st_size); /* Once a file is mapped, you can close the file descriptor. It's a * little odd, but quite useful. */ close(ifd); - verbose("mapped initrd %s size=%lu @ %p\n", name, st.st_size, iaddr); + verbose("mapped initrd %s size=%lu @ %p\n", name, len, (void*)mem-len); /* We return the initrd size. */ return len; } -/* Once we know how much memory we have, and the address the Guest kernel - * expects, we can construct simple linear page tables which will get the Guest - * far enough into the boot to create its own. +/* Once we know how much memory we have, we can construct simple linear page + * tables which set virtual == physical which will get the Guest far enough + * into the boot to create its own. * * We lay them out of the way, just below the initrd (which is why we need to * know its size). */ static unsigned long setup_pagetables(unsigned long mem, - unsigned long initrd_size, - unsigned long page_offset) + unsigned long initrd_size) { - u32 *pgdir, *linear; + unsigned long *pgdir, *linear; unsigned int mapped_pages, i, linear_pages; - unsigned int ptes_per_page = getpagesize()/sizeof(u32); + unsigned int ptes_per_page = getpagesize()/sizeof(void *); - /* Ideally we map all physical memory starting at page_offset. - * However, if page_offset is 0xC0000000 we can only map 1G of physical - * (0xC0000000 + 1G overflows). */ - if (mem <= -page_offset) - mapped_pages = mem/getpagesize(); - else - mapped_pages = -page_offset/getpagesize(); + mapped_pages = mem/getpagesize(); /* Each PTE page can map ptes_per_page pages: how many do we need? */ linear_pages = (mapped_pages + ptes_per_page-1)/ptes_per_page; /* We put the toplevel page directory page at the top of memory. */ - pgdir = (void *)mem - initrd_size - getpagesize(); + pgdir = from_guest_phys(mem) - initrd_size - getpagesize(); /* Now we use the next linear_pages pages as pte pages */ linear = (void *)pgdir - linear_pages*getpagesize(); @@ -465,20 +451,19 @@ static unsigned long setup_pagetables(unsigned long mem, for (i = 0; i < mapped_pages; i++) linear[i] = ((i * getpagesize()) | PAGE_PRESENT); - /* The top level points to the linear page table pages above. The - * entry representing page_offset points to the first one, and they - * continue from there. */ + /* The top level points to the linear page table pages above. */ for (i = 0; i < mapped_pages; i += ptes_per_page) { - pgdir[(i + page_offset/getpagesize())/ptes_per_page] - = (((u32)linear + i*sizeof(u32)) | PAGE_PRESENT); + pgdir[i/ptes_per_page] + = ((to_guest_phys(linear) + i*sizeof(void *)) + | PAGE_PRESENT); } - verbose("Linear mapping of %u pages in %u pte pages at %p\n", - mapped_pages, linear_pages, linear); + verbose("Linear mapping of %u pages in %u pte pages at %#lx\n", + mapped_pages, linear_pages, to_guest_phys(linear)); /* We return the top level (guest-physical) address: the kernel needs * to know where it is. */ - return (unsigned long)pgdir; + return to_guest_phys(pgdir); } /* Simple routine to roll all the commandline arguments together with spaces @@ -498,14 +483,17 @@ static void concat(char *dst, char *args[]) /* 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 top physical page to allow, the top level pagetable, the entry point and - * the page_offset constant for the Guest. */ -static int tell_kernel(u32 pgdir, u32 start, u32 page_offset) + * 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) { - u32 args[] = { LHREQ_INITIALIZE, - top/getpagesize(), pgdir, start, page_offset }; + unsigned long args[] = { LHREQ_INITIALIZE, + (unsigned long)guest_base, + guest_limit / getpagesize(), pgdir, start }; int fd; + verbose("Guest: %p - %p (%#lx)\n", + guest_base, guest_base + guest_limit, guest_limit); fd = open_or_die("/dev/lguest", O_RDWR); if (write(fd, args, sizeof(args)) < 0) err(1, "Writing to /dev/lguest"); @@ -515,11 +503,11 @@ static int tell_kernel(u32 pgdir, u32 start, u32 page_offset) } /*:*/ -static void set_fd(int fd, struct device_list *devices) +static void add_device_fd(int fd) { - FD_SET(fd, &devices->infds); - if (fd > devices->max_infd) - devices->max_infd = fd; + FD_SET(fd, &devices.infds); + if (fd > devices.max_infd) + devices.max_infd = fd; } /*L:200 @@ -537,36 +525,38 @@ static void set_fd(int fd, struct device_list *devices) * * This, of course, is merely a different *kind* of icky. */ -static void wake_parent(int pipefd, int lguest_fd, struct device_list *devices) +static void wake_parent(int pipefd, int lguest_fd) { /* Add the pipe from the Launcher to the fdset in the device_list, so * we watch it, too. */ - set_fd(pipefd, devices); + add_device_fd(pipefd); for (;;) { - fd_set rfds = devices->infds; - u32 args[] = { LHREQ_BREAK, 1 }; + fd_set rfds = devices.infds; + unsigned long args[] = { LHREQ_BREAK, 1 }; /* Wait until input is ready from one of the devices. */ - select(devices->max_infd+1, &rfds, NULL, NULL, NULL); + select(devices.max_infd+1, &rfds, NULL, NULL, NULL); /* Is it a message from the Launcher? */ if (FD_ISSET(pipefd, &rfds)) { - int ignorefd; + int fd; /* If read() returns 0, it means the Launcher has * exited. We silently follow. */ - if (read(pipefd, &ignorefd, sizeof(ignorefd)) == 0) + if (read(pipefd, &fd, sizeof(fd)) == 0) exit(0); - /* Otherwise it's telling us there's a problem with one - * of the devices, and we should ignore that file - * descriptor from now on. */ - FD_CLR(ignorefd, &devices->infds); + /* Otherwise it's telling us to change what file + * descriptors we're to listen to. */ + if (fd >= 0) + FD_SET(fd, &devices.infds); + else + FD_CLR(-fd - 1, &devices.infds); } else /* Send LHREQ_BREAK command. */ write(lguest_fd, args, sizeof(args)); } } /* This routine just sets up a pipe to the Waker process. */ -static int setup_waker(int lguest_fd, struct device_list *device_list) +static int setup_waker(int lguest_fd) { int pipefd[2], child; @@ -580,7 +570,7 @@ static int setup_waker(int lguest_fd, struct device_list *device_list) if (child == 0) { /* Close the "writing" end of our copy of the pipe */ close(pipefd[1]); - wake_parent(pipefd[0], lguest_fd, device_list); + wake_parent(pipefd[0], lguest_fd); } /* Close the reading end of our copy of the pipe. */ close(pipefd[0]); @@ -602,83 +592,128 @@ static void *_check_pointer(unsigned long addr, unsigned int size, { /* We have to separately check addr and addr+size, because size could * be huge and addr + size might wrap around. */ - if (addr >= top || addr + size >= top) - errx(1, "%s:%i: Invalid address %li", __FILE__, line, addr); + if (addr >= guest_limit || addr + size >= guest_limit) + errx(1, "%s:%i: Invalid address %#lx", __FILE__, line, addr); /* We return a pointer for the caller's convenience, now we know it's * safe to use. */ - return (void *)addr; + return from_guest_phys(addr); } /* A macro which transparently hands the line number to the real function. */ #define check_pointer(addr,size) _check_pointer(addr, size, __LINE__) -/* The Guest has given us the address of a "struct lguest_dma". We check it's - * OK and convert it to an iovec (which is a simple array of ptr/size - * pairs). */ -static u32 *dma2iov(unsigned long dma, struct iovec iov[], unsigned *num) +/* This function returns the next descriptor in the chain, or vq->vring.num. */ +static unsigned next_desc(struct virtqueue *vq, unsigned int i) { - unsigned int i; - struct lguest_dma *udma; - - /* First we make sure that the array memory itself is valid. */ - udma = check_pointer(dma, sizeof(*udma)); - /* Now we check each element */ - for (i = 0; i < LGUEST_MAX_DMA_SECTIONS; i++) { - /* A zero length ends the array. */ - if (!udma->len[i]) - break; + unsigned int next; - iov[i].iov_base = check_pointer(udma->addr[i], udma->len[i]); - iov[i].iov_len = udma->len[i]; - } - *num = i; + /* If this descriptor says it doesn't chain, we're done. */ + if (!(vq->vring.desc[i].flags & VRING_DESC_F_NEXT)) + return vq->vring.num; + + /* Check they're not leading us off end of descriptors. */ + next = vq->vring.desc[i].next; + /* Make sure compiler knows to grab that: we don't want it changing! */ + wmb(); - /* We return the pointer to where the caller should write the amount of - * the buffer used. */ - return &udma->used_len; + if (next >= vq->vring.num) + errx(1, "Desc next is %u", next); + + return next; +} + +/* This looks in the virtqueue and for the first available buffer, and converts + * it to an iovec for convenient access. Since descriptors consist of some + * number of output then some number of input descriptors, it's actually two + * iovecs, but we pack them into one and note how many of each there were. + * + * This function returns the descriptor number found, or vq->vring.num (which + * is never a valid descriptor number) if none was found. */ +static unsigned get_vq_desc(struct virtqueue *vq, + struct iovec iov[], + unsigned int *out_num, unsigned int *in_num) +{ + unsigned int i, head; + + /* Check it isn't doing very strange things with descriptor numbers. */ + if ((u16)(vq->vring.avail->idx - vq->last_avail_idx) > vq->vring.num) + errx(1, "Guest moved used index from %u to %u", + vq->last_avail_idx, vq->vring.avail->idx); + + /* If there's nothing new since last we looked, return invalid. */ + if (vq->vring.avail->idx == vq->last_avail_idx) + return vq->vring.num; + + /* Grab the next descriptor number they're advertising, and increment + * the index we've seen. */ + head = vq->vring.avail->ring[vq->last_avail_idx++ % vq->vring.num]; + + /* If their number is silly, that's a fatal mistake. */ + if (head >= vq->vring.num) + errx(1, "Guest says index %u is available", head); + + /* When we start there are none of either input nor output. */ + *out_num = *in_num = 0; + + i = head; + do { + /* Grab the first descriptor, and check it's OK. */ + iov[*out_num + *in_num].iov_len = vq->vring.desc[i].len; + iov[*out_num + *in_num].iov_base + = check_pointer(vq->vring.desc[i].addr, + vq->vring.desc[i].len); + /* If this is an input descriptor, increment that count. */ + if (vq->vring.desc[i].flags & VRING_DESC_F_WRITE) + (*in_num)++; + else { + /* If it's an output descriptor, they're all supposed + * to come before any input descriptors. */ + if (*in_num) + errx(1, "Descriptor has out after in"); + (*out_num)++; + } + + /* If we've got too many, that implies a descriptor loop. */ + if (*out_num + *in_num > vq->vring.num) + errx(1, "Looped descriptor"); + } while ((i = next_desc(vq, i)) != vq->vring.num); + + return head; } -/* This routine gets a DMA buffer from the Guest for a given key, and converts - * it to an iovec array. It returns the interrupt the Guest wants when we're - * finished, and a pointer to the "used_len" field to fill in. */ -static u32 *get_dma_buffer(int fd, void *key, - struct iovec iov[], unsigned int *num, u32 *irq) +/* Once 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) { - u32 buf[] = { LHREQ_GETDMA, (u32)key }; - unsigned long udma; - u32 *res; - - /* Ask the kernel for a DMA buffer corresponding to this key. */ - udma = write(fd, buf, sizeof(buf)); - /* They haven't registered any, or they're all used? */ - if (udma == (unsigned long)-1) - return NULL; - - /* Convert it into our iovec array */ - res = dma2iov(udma, iov, num); - /* The kernel stashes irq in ->used_len to get it out to us. */ - *irq = *res; - /* Return a pointer to ((struct lguest_dma *)udma)->used_len. */ - return res; + struct vring_used_elem *used; + + /* Get a pointer to the next entry in the used ring. */ + used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num]; + used->id = head; + used->len = len; + /* Make sure buffer is written before we update index. */ + wmb(); + vq->vring.used->idx++; } -/* This is a convenient routine to send the Guest an interrupt. */ -static void trigger_irq(int fd, u32 irq) +/* This actually sends the interrupt for this virtqueue */ +static void trigger_irq(int fd, struct virtqueue *vq) { - u32 buf[] = { LHREQ_IRQ, irq }; + unsigned long buf[] = { LHREQ_IRQ, vq->config.irq }; + + if (vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) + return; + + /* Send the Guest an interrupt tell them we used something up. */ if (write(fd, buf, sizeof(buf)) != 0) - err(1, "Triggering irq %i", irq); + err(1, "Triggering irq %i", vq->config.irq); } -/* This simply sets up an iovec array where we can put data to be discarded. - * This happens when the Guest doesn't want or can't handle the input: we have - * to get rid of it somewhere, and if we bury it in the ceiling space it will - * start to smell after a week. */ -static void discard_iovec(struct iovec *iov, unsigned int *num) +/* And here's the combo meal deal. Supersize me! */ +static void add_used_and_trigger(int fd, struct virtqueue *vq, + unsigned int head, int len) { - static char discard_buf[1024]; - *num = 1; - iov->iov_base = discard_buf; - iov->iov_len = sizeof(discard_buf); + add_used(vq, head, len); + trigger_irq(fd, vq); } /* Here is the input terminal setting we save, and the routine to restore them @@ -701,38 +736,39 @@ struct console_abort /* This is the routine which handles console input (ie. stdin). */ static bool handle_console_input(int fd, struct device *dev) { - u32 irq = 0, *lenp; int len; - unsigned int num; - struct iovec iov[LGUEST_MAX_DMA_SECTIONS]; + unsigned int head, in_num, out_num; + struct iovec iov[dev->vq->vring.num]; struct console_abort *abort = dev->priv; - /* First we get the console buffer from the Guest. The key is dev->mem - * which was set to 0 in setup_console(). */ - lenp = get_dma_buffer(fd, dev->mem, iov, &num, &irq); - if (!lenp) { - /* If it's not ready for input, warn and set up to discard. */ - warn("console: no dma buffer!"); - discard_iovec(iov, &num); - } + /* First we need a console buffer from the Guests's input 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 console in queue?"); /* This is why we convert to iovecs: the readv() call uses them, and so * it reads straight into the Guest's buffer. */ - len = readv(dev->fd, iov, num); + len = readv(dev->fd, iov, in_num); if (len <= 0) { /* This implies that the console is closed, is /dev/null, or - * something went terribly wrong. We still go through the rest - * of the logic, though, especially the exit handling below. */ + * something went terribly wrong. */ warnx("Failed to get console input, ignoring console."); - len = 0; + /* Put the input terminal back. */ + restore_term(); + /* Remove callback from input vq, so it doesn't restart us. */ + dev->vq->handle_output = NULL; + /* Stop listening to this fd: don't call us again. */ + return false; } - /* If we read the data into the Guest, fill in the length and send the - * interrupt. */ - if (lenp) { - *lenp = len; - trigger_irq(fd, irq); - } + /* Tell the Guest about the new input. */ + add_used_and_trigger(fd, dev->vq, head, len); /* Three ^C within one second? Exit. * @@ -746,7 +782,7 @@ static bool handle_console_input(int fd, struct device *dev) struct timeval now; gettimeofday(&now, NULL); if (now.tv_sec <= abort->start.tv_sec+1) { - u32 args[] = { LHREQ_BREAK, 0 }; + unsigned long args[] = { LHREQ_BREAK, 0 }; /* Close the fd so Waker will know it has to * exit. */ close(waker_fd); @@ -761,214 +797,163 @@ static bool handle_console_input(int fd, struct device *dev) /* Any other key resets the abort counter. */ abort->count = 0; - /* Now, if we didn't read anything, put the input terminal back and - * return failure (meaning, don't call us again). */ - if (!len) { - restore_term(); - return false; - } /* Everything went OK! */ return true; } -/* Handling console output is much simpler than input. */ -static u32 handle_console_output(int fd, const struct iovec *iov, - unsigned num, struct device*dev) +/* Handling output for console is simple: we just get all the output buffers + * and write them to stdout. */ +static void handle_console_output(int fd, struct virtqueue *vq) { - /* Whatever the Guest sends, write it to standard output. Return the - * number of bytes written. */ - return writev(STDOUT_FILENO, iov, num); -} - -/* Guest->Host network output is also pretty easy. */ -static u32 handle_tun_output(int fd, const struct iovec *iov, - unsigned num, struct device *dev) -{ - /* We put a flag in the "priv" pointer of the network device, and set - * it as soon as we see output. We'll see why in handle_tun_input() */ - *(bool *)dev->priv = true; - /* Whatever packet the Guest sent us, write it out to the tun - * device. */ - return writev(dev->fd, iov, num); + unsigned int head, out, in; + int len; + struct iovec iov[vq->vring.num]; + + /* Keep getting output buffers from the Guest until we run out. */ + while ((head = get_vq_desc(vq, iov, &out, &in)) != vq->vring.num) { + if (in) + errx(1, "Input buffers in output queue?"); + len = writev(STDOUT_FILENO, iov, out); + add_used_and_trigger(fd, vq, head, len); + } } -/* This matches the peer_key() in lguest_net.c. The key for any given slot - * is the address of the network device's page plus 4 * the slot number. */ -static unsigned long peer_offset(unsigned int peernum) +/* 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) { - return 4 * peernum; + unsigned int head, out, in; + int len; + struct iovec iov[vq->vring.num]; + + /* Keep getting output buffers from the Guest until we run out. */ + 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). */ + (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); + } } -/* This is where we handle a packet coming in from the tun device */ +/* This is where we handle a packet coming in from the tun device to our + * Guest. */ static bool handle_tun_input(int fd, struct device *dev) { - u32 irq = 0, *lenp; + unsigned int head, in_num, out_num; int len; - unsigned num; - struct iovec iov[LGUEST_MAX_DMA_SECTIONS]; + struct iovec iov[dev->vq->vring.num]; + struct virtio_net_hdr *hdr; - /* First we get a buffer the Guest has bound to its key. */ - lenp = get_dma_buffer(fd, dev->mem+peer_offset(NET_PEERNUM), iov, &num, - &irq); - if (!lenp) { + /* First we need a network buffer from the Guests's recv virtqueue. */ + head = get_vq_desc(dev->vq, iov, &out_num, &in_num); + if (head == dev->vq->vring.num) { /* Now, it's expected that if we try to send a packet too - * early, the Guest won't be ready yet. This is why we set a - * flag when the Guest sends its first packet. If it's sent a - * packet we assume it should be ready to receive them. - * - * Actually, this is what the status bits in the descriptor are - * for: we should *use* them. FIXME! */ - if (*(bool *)dev->priv) + * early, the Guest won't be ready yet. Wait until the device + * status says it's ready. */ + /* FIXME: Actually want DRIVER_ACTIVE here. */ + if (dev->desc->status & VIRTIO_CONFIG_S_DRIVER_OK) warn("network: no dma buffer!"); - discard_iovec(iov, &num); - } + /* We'll turn this back on if input buffers are registered. */ + return false; + } else if (out_num) + 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. */ - len = readv(dev->fd, iov, num); + len = readv(dev->fd, iov+1, in_num-1); if (len <= 0) err(1, "reading network"); - /* Write the used_len, and trigger the interrupt for the Guest */ - if (lenp) { - *lenp = len; - trigger_irq(fd, irq); - } + /* Tell the Guest about the new packet. */ + add_used_and_trigger(fd, dev->vq, head, sizeof(*hdr) + len); + verbose("tun input packet len %i [%02x %02x] (%s)\n", len, - ((u8 *)iov[0].iov_base)[0], ((u8 *)iov[0].iov_base)[1], - lenp ? "sent" : "discarded"); + ((u8 *)iov[1].iov_base)[0], ((u8 *)iov[1].iov_base)[1], + head != dev->vq->vring.num ? "sent" : "discarded"); + /* All good. */ return true; } -/* The last device handling routine is block output: the Guest has sent a DMA - * to the block device. It will have placed the command it wants in the - * "struct lguest_block_page". */ -static u32 handle_block_output(int fd, const struct iovec *iov, - unsigned num, struct device *dev) +/* This callback 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) { - struct lguest_block_page *p = dev->mem; - u32 irq, *lenp; - unsigned int len, reply_num; - struct iovec reply[LGUEST_MAX_DMA_SECTIONS]; - off64_t device_len, off = (off64_t)p->sector * 512; - - /* First we extract the device length from the dev->priv pointer. */ - device_len = *(off64_t *)dev->priv; - - /* We first check that the read or write is within the length of the - * block file. */ - if (off >= device_len) - err(1, "Bad offset %llu vs %llu", off, device_len); - /* Move to the right location in the block file. This shouldn't fail, - * but best to check. */ - if (lseek64(dev->fd, off, SEEK_SET) != off) - err(1, "Bad seek to sector %i", p->sector); - - verbose("Block: %s at offset %llu\n", p->type ? "WRITE" : "READ", off); - - /* They were supposed to bind a reply buffer at key equal to the start - * of the block device memory. We need this to tell them when the - * request is finished. */ - lenp = get_dma_buffer(fd, dev->mem, reply, &reply_num, &irq); - if (!lenp) - err(1, "Block request didn't give us a dma buffer"); - - if (p->type) { - /* A write request. The DMA they sent contained the data, so - * write it out. */ - len = writev(dev->fd, iov, num); - /* Grr... Now we know how long the "struct lguest_dma" they - * sent was, we make sure they didn't try to write over the end - * of the block file (possibly extending it). */ - if (off + len > device_len) { - /* Trim it back to the correct length */ - ftruncate64(dev->fd, device_len); - /* Die, bad Guest, die. */ - errx(1, "Write past end %llu+%u", off, len); - } - /* The reply length is 0: we just send back an empty DMA to - * interrupt them and tell them the write is finished. */ - *lenp = 0; - } else { - /* A read request. They sent an empty DMA to start the - * request, and we put the read contents into the reply - * buffer. */ - len = readv(dev->fd, reply, reply_num); - *lenp = len; - } - - /* The result is 1 (done), 2 if there was an error (short read or - * write). */ - p->result = 1 + (p->bytes != len); - /* Now tell them we've used their reply buffer. */ - trigger_irq(fd, irq); - - /* We're supposed to return the number of bytes of the output buffer we - * used. But the block device uses the "result" field instead, so we - * don't bother. */ - return 0; + add_device_fd(vq->dev->fd); + /* Tell waker to listen to it again */ + write(waker_fd, &vq->dev->fd, sizeof(vq->dev->fd)); } -/* This is the generic routine we call when the Guest sends some DMA out. */ -static void handle_output(int fd, unsigned long dma, unsigned long key, - struct device_list *devices) +/* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */ +static void handle_output(int fd, unsigned long addr) { struct device *i; - u32 *lenp; - struct iovec iov[LGUEST_MAX_DMA_SECTIONS]; - unsigned num = 0; - - /* Convert the "struct lguest_dma" they're sending to a "struct - * iovec". */ - lenp = dma2iov(dma, iov, &num); - - /* Check each device: if they expect output to this key, tell them to - * handle it. */ - for (i = devices->dev; i; i = i->next) { - if (i->handle_output && key == i->watch_key) { - /* We write the result straight into the used_len field - * for them. */ - *lenp = i->handle_output(fd, iov, num, i); - return; + struct virtqueue *vq; + + /* Check each virtqueue. */ + for (i = devices.dev; i; i = i->next) { + for (vq = i->vq; vq; vq = vq->next) { + if (vq->config.pfn == addr/getpagesize() + && vq->handle_output) { + verbose("Output to %s\n", vq->dev->name); + vq->handle_output(fd, vq); + return; + } } } - /* This can happen: the kernel sends any SEND_DMA which doesn't match - * another Guest to us. It could be that another Guest just left a - * network, for example. But it's unusual. */ - warnx("Pending dma %p, key %p", (void *)dma, (void *)key); + /* Early console write is done using notify on a nul-terminated string + * in Guest memory. */ + if (addr >= guest_limit) + errx(1, "Bad NOTIFY %#lx", addr); + + write(STDOUT_FILENO, from_guest_phys(addr), + strnlen(from_guest_phys(addr), guest_limit - addr)); } /* This is called when the waker wakes us up: check for incoming file * descriptors. */ -static void handle_input(int fd, struct device_list *devices) +static void handle_input(int fd) { /* select() wants a zeroed timeval to mean "don't wait". */ struct timeval poll = { .tv_sec = 0, .tv_usec = 0 }; for (;;) { struct device *i; - fd_set fds = devices->infds; + fd_set fds = devices.infds; /* If nothing is ready, we're done. */ - if (select(devices->max_infd+1, &fds, NULL, NULL, &poll) == 0) + if (select(devices.max_infd+1, &fds, NULL, NULL, &poll) == 0) break; /* Otherwise, call the device(s) which have readable * file 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)) { + int dev_fd; + if (i->handle_input(fd, i)) + continue; + /* If handle_input() returns false, it means we - * should no longer service it. - * handle_console_input() does this. */ - if (!i->handle_input(fd, i)) { - /* Clear it from the set of input file - * descriptors kept at the head of the - * device list. */ - FD_CLR(i->fd, &devices->infds); - /* Tell waker to ignore it too... */ - write(waker_fd, &i->fd, sizeof(i->fd)); - } + * should no longer service it. Networking and + * console do this when there's no input + * buffers to deliver into. Console also uses + * it when it discovers that stdin is + * closed. */ + 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)); } } } @@ -982,43 +967,93 @@ static void handle_input(int fd, struct device_list *devices) * routines to allocate them. * * This routine allocates a new "struct lguest_device_desc" from descriptor - * table in the devices array just above the Guest's normal memory. */ -static struct lguest_device_desc * -new_dev_desc(struct lguest_device_desc *descs, - u16 type, u16 features, u16 num_pages) + * table just above the Guest's normal memory. It returns a pointer to that + * descriptor. */ +static struct lguest_device_desc *new_dev_desc(u16 type) { - unsigned int i; + struct lguest_device_desc *d; - for (i = 0; i < LGUEST_MAX_DEVICES; i++) { - if (!descs[i].type) { - descs[i].type = type; - descs[i].features = features; - descs[i].num_pages = num_pages; - /* If they said the device needs memory, we allocate - * that now, bumping up the top of Guest memory. */ - if (num_pages) { - map_zeroed_pages(top, num_pages); - descs[i].pfn = top/getpagesize(); - top += num_pages*getpagesize(); - } - return &descs[i]; - } - } - errx(1, "too many devices"); + /* We only have one page for all the descriptors. */ + if (devices.desc_used + sizeof(*d) > getpagesize()) + errx(1, "Too many devices"); + + /* We don't need to set config_len or status: page is 0 already. */ + d = (void *)devices.descpage + devices.desc_used; + d->type = type; + devices.desc_used += sizeof(*d); + + return d; } -/* This monster routine does all the creation and setup of a new device, - * including caling new_dev_desc() to allocate the descriptor and device - * memory. */ -static struct device *new_device(struct device_list *devices, - u16 type, u16 num_pages, u16 features, - int fd, - bool (*handle_input)(int, struct device *), - unsigned long watch_off, - u32 (*handle_output)(int, - const struct iovec *, - unsigned, - struct device *)) +/* 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...]. + * + * This routine adds a new field to an existing device's descriptor. It only + * works for the last device, but that's OK because that's how we use it. */ +static void add_desc_field(struct device *dev, u8 type, u8 len, const void *c) +{ + /* This is the last descriptor, right? */ + assert(devices.descpage + devices.desc_used + == (u8 *)(dev->desc + 1) + dev->desc->config_len); + + /* We only have one page of device descriptions. */ + if (devices.desc_used + 2 + len > getpagesize()) + errx(1, "Too many devices"); + + /* Copy in the new config header: type then length. */ + devices.descpage[devices.desc_used++] = type; + devices.descpage[devices.desc_used++] = len; + memcpy(devices.descpage + devices.desc_used, c, len); + devices.desc_used += len; + + /* Update the device descriptor length: two byte head then data. */ + dev->desc->config_len += 2 + len; +} + +/* This routine adds a virtqueue to a device. We specify how many descriptors + * the virtqueue is to have. */ +static void add_virtqueue(struct device *dev, unsigned int num_descs, + void (*handle_output)(int fd, struct virtqueue *me)) +{ + unsigned int pages; + struct virtqueue **i, *vq = malloc(sizeof(*vq)); + void *p; + + /* First we need some pages for this virtqueue. */ + pages = (vring_size(num_descs) + getpagesize() - 1) / getpagesize(); + p = get_pages(pages); + + /* Initialize the configuration. */ + vq->config.num = num_descs; + vq->config.irq = devices.next_irq++; + vq->config.pfn = to_guest_phys(p) / getpagesize(); + + /* Initialize the vring. */ + vring_init(&vq->vring, num_descs, p); + + /* Add the configuration information to this device's descriptor. */ + add_desc_field(dev, VIRTIO_CONFIG_F_VIRTQUEUE, + sizeof(vq->config), &vq->config); + + /* Add to tail of list, so dev->vq is first vq, dev->vq->next is + * second. */ + for (i = &dev->vq; *i; i = &(*i)->next); + *i = vq; + + /* Link virtqueue back to device. */ + vq->dev = dev; + + /* Set up handler. */ + vq->handle_output = handle_output; + 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. */ +static struct device *new_device(const char *name, u16 type, int fd, + bool (*handle_input)(int, struct device *)) { struct device *dev = malloc(sizeof(*dev)); @@ -1026,27 +1061,25 @@ static struct device *new_device(struct device_list *devices, * 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. */ - *devices->lastdev = dev; + *devices.lastdev = dev; dev->next = NULL; - devices->lastdev = &dev->next; + devices.lastdev = &dev->next; /* Now we populate the fields one at a time. */ dev->fd = fd; /* If we have an input handler for this file descriptor, then we add it * to the device_list's fdset and maxfd. */ if (handle_input) - set_fd(dev->fd, devices); - dev->desc = new_dev_desc(devices->descs, type, features, num_pages); - dev->mem = (void *)(dev->desc->pfn * getpagesize()); + add_device_fd(dev->fd); + dev->desc = new_dev_desc(type); dev->handle_input = handle_input; - dev->watch_key = (unsigned long)dev->mem + watch_off; - dev->handle_output = handle_output; + dev->name = name; return dev; } /* Our first setup routine is the console. It's a fairly simple device, but * UNIX tty handling makes it uglier than it could be. */ -static void setup_console(struct device_list *devices) +static void setup_console(void) { struct device *dev; @@ -1062,127 +1095,38 @@ static void setup_console(struct device_list *devices) atexit(restore_term); } - /* We don't currently require any memory for the console, so we ask for - * 0 pages. */ - dev = new_device(devices, LGUEST_DEVICE_T_CONSOLE, 0, 0, - STDIN_FILENO, handle_console_input, - LGUEST_CONSOLE_DMA_KEY, handle_console_output); + dev = new_device("console", VIRTIO_ID_CONSOLE, + STDIN_FILENO, handle_console_input); /* We store the console state in dev->priv, and initialize it. */ dev->priv = malloc(sizeof(struct console_abort)); ((struct console_abort *)dev->priv)->count = 0; - verbose("device %p: console\n", - (void *)(dev->desc->pfn * getpagesize())); -} -/* Setting up a block file is also fairly straightforward. */ -static void setup_block_file(const char *filename, struct device_list *devices) -{ - int fd; - struct device *dev; - off64_t *device_len; - struct lguest_block_page *p; - - /* We open with O_LARGEFILE because otherwise we get stuck at 2G. We - * open with O_DIRECT because otherwise our benchmarks go much too - * fast. */ - fd = open_or_die(filename, O_RDWR|O_LARGEFILE|O_DIRECT); - - /* We want one page, and have no input handler (the block file never - * has anything interesting to say to us). Our timing will be quite - * random, so it should be a reasonable randomness source. */ - dev = new_device(devices, LGUEST_DEVICE_T_BLOCK, 1, - LGUEST_DEVICE_F_RANDOMNESS, - fd, NULL, 0, handle_block_output); - - /* We store the device size in the private area */ - device_len = dev->priv = malloc(sizeof(*device_len)); - /* This is the safe way of establishing the size of our device: it - * might be a normal file or an actual block device like /dev/hdb. */ - *device_len = lseek64(fd, 0, SEEK_END); - - /* The device memory is a "struct lguest_block_page". It's zeroed - * already, we just need to put in the device size. Block devices - * think in sectors (ie. 512 byte chunks), so we translate here. */ - p = dev->mem; - p->num_sectors = *device_len/512; - verbose("device %p: block %i sectors\n", - (void *)(dev->desc->pfn * getpagesize()), p->num_sectors); + /* 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. */ + add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd); + add_virtqueue(dev, VIRTQUEUE_NUM, handle_console_output); + + verbose("device %u: console\n", devices.device_num++); } +/*:*/ -/* - * Network Devices. +/*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 + * used to send packets to another guest in a 1:1 manner. * - * Setting up network devices is quite a pain, because we have three types. - * First, we have the inter-Guest network. This is a file which is mapped into - * the address space of the Guests who are on the network. Because it is a - * shared mapping, the same page underlies all the devices, and they can send - * DMA to each other. + * More sopisticated is to use one of the tools developed for project like UML + * to do networking. * - * Remember from our network driver, the Guest is told what slot in the page it - * is to use. We use exclusive fnctl locks to reserve a slot. If another - * Guest is using a slot, the lock will fail and we try another. Because fnctl - * locks are cleaned up automatically when we die, this cleverly means that our - * reservation on the slot will vanish if we crash. */ -static unsigned int find_slot(int netfd, const char *filename) -{ - struct flock fl; - - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_len = 1; - /* Try a 1 byte lock in each possible position number */ - for (fl.l_start = 0; - fl.l_start < getpagesize()/sizeof(struct lguest_net); - fl.l_start++) { - /* If we succeed, return the slot number. */ - if (fcntl(netfd, F_SETLK, &fl) == 0) - return fl.l_start; - } - errx(1, "No free slots in network file %s", filename); -} - -/* This function sets up the network file */ -static void setup_net_file(const char *filename, - struct device_list *devices) -{ - int netfd; - struct device *dev; - - /* We don't use open_or_die() here: for friendliness we create the file - * if it doesn't already exist. */ - netfd = open(filename, O_RDWR, 0); - if (netfd < 0) { - if (errno == ENOENT) { - netfd = open(filename, O_RDWR|O_CREAT, 0600); - if (netfd >= 0) { - /* If we succeeded, initialize the file with a - * blank page. */ - char page[getpagesize()]; - memset(page, 0, sizeof(page)); - write(netfd, page, sizeof(page)); - } - } - if (netfd < 0) - err(1, "cannot open net file '%s'", filename); - } - - /* We need 1 page, and the features indicate the slot to use and that - * no checksum is needed. We never touch this device again; it's - * between the Guests on the network, so we don't register input or - * output handlers. */ - dev = new_device(devices, LGUEST_DEVICE_T_NET, 1, - find_slot(netfd, filename)|LGUEST_NET_F_NOCSUM, - -1, NULL, 0, NULL); - - /* Map the shared file. */ - if (mmap(dev->mem, getpagesize(), PROT_READ|PROT_WRITE, - MAP_FIXED|MAP_SHARED, netfd, 0) != dev->mem) - err(1, "could not mmap '%s'", filename); - verbose("device %p: shared net %s, peer %i\n", - (void *)(dev->desc->pfn * getpagesize()), filename, - dev->desc->features & ~LGUEST_NET_F_NOCSUM); -} -/*:*/ + * Faster is to do virtio bonding in kernel. Doing this 1:1 would be + * completely generic ("here's my vring, attach to your vring") and would work + * for any traffic. Of course, namespace and permissions issues need to be + * dealt with. A more sophisticated "multi-channel" virtio_net.c could hide + * multiple inter-guest channels behind one interface, although it would + * require some manner of hotplugging new virtio channels. + * + * Finally, we could implement a virtio network switch in the kernel. :*/ static u32 str2ip(const char *ipaddr) { @@ -1217,7 +1161,7 @@ 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 * it up so packets will flow, the copies the MAC address into the hwaddr - * pointer (in practice, the Host's slot in the network device's memory). */ + * pointer. */ static void configure_device(int fd, const char *devname, u32 ipaddr, unsigned char hwaddr[6]) { @@ -1243,18 +1187,18 @@ static void configure_device(int fd, const char *devname, u32 ipaddr, memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6); } -/*L:195 The other kind of network is a Host<->Guest network. This can either - * use briding 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(const char *arg, struct device_list *devices) +/*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(const char *arg) { struct device *dev; struct ifreq ifr; int netfd, ipfd; u32 ip; const char *br_name = NULL; + u8 hwaddr[6]; /* 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 @@ -1270,21 +1214,13 @@ static void setup_tun_net(const char *arg, struct device_list *devices) * device: trust us! */ ioctl(netfd, TUNSETNOCSUM, 1); - /* We create the net device with 1 page, using the features field of - * the descriptor to tell the Guest it is in slot 1 (NET_PEERNUM), and - * that the device has fairly random timing. We do *not* specify - * LGUEST_NET_F_NOCSUM: these packets can reach the real world. - * - * We will put our MAC address is slot 0 for the Guest to see, so - * it will send packets to us using the key "peer_offset(0)": */ - dev = new_device(devices, LGUEST_DEVICE_T_NET, 1, - NET_PEERNUM|LGUEST_DEVICE_F_RANDOMNESS, netfd, - handle_tun_input, peer_offset(0), handle_tun_output); + /* First we create a new network device. */ + dev = new_device("net", VIRTIO_ID_NET, netfd, handle_tun_input); - /* We keep a flag which says whether we've seen packets come out from - * this network device. */ - dev->priv = malloc(sizeof(bool)); - *(bool *)dev->priv = false; + /* Network devices need a receive and a send queue, just like + * console. */ + add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd); + add_virtqueue(dev, VIRTQUEUE_NUM, handle_net_output); /* We need a socket to perform the magic network ioctls to bring up the * tap interface, connect to the bridge etc. Any socket will do! */ @@ -1300,44 +1236,251 @@ static void setup_tun_net(const char *arg, struct device_list *devices) } else /* It is an IP address to set up the device with */ ip = str2ip(arg); - /* We are peer 0, ie. first slot, so we hand dev->mem to this routine - * to write the MAC address at the start of the device memory. */ - configure_device(ipfd, ifr.ifr_name, ip, dev->mem); + /* Set up the tun device, and get the mac address for the interface. */ + configure_device(ipfd, ifr.ifr_name, ip, hwaddr); - /* Set "promisc" bit: we want every single packet if we're going to - * bridge to other machines (and otherwise it doesn't matter). */ - *((u8 *)dev->mem) |= 0x1; + /* Tell Guest what MAC address to use. */ + add_desc_field(dev, VIRTIO_CONFIG_NET_MAC_F, sizeof(hwaddr), hwaddr); + /* We don't seed the socket any more; setup is done. */ close(ipfd); - verbose("device %p: tun net %u.%u.%u.%u\n", - (void *)(dev->desc->pfn * getpagesize()), - (u8)(ip>>24), (u8)(ip>>16), (u8)(ip>>8), (u8)ip); + verbose("device %u: tun net %u.%u.%u.%u\n", + devices.device_num++, + (u8)(ip>>24),(u8)(ip>>16),(u8)(ip>>8),(u8)ip); if (br_name) verbose("attached to bridge: %s\n", br_name); } + + +/* + * Block device. + * + * 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. + * + * So we farm the I/O out to thread, and communicate with it via a pipe. */ + +/* This hangs off device->priv, with the data. */ +struct vblk_info +{ + /* The size of the file. */ + off64_t len; + + /* The file descriptor for the file. */ + int fd; + + /* IO thread listens on this file descriptor [0]. */ + int workpipe[2]; + + /* IO thread writes to this file descriptor to mark it done, then + * Launcher triggers interrupt to Guest. */ + int done_fd; +}; + +/* This is the core of the I/O thread. It returns true if it did something. */ +static bool service_io(struct device *dev) +{ + struct vblk_info *vblk = dev->priv; + unsigned int head, out_num, in_num, wlen; + int ret; + struct virtio_blk_inhdr *in; + struct virtio_blk_outhdr *out; + struct iovec iov[dev->vq->vring.num]; + off64_t off; + + head = get_vq_desc(dev->vq, iov, &out_num, &in_num); + if (head == dev->vq->vring.num) + return false; + + if (out_num == 0 || in_num == 0) + errx(1, "Bad virtblk cmd %u out=%u in=%u", + head, out_num, in_num); + + out = convert(&iov[0], struct virtio_blk_outhdr); + 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? */ + if (out->type & VIRTIO_BLK_T_BARRIER) + fdatasync(vblk->fd); + + if (out->type & VIRTIO_BLK_T_SCSI_CMD) { + fprintf(stderr, "Scsi commands unsupported\n"); + in->status = VIRTIO_BLK_S_UNSUPP; + wlen = sizeof(in); + } else if (out->type & VIRTIO_BLK_T_OUT) { + /* Write */ + + /* Move to the right location in the block file. This can fail + * if they try to write past end. */ + if (lseek64(vblk->fd, off, SEEK_SET) != off) + err(1, "Bad seek to sector %llu", out->sector); + + ret = writev(vblk->fd, iov+1, out_num-1); + verbose("WRITE to sector %llu: %i\n", out->sector, ret); + + /* Grr... Now we know how long the descriptor they sent was, we + * make sure they didn't try to write over the end of the block + * file (possibly extending it). */ + if (ret > 0 && off + ret > vblk->len) { + /* Trim it back to the correct length */ + ftruncate64(vblk->fd, vblk->len); + /* Die, bad Guest, die. */ + errx(1, "Write past end %llu+%u", off, ret); + } + wlen = sizeof(in); + in->status = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR); + } else { + /* Read */ + + /* Move to the right location in the block file. This can fail + * if they try to read past end. */ + if (lseek64(vblk->fd, off, SEEK_SET) != off) + err(1, "Bad seek to sector %llu", out->sector); + + ret = readv(vblk->fd, iov+1, in_num-1); + verbose("READ from sector %llu: %i\n", out->sector, ret); + if (ret >= 0) { + wlen = sizeof(in) + ret; + in->status = VIRTIO_BLK_S_OK; + } else { + wlen = sizeof(in); + in->status = VIRTIO_BLK_S_IOERR; + } + } + + /* We can't trigger an IRQ, because we're not the Launcher. It does + * that when we tell it we're done. */ + add_used(dev->vq, head, wlen); + return true; +} + +/* This is the thread which actually services the I/O. */ +static int io_thread(void *_dev) +{ + struct device *dev = _dev; + struct vblk_info *vblk = dev->priv; + char c; + + /* Close other side of workpipe so we get 0 read when main dies. */ + close(vblk->workpipe[1]); + /* Close the other side of the done_fd pipe. */ + close(dev->fd); + + /* 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, + * rather than waiting until we've done them all. I haven't + * measured to see if it makes any difference. */ + while (service_io(dev)) + write(vblk->done_fd, &c, 1); + } + return 0; +} + +/* When the thread says some I/O is done, we interrupt the Guest. */ +static bool handle_io_finish(int fd, struct device *dev) +{ + char c; + + /* If child died, presumably it printed message. */ + if (read(dev->fd, &c, 1) != 1) + exit(1); + + /* It did some work, so trigger the irq. */ + trigger_irq(fd, dev->vq); + return true; +} + +/* When the Guest submits some I/O, we wake the I/O thread. */ +static void handle_virtblk_output(int fd, struct virtqueue *vq) +{ + struct vblk_info *vblk = vq->dev->priv; + char c = 0; + + /* Wake up I/O thread and tell it to go to work! */ + if (write(vblk->workpipe[1], &c, 1) != 1) + /* Presumably it indicated why it died. */ + exit(1); +} + +/* This creates a virtual block device. */ +static void setup_block_file(const char *filename) +{ + int p[2]; + struct device *dev; + struct vblk_info *vblk; + void *stack; + u64 cap; + unsigned int val; + + /* This is the pipe the I/O thread will use to tell us I/O is done. */ + pipe(p); + + /* 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. */ + add_virtqueue(dev, VIRTQUEUE_NUM, handle_virtblk_output); + + /* Allocate the room for our own bookkeeping */ + vblk = dev->priv = malloc(sizeof(*vblk)); + + /* First we open the file and store the length. */ + vblk->fd = open_or_die(filename, O_RDWR|O_LARGEFILE); + vblk->len = lseek64(vblk->fd, 0, SEEK_END); + + /* Tell Guest how many sectors this device has. */ + cap = cpu_to_le64(vblk->len / 512); + add_desc_field(dev, VIRTIO_CONFIG_BLK_F_CAPACITY, sizeof(cap), &cap); + + /* Tell Guest not to put in too many descriptors at once: two are used + * for the in and out elements. */ + val = cpu_to_le32(VIRTQUEUE_NUM - 2); + add_desc_field(dev, VIRTIO_CONFIG_BLK_F_SEG_MAX, sizeof(val), &val); + + /* 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. */ + pipe(vblk->workpipe); + + /* Create stack for thread and run it */ + stack = malloc(32768); + if (clone(io_thread, stack + 32768, CLONE_VM, dev) == -1) + err(1, "Creating clone"); + + /* We don't need to keep the I/O thread's end of the pipes open. */ + close(vblk->done_fd); + close(vblk->workpipe[0]); + + verbose("device %u: virtblock %llu sectors\n", + devices.device_num, cap); +} /* That's the end of device setup. */ /*L:220 Finally we reach the core of the Launcher, which runs the Guest, serves * its input and output, and finally, lays it to rest. */ -static void __attribute__((noreturn)) -run_guest(int lguest_fd, struct device_list *device_list) +static void __attribute__((noreturn)) run_guest(int lguest_fd) { for (;;) { - u32 args[] = { LHREQ_BREAK, 0 }; - unsigned long arr[2]; + unsigned long args[] = { LHREQ_BREAK, 0 }; + unsigned long notify_addr; int readval; /* We read from the /dev/lguest device to run the Guest. */ - readval = read(lguest_fd, arr, sizeof(arr)); - - /* The read can only really return sizeof(arr) (the Guest did a - * SEND_DMA to us), or an error. */ + readval = read(lguest_fd, ¬ify_addr, sizeof(notify_addr)); - /* For a successful read, arr[0] is the address of the "struct - * lguest_dma", and arr[1] is the key the Guest sent to. */ - if (readval == sizeof(arr)) { - handle_output(lguest_fd, arr[0], arr[1], device_list); + /* One unsigned long means the Guest did HCALL_NOTIFY */ + if (readval == sizeof(notify_addr)) { + verbose("Notify on address %#lx\n", notify_addr); + handle_output(lguest_fd, notify_addr); continue; /* ENOENT means the Guest died. Reading tells us why. */ } else if (errno == ENOENT) { @@ -1351,7 +1494,7 @@ run_guest(int lguest_fd, struct device_list *device_list) /* Service input, then unset the BREAK which releases * the Waker. */ - handle_input(lguest_fd, device_list); + handle_input(lguest_fd); if (write(lguest_fd, args, sizeof(args)) < 0) err(1, "Resetting break"); } @@ -1365,7 +1508,6 @@ run_guest(int lguest_fd, struct device_list *device_list) static struct option opts[] = { { "verbose", 0, NULL, 'v' }, - { "sharenet", 1, NULL, 's' }, { "tunnet", 1, NULL, 't' }, { "block", 1, NULL, 'b' }, { "initrd", 1, NULL, 'i' }, @@ -1374,37 +1516,21 @@ static struct option opts[] = { static void usage(void) { errx(1, "Usage: lguest [--verbose] " - "[--sharenet=<filename>|--tunnet=(<ipaddr>|bridge:<bridgename>)\n" + "[--tunnet=(<ipaddr>|bridge:<bridgename>)\n" "|--block=<filename>|--initrd=<filename>]...\n" "<mem-in-mb> vmlinux [args...]"); } -/*L:100 The Launcher code itself takes us out into userspace, that scary place - * where pointers run wild and free! Unfortunately, like most userspace - * programs, it's quite boring (which is why everyone like to hack on the - * kernel!). Perhaps if you make up an Lguest Drinking Game at this point, it - * will get you through this section. Or, maybe not. - * - * The Launcher binary sits up high, usually starting at address 0xB8000000. - * Everything below this is the "physical" memory for the Guest. For example, - * if the Guest were to write a "1" at physical address 0, we would see a "1" - * in the Launcher at "(int *)0". Guest physical == Launcher virtual. - * - * This can be tough to get your head around, but usually it just means that we - * don't need to do any conversion when the Guest gives us it's "physical" - * addresses. - */ +/*L:105 The main routine is where the real work begins: */ int main(int argc, char *argv[]) { - /* Memory, top-level pagetable, code startpoint, PAGE_OFFSET and size - * of the (optional) initrd. */ - unsigned long mem = 0, pgdir, start, page_offset, initrd_size = 0; + /* 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. */ int i, c, lguest_fd; - /* The list of Guest devices, based on command line arguments. */ - struct device_list device_list; - /* The boot information for the Guest: at guest-physical address 0. */ - void *boot = (void *)0; + /* The boot information for the Guest. */ + struct boot_params *boot; /* If they specify an initrd file to load. */ const char *initrd_name = NULL; @@ -1412,11 +1538,12 @@ int main(int argc, char *argv[]) * device receive input from a file descriptor, we keep an fdset * (infds) and the maximum fd number (max_infd) with the head of the * list. We also keep a pointer to the last device, for easy appending - * to the list. */ - device_list.max_infd = -1; - device_list.dev = NULL; - device_list.lastdev = &device_list.dev; - FD_ZERO(&device_list.infds); + * to the list. Finally, we keep the next interrupt number to hand out + * (1: remember that 0 is used by the timer). */ + FD_ZERO(&devices.infds); + devices.max_infd = -1; + devices.lastdev = &devices.dev; + devices.next_irq = 1; /* We need to know how much memory so we can set up the device * descriptor and memory pages for the devices as we parse the command @@ -1424,9 +1551,16 @@ int main(int argc, char *argv[]) * of memory now. */ for (i = 1; i < argc; i++) { if (argv[i][0] != '-') { - mem = top = atoi(argv[i]) * 1024 * 1024; - device_list.descs = map_zeroed_pages(top, 1); - top += getpagesize(); + mem = atoi(argv[i]) * 1024 * 1024; + /* We start by mapping anonymous pages over all of + * guest-physical memory range. This fills it with 0, + * and ensures that the Guest won't be killed when it + * tries to access it. */ + guest_base = map_zeroed_pages(mem / getpagesize() + + DEVICE_PAGES); + guest_limit = mem; + guest_max = mem + DEVICE_PAGES*getpagesize(); + devices.descpage = get_pages(1); break; } } @@ -1437,14 +1571,11 @@ int main(int argc, char *argv[]) case 'v': verbose = true; break; - case 's': - setup_net_file(optarg, &device_list); - break; case 't': - setup_tun_net(optarg, &device_list); + setup_tun_net(optarg); break; case 'b': - setup_block_file(optarg, &device_list); + setup_block_file(optarg); break; case 'i': initrd_name = optarg; @@ -1459,56 +1590,60 @@ int main(int argc, char *argv[]) if (optind + 2 > argc) usage(); - /* We always have a console device */ - setup_console(&device_list); + verbose("Guest base is at %p\n", guest_base); - /* We start by mapping anonymous pages over all of guest-physical - * memory range. This fills it with 0, and ensures that the Guest - * won't be killed when it tries to access it. */ - map_zeroed_pages(0, mem / getpagesize()); + /* We always have a console device */ + setup_console(); /* Now we load the kernel */ - start = load_kernel(open_or_die(argv[optind+1], O_RDONLY), - &page_offset); + start = load_kernel(open_or_die(argv[optind+1], O_RDONLY)); + + /* Boot information is stashed at physical address 0 */ + boot = from_guest_phys(0); /* Map the initrd image if requested (at top of physical memory) */ if (initrd_name) { initrd_size = load_initrd(initrd_name, mem); /* These are the location in the Linux boot header where the * start and size of the initrd are expected to be found. */ - *(unsigned long *)(boot+0x218) = mem - initrd_size; - *(unsigned long *)(boot+0x21c) = initrd_size; + boot->hdr.ramdisk_image = mem - initrd_size; + boot->hdr.ramdisk_size = initrd_size; /* The bootloader type 0xFF means "unknown"; that's OK. */ - *(unsigned char *)(boot+0x210) = 0xFF; + boot->hdr.type_of_loader = 0xFF; } /* Set up the initial linear pagetables, starting below the initrd. */ - pgdir = setup_pagetables(mem, initrd_size, page_offset); + pgdir = setup_pagetables(mem, initrd_size); /* The Linux boot header contains an "E820" memory map: ours is a * simple, single region. */ - *(char*)(boot+E820NR) = 1; - *((struct e820entry *)(boot+E820MAP)) - = ((struct e820entry) { 0, mem, E820_RAM }); + boot->e820_entries = 1; + boot->e820_map[0] = ((struct e820entry) { 0, mem, E820_RAM }); /* The boot header contains a command line pointer: we put the command - * line after the boot header (at address 4096) */ - *(void **)(boot + 0x228) = boot + 4096; - concat(boot + 4096, argv+optind+2); + * line after the boot header. */ + boot->hdr.cmd_line_ptr = to_guest_phys(boot + 1); + concat((char *)(boot + 1), argv+optind+2); + + /* Boot protocol version: 2.07 supports the fields for lguest. */ + boot->hdr.version = 0x207; + + /* The hardware_subarch value of "1" tells the Guest it's an lguest. */ + boot->hdr.hardware_subarch = 1; - /* The guest type value of "1" tells the Guest it's under lguest. */ - *(int *)(boot + 0x23c) = 1; + /* Tell the entry path not to try to reload segment registers. */ + boot->hdr.loadflags |= KEEP_SEGMENTS; /* We tell the kernel to initialize the Guest: this returns the open * /dev/lguest file descriptor. */ - lguest_fd = tell_kernel(pgdir, start, page_offset); + lguest_fd = tell_kernel(pgdir, start); /* We fork off a child process, which wakes the Launcher whenever one * of the input file descriptors needs attention. Otherwise we would * run the Guest until it tries to output something. */ - waker_fd = setup_waker(lguest_fd, &device_list); + waker_fd = setup_waker(lguest_fd); /* Finally, run the Guest. This doesn't return. */ - run_guest(lguest_fd, &device_list); + run_guest(lguest_fd); } /*:*/ |