Finish the shuffle and boot interface, and add an ELF loading module.
authorH. Peter Anvin <hpa@zytor.com>
Thu, 15 Mar 2007 02:06:36 +0000 (19:06 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Thu, 15 Mar 2007 02:06:36 +0000 (19:06 -0700)
The shuffle and boot interface, including the library support, should now
work as advertised.  Add an ELF-loading module as a demo, and it's probably
useful for someone, too.

15 files changed:
NEWS
com32/include/syslinux/movebits.h
com32/lib/Makefile
com32/lib/syslinux/addlist.c
com32/lib/syslinux/dump_mmap.c [new file with mode: 0644]
com32/lib/syslinux/dump_movelist.c [new file with mode: 0644]
com32/lib/syslinux/freelist.c
com32/lib/syslinux/memmap.c
com32/lib/syslinux/movebits.c
com32/lib/syslinux/shuffle.c
com32/lib/syslinux/shuffle_pm.c
com32/lib/syslinux/shuffle_rm.c
com32/lib/syslinux/zonelist.c [new file with mode: 0644]
com32/modules/Makefile
com32/modules/elf.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index abff596..78cd027 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,13 @@ Changes in 3.40:
          internationalization of menu messages.
        * A new feature, TEXT HELP, allows the administrator to set
          a multi-line help message for individual selections.
+       * Fix API call 0x0012 (Shuffle and boot.)
+       * New API call 0x001a "Shuffle and boot to flat protected
+         mode."
+       * Introduce a library interface for loading arbitrary binary
+         formats with relatively easily understood code.  See
+         the elf.c32 module for an example on how to use it.
+       * New module "elf.c32", to load a protected-mode ELF kernel.
 
 Changes in 3.36:
        * MEMDISK: Disable EDD by default on floppy disks.  EDD can be
index 95adf6e..3a03c62 100644 (file)
@@ -2,10 +2,14 @@
 #define _SYSLINUX_MOVEBITS_H
 
 #include <inttypes.h>
-#include <setjmp.h>
+#include <stdio.h>
 
 typedef uint32_t addr_t;
 
+/*
+ * A syslinux_movelist is a linked list of move operations.  The ordering
+ * is important, so no sorting requirement can be imposed.
+ */
 struct syslinux_movelist {
   addr_t dst;
   addr_t src;
@@ -13,6 +17,32 @@ struct syslinux_movelist {
   struct syslinux_movelist *next;
 };
 
+/*
+ * A syslinux_memmap is a sorted, linked list of memory regions,
+ * guaranteed to satisfy the constraint that no adjacent zones have
+ * the same type.  Undefined memory ranges are represented with entries;
+ * they have type SMT_UNDEFINED.
+ *
+ * Note that there is no length field.  The length of a region is obtained
+ * by looking at the start of the next entry in the chain.
+ */
+enum syslinux_memmap_types {
+  SMT_ERROR = -2,              /* Internal error token */
+  SMT_END = -1,                        /* End of list */
+  SMT_UNDEFINED = 0,           /* Unknown range */
+  SMT_FREE = 1,                        /* Available memory */
+  SMT_RESERVED,                        /* Unusable memory */
+  SMT_ALLOC,                   /* Memory allocated by user */
+  SMT_ZERO,                    /* Memory that should be zeroed */
+};
+
+struct syslinux_memmap {
+  addr_t start;
+  enum syslinux_memmap_types type;
+  struct syslinux_memmap *next;
+};
+
+
 struct syslinux_pm_regs {
   uint32_t eax, ecx, edx, ebx;
   uint32_t esp, ebp, esi, edi;
@@ -20,25 +50,48 @@ struct syslinux_pm_regs {
 };
 
 /*
- * moves is computed from "frags" and "freemem".  "space" lists
- * free memory areas at our disposal, and is (src, cnt) only.
+ * moves is computed from "fraglist" and "memmap".  Areas that are
+ * to be zeroed should be marked as such in the memmap, not in the
+ * fraglist.
  */
 
-int syslinux_compute_movelist(struct syslinux_movelist **,
-                             struct syslinux_movelist *,
-                             struct syslinux_movelist *);
+int syslinux_compute_movelist(struct syslinux_movelist **movelist,
+                             struct syslinux_movelist *fraglist,
+                             struct syslinux_memmap *memmap);
 
-struct syslinux_movelist *syslinux_memory_map(void);
+struct syslinux_memmap *syslinux_memory_map(void);
 void syslinux_free_movelist(struct syslinux_movelist *);
 int syslinux_add_movelist(struct syslinux_movelist **,
                          addr_t dst, addr_t src, addr_t len);
-int syslinux_prepare_shuffle(struct syslinux_movelist *fraglist);
+int syslinux_allocate_from_list(struct syslinux_movelist **freelist,
+                               addr_t dst, addr_t len);
+int syslinux_prepare_shuffle(struct syslinux_movelist *fraglist,
+                            struct syslinux_memmap *memmap);
 int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist,
+                            struct syslinux_memmap *memmap,
                             uint16_t bootflags,
                             uint32_t edx, uint32_t esi, uint16_t ds,
                             uint16_t cs, uint16_t ip);
 int syslinux_shuffle_boot_pm(struct syslinux_movelist *fraglist,
+                            struct syslinux_memmap *memmap,
                             uint16_t bootflags,
                             struct syslinux_pm_regs *regs);
 
+/* Operatons on struct syslinux_memmap */
+struct syslinux_memmap *syslinux_init_memmap(void);
+int syslinux_add_memmap(struct syslinux_memmap **list,
+                       addr_t start, addr_t len,
+                       enum syslinux_memmap_types type);
+enum syslinux_memmap_types syslinux_memmap_type(struct syslinux_memmap *list,
+                                               addr_t start, addr_t len);
+int syslinux_memmap_largest(struct syslinux_memmap *list,
+                           enum syslinux_memmap_types type,
+                           addr_t *start, addr_t *len);
+void syslinux_free_memmap(struct syslinux_memmap *list);
+struct syslinux_memmap *syslinux_dup_memmap(struct syslinux_memmap *list);
+
+/* Debugging functions */
+void syslinux_dump_movelist(FILE *file, struct syslinux_movelist *ml);
+void syslinux_dump_memmap(FILE *file, struct syslinux_memmap *memmap);
+
 #endif /* _SYSLINUX_MOVEBITS_H */
index 1bbfade..158e2dc 100644 (file)
@@ -62,7 +62,12 @@ LIBOBJS = \
        jpeg/rgb24.o jpeg/bgr24.o jpeg/yuv420p.o jpeg/grey.o            \
        jpeg/rgba32.o jpeg/bgra32.o                                     \
        \
-       sys/x86_init_fpu.o math/pow.o math/strtod.o
+       sys/x86_init_fpu.o math/pow.o math/strtod.o                     \
+       \
+       syslinux/addlist.o syslinux/freelist.o syslinux/memmap.o        \
+       syslinux/movebits.o syslinux/shuffle.o syslinux/shuffle_pm.o    \
+       syslinux/shuffle_rm.o syslinux/zonelist.o                       \
+       syslinux/dump_mmap.o syslinux/dump_movelist.o
 
 BINDIR   = /usr/bin
 LIBDIR   = /usr/lib
index d544de9..faa80c1 100644 (file)
@@ -25,6 +25,9 @@
  *
  * ----------------------------------------------------------------------- */
 
+#include <stdlib.h>
+#include <syslinux/movebits.h>
+
 int syslinux_add_movelist(struct syslinux_movelist **list,
                      addr_t dst, addr_t src, addr_t len)
 {
diff --git a/com32/lib/syslinux/dump_mmap.c b/com32/lib/syslinux/dump_mmap.c
new file mode 100644 (file)
index 0000000..b25b33f
--- /dev/null
@@ -0,0 +1,48 @@
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2007 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * dump_mmap.c
+ *
+ * Writes a syslinux_memmap out to a specified file.  This is
+ * intended for debugging.
+ */
+
+#include <stdio.h>
+#include <syslinux/movebits.h>
+
+void syslinux_dump_memmap(FILE *file, struct syslinux_memmap *memmap)
+{
+  fprintf(file, "%10s %10s %10s\n"
+         "--------------------------------\n",
+         "Start", "Length", "Type");
+  while (memmap->type != SMT_END) {
+    fprintf(file, "0x%08x 0x%08x %10d\n", memmap->start,
+           memmap->next->start - memmap->start, memmap->type);
+    memmap = memmap->next;
+  }
+}
diff --git a/com32/lib/syslinux/dump_movelist.c b/com32/lib/syslinux/dump_movelist.c
new file mode 100644 (file)
index 0000000..b4ff423
--- /dev/null
@@ -0,0 +1,48 @@
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2007 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * dump_movelist.c
+ *
+ * Writes a syslinux_movelist out to a specified file.  This is
+ * intended for debugging.
+ */
+
+#include <stdio.h>
+#include <syslinux/movebits.h>
+
+void syslinux_dump_movelist(FILE *file, struct syslinux_movelist *ml)
+{
+  fprintf(file, "%10s %10s %10s\n"
+         "--------------------------------\n",
+         "Dest", "Src", "Length");
+  while (ml) {
+    fprintf(file, "0x%08x 0x%08x 0x%08x\n", ml->dst, ml->src, ml->len);
+    ml = ml->next;
+  }
+}
+
index 4b7d1f9..b37dc6e 100644 (file)
@@ -31,7 +31,8 @@
  * Frees a syslinux_movelist
  */
 
-#include <syslinux/movelist.h>
+#include <stdlib.h>
+#include <syslinux/movebits.h>
 
 void syslinux_free_movelist(struct syslinux_movelist *list)
 {
index 05b2edd..45a5efd 100644 (file)
 #include <setjmp.h>
 #include <string.h>
 
+#include <com32.h>
 #include <syslinux/movebits.h>
 
-static struct movelist *
-new_movelist(addr_t src, addr_t len)
-{
-  struct movelist *ml = malloc(sizeof(struct movelist));
-
-  if ( !ml )
-    return NULL;
-
-  ml->dst = (addr_t)0;
-  ml->src = src;
-  ml->len = len;
-  ml->next = NULL;
-
-  return ml;
-}
-
 struct e820_entry {
   uint64_t start;
   uint64_t len;
   uint32_t type;
 };
 
-struct movelist *syslinux_memory_map(void)
+struct syslinux_memmap *syslinux_memory_map(void)
 {
   static com32sys_t ireg, zireg;
   com32sys_t oreg;
-  struct movelist *ml = NULL;
-  struct movelist **mlp = &ml;
-  struct movelist *me;
   struct e820_entry *e820buf = __com32.cs_bounce;
   uint64_t start, len, maxlen;
   int memfound = 0;
+  struct syslinux_memmap *mmap;
+  enum syslinux_memmap_types type;
+
+  mmap = syslinux_init_memmap();
+  if (!mmap)
+    goto bail;
 
   /* Use INT 12h to get DOS memory above 0x7c00 */
   __intcall(0x12, &zireg, &oreg);
   if (oreg.eax.w[0] > 31 && oreg.eax.w[0] <= 640) {
     addr_t dosmem = (oreg.eax.w[0] << 10) - 0x7c00;
-    me = new_movelist(0, 0x7c00, dosmem);
-    if (!me)
+    if (syslinux_add_memmap(&mmap, 0x7c00, dosmem, SMT_FREE))
       goto bail;
-    *mlp = me;
-    mlp = &me->next;
   }
 
   /* First try INT 15h AX=E820h */
@@ -101,82 +86,68 @@ struct movelist *syslinux_memory_map(void)
        (oreg.ecx.l < 20))
       break;
 
-    if (e820buf.type == 1) {
-      start = e820buf.start;
-      len = e820buf.len;
-
-      if (start < 0x100000000ULL) {
-       /* Don't rely on E820 being valid for low memory.  Doing so
-          could mean stuff like overwriting the PXE stack even when
-          using "keeppxe", etc. */
-       if (start < 0x100000ULL) {
-         if (len > 0x100000ULL-start)
-           len -= 0x100000ULL-start;
-         else
-           len = 0;
-         start = 0x100000ULL;
-       }
-
-       maxlen = 0x100000000ULL-start;
-       if (len > maxlen)
-         len = maxlen;
-
-       if (len) {
-         me = new_movelist(0, (addr_t)start, (addr_t)len);
-         if (!me)
-           goto bail;
-         *mlp = me;
-         mlp = &me->next;
-         memfound = 1;
-       }
+    type = e820buf->type == 1 ? SMT_FREE : SMT_RESERVED;
+    start = e820buf->start;
+    len = e820buf->len;
+
+    if (start < 0x100000000ULL) {
+      /* Don't rely on E820 being valid for low memory.  Doing so
+        could mean stuff like overwriting the PXE stack even when
+        using "keeppxe", etc. */
+      if (start < 0x100000ULL) {
+       if (len > 0x100000ULL-start)
+         len -= 0x100000ULL-start;
+       else
+         len = 0;
+       start = 0x100000ULL;
+      }
+      
+      maxlen = 0x100000000ULL-start;
+      if (len > maxlen)
+       len = maxlen;
+      
+      if (len) {
+       if (syslinux_add_memmap(&mmap, (addr_t)start, (addr_t)len, type))
+         goto bail;
+       memfound = 1;
       }
     }
-
+    
     ireg.ebx.l = oreg.ebx.l;
   } while (oreg.ebx.l);
 
   if (memfound)
-    return ml;
+    return mmap;
 
   /* 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]) {
-    me = new_movelist(0, (addr_t)1 << 20, oreg.ecx.w[0] << 10);
-    if (!me)
+    if (syslinux_add_memmap(&mmap, (addr_t)1 << 20, oreg.ecx.w[0] << 10,
+                           SMT_FREE))
       goto bail;
-    *mlp = me;
-    mlp = &me->next;
 
     if (oreg.edx.w[0]) {
-      me = new_movelist(0, (addr_t)16 << 20, oreg.edx.w[0] << 16);
-      if (!me)
+      if (syslinux_add_memmap(&mmap, (addr_t)16 << 20, oreg.edx.w[0] << 16,
+                             SMT_FREE))
        goto bail;
-      *mlp = me;
-      mlp = &me->next;
     }
 
-    return ml;
+    return mmap;
   }
 
   /* Finally try INT 15h AH=88h */
   ireg.eax.w[0] = 0x8800;
   if (!(oreg.eflags.l & EFLAGS_CF) && oreg.eax.w[0]) {
-    me = new_movelist(0, (addr_t)1 << 20, oreg.eax.w[0] << 10);
-    if (!me)
+    if (syslinux_add_memmap(&mmap, (addr_t)1 << 20, oreg.ecx.w[0] << 10,
+                           SMT_FREE))
       goto bail;
-    *mlp = me;
-    mlp = &me->next;
   }
-
-  return ml;
+  
+  return mmap;
 
  bail:
-  while (ml) {
-    me = ml;
-    ml = ml->next;
-    free(ml);
-  }
+  syslinux_free_memmap(mmap);
   return NULL;
 }
index 4689508..17e0ade 100644 (file)
  *
  * Utility function to take a list of memory areas to shuffle and
  * convert it to a set of shuffle operations.
+ *
+ * Note: a lot of the functions in this file deal with "parent pointers",
+ * which are pointers to a pointer to a list, or part of the list.
+ * They can be pointers to a variable holding the list root pointer,
+ * or pointers to a next field of a previous entry.
  */
 
 #include <assert.h>
 #include <syslinux/movebits.h>
 
 #ifdef TEST
+# define DEBUG 1
+#else
+# define DEBUG 0
+#endif
+
+#if DEBUG
+# include <stdio.h>
 # define dprintf printf
 #else
 # define dprintf(...) ((void)0)
@@ -68,6 +80,10 @@ new_movelist(addr_t dst, addr_t src, addr_t len)
   return ml;
 }
 
+/*
+ * Take a chunk, entirely confined in **parentptr, and split it off so that
+ * it has its own structure.
+ */
 static struct syslinux_movelist **
 split_movelist(addr_t start, addr_t len, struct syslinux_movelist **parentptr)
 {
@@ -89,6 +105,7 @@ split_movelist(addr_t start, addr_t len, struct syslinux_movelist **parentptr)
     ml = m;                    /* Continue processing the new node */
   }
 
+  /* Split off the end */
   if ( ml->len > len ) {
     addr_t l = ml->len - len;
 
@@ -101,6 +118,7 @@ split_movelist(addr_t start, addr_t len, struct syslinux_movelist **parentptr)
   return parentptr;
 }
 
+#if 0
 static void
 delete_movelist(struct syslinux_movelist **parentptr)
 {
@@ -108,6 +126,7 @@ delete_movelist(struct syslinux_movelist **parentptr)
   *parentptr = o->next;
   free(o);
 }
+#endif
 
 /*
  * Scan the freelist looking for a particular chunk of memory
@@ -187,6 +206,7 @@ allocate_from(addr_t start, addr_t len, struct syslinux_movelist **parentptr)
   free(c);
 }
 
+#if 0
 /*
  * Remove any chunk from the freelist which is already
  * claimed by source data.  This somewhat frivolously
@@ -206,6 +226,7 @@ tidy_freelist(struct syslinux_movelist **frags,
     frags = &f->next;
   }
 }
+#endif
 
 /*
  * moves is computed from "frags" and "freemem".  "space" lists
@@ -215,19 +236,75 @@ tidy_freelist(struct syslinux_movelist **frags,
 int
 syslinux_compute_movelist(struct syslinux_movelist **moves,
                          struct syslinux_movelist *frags,
-                         struct syslinux_movelist *space)
+                         struct syslinux_memmap *memmap)
 {
-  struct syslinux_movelist *mv;
+  struct syslinux_memmap *mmap = NULL, *mm;
+  struct syslinux_movelist *mv, *space;
   struct syslinux_movelist *f, **fp, **ep;
   struct syslinux_movelist *o, **op;
-  *moves = NULL;
   addr_t needbase, needlen, copysrc, copydst, copylen;
   addr_t freebase, freelen;
+  addr_t mstart;
+  int m_ok;
+  int rv = -1;
+
+  dprintf("entering syslinux_compute_movelist()...\n");
 
-  if ( setjmp(new_movelist_bail) )
-    return -1;                 /* malloc() failed */
+  if (setjmp(new_movelist_bail))
+    goto bail;
+
+  *moves = NULL;
 
-  tidy_freelist(&frags, &space);
+  /* Mark any memory that's in use by source material busy in the memory map */
+  mmap = syslinux_dup_memmap(memmap);
+  if (!mmap)
+    goto bail;
+
+#if DEBUG
+  dprintf("Initial memory map:\n");
+  syslinux_dump_memmap(stdout, mmap);
+#endif
+
+  for (f = frags; f; f = f->next) {
+    if (syslinux_add_memmap(&mmap, f->src, f->len, SMT_ALLOC))
+      goto bail;
+  }
+
+#if DEBUG
+  dprintf("Adjusted memory map:\n");
+  syslinux_dump_memmap(stdout, mmap);
+#endif
+
+  /* Compute the freelist in movelist format. */
+  space = NULL;
+  ep = &space;
+  mstart = -1;
+  m_ok = 0;
+
+  /* Yes, we really do want to run through the loop on SMT_END */
+  for (mm = mmap; mm; mm = mm->next) {
+    /* We can safely use to-be-zeroed memory as a scratch area. */
+    if (mmap->type == SMT_FREE || mmap->type == SMT_ZERO) {
+      if (!m_ok) {
+       m_ok = 1;
+       mstart = mmap->start;
+      }
+    } else {
+      if (m_ok) {
+       f = new_movelist(0, mstart, mmap->start - mstart);
+       *ep = f;
+       ep = &f->next;
+       m_ok = 0;
+      }
+    }
+
+    mmap = mmap->next;
+  }
+
+#if DEBUG
+  dprintf("Computed free list:\n");
+  syslinux_dump_movelist(stdout, space);
+#endif
 
   for ( fp = &frags, f = *fp ; f ; fp = &f->next, f = *fp ) {
     dprintf("@: 0x%08x bytes at 0x%08x -> 0x%08x\n",
@@ -333,8 +410,6 @@ syslinux_compute_movelist(struct syslinux_movelist **moves,
 
     if ( copylen < needlen ) {
       /* Didn't get all we wanted, so we have to split the chunk */
-      addr_t l;
-
       fp = split_movelist(f->src, copylen+(needbase-f->dst), fp);
       f = *fp;
     }
@@ -362,7 +437,11 @@ syslinux_compute_movelist(struct syslinux_movelist **moves,
     space = mv;
   }
 
-  return 0;
+  rv = 0;
+ bail:  
+  if (mmap)
+    syslinux_free_memmap(mmap);
+  return rv;
 }
 
 #ifdef TEST
@@ -403,6 +482,6 @@ int main(int argc, char *argv[])
     }
     return 0;
   }
-}
+ }
 
 #endif /* TEST */
index ad6e7d0..2271954 100644 (file)
@@ -42,23 +42,21 @@ struct shuffle_descriptor {
   uint32_t dst, src, len;
 };
 
-int syslinux_prepare_shuffle(struct syslinux_movelist *fraglist)
+int syslinux_prepare_shuffle(struct syslinux_movelist *fraglist,
+                            struct syslinux_memmap *memmap)
 {
-  struct syslinux_movelist *moves = NULL, *freelist = NULL, *mp;
-  int n;
+  struct syslinux_movelist *moves = NULL, *mp;
+  struct syslinux_memmap *ml;
   struct shuffle_descriptor *dp;
   int np, rv = -1;
 
-  freelist = syslinux_memory_map();
-  if (!freelist)
-    goto bail;
-
-  if (syslinux_compute_movelist(&moves, fraglist, freelist))
+  if (syslinux_compute_movelist(&moves, fraglist, memmap))
     goto bail;
 
   dp = __com32.cs_bounce;
   np = 0;
 
+  /* Copy the move sequence into the bounce buffer */
   for (mp = moves; mp; mp = mp->next) {
     if (np >= 65536/12)
       goto bail;               /* Way too many descriptors... */
@@ -66,14 +64,26 @@ int syslinux_prepare_shuffle(struct syslinux_movelist *fraglist)
     dp->dst = mp->dst;
     dp->src = mp->src;
     dp->len = mp->len;
-    np++;
+    dp++; np++;
+  }
+
+  /* Copy any zeroing operations into the bounce buffer */
+  for (ml = memmap; ml->type != SMT_END; ml = ml->next) {
+    if (ml->type == SMT_ZERO) {
+      if (np >= 65536/12)
+       goto bail;
+
+      dp->dst = ml->start;
+      dp->src = (addr_t)-1;    /* bzero this region */
+      dp->len = ml->next->start - ml->start;
+      dp++; np++;
+    }
   }
 
   rv = np;
 
  bail:
-  if (freelist)
-    syslinux_free_movelist(freelist);
+  /* This is safe only because free() doesn't use the bounce buffer!!!! */
   if (moves)
     syslinux_free_movelist(moves);
 
index 45235e0..f8af66a 100644 (file)
@@ -38,6 +38,7 @@
 #include <syslinux/movebits.h>
 
 int syslinux_shuffle_boot_pm(struct syslinux_movelist *fraglist,
+                            struct syslinux_memmap *memmap,
                             uint16_t bootflags,
                             struct syslinux_pm_regs *regs)
 {
@@ -45,7 +46,7 @@ int syslinux_shuffle_boot_pm(struct syslinux_movelist *fraglist,
   com32sys_t ireg;
   char *regbuf;
 
-  nd = syslinux_prepare_shuffle(fraglist);
+  nd = syslinux_prepare_shuffle(fraglist, memmap);
   if (nd < 0)
     return -1;
   
index a72d87e..e676e1d 100644 (file)
@@ -38,6 +38,7 @@
 #include <syslinux/movebits.h>
 
 int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist,
+                            struct syslinux_memmap *memmap,
                             uint16_t bootflags,
                             uint32_t edx, uint32_t esi, uint16_t ds,
                             uint16_t cs, uint16_t ip)
@@ -45,7 +46,7 @@ int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist,
   int nd;
   com32sys_t ireg;
 
-  nd = syslinux_prepare_shuffle(fraglist);
+  nd = syslinux_prepare_shuffle(fraglist, memmap);
   if (nd < 0)
     return -1;
   
diff --git a/com32/lib/syslinux/zonelist.c b/com32/lib/syslinux/zonelist.c
new file mode 100644 (file)
index 0000000..c694800
--- /dev/null
@@ -0,0 +1,242 @@
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2007 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * zonelist.c
+ *
+ * Deal with syslinux_memmap's, which are data structures designed to
+ * hold memory maps.  A zonelist is a sorted linked list of memory
+ * ranges, with the guarantee that no two adjacent blocks have the
+ * same range type.  Additionally, all unspecified memory have a range
+ * type of zero.
+ */
+
+#include <stdlib.h>
+#include <syslinux/movebits.h>
+
+/*
+ * Create an empty syslinux_memmap list.
+ */
+struct syslinux_memmap *syslinux_init_memmap(void)
+{
+  struct syslinux_memmap *sp, *ep;
+
+  sp = malloc(sizeof(*sp));
+  if (!sp)
+    return NULL;
+
+  ep = malloc(sizeof(*ep));
+  if (!ep) {
+    free(sp);
+    return NULL;
+  }
+
+  sp->start = 0;
+  sp->type  = SMT_UNDEFINED;
+  sp->next  = ep;
+
+  ep->start = 0;               /* Wrap around... */
+  ep->type  = SMT_END;         /* End of chain */
+  ep->next  = NULL;
+
+  return sp;
+}
+
+/*
+ * Add an item to a syslinux_memmap list, potentially overwriting
+ * what is already there.
+ */
+int syslinux_add_memmap(struct syslinux_memmap **list,
+                       addr_t start, addr_t len,
+                       enum syslinux_memmap_types type)
+{
+  addr_t last;
+  struct syslinux_memmap *mp, **mpp;
+  struct syslinux_memmap *mpi, **mppi;
+  struct syslinux_memmap *range;
+  enum syslinux_memmap_types oldtype;
+
+  /* Remove this to make len == 0 mean all of memory */
+  if (len == 0)
+    return 0;
+
+  /* Last byte -- to avoid rollover */
+  last = start+len-1;
+
+  mpp = list;
+  oldtype = SMT_ERROR;
+  while (mp = *mpp, start > mp->start && mp->type != SMT_END) {
+    oldtype = mp->type;
+    mpp = &mp->next;
+  }
+
+  /* Remember where we started messing with things. */
+  mppi = mpp;
+
+  if (start < mp->start || mp->type == SMT_END) {
+    range = malloc(sizeof(*range));
+    if (!range)
+      return -1;
+
+    range->start = start;
+    range->type  = type;
+    *mpp = range;
+    range->next = mp;
+    mpp = &range->next;
+  }
+
+  while (mp = *mpp, last > mp->start-1) {
+    oldtype = mp->type;
+    mp->type = type;
+    mpp = &mp->next;
+  }
+
+  if (last < mp->start-1) {
+    range = malloc(sizeof(*range));
+    if (!range)
+      return -1;
+
+    range->start = last+1;
+    range->type  = oldtype;
+    *mpp = range;
+    range->next = mp;
+  }
+
+  /* Now the map is correct, but quite possibly not optimal.  Scan the
+     map for ranges which are redundant and remove them.  This is
+     technically excessive, since we scan the list to the end even
+     though only part of it could have changed.  Eventually we might
+     care enough to save an end pointer from the operation above. */
+  mpi = *mppi;
+  while (mpi->type != SMT_END) {
+    mp = mpi->next;
+    if (mpi->type == mp->type) {
+      mpi->next = mp->next;
+      free(mp);
+    } else {
+      /* Go on to the next one */
+      mpi = mp;
+    }
+  }
+
+  return 0;
+}
+
+/*
+ * Verify what type a certain memory region is.  This function returns
+ * SMT_ERROR if the memory region has multiple types.
+ */
+enum syslinux_memmap_types syslinux_memmap_type(struct syslinux_memmap *list,
+                                               addr_t start, addr_t len)
+{
+  addr_t last, llast;
+
+  last = start+len-1;
+
+  while (list->type != SMT_END) {
+    llast = list->next->start-1;
+    if (list->start <= start) {
+      if (llast >= last)
+       return list->type;      /* Region has a well-defined type */
+      else if (llast >= start)
+       return SMT_ERROR;       /* Crosses region boundary */
+    }
+    list = list->next;
+  }
+
+  return SMT_ERROR;            /* Internal error? */
+}
+
+/*
+ * Find the largest zone of a specific type.  Returns -1 on failure.
+ */
+int syslinux_memmap_largest(struct syslinux_memmap *list,
+                           enum syslinux_memmap_types type,
+                           addr_t *start, addr_t *len)
+{
+  addr_t size, best_size = 0;
+  struct syslinux_memmap *best = NULL;
+
+  while (list->type != SMT_END) {
+    size = list->next->start - list->start;
+
+    if (list->type == type && size > best_size) {
+      best = list;
+      best_size = size;
+    }
+    
+    list = list->next;
+  }
+
+  if (!best)
+    return -1;
+
+  *start = best->start;
+  *len = best_size;
+
+  return 0;
+}
+
+/*
+ * Free a zonelist.
+ */
+void syslinux_free_memmap(struct syslinux_memmap *list)
+{
+  struct syslinux_memmap *ml;
+
+  while (list) {
+    ml = list;
+    list = list->next;
+    free(ml);
+  }
+}
+
+/*
+ * Duplicate a zonelist.  Returns NULL on failure.
+ */
+struct syslinux_memmap *syslinux_dup_memmap(struct syslinux_memmap *list)
+{
+  struct syslinux_memmap *newlist = NULL, **nlp = &newlist;
+  struct syslinux_memmap *ml;
+  
+  while (list) {
+    ml = malloc(sizeof(*ml));
+    if (!ml) {
+      syslinux_free_memmap(newlist);
+      return NULL;
+    }
+    ml->start = list->start;
+    ml->type = list->type;
+    ml->next = NULL;
+    *nlp = ml;
+    nlp = &ml->next;
+    
+    list = list->next;
+  }
+
+  return newlist;
+}
index 297e1b1..1f5643e 100644 (file)
@@ -44,7 +44,8 @@ AUXDIR   = $(LIBDIR)/syslinux
 INCDIR   = /usr/include
 COM32DIR = $(AUXDIR)/com32
 
-MODULES          = chain.c32 menu.c32 vesamenu.c32 ethersel.c32 mboot.c32 dmitest.c32 cpuidtest.c32 pcitest.c32
+MODULES          = chain.c32 menu.c32 vesamenu.c32 ethersel.c32 mboot.c32 \
+           dmitest.c32 cpuidtest.c32 pcitest.c32 elf.c32
 TESTFILES = 
 
 all: $(MODULES) $(TESTFILES)
diff --git a/com32/modules/elf.c b/com32/modules/elf.c
new file mode 100644 (file)
index 0000000..0b4aa35
--- /dev/null
@@ -0,0 +1,339 @@
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2007 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * elf.c
+ *
+ * Module to load a protected-mode ELF kernel
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <elf.h>
+#include <console.h>
+
+#include <syslinux/movebits.h>
+
+/* If we don't have this much memory for the stack, signal failure */
+#define MIN_STACK      512
+
+#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);
+}
+
+/*
+ * Load a file into memory
+ */
+int read_file(const char *filename, void **ptr, size_t *lenp)
+{
+  int fd;
+  size_t len;
+  ssize_t rv;
+  struct stat st;
+  char *data = NULL;
+
+  dprintf("filename = \"%s\"\n", filename);
+
+  fd = open(filename, O_RDONLY);
+  if (fd < 0)
+    return -1;
+
+  dprintf("file open...\n");
+
+  if (fstat(fd, &st) < 0)
+    goto bail;
+
+  len = st.st_size;
+
+  dprintf("Allocating %zu bytes...\n", len);
+
+  data = malloc(len);
+  if (!data) 
+    goto bail;
+  
+  *ptr  = data;
+  *lenp = len;
+
+  while (len) {
+    dprintf("Reading %zu bytes... ", len);
+    rv = read(fd, data, len);
+    dprintf("%zd bytes read\n", rv);
+    if (rv <= 0)
+      goto bail;               /* Syslinux doesn't EINTR */
+    data += rv;
+    len -= rv;
+  }
+
+  close(fd);
+
+  return 0;                    /* All data read */
+
+ bail:
+  if (data)
+    free(data);
+  close(fd);
+  return -1;
+}
+
+int boot_elf(void *ptr, size_t len, char **argv)
+{
+  char *cptr = ptr;
+  Elf32_Ehdr *eh = ptr;
+  Elf32_Phdr *ph;
+  unsigned int i;
+  struct syslinux_movelist *ml = NULL;
+  struct syslinux_memmap *mmap = NULL, *amap = NULL;
+  struct syslinux_pm_regs regs;
+  int argc;
+  addr_t argsize;
+  char **argp;
+  addr_t lstart, llen;
+  char *stack_frame = NULL;
+  addr_t stack_frame_size;
+  addr_t stack_pointer;
+  uint32_t *spp;
+  char *sfp;
+  addr_t sfa;
+
+  memset(&regs, 0, sizeof regs);
+
+  /*
+   * 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) )
+    goto bail;
+
+  /* Must be ELF, 32-bit, littleendian, version 1 */
+  if ( memcmp(eh->e_ident, "\x7f""ELF\1\1\1", 6) )
+    goto bail;
+
+  /* Is this a worthwhile test?  In particular x86-64 normally
+     would imply ELF64 support, which we could do as long as
+     the addresses are 32-bit addresses, and entry is 32 bits.
+     64-bit addresses would take a lot more work. */
+  if ( eh->e_machine != EM_386 && eh->e_machine != EM_486 &&
+       eh->e_machine != EM_X86_64 )
+    goto bail;
+
+  if ( eh->e_version != EV_CURRENT )
+    goto bail;
+
+  if ( eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= len )
+    goto bail;
+
+  if ( eh->e_phentsize < sizeof(Elf32_Phdr) )
+    goto bail;
+
+  if ( !eh->e_phnum )
+    goto bail;
+
+  if ( eh->e_phoff+eh->e_phentsize*eh->e_phnum > len )
+    goto bail;
+
+  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
+
+  ph = (Elf32_Phdr *)(cptr+eh->e_phoff);
+
+  for (i = 0; i < eh->e_phnum; i++) {
+    if (ph->p_type == PT_LOAD && ph->p_memsz >= ph->p_filesz) {
+      /* 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. */
+
+      dprintf("Segment at 0x%08x len 0x%08x\n", ph->p_paddr, ph->p_memsz);
+
+      if (syslinux_memmap_type(amap, ph->p_paddr, ph->p_memsz) != SMT_FREE) {
+       dprintf("Region not free!\n");
+       goto bail;              /* Memory region unavailable */
+      }
+
+      /* Mark this region as allocated in the available map */
+      if (syslinux_add_memmap(&amap, ph->p_paddr, ph->p_memsz, SMT_ALLOC))
+       goto bail;
+
+      if (ph->p_filesz) {
+       /* Data present region.  Create a move entry for it. */
+       if (syslinux_add_movelist(&ml, ph->p_paddr, (addr_t)cptr+ph->p_offset,
+                                 ph->p_filesz))
+         goto bail;
+      }
+      if (ph->p_memsz > ph->p_filesz) {
+       /* Zero-filled region.  Mark as a zero region in the memory map. */
+       if (syslinux_add_memmap(&mmap, ph->p_paddr+ph->p_filesz,
+                               ph->p_memsz - ph->p_filesz, SMT_ZERO))
+         goto bail;
+      }
+    } else {
+      /* Ignore this program header */
+    }
+
+    ph = (Elf32_Phdr *)((char *)ph + eh->e_phentsize);
+  }
+
+  /* Create the invocation record (initial stack frame) */
+
+  argsize = argc = 0;
+  for (argp = argv; *argp; argp++) {
+    dprintf("argv[%2d] = \"%s\"\n", argc, *argp);
+    argc++;
+    argsize += strlen(*argp)+1;
+  }
+
+  /* We need the argument strings, argument pointers,
+     argc, plus four zero-word terminators. */
+  stack_frame_size = argsize + argc*sizeof(char *) + 5*sizeof(long);
+  stack_frame_size = (stack_frame_size+15) & ~15;
+  stack_frame = calloc(stack_frame_size, 1);
+  if (!stack_frame)
+    goto bail;
+
+#if DEBUG
+  dprintf("Right before syslinux_memmap_largest()...\n");
+  syslinux_dump_memmap(stdout, amap);
+#endif
+
+  if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen))
+    goto bail;                 /* NO free memory?! */
+
+  if (llen < stack_frame_size+MIN_STACK+16)
+    goto bail;                 /* Insufficient memory  */
+
+  /* Initial stack pointer address */
+  stack_pointer = (lstart+llen-stack_frame_size) & ~15;
+
+  dprintf("Stack frame at 0x%08x len 0x%08x\n",
+         stack_pointer, stack_frame_size);
+
+  /* Create the stack frame.  sfp is the pointer in current memory for
+     the next argument string, sfa is the address in its final resting place.
+     spp is the pointer into the argument array in current memory. */
+  spp = (uint32_t *)stack_frame;
+  sfp = stack_frame + argc*sizeof(char *) + 5*sizeof(long);
+  sfa = stack_pointer + argc*sizeof(char *) + 5*sizeof(long);
+  
+  *spp++ = argc;
+  for (argp = argv; *argp; argp++) {
+    int bytes = strlen(*argp) + 1; /* Including final null */
+    *spp++ = sfa;
+    memcpy(sfp, *argp, bytes);
+    sfp += bytes;
+    sfa += bytes;
+  }
+  /* Zero fields are aready taken care of by calloc() */
+  
+  /* ... and we'll want to move it into the right place... */
+#if DEBUG
+  if (syslinux_memmap_type(amap, stack_pointer, stack_frame_size)
+      != SMT_FREE) {
+    dprintf("Stack frame area not free (how did that happen?)!\n");
+    goto bail;         /* Memory region unavailable */
+  }
+#endif
+
+  if (syslinux_add_memmap(&amap, stack_pointer, stack_frame_size, SMT_ALLOC))
+    goto bail;
+
+  if (syslinux_add_movelist(&ml, stack_pointer, (addr_t)stack_frame,
+                           stack_frame_size))
+    goto bail;
+
+  memset(&regs, 0, sizeof regs);
+  regs.eip = eh->e_entry;
+  regs.esp = stack_pointer;
+
+#if DEBUG
+  dprintf("Final memory map:\n");
+  syslinux_dump_memmap(stdout, mmap);
+
+  dprintf("Final available map:\n");
+  syslinux_dump_memmap(stdout, amap);
+
+  dprintf("Movelist:\n");
+  syslinux_dump_movelist(stdout, ml);
+#endif
+
+  /* This should not return... */
+  fputs("Booting...\n", stdout);
+  syslinux_shuffle_boot_pm(ml, mmap, 0, &regs);
+
+ bail:
+  if (stack_frame)
+    free(stack_frame);
+  syslinux_free_memmap(amap);
+  syslinux_free_memmap(mmap);
+  syslinux_free_movelist(ml);
+
+  return -1;
+}
+
+int main(int argc, char *argv[])
+{
+  void *data;
+  size_t data_len;
+
+  openconsole(&dev_null_r, &dev_stdcon_w);
+
+  if (argc < 2) {
+    error("Usage: elf.c32 elf_file arguments...\n");
+    return 1;
+  }
+
+  if (read_file(argv[1], &data, &data_len)) {
+    error("Unable to load file\n");
+    return 1;
+  }
+
+  boot_elf(data, data_len, &argv[1]);
+  error("Invalid ELF file or insufficient memory\n");
+  return 1;
+}