summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--com32/lib/syslinux/movebits.c148
-rw-r--r--com32/modules/sdi.c11
2 files changed, 133 insertions, 26 deletions
diff --git a/com32/lib/syslinux/movebits.c b/com32/lib/syslinux/movebits.c
index 5e4a0c39..1114bfb8 100644
--- a/com32/lib/syslinux/movebits.c
+++ b/com32/lib/syslinux/movebits.c
@@ -1,6 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* 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
@@ -43,6 +44,8 @@
#include <stdlib.h>
#include <inttypes.h>
#include <setjmp.h>
+#include <minmax.h>
+#include <stdbool.h>
#include <syslinux/movebits.h>
@@ -61,9 +64,6 @@
# define dprintf(...) ((void)0)
#endif
-#define min(x,y) ((x) < (y) ? (x) : (y))
-#define max(x,y) ((x) > (y) ? (x) : (y))
-
static jmp_buf new_movelist_bail;
static struct syslinux_movelist *
@@ -230,6 +230,97 @@ allocate_from(struct syslinux_memmap **mmap, addr_t start, addr_t len)
}
/*
+ * Find chunks of a movelist which are one-to-many (one source, multiple
+ * destinations.) Those chunks can get turned into post-shuffle copies,
+ * to avoid confusing the shuffler.
+ */
+static void shuffle_dealias(struct syslinux_movelist **fraglist,
+ struct syslinux_movelist **postcopy)
+{
+ struct syslinux_movelist *mp, **mpp, *mx, *np;
+ addr_t ps, pe, xs, xe, delta;
+ bool advance;
+
+#if DEBUG
+ dprintf("Before alias resolution:\n");
+ syslinux_dump_movelist(stdout, *fraglist);
+#endif
+
+ *postcopy = NULL;
+
+ /*
+ * Note: as written, this is an O(n^2) algorithm; by producing a list
+ * sorted by destination address we could reduce it to O(n log n).
+ */
+ mpp = fraglist;
+ while ((mp = *mpp)) {
+ dprintf("mp -> (%#x,%#x,%#x)\n", mp->dst, mp->src, mp->len);
+ ps = mp->src;
+ pe = mp->src + mp->len - 1;
+ for (mx = *fraglist; mx != mp; mx = mx->next) {
+ dprintf("mx -> (%#x,%#x,%#x)\n", mx->dst, mx->src, mx->len);
+ /*
+ * If there is any overlap between mx and mp, mp should be
+ * modified and possibly split.
+ */
+ xs = mx->src;
+ xe = mx->src + mx->len - 1;
+
+ dprintf("?: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe);
+
+ if (pe <= xs || ps >= xe)
+ continue; /* No overlap */
+
+ advance = false;
+ *mpp = mp->next; /* Remove from list */
+
+ if (pe > xe) {
+ delta = pe-xe;
+ np = new_movelist(mp->dst+mp->len-delta, mp->src+mp->len-delta, delta);
+ mp->len -= delta; pe = xe;
+ np->next = *mpp;
+ *mpp = np;
+ advance = true;
+ }
+ if (ps < xs) {
+ delta = xs-ps;
+ np = new_movelist(mp->dst, ps, delta);
+ mp->src += delta; ps = mp->src;
+ mp->dst += delta;
+ mp->len -= delta;
+ np->next = *mpp;
+ *mpp = np;
+ advance = true;
+ }
+
+ assert(ps >= xs && pe <= xe);
+
+ dprintf("Overlap: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe);
+
+ mp->src = mx->dst + (ps-xs);
+ mp->next = *postcopy;
+ *postcopy = mp;
+
+ mp = *mpp;
+
+ if (!advance)
+ goto restart;
+ }
+
+ mpp = &mp->next;
+ restart:
+ ;
+ }
+
+#if DEBUG
+ dprintf("After alias resolution:\n");
+ syslinux_dump_movelist(stdout, *fraglist);
+ dprintf("Post-shuffle copies:\n");
+ syslinux_dump_movelist(stdout, *postcopy);
+#endif
+}
+
+/*
* The code to actually emit moving of a chunk into its final place.
*/
static void
@@ -304,7 +395,6 @@ move_chunk(struct syslinux_movelist ***moves,
* moves is computed from "frags" and "freemem". "space" lists
* free memory areas at our disposal, and is (src, cnt) only.
*/
-
int
syslinux_compute_movelist(struct syslinux_movelist **moves,
struct syslinux_movelist *ifrags,
@@ -313,6 +403,7 @@ syslinux_compute_movelist(struct syslinux_movelist **moves,
struct syslinux_memmap *mmap = NULL;
const struct syslinux_memmap *mm, *ep;
struct syslinux_movelist *frags = NULL;
+ struct syslinux_movelist *postcopy = NULL;
struct syslinux_movelist *mv;
struct syslinux_movelist *f, **fp;
struct syslinux_movelist *o, **op;
@@ -342,6 +433,9 @@ syslinux_compute_movelist(struct syslinux_movelist **moves,
frags = dup_movelist(ifrags);
+ /* Process one-to-many conditions */
+ shuffle_dealias(&frags, &postcopy);
+
for (mm = memmap; mm->type != SMT_END; mm = mm->next)
add_freelist(&mmap, mm->start, mm->next->start - mm->start,
mm->type == SMT_ZERO ? SMT_FREE : mm->type);
@@ -540,12 +634,20 @@ syslinux_compute_movelist(struct syslinux_movelist **moves,
move_chunk(&moves, &mmap, fp, copylen);
}
+ /* Finally, append the postcopy chain to the end of the moves list */
+ for (op = moves; (o = *op); op = &o->next)
+ ; /* Locate the end of the list */
+ *op = postcopy;
+ postcopy = NULL;
+
rv = 0;
bail:
if (mmap)
syslinux_free_memmap(mmap);
if (frags)
free_movelist(&frags);
+ if (postcopy)
+ free_movelist(&postcopy);
return rv;
}
@@ -559,32 +661,42 @@ int main(int argc, char *argv[])
unsigned long d, s, l;
struct syslinux_movelist *frags;
struct syslinux_movelist **fep = &frags;
- struct syslinux_movelist *space;
- struct syslinux_movelist **sep = &space;
struct syslinux_movelist *mv, *moves;
+ struct syslinux_memmap *memmap;
+
+ (void)argc;
+
+ memmap = syslinux_init_memmap();
f = fopen(argv[1], "r");
- while ( fscanf(f, "%lx %lx %lx", &d, &s, &l) == 3 ) {
+ while ( fscanf(f, "%lx %lx %lx", &s, &d, &l) == 3 ) {
if ( d ) {
- mv = new_movelist(d, s, l);
- *fep = mv;
- fep = &mv->next;
+ if (s == -1UL) {
+ syslinux_add_memmap(&memmap, d, l, SMT_ZERO);
+ } else {
+ mv = new_movelist(d, s, l);
+ *fep = mv;
+ fep = &mv->next;
+ }
} else {
- mv = new_movelist(0, s, l);
- *sep = mv;
- sep = &mv->next;
+ syslinux_add_memmap(&memmap, s, l, SMT_FREE);
}
}
fclose(f);
- if ( syslinux_compute_movelist(&moves, frags, space) ) {
+ *fep = NULL;
+
+ printf("Input move list:\n");
+ syslinux_dump_movelist(stdout, frags);
+ printf("Input free list:\n");
+ syslinux_dump_memmap(stdout, memmap);
+
+ if ( syslinux_compute_movelist(&moves, frags, memmap) ) {
printf("Failed to compute a move sequence\n");
return 1;
} else {
- for ( mv = moves ; mv ; mv = mv->next ) {
- printf("0x%08x bytes at 0x%08x -> 0x%08x\n",
- mv->len, mv->src, mv->dst);
- }
+ printf("Final move list:\n");
+ syslinux_dump_movelist(stdout, moves);
return 0;
}
}
diff --git a/com32/modules/sdi.c b/com32/modules/sdi.c
index 7c898591..a5df4dab 100644
--- a/com32/modules/sdi.c
+++ b/com32/modules/sdi.c
@@ -1,6 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2008 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
@@ -116,14 +117,8 @@ static int boot_sdi(void *ptr, size_t len)
}
if (syslinux_add_memmap(&amap, 0x7c00, hdr->BootCodeSize, SMT_ALLOC))
goto bail;
-
- /* The shuffle library doesn't handle duplication well... */
- boot_blob = malloc(hdr->BootCodeSize);
- if (!boot_blob)
- goto bail;
- memcpy(boot_blob, (char *)ptr + hdr->BootCodeOffset, hdr->BootCodeSize);
-
- if (syslinux_add_movelist(&ml, 0x7c00, (addr_t)boot_blob, hdr->BootCodeSize))
+ if (syslinux_add_movelist(&ml, 0x7c00, (addr_t)ptr + hdr->BootCodeOffset,
+ hdr->BootCodeSize))
goto bail;
/* **** Map the entire image to SDI_LOAD_ADDR **** */