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
#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;
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;
};
/*
- * 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 */
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
*
* ----------------------------------------------------------------------- */
+#include <stdlib.h>
+#include <syslinux/movebits.h>
+
int syslinux_add_movelist(struct syslinux_movelist **list,
addr_t dst, addr_t src, addr_t len)
{
--- /dev/null
+/* ----------------------------------------------------------------------- *
+ *
+ * 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;
+ }
+}
--- /dev/null
+/* ----------------------------------------------------------------------- *
+ *
+ * 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;
+ }
+}
+
* Frees a syslinux_movelist
*/
-#include <syslinux/movelist.h>
+#include <stdlib.h>
+#include <syslinux/movebits.h>
void syslinux_free_movelist(struct syslinux_movelist *list)
{
#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 */
(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;
}
*
* 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)
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)
{
ml = m; /* Continue processing the new node */
}
+ /* Split off the end */
if ( ml->len > len ) {
addr_t l = ml->len - len;
return parentptr;
}
+#if 0
static void
delete_movelist(struct syslinux_movelist **parentptr)
{
*parentptr = o->next;
free(o);
}
+#endif
/*
* Scan the freelist looking for a particular chunk of memory
free(c);
}
+#if 0
/*
* Remove any chunk from the freelist which is already
* claimed by source data. This somewhat frivolously
frags = &f->next;
}
}
+#endif
/*
* moves is computed from "frags" and "freemem". "space" lists
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",
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;
}
space = mv;
}
- return 0;
+ rv = 0;
+ bail:
+ if (mmap)
+ syslinux_free_memmap(mmap);
+ return rv;
}
#ifdef TEST
}
return 0;
}
-}
+ }
#endif /* TEST */
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... */
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);
#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)
{
com32sys_t ireg;
char *regbuf;
- nd = syslinux_prepare_shuffle(fraglist);
+ nd = syslinux_prepare_shuffle(fraglist, memmap);
if (nd < 0)
return -1;
#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)
int nd;
com32sys_t ireg;
- nd = syslinux_prepare_shuffle(fraglist);
+ nd = syslinux_prepare_shuffle(fraglist, memmap);
if (nd < 0)
return -1;
--- /dev/null
+/* ----------------------------------------------------------------------- *
+ *
+ * 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;
+}
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)
--- /dev/null
+/* ----------------------------------------------------------------------- *
+ *
+ * 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(®s, 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(®s, 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, ®s);
+
+ 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;
+}