aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-23 09:03:07 -0700
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-23 09:03:07 -0700
commit0d6810091cdbd05efeb31654c6a41a6cbdfdd2c8 (patch)
tree44d79f8133ea6acd791fe4f32188789c2c65da93
parenta98ce5c6feead6bfedefabd46cb3d7f5be148d9a (diff)
parent43d33b21a03d3abcc8cbdeb4d52bc4568f822c5e (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 ...
-rw-r--r--Documentation/lguest/Makefile26
-rw-r--r--Documentation/lguest/lguest.c1629
-rw-r--r--Documentation/lguest/lguest.txt72
-rw-r--r--arch/i386/Kconfig32
-rw-r--r--arch/i386/Makefile3
-rw-r--r--arch/x86/kernel/asm-offsets_32.c1
-rw-r--r--arch/x86/lguest/Kconfig14
-rw-r--r--arch/x86/lguest/Makefile1
-rw-r--r--arch/x86/lguest/boot.c (renamed from drivers/lguest/lguest.c)102
-rw-r--r--arch/x86/lguest/i386_head.S (renamed from drivers/lguest/lguest_asm.S)46
-rw-r--r--arch/x86/xen/Kconfig5
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/block/Kconfig6
-rw-r--r--drivers/block/Makefile2
-rw-r--r--drivers/block/lguest_blk.c421
-rw-r--r--drivers/block/virtio_blk.c308
-rw-r--r--drivers/char/Kconfig4
-rw-r--r--drivers/char/Makefile2
-rw-r--r--drivers/char/hvc_lguest.c177
-rw-r--r--drivers/char/virtio_console.c225
-rw-r--r--drivers/kvm/Kconfig4
-rw-r--r--drivers/lguest/Kconfig13
-rw-r--r--drivers/lguest/Makefile10
-rw-r--r--drivers/lguest/core.c568
-rw-r--r--drivers/lguest/hypercalls.c177
-rw-r--r--drivers/lguest/interrupts_and_traps.c125
-rw-r--r--drivers/lguest/io.c626
-rw-r--r--drivers/lguest/lg.h189
-rw-r--r--drivers/lguest/lguest_bus.c218
-rw-r--r--drivers/lguest/lguest_device.c373
-rw-r--r--drivers/lguest/lguest_user.c138
-rw-r--r--drivers/lguest/page_tables.c250
-rw-r--r--drivers/lguest/segments.c28
-rw-r--r--drivers/lguest/x86/core.c577
-rw-r--r--drivers/lguest/x86/switcher_32.S (renamed from drivers/lguest/switcher.S)7
-rw-r--r--drivers/net/Kconfig6
-rw-r--r--drivers/net/Makefile2
-rw-r--r--drivers/net/lguest_net.c555
-rw-r--r--drivers/net/virtio_net.c435
-rw-r--r--drivers/virtio/Kconfig8
-rw-r--r--drivers/virtio/Makefile2
-rw-r--r--drivers/virtio/config.c13
-rw-r--r--drivers/virtio/virtio.c189
-rw-r--r--drivers/virtio/virtio_ring.c313
-rw-r--r--include/asm-x86/Kbuild3
-rw-r--r--include/asm-x86/bootparam.h108
-rw-r--r--include/asm-x86/e820.h28
-rw-r--r--include/asm-x86/e820_32.h21
-rw-r--r--include/asm-x86/e820_64.h20
-rw-r--r--include/asm-x86/ist.h12
-rw-r--r--include/asm-x86/lguest.h86
-rw-r--r--include/asm-x86/lguest_hcall.h71
-rw-r--r--include/linux/Kbuild5
-rw-r--r--include/linux/apm_bios.h30
-rw-r--r--include/linux/edd.h137
-rw-r--r--include/linux/lguest.h80
-rw-r--r--include/linux/lguest_bus.h51
-rw-r--r--include/linux/lguest_launcher.h112
-rw-r--r--include/linux/mod_devicetable.h6
-rw-r--r--include/linux/screen_info.h81
-rw-r--r--include/linux/virtio.h110
-rw-r--r--include/linux/virtio_blk.h51
-rw-r--r--include/linux/virtio_config.h111
-rw-r--r--include/linux/virtio_console.h12
-rw-r--r--include/linux/virtio_net.h36
-rw-r--r--include/linux/virtio_ring.h119
-rw-r--r--include/video/Kbuild1
-rw-r--r--include/video/edid.h9
-rw-r--r--scripts/mod/file2alias.c18
70 files changed, 4822 insertions, 4401 deletions
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 pla