[syslinux] Loading Xen with PXELinux

Tim Deegan Tim.Deegan at cl.cam.ac.uk
Fri May 6 08:53:48 PDT 2005


Hi,

Attached is a patch against 3.08-pre10 to include a com32 module for
loading Multiboot kernels (com32/modules/mboot.c32) and some brief
documentation (com32/modules/mboot.doc).  It lets you load Xen with a
line like:

DEFAULT mboot.c32 xen.gz dom0_mem=15000 noreboot --- linux.gz console=tty0 root=/dev/hda1 --- initrd.img

(the "---" separates module command lines from each other).  I've tested
it with Xen, gnumach and fiasco, and had success reports from other
people using it with Xen.  I haven't had a chance to test it on a
terribly wide range of hardware, though.

GNU HURD has very long command-lines (>256 chars) so to load it I had to
increase the maximum command-line length in config.inc.  I didn't put
that in the patch because I don't know if it might cause other problems.

Cheers,

Tim.

-- 
Tim Deegan                           (My opinions, not the University's)
Systems Research Group
University of Cambridge Computer Laboratory
-------------- next part --------------
diff -rNu syslinux-3.08-pre10/com32/modules/i386-elf.h syslinux-3.08-pre10-mboot/com32/modules/i386-elf.h
--- syslinux-3.08-pre10/com32/modules/i386-elf.h	1970-01-01 01:00:00.000000000 +0100
+++ syslinux-3.08-pre10-mboot/com32/modules/i386-elf.h	2005-05-06 16:35:36.000000000 +0100
@@ -0,0 +1,237 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2001,2002  Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* 32-bit data types */
+
+typedef unsigned long Elf32_Addr;
+typedef unsigned short Elf32_Half;
+typedef unsigned long Elf32_Off;
+typedef signed long Elf32_Sword;
+typedef unsigned long Elf32_Word;
+/* "unsigned char" already exists */
+
+/* ELF header */
+typedef struct
+{
+  
+#define EI_NIDENT 16
+  
+  /* first four characters are defined below */
+#define EI_MAG0		0
+#define ELFMAG0		0x7f
+#define EI_MAG1		1
+#define ELFMAG1		'E'
+#define EI_MAG2		2
+#define ELFMAG2		'L'
+#define EI_MAG3		3
+#define ELFMAG3		'F'
+  
+#define EI_CLASS	4	/* data sizes */
+#define ELFCLASS32	1	/* i386 -- up to 32-bit data sizes present */
+  
+#define EI_DATA		5	/* data type and ordering */
+#define ELFDATA2LSB	1	/* i386 -- LSB 2's complement */
+  
+#define EI_VERSION	6	/* version number.  "e_version" must be the same */
+#define EV_CURRENT      1	/* current version number */
+
+#define EI_OSABI	7	/* operating system/ABI indication */
+#define ELFOSABI_FREEBSD	9
+  
+#define EI_ABIVERSION	8	/* ABI version */
+  
+#define EI_PAD		9	/* from here in is just padding */
+  
+#define EI_BRAND	8	/* start of OS branding (This is
+				   obviously illegal against the ELF
+				   standard.) */
+  
+  unsigned char e_ident[EI_NIDENT];	/* basic identification block */
+  
+#define ET_EXEC		2	/* we only care about executable types */
+  Elf32_Half e_type;		/* file types */
+  
+#define EM_386		3	/* i386 -- obviously use this one */
+  Elf32_Half e_machine;	/* machine types */
+  Elf32_Word e_version;	/* use same as "EI_VERSION" above */
+  Elf32_Addr e_entry;		/* entry point of the program */
+  Elf32_Off e_phoff;		/* program header table file offset */
+  Elf32_Off e_shoff;		/* section header table file offset */
+  Elf32_Word e_flags;		/* flags */
+  Elf32_Half e_ehsize;		/* elf header size in bytes */
+  Elf32_Half e_phentsize;	/* program header entry size */
+  Elf32_Half e_phnum;		/* number of entries in program header */
+  Elf32_Half e_shentsize;	/* section header entry size */
+  Elf32_Half e_shnum;		/* number of entries in section header */
+  
+#define SHN_UNDEF       0
+#define SHN_LORESERVE   0xff00
+#define SHN_LOPROC      0xff00
+#define SHN_HIPROC      0xff1f
+#define SHN_ABS         0xfff1
+#define SHN_COMMON      0xfff2
+#define SHN_HIRESERVE   0xffff
+  Elf32_Half e_shstrndx;	/* section header table index */
+}
+Elf32_Ehdr;
+
+
+#define BOOTABLE_I386_ELF(h) \
+ ((h.e_ident[EI_MAG0] == ELFMAG0) & (h.e_ident[EI_MAG1] == ELFMAG1) \
+  & (h.e_ident[EI_MAG2] == ELFMAG2) & (h.e_ident[EI_MAG3] == ELFMAG3) \
+  & (h.e_ident[EI_CLASS] == ELFCLASS32) & (h.e_ident[EI_DATA] == ELFDATA2LSB) \
+  & (h.e_ident[EI_VERSION] == EV_CURRENT) & (h.e_type == ET_EXEC) \
+  & (h.e_machine == EM_386) & (h.e_version == EV_CURRENT))
+
+/* section table - ? */
+typedef struct
+{
+  Elf32_Word	sh_name;		/* Section name (string tbl index) */
+  Elf32_Word	sh_type;		/* Section type */
+  Elf32_Word	sh_flags;		/* Section flags */
+  Elf32_Addr	sh_addr;		/* Section virtual addr at execution */
+  Elf32_Off	sh_offset;		/* Section file offset */
+  Elf32_Word	sh_size;		/* Section size in bytes */
+  Elf32_Word	sh_link;		/* Link to another section */
+  Elf32_Word	sh_info;		/* Additional section information */
+  Elf32_Word	sh_addralign;		/* Section alignment */
+  Elf32_Word	sh_entsize;		/* Entry size if section holds table */
+}
+Elf32_Shdr;
+
+/* symbol table - page 4-25, figure 4-15 */
+typedef struct
+{
+  Elf32_Word st_name;
+  Elf32_Addr st_value;
+  Elf32_Word st_size;
+  unsigned char st_info;
+  unsigned char st_other;
+  Elf32_Half st_shndx;
+}
+Elf32_Sym;
+
+/* symbol type and binding attributes - page 4-26 */
+
+#define ELF32_ST_BIND(i)    ((i) >> 4)
+#define ELF32_ST_TYPE(i)    ((i) & 0xf)
+#define ELF32_ST_INFO(b,t)  (((b)<<4)+((t)&0xf))
+
+/* symbol binding - page 4-26, figure 4-16 */
+
+#define STB_LOCAL    0
+#define STB_GLOBAL   1
+#define STB_WEAK     2
+#define STB_LOPROC  13
+#define STB_HIPROC  15
+
+/* symbol types - page 4-28, figure 4-17 */
+
+#define STT_NOTYPE   0
+#define STT_OBJECT   1
+#define STT_FUNC     2
+#define STT_SECTION  3
+#define STT_FILE     4
+#define STT_LOPROC  13
+#define STT_HIPROC  15
+
+
+/* Macros to split/combine relocation type and symbol page 4-32 */
+
+#define ELF32_R_SYM(__i)	((__i)>>8)
+#define ELF32_R_TYPE(__i)	((unsigned char) (__i))
+#define ELF32_R_INFO(__s, __t)	(((__s)<<8) + (unsigned char) (__t))
+
+
+/* program header - page 5-2, figure 5-1 */
+
+typedef struct
+{
+  Elf32_Word p_type;
+  Elf32_Off p_offset;
+  Elf32_Addr p_vaddr;
+  Elf32_Addr p_paddr;
+  Elf32_Word p_filesz;
+  Elf32_Word p_memsz;
+  Elf32_Word p_flags;
+  Elf32_Word p_align;
+}
+Elf32_Phdr;
+
+/* segment types - page 5-3, figure 5-2 */
+
+#define PT_NULL		0
+#define PT_LOAD		1
+#define PT_DYNAMIC	2
+#define PT_INTERP	3
+#define PT_NOTE		4
+#define PT_SHLIB	5
+#define PT_PHDR		6
+
+#define PT_LOPROC	0x70000000
+#define PT_HIPROC	0x7fffffff
+
+/* segment permissions - page 5-6 */
+
+#define PF_X		0x1
+#define PF_W		0x2
+#define PF_R		0x4
+#define PF_MASKPROC	0xf0000000
+
+
+/* dynamic structure - page 5-15, figure 5-9 */
+
+typedef struct
+{
+  Elf32_Sword d_tag;
+  union
+  {
+    Elf32_Word d_val;
+    Elf32_Addr d_ptr;
+  }
+  d_un;
+}
+Elf32_Dyn;
+
+/* Dynamic array tags - page 5-16, figure 5-10.  */
+
+#define DT_NULL		0
+#define DT_NEEDED	1
+#define DT_PLTRELSZ	2
+#define DT_PLTGOT	3
+#define DT_HASH		4
+#define DT_STRTAB	5
+#define DT_SYMTAB	6
+#define DT_RELA		7
+#define DT_RELASZ	8
+#define DT_RELAENT      9
+#define DT_STRSZ	10
+#define DT_SYMENT	11
+#define DT_INIT		12
+#define DT_FINI		13
+#define DT_SONAME	14
+#define DT_RPATH	15
+#define DT_SYMBOLIC	16
+#define DT_REL		17
+#define DT_RELSZ	18
+#define DT_RELENT	19
+#define DT_PLTREL	20
+#define DT_DEBUG	21
+#define DT_TEXTREL	22
+#define DT_JMPREL	23
diff -rNu syslinux-3.08-pre10/com32/modules/Makefile syslinux-3.08-pre10-mboot/com32/modules/Makefile
--- syslinux-3.08-pre10/com32/modules/Makefile	2005-01-05 00:07:01.000000000 +0000
+++ syslinux-3.08-pre10-mboot/com32/modules/Makefile	2005-05-06 16:35:36.000000000 +0100
@@ -45,7 +45,7 @@
 INCDIR   = /usr/include
 COM32DIR = $(AUXDIR)/com32
 
