diff options
Diffstat (limited to 'Documentation')
-rw-r--r-- | Documentation/filesystems/9p.txt | 8 | ||||
-rw-r--r-- | Documentation/lguest/Makefile | 26 | ||||
-rw-r--r-- | Documentation/lguest/lguest.c | 1629 | ||||
-rw-r--r-- | Documentation/lguest/lguest.txt | 72 |
4 files changed, 921 insertions, 814 deletions
diff --git a/Documentation/filesystems/9p.txt b/Documentation/filesystems/9p.txt index b90f537af35..bf8080640eb 100644 --- a/Documentation/filesystems/9p.txt +++ b/Documentation/filesystems/9p.txt @@ -42,10 +42,12 @@ OPTIONS trans=name select an alternative transport. Valid options are currently: - unix - specifying a named pipe mount point - tcp - specifying a normal TCP/IP connection - fd - used passed file descriptors for connection + unix - specifying a named pipe mount point + tcp - specifying a normal TCP/IP connection + fd - used passed file descriptors for connection (see rfdno and wfdno) + virtio - connect to the next virtio channel available + (from lguest or KVM with trans_virtio module) uname=name user name to attempt mount as on the remote server. The server may override or ignore this value. Certain user diff --git a/Documentation/lguest/Makefile b/Documentation/lguest/Makefile index c0b7a455639..bac037eb1cd 100644 --- a/Documentation/lguest/Makefile +++ b/Documentation/lguest/Makefile @@ -1,28 +1,8 @@ # This creates the demonstration utility "lguest" which runs a Linux guest. - -# For those people that have a separate object dir, look there for .config -KBUILD_OUTPUT := ../.. -ifdef O - ifeq ("$(origin O)", "command line") - KBUILD_OUTPUT := $(O) - endif -endif -# We rely on CONFIG_PAGE_OFFSET to know where to put lguest binary. -include $(KBUILD_OUTPUT)/.config -LGUEST_GUEST_TOP := ($(CONFIG_PAGE_OFFSET) - 0x08000000) - -CFLAGS:=-Wall -Wmissing-declarations -Wmissing-prototypes -O3 -Wl,-T,lguest.lds +CFLAGS:=-Wall -Wmissing-declarations -Wmissing-prototypes -O3 -I../../include LDLIBS:=-lz -# Removing this works for some versions of ld.so (eg. Ubuntu Feisty) and -# not others (eg. FC7). -LDFLAGS+=-static -all: lguest.lds lguest -# The linker script on x86 is so complex the only way of creating one -# which will link our binary in the right place is to mangle the -# default one. -lguest.lds: - $(LD) --verbose | awk '/^==========/ { PRINT=1; next; } /SIZEOF_HEADERS/ { gsub(/0x[0-9A-F]*/, "$(LGUEST_GUEST_TOP)") } { if (PRINT) print $$0; }' > $@ +all: lguest clean: - rm -f lguest.lds lguest + rm -f lguest 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 wil |