First attempt at a rewritten mboot module
authorH. Peter Anvin <hpa@zytor.com>
Sun, 26 Apr 2009 22:15:24 +0000 (15:15 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Sun, 26 Apr 2009 22:15:24 +0000 (15:15 -0700)
First attempt at rewriting the mboot module to use the Syslinux
shuffle APIs.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
com32/Makefile
com32/mboot/Makefile [new file with mode: 0644]
com32/mboot/apm.c [new file with mode: 0644]
com32/mboot/map.c [new file with mode: 0644]
com32/mboot/mb_header.h [new file with mode: 0644]
com32/mboot/mb_info.h [new file with mode: 0644]
com32/mboot/mboot.c [new file with mode: 0644]
com32/mboot/mboot.h [new file with mode: 0644]
com32/mboot/mem.c [new file with mode: 0644]

index 64049d0..4a58485 100644 (file)
@@ -1,3 +1,3 @@
-SUBDIRS = lib gpllib libutil modules menu samples rosh cmenu hdt
+SUBDIRS = lib gpllib libutil modules mboot menu samples rosh cmenu hdt
 all tidy dist clean spotless install:
        set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
diff --git a/com32/mboot/Makefile b/com32/mboot/Makefile
new file mode 100644 (file)
index 0000000..64c1d07
--- /dev/null
@@ -0,0 +1,46 @@
+## -----------------------------------------------------------------------
+##
+##   Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
+##   Copyright 2009 Intel Corporation; author: 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, Inc., 51 Franklin St, Fifth Floor,
+##   Boston MA 02110-1301, USA; either version 2 of the License, or
+##   (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+##
+## Multiboot module
+##
+
+topdir = ../..
+include ../MCONFIG
+
+LIBS      = ../libutil/libutil_com.a ../lib/libcom32.a $(LIBGCC)
+LNXLIBS           = ../libutil/libutil_lnx.a
+
+MODULES          = mboot.c32
+TESTFILES =
+
+OBJS = mboot.o map.o mem.o apm.o
+
+all: $(MODULES) $(TESTFILES)
+
+mboot.elf : $(OBJS) $(LIBS) $(C_LIBS)
+       $(LD) $(LDFLAGS) -o $@ $^
+
+tidy dist:
+       rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp
+
+clean: tidy
+       rm -f *.lnx
+
+spotless: clean
+       rm -f *.lss *.c32 *.com
+       rm -f *~ \#*
+
+install:
+
+-include .*.d
diff --git a/com32/mboot/apm.c b/com32/mboot/apm.c
new file mode 100644 (file)
index 0000000..2cfb7de
--- /dev/null
@@ -0,0 +1,87 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2009 Intel Corporation; author: 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, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ *   Based on code from the Linux kernel:
+ *
+ *   Copyright (C) 1991, 1992 Linus Torvalds
+ *   Copyright 2007 rPath, Inc. - All Rights Reserved
+ *
+ *   Original APM BIOS checking by Stephen Rothwell, May 1994
+ *   (sfr@canb.auug.org.au)
+ *
+ *   This file is part of the Linux kernel, and is made available under
+ *   the terms of the GNU General Public License version 2.
+ *
+ * ----------------------------------------------------------------------- */
+
+
+/*
+ * apm.c
+ *
+ * APM information for Multiboot
+ */
+
+#include "mboot.h"
+#include <com32.h>
+
+void mboot_apm(void)
+{
+  static struct apm_info apm;
+  com32sys_t ireg, oreg;
+
+  memset(&ireg, 0, sizeof ireg);
+
+  ireg.eax.w[0] = 0x5300;
+  __intcall(0x15, &ireg, &oreg);
+
+  if (oreg.eflags.l & EFLAGS_CF)
+    return;                    /* No APM BIOS */
+
+  if (oreg.ebx.w[0] != 0x504d)
+    return;                    /* No "PM" signature */
+
+  if (!(oreg.ecx.w[0] & 0x02))
+    return;                    /* 32 bits not supported */
+
+  /* Disconnect first, just in case */
+  ireg.eax.b[0] = 0x04;
+  __intcall(0x15, &ireg, &oreg);
+
+  /* 32-bit connect */
+  ireg.eax.b[0] = 0x03;
+  __intcall(0x15, &ireg, &oreg);
+
+  apm.cseg        = oreg.eax.w[0];
+  apm.offset      = oreg.ebx.l;
+  apm.cseg_16     = oreg.ecx.w[0];
+  apm.dseg_16     = oreg.edx.w[0];
+  apm.cseg_len    = oreg.esi.w[0];
+  apm.cseg_16_len = oreg.esi.w[1];
+  apm.dseg_16_len = oreg.edi.w[0];
+
+  /* Redo the installation check as the 32-bit connect;
+     some BIOSes return different flags this way... */
+
+  ireg.eax.b[0] = 0x00;
+  __intcall(0x15, &ireg, &oreg);
+
+  if ((oreg.eflags.l & EFLAGS_CF) || (oreg.ebx.w[0] != 0x504d)) {
+    /* Failure with 32-bit connect, try to disconect and ignore */
+    ireg.eax.b[0] = 0x04;
+    __intcall(0x15, &ireg, NULL);
+    return;
+  }
+
+  apm.version = oreg.eax.w[0];
+
+  mbinfo.apm_table = map_data(&apm, sizeof apm, 4, false);
+  if (mbinfo.apm_table)
+    mbinfo.flags |= MB_INFO_APM_TABLE;
+}
diff --git a/com32/mboot/map.c b/com32/mboot/map.c
new file mode 100644 (file)
index 0000000..c629079
--- /dev/null
@@ -0,0 +1,286 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * map.c
+ *
+ * Functions that deal with the memory map of various objects
+ */
+
+#include "mboot.h"
+
+static struct syslinux_movelist *ml = NULL;
+static struct syslinux_memmap *mmap = NULL, *amap = NULL;
+static struct multiboot_header *mbh;
+static addr_t mboot_high_water_mark = 0;
+
+/*
+ * Note: although there is no such thing in the spec, at least Xen makes
+ * assumptions as to where in the memory space Grub would have loaded
+ * certain things.  To support that, if "high" is set, then allocate this
+ * at an address strictly above any previous allocations.
+ *
+ * As a precaution, this also pads the data with zero up to the next
+ * alignment datum.
+ */
+addr_t map_data(const void *data, size_t len, int align, bool high)
+{
+  addr_t start = high ? mboot_high_water_mark : 0;
+  addr_t pad = (len+align-1) & ~(align-1);
+  addr_t xlen = len+pad;
+
+  if (syslinux_memmap_find(amap, SMT_FREE, &start, &xlen, align) ||
+      syslinux_add_memmap(&amap, start, len+pad, SMT_ALLOC) ||
+      syslinux_add_movelist(&ml, start, (addr_t)data, len) ||
+      (pad && syslinux_add_memmap(&mmap, start+len, pad, SMT_ZERO))) {
+    printf("Cannot map %zu bytes\n", len+pad);
+    return 0;
+  }
+
+  if (start+len+pad > mboot_high_water_mark)
+    mboot_high_water_mark = start+len+pad;
+
+  return start;
+}
+
+addr_t map_string(const char *string)
+{
+  if (!string)
+    return 0;
+  else
+    return map_data(string, strlen(string)+1, 4, true);
+}
+
+int map_image(void *ptr, size_t len)
+{
+  int mbh_len;
+  char *cptr = ptr;
+  Elf32_Ehdr *eh = ptr;
+  Elf32_Phdr *ph;
+  Elf32_Shdr *sh;
+  unsigned int i;
+  char *stack_frame = NULL;
+  uint32_t bad_flags;
+
+  regs.eax = MULTIBOOT_VALID;
+
+  /*
+   * Search for the multiboot header...
+   */
+  mbh_len = 0;
+  for (i = 0 ; i < MULTIBOOT_SEARCH ; i += 4) {
+    mbh = (struct multiboot_header *)((char *)ptr + i);
+    if (mbh->magic != MULTIBOOT_MAGIC)
+      continue;
+    if (mbh->magic + mbh->flags + mbh->checksum)
+      continue;
+    if (mbh->flags & MULTIBOOT_VIDEO_MODE)
+      mbh_len = 48;
+    else if (mbh->flags & MULTIBOOT_AOUT_KLUDGE)
+      mbh_len = 32;
+    else
+      mbh_len = 12;
+
+    if (i + mbh_len < len)
+      mbh_len = 0;             /* Invalid... */
+    else
+      break;                   /* Found something... */
+  }
+
+  if (mbh_len) {
+    bad_flags = mbh->flags & (MULTIBOOT_UNSUPPORTED|MULTIBOOT_VIDEO_MODE);
+    if (bad_flags) {
+      printf("Unsupported Multiboot flags set: %#x\n", bad_flags);
+      return -1;
+    }
+  }
+
+  /*
+   * Note: mmap is the memory map (containing free and zeroed regions)
+   * needed by syslinux_shuffle_boot_pm(); amap is a map where we keep
+   * track ourselves which target memory ranges have already been
+   * allocated.
+   */
+  if ( len < sizeof(Elf32_Ehdr) ||
+       memcmp(eh->e_ident, "\x7f""ELF\1\1\1", 6) ||
+       (eh->e_machine != EM_386 && eh->e_machine != EM_486 &&
+       eh->e_machine != EM_X86_64) ||
+       eh->e_version != EV_CURRENT ||
+       eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= len ||
+       eh->e_phentsize < sizeof(Elf32_Phdr) ||
+       !eh->e_phnum ||
+       eh->e_phoff+eh->e_phentsize*eh->e_phnum > len )
+    eh = NULL;                 /* No valid ELF header found */
+
+  mmap = syslinux_memory_map();
+  amap = syslinux_dup_memmap(mmap);
+  if (!mmap || !amap)
+    goto bail;
+
+#if DEBUG
+  dprintf("Initial memory map:\n");
+  syslinux_dump_memmap(stdout, mmap);
+#endif
+
+  /*
+   * Note: the Multiboot Specification implies that AOUT_KLUDGE should
+   * have precedence over the ELF header.  However, Grub disagrees, and
+   * Grub is "the reference bootloader" for the Multiboot Specification.
+   * This is insane, since it makes the AOUT_KLUDGE bit functionally
+   * useless, but at least Solaris apparently depends on this behavior.
+   */
+  if (eh) {
+    regs.eip = eh->e_entry;
+
+    ph = (Elf32_Phdr *)(cptr+eh->e_phoff);
+
+    for (i = 0; i < eh->e_phnum; i++) {
+      if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) {
+       /* This loads at p_paddr, which is arguably the correct semantics.
+          The SysV spec says that SysV loads at p_vaddr (and thus Linux does,
+          too); that is, however, a major brainfuckage in the spec. */
+       addr_t addr  = ph->p_paddr;
+       addr_t msize = ph->p_memsz;
+       addr_t dsize = min(msize, ph->p_filesz);
+
+       dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
+               addr, dsize, msize);
+
+       if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) {
+         printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
+                addr, msize);
+         goto bail;            /* Memory region unavailable */
+       }
+
+       /* Mark this region as allocated in the available map */
+       if (syslinux_add_memmap(&amap, addr, msize, SMT_ALLOC))
+         goto bail;
+
+       if (ph->p_filesz) {
+         /* Data present region.  Create a move entry for it. */
+         if (syslinux_add_movelist(&ml, addr, (addr_t)cptr+ph->p_offset,
+                                   dsize))
+           goto bail;
+       }
+       if (msize > dsize) {
+         /* Zero-filled region.  Mark as a zero region in the memory map. */
+         if (syslinux_add_memmap(&mmap, addr+dsize, msize-dsize, SMT_ZERO))
+           goto bail;
+       }
+       if (addr+msize > mboot_high_water_mark)
+         mboot_high_water_mark = addr+msize;
+      } else {
+       /* Ignore this program header */
+      }
+
+      ph = (Elf32_Phdr *)((char *)ph + eh->e_phentsize);
+    }
+
+    /* Load the ELF symbol table */
+    if (eh->e_shoff) {
+      addr_t addr, len;
+
+      sh = (Elf32_Shdr *)((char *)eh + eh->e_shoff);
+
+      len = eh->e_shentsize * eh->e_shnum;
+      addr = map_data(sh, len, 4096, true);
+      if (!addr)
+       goto bail;
+
+      mbinfo.flags |= MB_INFO_ELF_SHDR;
+      mbinfo.syms.e.addr  = addr;
+      mbinfo.syms.e.num   = eh->e_shnum;
+      mbinfo.syms.e.size  = eh->e_shentsize;
+      mbinfo.syms.e.shndx = eh->e_shstrndx;
+
+      for (i = 0; i < eh->e_shnum; i++) {
+       addr_t align;
+
+       if (!sh[i].sh_size)
+         continue;             /* Empty section */
+       if (sh[i].sh_flags & SHF_ALLOC)
+         continue;             /* SHF_ALLOC sections should have PHDRs */
+
+       align = sh[i].sh_addralign ? sh[i].sh_addralign : 0;
+       addr = map_data((char *)ptr + sh[i].sh_offset, sh[i].sh_size,
+                       align, true);
+       if (!addr)
+         goto bail;
+       sh[i].sh_addr = addr;
+      }
+    }
+  } else if (mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE)) {
+    /*
+     * a.out kludge thing...
+     */
+    char *data_ptr;
+    addr_t data_len, bss_len;
+
+    regs.eip = mbh->entry_addr;
+
+    data_ptr = (char *)mbh - (mbh->header_addr - mbh->load_addr);
+    data_len = mbh->load_end_addr - mbh->load_addr;
+    bss_len  = mbh->bss_end_addr - mbh->load_end_addr;
+
+    if (syslinux_memmap_type(amap, mbh->load_addr, data_len+bss_len)
+       != SMT_FREE) {
+      printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
+            mbh->load_addr, data_len+bss_len);
+      goto bail;               /* Memory region unavailable */
+    }
+    if (syslinux_add_memmap(&amap, mbh->load_addr,
+                           data_len+bss_len, SMT_ALLOC))
+      goto bail;
+    if (data_len)
+      if (syslinux_add_movelist(&ml, mbh->load_addr, (addr_t)data_ptr,
+                               data_len))
+       goto bail;
+    if (bss_len)
+      if (syslinux_add_memmap(&mmap, mbh->load_end_addr, bss_len, SMT_ZERO))
+       goto bail;
+    if (mbh->bss_end_addr > mboot_high_water_mark)
+      mboot_high_water_mark = mbh->bss_end_addr;
+  } else {
+    printf("Invalid Multiboot image: neither ELF header nor a.out kludge found\n");
+    goto bail;
+  }
+
+ bail:
+  if (stack_frame)
+    free(stack_frame);
+  syslinux_free_memmap(amap);
+  syslinux_free_memmap(mmap);
+  syslinux_free_movelist(ml);
+
+  return -1;
+}
+
+void mboot_run(int bootflags)
+{
+  syslinux_shuffle_boot_pm(ml, mmap, bootflags, &regs);
+}
diff --git a/com32/mboot/mb_header.h b/com32/mboot/mb_header.h
new file mode 100644 (file)
index 0000000..4215323
--- /dev/null
@@ -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 --git a/com32/mboot/mb_info.h b/com32/mboot/mb_info.h
new file mode 100644 (file)
index 0000000..ec12fe8
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ *  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.
+ */
+
+#include <inttypes.h>
+
+struct mod_list
+{
+  /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */
+  uint32_t mod_start;
+  uint32_t mod_end;
+
+  /* Module command line */
+  uint32_t cmdline;
+
+  /* padding to take it to 16 bytes (must be zero) */
+  uint32_t 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
+{
+  uint32_t size;
+  uint64_t BaseAddr;
+  uint64_t Length;
+  uint32_t Type;
+
+  /* unspecified optional padding... */
+} __attribute__((packed));
+
+/* usable memory "Type", all others are reserved.  */
+#define MB_ARD_MEMORY          1
+
+
+/* Drive Info structure.  */
+struct drive_info
+{
+  /* The size of this structure.  */
+  uint32_t size;
+
+  /* The BIOS drive number.  */
+  uint8_t  drive_number;
+
+  /* The access mode (see below).  */
+  uint8_t  drive_mode;
+
+  /* The BIOS geometry.  */
+  uint16_t drive_cylinders;
+  uint8_t  drive_heads;
+  uint8_t  drive_sectors;
+
+  /* The array of I/O ports used for the drive.  */
+  uint16_t drive_ports[0];
+};
+
+/* Drive Mode.  */
+#define MB_DI_CHS_MODE         0
+#define MB_DI_LBA_MODE         1
+
+
+/* APM BIOS info.  */
+struct apm_info
+{
+  uint16_t version;
+  uint16_t cseg;
+  uint32_t offset;
+  uint16_t cseg_16;
+  uint16_t dseg_16;
+  uint16_t cseg_len;
+  uint16_t cseg_16_len;
+  uint16_t 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 */
+  uint32_t flags;
+
+  /* Available memory from BIOS */
+  uint32_t mem_lower;
+  uint32_t mem_upper;
+
+  /* "root" partition */
+  uint32_t boot_device;
+
+  /* Kernel command line */
+  uint32_t cmdline;
+
+  /* Boot-Module list */
+  uint32_t mods_count;
+  uint32_t mods_addr;
+
+  union
+  {
+    struct
+    {
+      /* (a.out) Kernel symbol table info */
+      uint32_t tabsize;
+      uint32_t strsize;
+      uint32_t addr;
+      uint32_t pad;
+    }
+    a;
+
+    struct
+    {
+      /* (ELF) Kernel section header table */
+      uint32_t num;
+      uint32_t size;
+      uint32_t addr;
+      uint32_t shndx;
+    }
+    e;
+  }
+  syms;
+
+  /* Memory Mapping buffer */
+  uint32_t mmap_length;
+  uint32_t mmap_addr;
+
+  /* Drive Info buffer */
+  uint32_t drives_length;
+  uint32_t drives_addr;
+
+  /* ROM configuration table */
+  uint32_t config_table;
+
+  /* Boot Loader Name */
+  uint32_t boot_loader_name;
+
+  /* APM table */
+  uint32_t apm_table;
+
+  /* Video */
+  uint32_t vbe_control_info;
+  uint32_t vbe_mode_info;
+  uint16_t vbe_mode;
+  uint16_t vbe_interface_seg;
+  uint16_t vbe_interface_off;
+  uint16_t 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 --git a/com32/mboot/mboot.c b/com32/mboot/mboot.c
new file mode 100644 (file)
index 0000000..60bdaaa
--- /dev/null
@@ -0,0 +1,199 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * mboot.c
+ *
+ * Module to load a multiboot kernel
+ */
+
+#include "mboot.h"
+
+struct multiboot_info mbinfo;
+struct syslinux_pm_regs regs;
+
+struct module_data {
+  void *data;
+  size_t len;
+  const char *cmdline;
+};
+
+static int map_modules(struct module_data *modules, int nmodules)
+{
+  struct mod_list *mod_list;
+  addr_t map_list = 0;
+  size_t list_size = nmodules * sizeof *mod_list;
+  int i;
+
+  mod_list = malloc(list_size);
+  if (!mod_list) {
+    printf("Failed to allocate module list\n");
+    return -1;
+  }
+
+  for (i = 0; i < nmodules; i++) {
+    addr_t mod_map = 0;
+    addr_t cmd_map = 0;
+
+    cmd_map = map_string(modules[i].cmdline);
+
+    mod_map = map_data(modules[i].data, modules[i].len, 4096, true);
+    if (!mod_map) {
+      printf("Failed to map module (memory fragmentation issue?)\n");
+      return -1;
+    }
+    mod_list[i].mod_start = mod_map;
+    mod_list[i].mod_end = mod_map + modules[i].len;
+    mod_list[i].cmdline = cmd_map;
+    mod_list[i].pad = 0;
+  }
+
+  map_list = map_data(mod_list, list_size, 16, false);
+  if (!map_list) {
+    printf("Cannot map module list\n");
+    return -1;
+  }
+
+  mbinfo.flags |= MB_INFO_MODS;
+  mbinfo.mods_count = nmodules;
+  mbinfo.mods_addr = map_list;
+  return 0;
+}
+
+static int get_modules(char **argv, struct module_data **mdp)
+{
+  char **argp, **argx;
+  struct module_data *md, *mp;
+  int rv;
+  int module_count = 1;
+  int arglen;
+  const char module_separator[] = "---";
+
+  for (argp = argv; *argp; argp++) {
+    if (!strcmp(*argp, module_separator))
+      module_count++;
+  }
+
+  *mdp = md = malloc(module_count * sizeof(struct module_data));
+  if (!md) {
+    error("Out of memory!\n");
+    return -1;
+  }
+
+  mp = md;
+  argp = argv;
+  while (*argp) {
+    printf("Loading %s... ", *argp);
+    if (md == mp) {
+      /* Transparently decompress the primary image */
+      rv = zloadfile(*argp, &mp->data, &mp->len);
+    } else {
+      /* Leave decompressing auxilliary modules to the OS */
+      rv = loadfile(*argp, &mp->data, &mp->len);
+    }
+
+    if (rv) {
+      printf("failed!\n");
+      return -1;
+    }
+    printf("ok\n");
+
+    argp++;
+    arglen = 0;
+    for (argx = argp; *argx && strcmp(*argx, module_separator); argx++)
+      arglen += strlen(*argx)+1;
+
+    if (arglen == 0) {
+      mp->cmdline = NULL;
+    } else {
+      char *p;
+      md->cmdline = p = malloc(arglen);
+      for ( ; *argp && strcmp(*argp, module_separator); argp++) {
+       p = strpcpy(p, *argp);
+       *p++ = ' ';
+      }
+      *--p = '\0';
+    }
+    mp++;
+  }
+
+  return module_count;
+}
+
+int main(int argc, char *argv[])
+{
+  int nmodules;
+  struct module_data *modules;
+  bool keeppxe = false;
+
+  openconsole(&dev_null_r, &dev_stdcon_w);
+
+  if (argc < 2) {
+    error("Usage: mboot.c32 mboot_file args... [--- module args...]...\n");
+    return 1;
+  }
+
+  /* Load the files */
+  nmodules = get_modules(argv+1, &modules);
+  if (nmodules < 1)
+    return 1;                  /* Failure */
+
+  /*
+   * Map the primary image.  This should be done before mapping anything
+   * else, since it will have fixed address requirements.
+   */
+  if (map_image(modules[0].data, modules[0].len))
+    return 1;
+
+  /* Map auxilliary images */
+  if (nmodules > 1) {
+    if (map_modules(modules+1, nmodules-1))
+      return 1;
+  }
+
+  /* Map the mbinfo structure */
+  regs.ebx = map_data(&mbinfo, sizeof mbinfo, 4, false);
+  if (!regs.ebx)
+    return 1;
+
+  /* Map the primary command line */
+  if (modules[0].cmdline) {
+    mbinfo.cmdline = map_string(modules[0].cmdline);
+    if (mbinfo.cmdline)
+      mbinfo.flags |= MB_INFO_CMDLINE;
+  }
+
+  /* Add auxilliary information */
+  mboot_make_memmap();
+  mboot_apm();
+
+  /* Run it */
+  mboot_run(keeppxe ? 3 : 0);
+  error("mboot.c32: boot failed\n");
+  return 1;
+}
diff --git a/com32/mboot/mboot.h b/com32/mboot/mboot.h
new file mode 100644 (file)
index 0000000..bf5d81c
--- /dev/null
@@ -0,0 +1,85 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * mboot.h
+ *
+ * Module to load a multiboot kernel
+ */
+
+#ifndef MBOOT_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <minmax.h>
+#include <sys/stat.h>
+#include <elf.h>
+#include <console.h>
+
+#include <syslinux/loadfile.h>
+#include <syslinux/movebits.h>
+#include <syslinux/bootpm.h>
+
+#include "mb_header.h"
+#include "mb_info.h"
+
+#define DEBUG 0
+#if DEBUG
+# define dprintf printf
+#else
+# define dprintf(f, ...) ((void)0)
+#endif
+
+static inline void error(const char *msg)
+{
+  fputs(msg, stderr);
+}
+
+/* mboot.c */
+extern struct multiboot_info mbinfo;
+extern struct syslinux_pm_regs regs;
+
+/* map.c */
+addr_t map_data(const void *data, size_t len, int align, bool high);
+addr_t map_string(const char *string);
+int map_image(void *ptr, size_t len);
+void mboot_run(int bootflags);
+
+/* mem.c */
+void mboot_make_memmap(void);
+
+/* apm.c */
+void mboot_apm(void);
+
+#endif /* MBOOT_H */
diff --git a/com32/mboot/mem.c b/com32/mboot/mem.c
new file mode 100644 (file)
index 0000000..ecbf5c1
--- /dev/null
@@ -0,0 +1,215 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009 H. Peter Anvin - All Rights Reserved
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * mem.c
+ *
+ * Obtain a memory map for a Multiboot OS
+ *
+ * This differs from the libcom32 memory map functions in that it doesn't
+ * attempt to filter out memory regions...
+ */
+
+#include "mboot.h"
+#include <com32.h>
+
+struct e820_entry {
+  uint64_t start;
+  uint64_t len;
+  uint32_t type;
+  uint32_t extattr;
+};
+
+#define RANGE_ALLOC_BLOCK      128
+
+static int mboot_scan_memory(struct AddrRangeDesc **ardp, uint32_t *dosmem)
+{
+  com32sys_t ireg, oreg;
+  struct e820_entry *e820buf = __com32.cs_bounce;
+  struct AddrRangeDesc *ard;
+  size_t ard_count, ard_space;
+
+  /* Use INT 12h to get DOS memory */
+  __intcall(0x12, &__com32_zero_regs, &oreg);
+  *dosmem = oreg.eax.w[0] << 10;
+  if (*dosmem < 32*1024 || *dosmem > 640*1024) {
+    /* INT 12h reports nonsense... now what? */
+    uint16_t ebda_seg = *(uint16_t *)0x40e;
+    if (ebda_seg >= 0x8000 && ebda_seg < 0xa000)
+      *dosmem = ebda_seg << 4;
+    else
+      *dosmem = 640*1024;      /* Hope for the best... */
+  }
+
+  /* Allocate initial space */
+  *ardp = ard = malloc(RANGE_ALLOC_BLOCK * sizeof *ard);
+  if (!ard)
+    return 0;
+
+  ard_count = 0;
+  ard_space = RANGE_ALLOC_BLOCK;
+
+  /* First try INT 15h AX=E820h */
+  memset(&ireg, 0, sizeof ireg);
+  ireg.eax.l    = 0xe820;
+  ireg.edx.l    = 0x534d4150;
+  /* ireg.ebx.l    = 0; */
+  ireg.ecx.l    = sizeof(*e820buf);
+  ireg.es       = SEG(e820buf);
+  ireg.edi.w[0] = OFFS(e820buf);
+  memset(e820buf, 0, sizeof *e820buf);
+  /* Set this in case the BIOS doesn't, but doesn't change %ecx to match. */
+  e820buf->extattr = 1;
+
+  do {
+    __intcall(0x15, &ireg, &oreg);
+
+    if ((oreg.eflags.l & EFLAGS_CF) ||
+       (oreg.eax.l != 0x534d4150) ||
+       (oreg.ecx.l < 20))
+      break;
+
+    if (oreg.ecx.l < 24)
+      e820buf->extattr = 1;    /* Enabled, normal */
+
+    if (!(e820buf->extattr & 1))
+      continue;
+
+    if (ard_count >= ard_space) {
+      ard_space += RANGE_ALLOC_BLOCK;
+      *ardp = ard = realloc(ard, ard_space*sizeof *ard);
+      if (!ard)
+       return ard_count;
+    }
+
+    ard[ard_count].size     = 20;
+    ard[ard_count].BaseAddr = e820buf->start;
+    ard[ard_count].Length   = e820buf->len;
+    ard[ard_count].Type     = e820buf->type;
+    ard_count++;
+
+    ireg.ebx.l = oreg.ebx.l;
+  } while (oreg.ebx.l);
+
+  if (ard_count)
+    return ard_count;
+
+  ard[0].size = 20;
+  ard[0].BaseAddr = 0;
+  ard[0].Length = *dosmem << 10;
+  ard[0].Type = 1;
+
+  /* Next try INT 15h AX=E801h */
+  ireg.eax.w[0] = 0xe801;
+  __intcall(0x15, &ireg, &oreg);
+
+  if (!(oreg.eflags.l & EFLAGS_CF) && oreg.ecx.w[0]) {
+    ard[1].size = 20;
+    ard[1].BaseAddr = 1 << 20;
+    ard[1].Length = oreg.ecx.w[0] << 10;
+    ard[1].Type = 1;
+
+    if (oreg.edx.w[0]) {
+      ard[2].size = 20;
+      ard[2].BaseAddr = 16 << 20;
+      ard[2].Length = oreg.edx.w[0] << 16;
+      ard[2].Type = 1;
+      return 3;
+    } else {
+      return 2;
+    }
+  }
+
+  /* Finally try INT 15h AH=88h */
+  ireg.eax.w[0] = 0x8800;
+  if (!(oreg.eflags.l & EFLAGS_CF) && oreg.eax.w[0]) {
+    ard[1].size = 20;
+    ard[1].BaseAddr = 1 << 20;
+    ard[1].Length = oreg.ecx.w[0] << 10;
+    ard[1].Type = 1;
+    return 2;
+  }
+
+  return 1;                    /* ... problematic ... */
+}
+
+void mboot_make_memmap(void)
+{
+  int i, nmap;
+  struct AddrRangeDesc *ard;
+  uint32_t lowmem, highmem;
+  uint32_t highrsvd;
+
+  /* Always report DOS memory as "lowmem", this may be overly conservative
+     (e.g. if we're dropping PXE), but it should be *safe*... */
+
+  nmap = mboot_scan_memory(&ard, &lowmem);
+
+  highmem  = 0x100000;
+  highrsvd = 0xfff00000;
+
+ again:
+  for (i = 0; i < nmap; i++) {
+    uint64_t start, end;
+
+    start = ard[i].BaseAddr;
+    end = start + ard[i].Length;
+
+    if (end < start)
+      end = ~0ULL;
+
+    if (start & 0xffffffff00000000ULL)
+      continue;                        /* Not interested in 64-bit memory */
+
+    if (start < highmem)
+      start = highmem;
+
+    if (end <= start)
+      continue;
+
+    if (ard[i].Type == 1 && start == highmem) {
+      highmem = end;
+      goto again;
+    } else if (ard[i].Type != 1 && start < highrsvd)
+      highrsvd = start;
+  }
+
+  if (highmem > highrsvd)
+    highmem = highrsvd;
+
+  mbinfo.mem_lower = lowmem >> 10;
+  mbinfo.mem_upper = (highmem - 0x100000) >> 10;
+  mbinfo.flags |= MB_INFO_MEMORY;
+
+  /* XXX: Should this be +4? */
+  mbinfo.mmap_addr = map_data(ard, nmap*sizeof *ard, 4, false);
+  if (mbinfo.mmap_addr) {
+    mbinfo.mmap_length = nmap*sizeof *ard;
+    mbinfo.flags |= MB_INFO_MEM_MAP;
+  }
+}