-MODULES	  = chain.c32 menu.c32 ethersel.c32
+MODULES	  = chain.c32 menu.c32 ethersel.c32 mboot.c32
 TESTFILES = menu.lnx
 
 all: $(MODULES) $(TESTFILES)
diff -rNu syslinux-3.08-pre10/com32/modules/mb_header.h syslinux-3.08-pre10-mboot/com32/modules/mb_header.h
--- syslinux-3.08-pre10/com32/modules/mb_header.h	1970-01-01 01:00:00.000000000 +0100
+++ syslinux-3.08-pre10-mboot/com32/modules/mb_header.h	2005-05-06 16:35:36.000000000 +0100
@@ -0,0 +1,90 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000   Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ *  MultiBoot Header description
+ */
+
+struct multiboot_header
+{
+  /* Must be MULTIBOOT_MAGIC - see below.  */
+  unsigned magic;
+  
+  /* Feature flags - see below.  */
+  unsigned flags;
+  
+  /*
+   * Checksum
+   *
+   * The above fields plus this one must equal 0 mod 2^32.
+   */
+  unsigned checksum;
+  
+  /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set.  */
+  unsigned header_addr;
+  unsigned load_addr;
+  unsigned load_end_addr;
+  unsigned bss_end_addr;
+  unsigned entry_addr;
+
+  /* These are only valid if MULTIBOOT_VIDEO_MODE is set.  */
+  unsigned mode_type;
+  unsigned width;
+  unsigned height;
+  unsigned depth;
+};
+
+/*
+ * The entire multiboot_header must be contained
+ * within the first MULTIBOOT_SEARCH bytes of the kernel image.
+ */
+#define MULTIBOOT_SEARCH		8192
+#define MULTIBOOT_FOUND(addr, len) \
+  (! ((addr) & 0x3) \
+   && (len) >= 12 \
+   && *((int *) (addr)) == MULTIBOOT_MAGIC \
+   && ! (*((unsigned *) (addr)) + *((unsigned *) (addr + 4)) \
+	 + *((unsigned *) (addr + 8))) \
+   && (! (MULTIBOOT_AOUT_KLUDGE & *((int *) (addr + 4))) || (len) >= 32) \
+   && (! (MULTIBOOT_VIDEO_MODE & *((int *) (addr + 4))) || (len) >= 48))
+
+/* Magic value identifying the multiboot_header.  */
+#define MULTIBOOT_MAGIC			0x1BADB002
+
+/*
+ * Features flags for 'flags'.
+ * If a boot loader sees a flag in MULTIBOOT_MUSTKNOW set
+ * and it doesn't understand it, it must fail.
+ */
+#define MULTIBOOT_MUSTKNOW		0x0000FFFF
+
+/* currently unsupported flags...  this is a kind of version number.  */
+#define MULTIBOOT_UNSUPPORTED		0x0000FFF8
+
+/* Align all boot modules on i386 page (4KB) boundaries.  */
+#define MULTIBOOT_PAGE_ALIGN		0x00000001
+
+/* Must pass memory information to OS.  */
+#define MULTIBOOT_MEMORY_INFO		0x00000002
+
+/* Must pass video information to OS.  */
+#define MULTIBOOT_VIDEO_MODE		0x00000004
+
+/* This flag indicates the use of the address fields in the header.  */
+#define MULTIBOOT_AOUT_KLUDGE		0x00010000
diff -rNu syslinux-3.08-pre10/com32/modules/mb_info.h syslinux-3.08-pre10-mboot/com32/modules/mb_info.h
--- syslinux-3.08-pre10/com32/modules/mb_info.h	1970-01-01 01:00:00.000000000 +0100
+++ syslinux-3.08-pre10-mboot/com32/modules/mb_info.h	2005-05-06 16:35:36.000000000 +0100
@@ -0,0 +1,217 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000  Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ *  The structure type "mod_list" is used by the "multiboot_info" structure.
+ */
+
+struct mod_list
+{
+  /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */
+  unsigned long mod_start;
+  unsigned long mod_end;
+  
+  /* Module command line */
+  unsigned long cmdline;
+  
+  /* padding to take it to 16 bytes (must be zero) */
+  unsigned long pad;
+};
+
+
+/*
+ *  INT-15, AX=E820 style "AddressRangeDescriptor"
+ *  ...with a "size" parameter on the front which is the structure size - 4,
+ *  pointing to the next one, up until the full buffer length of the memory
+ *  map has been reached.
+ */
+
+struct AddrRangeDesc
+{
+  unsigned long size;
+  unsigned long long BaseAddr;
+  unsigned long long Length;
+  unsigned long Type;
+  
+  /* unspecified optional padding... */
+};
+
+/* usable memory "Type", all others are reserved.  */
+#define MB_ARD_MEMORY		1
+
+
+/* Drive Info structure.  */
+struct drive_info
+{
+  /* The size of this structure.  */
+  unsigned long size;
+
+  /* The BIOS drive number.  */
+  unsigned char drive_number;
+
+  /* The access mode (see below).  */
+  unsigned char drive_mode;
+
+  /* The BIOS geometry.  */
+  unsigned short drive_cylinders;
+  unsigned char drive_heads;
+  unsigned char drive_sectors;
+
+  /* The array of I/O ports used for the drive.  */
+  unsigned short drive_ports[0];
+};
+
+/* Drive Mode.  */
+#define MB_DI_CHS_MODE		0
+#define MB_DI_LBA_MODE		1
+
+
+/* APM BIOS info.  */
+struct apm_info
+{
+  unsigned short version;
+  unsigned short cseg;
+  unsigned long offset;
+  unsigned short cseg_16;
+  unsigned short dseg_16;
+  unsigned short cseg_len;
+  unsigned short cseg_16_len;
+  unsigned short dseg_16_len;
+};
+
+
+/*
+ *  MultiBoot Info description
+ *
+ *  This is the struct passed to the boot image.  This is done by placing
+ *  its address in the EAX register.
+ */
+
+struct multiboot_info
+{
+  /* MultiBoot info version number */
+  unsigned long flags;
+  
+  /* Available memory from BIOS */
+  unsigned long mem_lower;
+  unsigned long mem_upper;
+  
+  /* "root" partition */
+  unsigned long boot_device;
+  
+  /* Kernel command line */
+  unsigned long cmdline;
+  
+  /* Boot-Module list */
+  unsigned long mods_count;
+  unsigned long mods_addr;
+  
+  union
+  {
+    struct
+    {
+      /* (a.out) Kernel symbol table info */
+      unsigned long tabsize;
+      unsigned long strsize;
+      unsigned long addr;
+      unsigned long pad;
+    }
+    a;
+    
+    struct
+    {
+      /* (ELF) Kernel section header table */
+      unsigned long num;
+      unsigned long size;
+      unsigned long addr;
+      unsigned long shndx;
+    }
+    e;
+  }
+  syms;
+  
+  /* Memory Mapping buffer */
+  unsigned long mmap_length;
+  unsigned long mmap_addr;
+  
+  /* Drive Info buffer */
+  unsigned long drives_length;
+  unsigned long drives_addr;
+  
+  /* ROM configuration table */
+  unsigned long config_table;
+  
+  /* Boot Loader Name */
+  unsigned long boot_loader_name;
+
+  /* APM table */
+  unsigned long apm_table;
+
+  /* Video */
+  unsigned long vbe_control_info;
+  unsigned long vbe_mode_info;
+  unsigned short vbe_mode;
+  unsigned short vbe_interface_seg;
+  unsigned short vbe_interface_off;
+  unsigned short vbe_interface_len;
+};
+
+/*
+ *  Flags to be set in the 'flags' parameter above
+ */
+
+/* is there basic lower/upper memory information? */
+#define MB_INFO_MEMORY			0x00000001
+/* is there a boot device set? */
+#define MB_INFO_BOOTDEV			0x00000002
+/* is the command-line defined? */
+#define MB_INFO_CMDLINE			0x00000004
+/* are there modules to do something with? */
+#define MB_INFO_MODS			0x00000008
+
+/* These next two are mutually exclusive */
+
+/* is there a symbol table loaded? */
+#define MB_INFO_AOUT_SYMS		0x00000010
+/* is there an ELF section header table? */
+#define MB_INFO_ELF_SHDR		0x00000020
+
+/* is there a full memory map? */
+#define MB_INFO_MEM_MAP			0x00000040
+
+/* Is there drive info?  */
+#define MB_INFO_DRIVE_INFO		0x00000080
+
+/* Is there a config table?  */
+#define MB_INFO_CONFIG_TABLE		0x00000100
+
+/* Is there a boot loader name?  */
+#define MB_INFO_BOOT_LOADER_NAME	0x00000200
+
+/* Is there a APM table?  */
+#define MB_INFO_APM_TABLE		0x00000400
+
+/* Is there video information?  */
+#define MB_INFO_VIDEO_INFO		0x00000800
+
+/*
+ *  The following value must be present in the EAX register.
+ */
+
+#define MULTIBOOT_VALID			0x2BADB002
diff -rNu syslinux-3.08-pre10/com32/modules/mboot.c syslinux-3.08-pre10-mboot/com32/modules/mboot.c
--- syslinux-3.08-pre10/com32/modules/mboot.c	1970-01-01 01:00:00.000000000 +0100
+++ syslinux-3.08-pre10-mboot/com32/modules/mboot.c	2005-05-06 16:35:36.000000000 +0100
@@ -0,0 +1,979 @@
+/*
+ *  mboot.c
+ *
+ *  Loader for Multiboot-compliant kernels and modules.
+ * 
+ *  Copyright (C) 2005 Tim Deegan <Tim.Deegan at cl.cam.ac.uk>
+ *  Parts based on GNU GRUB, Copyright (C) 2000  Free Software Foundation, Inc.
+ *  Parts based on SYSLINUX, Copyright (C) 1994-2005  H. Peter Anvin.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ * 
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <malloc.h>
+#include <consoles.h>
+#include <zlib.h>
+#include <com32.h>
+
+#include "i386-elf.h"
+#include "mb_info.h"
+#include "mb_header.h"
+
+#include <klibc/compiler.h> /* For __constructor */
+
+#define MIN(_x, _y) (((_x)<(_y))?(_x):(_y))
+#define MAX(_x, _y) (((_x)>(_y))?(_x):(_y))
+
+/* Define this for some more printout */
+#undef DEBUG
+
+/* Memory magic numbers */
+#define STACK_SIZE      0x20000      /* XXX Could be much smaller */
+#define MALLOC_SIZE     0x100000     /* XXX Could be much smaller */
+#define MIN_RUN_ADDR    0x10000      /* Lowest address we'll consider using */
+#define MEM_HOLE_START  0xa0000      /* Memory hole runs from 640k ... */
+#define MEM_HOLE_END    0x100000     /* ... to 1MB */
+#define X86_PAGE_SIZE   0x1000
+
+size_t __stack_size = STACK_SIZE;    /* How much stack we'll use */
+extern void *__mem_end;              /* Start of malloc() heap */
+extern char _end[];                  /* End of static data */
+
+/* Pointer to free memory for loading into: load area is between here
+ * and section_addr */
+static char *next_load_addr;
+
+/* Memory map for run-time */
+typedef struct section section_t;
+struct section {
+    size_t dest;                     /* Start of run-time allocation */
+    char *src;                       /* Current location of data for memmove(),
+                                      * or NULL for bzero() */
+    size_t size;                     /* Length of allocation */
+};
+static char *section_addr;
+static int section_count;
+
+static size_t max_run_addr;          /* Highest address we'll consider using */
+static size_t next_mod_run_addr;     /* Where the next module will be put */
+
+/* File loads are in units of this much */
+#define LOAD_CHUNK 0x20000
+
+/* Layout of the input to the 32-bit lidt instruction */
+struct lidt_operand {
+    unsigned int limit:16;
+    unsigned int base:32;
+} __attribute__((packed));
+
+/* Magic strings */
+static const char version_string[]   = "COM32 Multiboot loader v0.1";
+static const char copyright_string[] = "Copyright (C) 2005 Tim Deegan.";
+static const char module_separator[] = "---";
+
+
+/*
+ *  Start of day magic, run from __start during library init.
+ */
+
+static void __constructor check_version(void)
+    /* Check the SYSLINUX version.  Docs say we should be OK from v2.08, 
+     * but in fact we crash on anything below v2.12 (when libc came in). */
+{
+    com32sys_t regs_in, regs_out;
+    const char *p, *too_old = "Fatal: SYSLINUX image is too old; "
+                              "mboot.c32 needs at least version 2.12.\r\n";
+
+    memset(&regs_in, 0, sizeof(regs_in));
+    regs_in.eax.l = 0x0001;  /* "Get version" */
+    __intcall(0x22, &regs_in, &regs_out);
+    if (regs_out.ecx.w[0] >= 0x020c) return;
+
+    /* Pointless: on older versions this print fails too. :( */
+    for (p = too_old ; *p ; p++) {
+        memset(&regs_in, 0, sizeof(regs_in));
+        regs_in.eax.b[1] = 0x02;      /* "Write character" */
+        regs_in.edx.b[0] = *p;
+        __intcall(0x21, &regs_in, &regs_out);
+    }
+
+    __intcall(0x20, &regs_in, &regs_out);  /* "Terminate program" */
+}
+
+
+static void __constructor grab_memory(void)
+    /* Runs before init_memory_arena() (com32/lib/malloc.c) to let
+     * the malloc() code know how much space it's allowed to use.
+     * We don't use malloc() directly, but some of the library code
+     * does (zlib, for example). */ 
+{ 
+    /* Find the stack pointer */
+    register char * sp;
+    asm volatile("movl %%esp, %0" : "=r" (sp));
+
+    /* Initialize the allocation of *run-time* memory: don't let ourselves 
+     * overwrite the stack during the relocation later. */
+    max_run_addr = (size_t) sp - (MALLOC_SIZE + STACK_SIZE);
+
+    /* Move the end-of-memory marker: malloc() will use only memory
+     * above __mem_end and below the stack.  We will load files starting
+     * at the old __mem_end and working towards the new one, and allocate
+     * section descriptors at the top of that area, working down. */
+    next_load_addr = __mem_end; 
+    section_addr = sp - (MALLOC_SIZE + STACK_SIZE);
+    section_count = 0;
+
+    /* But be careful not to move it the wrong direction if memory is
+     * tight.  Instead we'll fail more gracefully later, when we try to 
+     * load a file and find that next_load_addr > section_addr. */
+    __mem_end = MAX(section_addr, next_load_addr);
+}
+
+
+
+
+/*
+ *  Run-time memory map functions: allocating and recording allocations.
+ */
+
+static int cmp_sections(const void *a, const void *b) 
+    /* For sorting section descriptors by destination address */
+{
+    const section_t *sa = a;
+    const section_t *sb = b;
+    if (sa->dest < sb->dest) return -1;
+    if (sa->dest > sb->dest) return 1;
+    return 0;
+}
+
+
+static void add_section(size_t dest, char *src, size_t size)
+    /* Adds something to the list of sections to relocate. */
+{
+    section_t *sec;
+
+#ifdef DEBUG
+    printf("SECTION: %#8.8x --> %#8.8x (%#x)\n", (size_t) src, dest, size);
+#endif
+
+    section_addr -= sizeof (section_t); 
+    if (section_addr < next_load_addr) {
+        printf("Fatal: out of memory allocating section descriptor.\n");
+        exit(1);
+    }
+    sec = (section_t *) section_addr;
+    section_count++;
+
+    sec->src = src;
+    sec->dest = dest;
+    sec->size = size;
+    
+    /* Keep the list sorted */
+    qsort(sec, section_count, sizeof (section_t), cmp_sections);
+}
+
+
+static size_t place_low_section(size_t size, size_t align) 
+    /* Find a space in the run-time memory map, below 640K */
+{
+    int i;
+    size_t start;
+    section_t *sections = (section_t *) section_addr;
+
+    start = MIN_RUN_ADDR;
+    start = (start + (align-1)) & ~(align-1);
+
+    /* Section list is sorted by destination, so can do this in one pass */
+    for (i = 0; i < section_count; i++) {
+        if (sections[i].dest < start + size) {
+            /* Hit the bottom of this section */
+            start = sections[i].dest + sections[i].size;
+            start = (start + (align-1)) & ~(align-1);
+        }
+    }
+    if (start + size < MEM_HOLE_START) return start;
+    else return 0;
+}
+
+
+static size_t place_module_section(size_t size, size_t align) 
+    /* Find a space in the run-time memory map for this module. */
+{
+    /* Ideally we'd run through the sections looking for a free space
+     * like place_low_section() does, but some OSes (Xen, at least)
+     * assume that the bootloader has loaded all the modules
+     * consecutively, above the kernel.  So, what we actually do is
+     * keep a pointer to the highest address allocated so far, and
+     * always allocate modules there. */ 
+
+    size_t start = next_mod_run_addr;
+    start = (start + (align-1)) & ~(align-1);
+
+    if (start + size > max_run_addr) return 0;
+
+    next_mod_run_addr = start + size;
+    return start;
+}
+
+
+static void place_kernel_section(size_t start, size_t size)
+    /* Allocate run-time space for part of the kernel, checking for
+     * sanity.  We assume the kernel isn't broken enough to have
+     * overlapping segments. */
+{
+    /* We always place modules above the kernel */
+    next_mod_run_addr = MAX(next_mod_run_addr, start + size);
+
+    if (start > max_run_addr || start + size > max_run_addr) {
+        /* Overruns the end of memory */
+        printf("Fatal: kernel loads too high (%#8.8x+%#x > %#8.8x).\n",
+               start, size, max_run_addr);
+        exit(1);
+    }
+    if (start >= MEM_HOLE_END) {
+        /* Above the memory hole: easy */
+#ifdef DEBUG
+        printf("Placed kernel section (%#8.8x+%#x)\n", start, size);
+#endif
+        return;
+    }
+    if (start >= MEM_HOLE_START) {
+        /* In the memory hole.  Not so good */
+        printf("Fatal: kernel load address (%#8.8x) is in the memory hole.\n",
+               start);
+        exit(1);
+    }
+    if (start + size > MEM_HOLE_START) {
+        /* Too big for low memory */
+        printf("Fatal: kernel (%#8.8x+%#x) runs into the memory hole.\n",
+               start, size);
+        exit(1);
+    }   
+    if (start < MIN_RUN_ADDR) {
+        /* Loads too low */
+        printf("Fatal: kernel load address (%#8.8x) is too low (<%#8.8x).\n",
+               start, MIN_RUN_ADDR);
+        exit(1);
+    }
+    /* Kernel loads below the memory hole: OK */
+#ifdef DEBUG
+    printf("Placed kernel section (%#8.8x+%#x)\n", start, size);
+#endif
+}
+
+
+static void reorder_sections(void) 
+    /* Reorders sections into a safe order, where no relocation 
+     * overwrites the source of a later one.  */
+{
+    section_t *secs = (section_t *) section_addr;
+    section_t tmp;
+    int i, j, tries;
+
+#ifdef DEBUG
+    printf("Relocations:\n");
+    for (i = 0; i < section_count ; i++) {
+        printf("    %#8.8x --> %#8.8x (%#x)\n", 
+               (size_t)secs[i].src, secs[i].dest, secs[i].size); 
+    }
+#endif
+
+    for (i = 0; i < section_count; i++) {
+        tries = 0;
+    scan_again:
+        for (j = i + 1 ; j < section_count; j++) {
+            if (secs[j].src != NULL
+                && secs[i].dest + secs[i].size > (size_t) secs[j].src 
+                && secs[i].dest < (size_t) secs[j].src + secs[j].size) {
+                /* Would overwrite the source of the later move */
+                if (++tries > section_count) {
+                    /* Deadlock! */
+                    /* XXX Try to break deadlocks? */
+                    printf("Fatal: circular dependence in relocations.\n");
+                    exit(1);
+                }
+                /* Swap these sections (using struct copies) */
+                tmp = secs[i]; secs[i] = secs[j]; secs[j] = tmp;
+                /* Start scanning again from the new secs[i]... */
+                goto scan_again;
+            }
+        }
+    }
+
+#ifdef DEBUG
+    printf("Relocations:\n");
+    for (i = 0; i < section_count ; i++) {
+        printf("    %#8.8x --> %#8.8x (%#x)\n", 
+               (size_t)secs[i].src, secs[i].dest, secs[i].size); 
+    }
+#endif
+}
+
+
+static void init_mmap(struct multiboot_info *mbi)
+    /* Get a full memory map from the BIOS to pass to the kernel. */
+{
+    com32sys_t regs_in, regs_out;
+    struct AddrRangeDesc *e820;
+    int e820_slots;
+    size_t mem_lower, mem_upper, run_addr, mmap_size;
+    register size_t sp;
+
+    /* Default values for mem_lower and mem_upper in case the BIOS won't 
+     * tell us: 640K, and all memory up to the stack. */
+    asm volatile("movl %%esp, %0" : "=r" (sp));
+    mem_upper = (sp - MEM_HOLE_END) / 1024;
+    mem_lower = (MEM_HOLE_START) / 1024;
+
+#ifdef DEBUG
+    printf("Requesting memory map from BIOS:\n");
+#endif
+    
+    /* Ask the BIOS for the full memory map of the machine.  We'll
+     * build it in Multiboot format (i.e. with size fields) in the
+     * bounce buffer, and then allocate some high memory to keep it in
+     * until boot time. */
+    e820 = __com32.cs_bounce;
+    e820_slots = 0;
+    regs_out.ebx.l = 0;
+    
+    while(((void *)(e820 + 1)) < __com32.cs_bounce + __com32.cs_bounce_size) 
+    {
+
+        e820->size = sizeof(*e820) - sizeof(e820->size);
+      
+        /* Ask the BIOS to fill in this descriptor */
+        regs_in.eax.l = 0xe820;         /* "Get system memory map" */
+        regs_in.ebx.l = regs_out.ebx.l; /* Continuation value from last call */
+        regs_in.ecx.l = 20;             /* Size of buffer to write into */
+        regs_in.edx.l = 0x534d4150;     /* "SMAP" */
+        regs_in.es = SEG(&e820->BaseAddr);
+        regs_in.edi.w[0] = OFFS(&e820->BaseAddr);
+        __intcall(0x15, &regs_in, &regs_out);
+        
+        if ((regs_out.eflags.l & EFLAGS_CF) != 0 && regs_out.ebx.l != 0)
+            break;  /* End of map */
+
+        if (((regs_out.eflags.l & EFLAGS_CF) != 0 && regs_out.ebx.l == 0)
+            || (regs_out.eax.l != 0x534d4150))
+        {           
+            /* Error */
+            printf("Error %x reading E820 memory map: %s.\n", 
+                   (int) regs_out.eax.b[0],
+                   (regs_out.eax.b[0] == 0x80) ? "invalid command" :
+                   (regs_out.eax.b[0] == 0x86) ? "not supported" :
+                   "unknown error");
+            break;
+        } 
+
+        /* Success */
+#ifdef DEBUG
+        printf("    %#16.16Lx -- %#16.16Lx : ", 
+               e820->BaseAddr, e820->BaseAddr + e820->Length);
+        switch (e820->Type) {
+        case 1: printf("Available\n"); break; 
+        case 2: printf("Reserved\n"); break; 
+        case 3: printf("ACPI Reclaim\n"); break; 
+        case 4: printf("ACPI NVS\n"); break; 
+        default: printf("? (Reserved)\n"); break; 
+        }
+#endif
+
+        if (e820->Type == 1) {
+            if (e820->BaseAddr == 0) {
+                mem_lower = MIN(MEM_HOLE_START, e820->Length) / 1024;
+            } else if (e820->BaseAddr == MEM_HOLE_END) {
+                mem_upper = MIN(0xfff00000, e820->Length) / 1024;
+            }
+        }
+
+        /* Move to next slot */ 
+        e820++;
+        e820_slots++;
+        
+        /* Done? */
+        if (regs_out.ebx.l == 0) 
+            break;
+    }
+
+    /* Record the simple information in the MBI */
+    mbi->flags |= MB_INFO_MEMORY;
+    mbi->mem_lower = mem_lower;
+    mbi->mem_upper = mem_upper;
+    
+    /* Record the full memory map in the MBI */
+    if (e820_slots != 0) {
+        mmap_size = e820_slots * sizeof(*e820);
+        /* Where will it live at run time? */ 
+        run_addr = place_low_section(mmap_size, 1);
+        if (run_addr == 0) {
+            printf("Fatal: can't find space for the e820 mmap.\n");
+            exit(1);
+        }
+        /* Where will it live now? */
+        e820 = (struct AddrRangeDesc *) next_load_addr;
+        if (next_load_addr + mmap_size > section_addr) {
+            printf("Fatal: out of memory storing the e820 mmap.\n");
+            exit(1);
+        }
+        next_load_addr += mmap_size;
+        /* Copy it out of the bounce buffer */
+        memcpy(e820, __com32.cs_bounce, mmap_size);
+        /* Remember to copy it again at run time */
+        add_section(run_addr, (char *) e820, mmap_size);
+        /* Record it in the MBI */
+        mbi->flags |= MB_INFO_MEM_MAP;
+        mbi->mmap_length = mmap_size;
+        mbi->mmap_addr = run_addr;
+    }
+}
+
+
+
+
+/*
+ *  Code for loading and parsing files.
+ */
+
+static void load_file(char *filename, char **startp, size_t *sizep) 
+    /* Load a file into memory.  Returns where it is and how big via
+     * startp and sizep */
+{
+    gzFile fp;
+    char *start;
+    int bsize;
+    
+    printf("Loading %s.", filename);
+
+    start = next_load_addr;
+    startp[0] = start;
+    sizep[0] = 0;
+
+    /* Open the file */
+    if ((fp = gzopen(filename, "r")) == NULL) {
+        printf("\nFatal: cannot open %s\n", filename);
+        exit(1);
+    }
+    
+    while (next_load_addr + LOAD_CHUNK <= section_addr) {
+        bsize = gzread(fp, next_load_addr, LOAD_CHUNK);
+        printf("%s",".");
+
+        if (bsize < 0) {
+            printf("\nFatal: read error in %s\n", filename);
+            gzclose(fp);
+            exit(1);
+        }
+      
+        next_load_addr += bsize;
+        sizep[0] += bsize;
+
+        if (bsize < LOAD_CHUNK) {
+            printf("%s","\n");
+            gzclose(fp);        
+            return;
+        }
+    }
+
+    /* Running out of memory.  Try and use up the last bit */
+    if (section_addr > next_load_addr) {
+        bsize = gzread(fp, next_load_addr, section_addr - next_load_addr);
+        printf("%s",".");
+    } else {
+        bsize = 0;
+    }
+    
+    if (bsize < 0) {
+        gzclose(fp);
+        printf("\nFatal: read error in %s\n", filename);
+        exit(1);
+    }
+    
+    next_load_addr += bsize;
+    sizep[0] += bsize;
+    
+    if (!gzeof(fp)) {
+        gzclose(fp);
+        printf("\nFatal: out of memory reading %s\n", filename);
+        exit(1);
+    }
+
+    printf("%s","\n");
+    gzclose(fp);
+    return;
+}
+
+
+static size_t load_kernel(char *cmdline) 
+    /* Load a multiboot/elf32 kernel and allocate run-time memory for it.
+     * Returns the kernel's entry address.  */
+{
+    unsigned int i;
+    char *load_addr;                  /* Where the image was loaded */
+    size_t load_size;                              /* How big it is */
+    char *seg_addr;                   /* Where a segment was loaded */
+    size_t seg_size, bss_size;                     /* How big it is */
+    size_t run_addr, run_size;            /* Where it should be put */
+    char *p; 
+    Elf32_Ehdr *ehdr;
+    Elf32_Phdr *phdr;
+    struct multiboot_header *mbh;
+
+    printf("Kernel: %s\n", cmdline);
+
+    load_addr = 0; 
+    load_size = 0;
+    p = strchr(cmdline, ' '); 
+    if (p != NULL) *p = 0;
+    load_file(cmdline, &load_addr, &load_size);
+    if (load_size < 12) {
+        printf("Fatal: %s is too short to be a multiboot kernel.", 
+               cmdline);
+        exit(1);
+    }
+    if (p != NULL) *p = ' ';
+
+
+    /* Look for a multiboot header in the first 8k of the file */
+    for (i = 0; i <= MIN(load_size - 12, MULTIBOOT_SEARCH - 12); i += 4)
+    {
+        mbh = (struct multiboot_header *)(load_addr + i);
+        if (mbh->magic != MULTIBOOT_MAGIC 
+            || ((mbh->magic+mbh->flags+mbh->checksum) & 0xffffffff))
+        {
+            /* Not a multiboot header */
+            continue;
+        }
+        if (mbh->flags & (MULTIBOOT_UNSUPPORTED | MULTIBOOT_VIDEO_MODE)) {
+            /* Requires options we don't support */
+            printf("Fatal: Kernel requires multiboot options "
+                   "that I don't support: %#x.\n", 
+                   mbh->flags & (MULTIBOOT_UNSUPPORTED|MULTIBOOT_VIDEO_MODE));
+            exit(1);
+        }
+
+        /* This kernel will do: figure out where all the pieces will live */
+
+        if (mbh->flags & MULTIBOOT_AOUT_KLUDGE) {
+
+            /* Use the offsets in the multiboot header */
+#ifdef DEBUG
+            printf("Using multiboot header.\n");
+#endif
+
+            /* Where is the code in the loaded file? */
+            seg_addr = ((char *)mbh) - (mbh->header_addr - mbh->load_addr);
+
+            /* How much code is there? */
+            run_addr = mbh->load_addr;
+            if (mbh->load_end_addr != 0) 
+                seg_size = mbh->load_end_addr - mbh->load_addr;
+            else 
+                seg_size = load_size - (seg_addr - load_addr);
+
+            /* How much memory will it take up? */ 
+            if (mbh->bss_end_addr != 0)
+                run_size = mbh->bss_end_addr - mbh->load_addr;
+            else
+                run_size = seg_size;
+            
+            if (seg_size > run_size) {
+                printf("Fatal: can't put %i bytes of kernel into %i bytes " 
+                       "of memory.\n", seg_size, run_size);
+                exit(1);
+            }
+            if (seg_addr + seg_size > load_addr + load_size) {
+                printf("Fatal: multiboot load segment runs off the " 
+                       "end of the file.\n");
+                exit(1);
+            }
+
+            /* Does it fit where it wants to be? */
+            place_kernel_section(run_addr, run_size);
+            
+            /* Put it on the relocation list */
+            if (seg_size < run_size) {
+                /* Set up the kernel BSS too */
+                if (seg_size > 0) 
+                    add_section(run_addr, seg_addr, seg_size);
+                bss_size = run_size - seg_size;
+                add_section(run_addr + seg_size, NULL, bss_size);
+            } else {
+                /* No BSS */
+                add_section(run_addr, seg_addr, run_size);
+            }
+            
+            /* Done. */
+            return mbh->entry_addr;
+            
+        } else {
+          
+            /* Now look for an ELF32 header */    
+            ehdr = (Elf32_Ehdr *)load_addr;
+            if (*(unsigned long *)ehdr != 0x464c457f 
+                || ehdr->e_ident[EI_DATA] != ELFDATA2LSB
+                || ehdr->e_ident[EI_CLASS] != ELFCLASS32
+                || ehdr->e_machine != EM_386)
+            {
+                printf("Fatal: kernel has neither ELF32/x86 nor multiboot load"
+                       " headers.\n");
+                exit(1);
+            }
+            if (ehdr->e_phoff + ehdr->e_phnum*sizeof (*phdr) > load_size) {
+                printf("Fatal: malformed ELF header overruns EOF.\n");
+                exit(1);
+            }
+            if (ehdr->e_phnum <= 0) {
+                printf("Fatal: ELF kernel has no program headers.\n");
+                exit(1);
+            }
+
+#ifdef DEBUG
+            printf("Using ELF header.\n");
+#endif
+
+            if (ehdr->e_type != ET_EXEC 
+                || ehdr->e_version != EV_CURRENT
+                || ehdr->e_phentsize != sizeof (Elf32_Phdr)) {
+                printf("Warning: funny-looking ELF header.\n");
+            }
+            phdr = (Elf32_Phdr *)(load_addr + ehdr->e_phoff);
+
+            /* Obey the program headers to load the kernel */
+            for(i = 0; i < ehdr->e_phnum; i++) {
+
+                /* How much is in this segment? */
+                run_size = phdr[i].p_memsz;
+                if (phdr[i].p_type != PT_LOAD) 
+                    seg_size = 0;
+                else 
+                    seg_size = (size_t)phdr[i].p_filesz;
+                
+                /* Where is it in the loaded file? */
+                seg_addr = load_addr + phdr[i].p_offset;
+                if (seg_addr + seg_size > load_addr + load_size) {
+                    printf("Fatal: ELF load segment runs off the " 
+                           "end of the file.\n");
+                    exit(1);
+                }
+
+                /* Skip segments that don't take up any memory */
+                if (run_size == 0) continue;
+
+                /* Place the segment where it wants to be */
+                run_addr = phdr[i].p_paddr;
+                place_kernel_section(run_addr, run_size);
+
+                /* Put it on the relocation list */
+                if (seg_size < run_size) {
+                    /* Set up the kernel BSS too */
+                    if (seg_size > 0) 
+                        add_section(run_addr, seg_addr, seg_size);
+                    bss_size = run_size - seg_size;
+                    add_section(run_addr + seg_size, NULL, bss_size);
+                } else {
+                    /* No BSS */
+                    add_section(run_addr, seg_addr, run_size);
+                }
+            }
+         
+            /* Done! */
+            return ehdr->e_entry;
+        }
+    }
+
+    /* This is not a multiboot kernel */
+    printf("Fatal: not a multiboot kernel.\n");
+    exit(1);
+}
+
+
+
+static void load_module(struct mod_list *mod, char *cmdline) 
+    /* Load a multiboot module and allocate a memory area for it */
+{
+    char *load_addr, *p;
+    size_t load_size, run_addr;
+    
+    printf("Module: %s\n", cmdline);
+
+    load_addr = 0; 
+    load_size = 0;
+    p = strchr(cmdline, ' '); 
+    if (p != NULL) *p = 0;
+    load_file(cmdline, &load_addr, &load_size);
+    if (p != NULL) *p = ' ';
+
+    /* Decide where it's going to live */
+    run_addr = place_module_section(load_size, X86_PAGE_SIZE);
+    if (run_addr == 0) {
+        printf("Fatal: can't find space for this module.\n");
+        exit(1);
+    }
+    add_section(run_addr, load_addr, load_size);
+    
+    /* Remember where we put it */
+    mod->mod_start = run_addr;
+    mod->mod_end = run_addr + load_size;
+    mod->pad = 0;
+
+#ifdef DEBUG
+    printf("Placed module (%#8.8x+%#x)\n", run_addr, load_size);
+#endif
+}
+
+
+
+
+/*
+ *  Code for shuffling sections into place and booting the new kernel
+ */
+
+static void trampoline_start(section_t *secs, int sec_count, 
+                             size_t mbi_run_addr, size_t entry)
+    /* Final shuffle-and-boot code.  Running on the stack; no external code
+     * or data can be relied on. */
+{
+    int i;
+    struct lidt_operand idt;
+
+    /* SYSLINUX has set up SS, DS and ES as 32-bit 0--4G data segments,
+     * but doesn't specify FS and GS.  Multiboot wants them all to be
+     * the same, so we'd better do that before we overwrite the GDT. */
+    asm volatile("movl %ds, %ecx; movl %ecx, %fs; movl %ecx, %gs");
+
+    /* Turn off interrupts */
+    asm volatile("cli");
+
+    /* SYSLINUX has set up an IDT at 0x100000 that does all the
+     * comboot calls, and we're about to overwrite it.  The Multiboot
+     * spec says that the kernel must set up its own IDT before turning
+     * on interrupts, but it's still entitled to use BIOS calls, so we'll
+     * put the IDT back to the BIOS one at the base of memory. */
+    idt.base = 0;
+    idt.limit = 0x800;
+    asm volatile("lidt %0" : : "m" (idt));
+
+    /* Now, shuffle the sections */
+    for (i = 0; i < sec_count; i++) {
+        if (secs[i].src == NULL) {
+            /* asm bzero() code from com32/lib/memset.c */
+            char *q = (char *) secs[i].dest;
+            size_t nl = secs[i].size >> 2;
+            asm volatile("cld ; rep ; stosl ; movl %3,%0 ; rep ; stosb"
+                         : "+c" (nl), "+D" (q)
+                         : "a" (0x0U), "r" (secs[i].size & 3));
+        } else {
+            /* asm memmove() code from com32/lib/memmove.c */
+            const char *p = secs[i].src;
+            char *q = (char *) secs[i].dest;
+            size_t n = secs[i].size;
+            if ( q < p ) {
+                asm volatile("cld ; rep ; movsb" 
+                             : "+c" (n), "+S" (p), "+D" (q));
+            } else {
+                p += (n-1);
+                q += (n-1);
+                asm volatile("std ; rep ; movsb" 
+                             : "+c" (n), "+S" (p), "+D" (q));
+            }
+        }
+    }
+
+    /* Now set up the last tiny bit of Multiboot environment... */
+
+    asm volatile(
+
+        /* A20 is already enabled.
+         * CR0 already has PG cleared and PE set.
+         * EFLAGS already has VM and IF cleared.
+         * ESP is the kernels' problem.
+         * GDTR is the kernel's problem.
+         * CS is already a 32-bit, 0--4G code segments.
+         * DS, ES, FS and GS are already 32-bit, 0--4G data segments. 
+         * EBX must point to the MBI: */
+
+        "movl %0, %%ebx;" 
+    
+        /* EAX must be the Multiboot magic number. */
+
+        "movl $0x2badb002, %%eax;"
+
+        /* Start the kernel. */
+
+        "jmp *%1" 
+
+        : : "m" (mbi_run_addr), "r" (entry));    
+
+}
+static void trampoline_end(void) {}
+
+
+static void boot(size_t mbi_run_addr, size_t entry) 
+    /* Tidy up SYSLINUX, shuffle memory and boot the kernel */
+{
+    com32sys_t regs;
+    section_t *tr_sections;
+    void (*trampoline)(section_t *, int, size_t, size_t);
+    size_t trampoline_size;
+
+    /* Make sure the relocations are safe. */
+    reorder_sections();
+
+    /* Copy the shuffle-and-boot code and the array of relocations
+     * onto the memory we previously used for malloc() heap.  This is
+     * safe because it's not the source or the destination of any
+     * copies, and there'll be no more library calls after the copy. */
+
+    tr_sections = ((section_t *) section_addr) + section_count; 
+    trampoline = (void *) (tr_sections + section_count);
+    trampoline_size = (void *)&trampoline_end - (void *)&trampoline_start;
+
+#ifdef DEBUG
+    printf("tr_sections:     %p\n"
+           "trampoline:      %p\n"
+           "trampoline_size: %#8.8x\n"
+           "max_run_addr:    %#8.8x\n",
+           tr_sections, trampoline, trampoline_size, max_run_addr);
+#endif
+
+    printf("Booting: MBI=%#8.8x, entry=%#8.8x\n", mbi_run_addr, entry);
+
+    memmove(tr_sections, section_addr, section_count * sizeof (section_t));
+    memmove(trampoline, trampoline_start, trampoline_size);
+      
+    /* Tell SYSLINUX to clean up */
+    regs.eax.l = 0x000c; /* "Perform final cleanup" */
+    regs.edx.l = 0;      /* "Normal cleanup" */
+    __intcall(0x22, &regs, NULL);
+
+    /* Into the unknown */
+    trampoline(tr_sections, section_count, mbi_run_addr, entry);
+}
+
+
+int main(int argc, char **argv)
+    /* Parse the command-line and invoke loaders */
+{
+    struct multiboot_info *mbi;
+    struct mod_list *modp;
+    int modules;
+    int mbi_reloc_offset;
+    char *p;
+    size_t mbi_run_addr, mbi_size, entry;
+    int i;
+
+    /* Say hello */
+    console_ansi_std();
+    printf("%s.  %s\n", version_string, copyright_string);
+
+    if (argc < 2 || !strcmp(argv[1], module_separator)) {
+        printf("Fatal: No kernel filename!\n");
+        exit(1);
+    }
+
+#ifdef DEBUG
+    printf("_end:           %p\n"
+           "argv[1]:        %p\n"
+           "next_load_addr: %p\n"
+           "section_addr    %p\n"
+           "__mem_end:      %p\n"
+           "argv[0]:        %p\n",
+           &_end, argv[1], next_load_addr, section_addr, __mem_end, argv[0]);
+#endif
+
+    /* How much space will the MBI need? */
+    modules = 0; 
+    mbi_size = sizeof(struct multiboot_info) + strlen(version_string) + 5;
+    for (i = 1 ; i < argc ; i++) {
+        if (!strcmp(argv[i], module_separator)) {
+            modules++; 
+            mbi_size += sizeof(struct mod_list) + 1;
+        } else {
+            mbi_size += strlen(argv[i]) + 1;
+        }
+    }
+    
+    /* Allocate space in the load buffer for the MBI, all the command
+     * lines, and all the module details. */
+    mbi = (struct multiboot_info *)next_load_addr;
+    next_load_addr += mbi_size;
+    if (next_load_addr > section_addr) {
+        printf("Fatal: out of memory allocating for boot metadata.\n");
+        exit(1);
+    }
+    memset(mbi, 0, sizeof (struct multiboot_info));
+    p = (char *)(mbi + 1);
+    mbi->flags = MB_INFO_CMDLINE | MB_INFO_BOOT_LOADER_NAME;
+
+    /* Figure out the memory map.
+     * N.B. Must happen before place_section() is called */
+    init_mmap(mbi);
+
+    mbi_run_addr = place_low_section(mbi_size, 4);
+    if (mbi_run_addr == 0) {
+        printf("Fatal: can't find space for the MBI!\n");
+        exit(1);
+    }
+    mbi_reloc_offset = (size_t)mbi - mbi_run_addr;
+    add_section(mbi_run_addr, (void *)mbi, mbi_size);
+
+    /* Module info structs */
+    modp = (struct mod_list *) (((size_t)p + 3) & ~3);
+    if (modules > 0) mbi->flags |= MB_INFO_MODS;
+    mbi->mods_count = modules;
+    mbi->mods_addr = ((size_t)modp) - mbi_reloc_offset;
+    p = (char *)(modp + modules);
+
+    /* Command lines: first kernel, then modules */
+    mbi->cmdline = ((size_t)p) - mbi_reloc_offset;
+    modules = 0;
+    for (i = 1 ; i < argc ; i++) {
+        if (!strcmp(argv[i], module_separator)) {
+            *p++ = '\0';
+            modp[modules++].cmdline = ((size_t)p) - mbi_reloc_offset;
+        } else {
+            strcpy(p, argv[i]);
+            p += strlen(argv[i]);
+            *p++ = ' ';
+        }
+    }
+    *p++ = '\0';
+
+    /* Bootloader ID */
+    strcpy(p, version_string);
+    mbi->boot_loader_name = ((size_t)p) - mbi_reloc_offset;
+    p += strlen(version_string) + 1;
+
+    /* Now, do all the loading, and boot it */
+    entry = load_kernel((char *)(mbi->cmdline + mbi_reloc_offset));
+    for (i=0; i<modules; i++) {
+        load_module(&(modp[i]), (char *)(modp[i].cmdline + mbi_reloc_offset));
+    }
+    boot(mbi_run_addr, entry);
+    
+    return 1;
+}
+
+/* 
+ *  EOF
+ */
diff -rNu syslinux-3.08-pre10/com32/modules/mboot.doc syslinux-3.08-pre10-mboot/com32/modules/mboot.doc
--- syslinux-3.08-pre10/com32/modules/mboot.doc	1970-01-01 01:00:00.000000000 +0100
+++ syslinux-3.08-pre10-mboot/com32/modules/mboot.doc	2005-05-06 16:39:18.000000000 +0100
@@ -0,0 +1,27 @@
+
+mboot.c32
+---------
+
+mboot.c32 is a 32-bit comboot module that allows SYSLINUX and its
+variants to load and boot kernels that use the Multiboot standard
+(e.g. the Xen virtual machine monitor, and the Fiasco and GNU Mach
+microkernels).
+
+To load a multiboot kernel and modules in SYSLINUX, put mboot.c32 (from
+com32/modules) in the boot directory, and load it as the "kernel" in the
+configuration file.  The command-line to pass to mboot.c32 is the kernel
+command-line, followed by all the module command lines, separated with
+'---'.  For example, to load a Xen VMM, xenlinux and an initrd:
+
+DEFAULT mboot.c32 xen.gz dom0_mem=15000 nosmp noacpi --- linux.gz console=tty0 root=/dev/hda1 --- initrd.img
+
+or, as a choice in a menu:
+
+LABEL Xen
+  KERNEL mboot.c32 
+  APPEND xen.gz dom0_mem=15000 nosmp noacpi --- linux.gz console=tty0 root=/dev/hda1 --- initrd.img
+
+mboot.c32 requires version 2.12 or later of SYSLINUX.
+
+Tim Deegan, May 2005
+


More information about the Syslinux mailing list