summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/com32/lib/syslinux/shuffle.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/com32/lib/syslinux/shuffle.c')
-rw-r--r--contrib/syslinux-4.02/com32/lib/syslinux/shuffle.c275
1 files changed, 275 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/com32/lib/syslinux/shuffle.c b/contrib/syslinux-4.02/com32/lib/syslinux/shuffle.c
new file mode 100644
index 0000000..6b5a601
--- /dev/null
+++ b/contrib/syslinux-4.02/com32/lib/syslinux/shuffle.c
@@ -0,0 +1,275 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2007-2009 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * shuffle.c
+ *
+ * Common code for "shuffle and boot" operation; generates a shuffle list
+ * and puts it in the bounce buffer. Returns the number of shuffle
+ * descriptors.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <com32.h>
+#include <minmax.h>
+#include <syslinux/movebits.h>
+#include <klibc/compiler.h>
+
+#ifndef DEBUG
+# define DEBUG 0
+#endif
+
+#define dprintf(f, ...) ((void)0)
+#define dprintf2(f, ...) ((void)0)
+
+#if DEBUG
+# include <stdio.h>
+# undef dprintf
+# define dprintf printf
+# if DEBUG > 1
+# undef dprintf2
+# define dprintf2 printf
+# endif
+#endif
+
+struct shuffle_descriptor {
+ uint32_t dst, src, len;
+};
+
+static int shuffler_size;
+
+static void __constructor __syslinux_get_shuffer_size(void)
+{
+ static com32sys_t reg;
+
+ reg.eax.w[0] = 0x0023;
+ __intcall(0x22, &reg, &reg);
+
+ shuffler_size = (reg.eflags.l & EFLAGS_CF) ? 2048 : reg.ecx.w[0];
+}
+
+/*
+ * Allocate descriptor memory in these chunks; if this is large we may
+ * waste memory, if it is small we may get slow convergence.
+ */
+#define DESC_BLOCK_SIZE 256
+
+int syslinux_do_shuffle(struct syslinux_movelist *fraglist,
+ struct syslinux_memmap *memmap,
+ addr_t entry_point, addr_t entry_type,
+ uint16_t bootflags)
+{
+ int rv = -1;
+ struct syslinux_movelist *moves = NULL, *mp;
+ struct syslinux_memmap *rxmap = NULL, *ml;
+ struct shuffle_descriptor *dp, *dbuf;
+ int np;
+ int desc_blocks, need_blocks;
+ int need_ptrs;
+ addr_t desczone, descfree, descaddr, descoffs;
+ int nmoves, nzero;
+ com32sys_t ireg;
+
+ descaddr = 0;
+ dp = dbuf = NULL;
+
+ /* Count the number of zero operations */
+ nzero = 0;
+ for (ml = memmap; ml->type != SMT_END; ml = ml->next) {
+ if (ml->type == SMT_ZERO)
+ nzero++;
+ }
+
+ /* Find the largest contiguous region unused by input *and* output;
+ this is where we put the move descriptor list and safe area */
+
+ rxmap = syslinux_dup_memmap(memmap);
+ if (!rxmap)
+ goto bail;
+ /* Avoid using the low 1 MB for the shuffle area -- this avoids
+ possible interference with the real mode code or stack */
+ if (syslinux_add_memmap(&rxmap, 0, 1024 * 1024, SMT_RESERVED))
+ goto bail;
+ for (mp = fraglist; mp; mp = mp->next) {
+ if (syslinux_add_memmap(&rxmap, mp->src, mp->len, SMT_ALLOC) ||
+ syslinux_add_memmap(&rxmap, mp->dst, mp->len, SMT_ALLOC))
+ goto bail;
+ }
+ if (syslinux_memmap_largest(rxmap, SMT_FREE, &desczone, &descfree))
+ goto bail;
+
+ syslinux_free_memmap(rxmap);
+
+ dprintf("desczone = 0x%08x, descfree = 0x%08x\n", desczone, descfree);
+
+ rxmap = syslinux_dup_memmap(memmap);
+ if (!rxmap)
+ goto bail;
+
+ desc_blocks = (nzero + DESC_BLOCK_SIZE - 1) / DESC_BLOCK_SIZE;
+ for (;;) {
+ /* We want (desc_blocks) allocation blocks, plus the terminating
+ descriptor, plus the shuffler safe area. */
+ addr_t descmem = desc_blocks *
+ sizeof(struct shuffle_descriptor) * DESC_BLOCK_SIZE
+ + sizeof(struct shuffle_descriptor) + shuffler_size;
+
+ descaddr = (desczone + descfree - descmem) & ~3;
+
+ if (descaddr < desczone)
+ goto bail; /* No memory block large enough */
+
+ /* Mark memory used by shuffle descriptors as reserved */
+ if (syslinux_add_memmap(&rxmap, descaddr, descmem, SMT_RESERVED))
+ goto bail;
+
+#if DEBUG > 1
+ syslinux_dump_movelist(stdout, fraglist);
+#endif
+
+ if (syslinux_compute_movelist(&moves, fraglist, rxmap))
+ goto bail;
+
+ nmoves = 0;
+ for (mp = moves; mp; mp = mp->next)
+ nmoves++;
+
+ need_blocks = (nmoves + nzero + DESC_BLOCK_SIZE - 1) / DESC_BLOCK_SIZE;
+
+ if (desc_blocks >= need_blocks)
+ break; /* Sufficient memory, yay */
+
+ desc_blocks = need_blocks; /* Try again... */
+ }
+
+#if DEBUG > 1
+ dprintf("Final movelist:\n");
+ syslinux_dump_movelist(stdout, moves);
+#endif
+
+ syslinux_free_memmap(rxmap);
+ rxmap = NULL;
+
+ need_ptrs = nmoves + nzero + 1;
+ dbuf = malloc(need_ptrs * sizeof(struct shuffle_descriptor));
+ if (!dbuf)
+ goto bail;
+
+ descoffs = descaddr - (addr_t) dbuf;
+
+#if DEBUG
+ dprintf("nmoves = %d, nzero = %d, dbuf = %p, offs = 0x%08x\n",
+ nmoves, nzero, dbuf, descoffs);
+#endif
+
+ /* Copy the move sequence into the descriptor buffer */
+ np = 0;
+ dp = dbuf;
+ for (mp = moves; mp; mp = mp->next) {
+ dp->dst = mp->dst;
+ dp->src = mp->src;
+ dp->len = mp->len;
+ dprintf2("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len);
+ dp++;
+ np++;
+ }
+
+ /* Copy bzero operations into the descriptor buffer */
+ for (ml = memmap; ml->type != SMT_END; ml = ml->next) {
+ if (ml->type == SMT_ZERO) {
+ dp->dst = ml->start;
+ dp->src = (addr_t) - 1; /* bzero region */
+ dp->len = ml->next->start - ml->start;
+ dprintf2("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len);
+ dp++;
+ np++;
+ }
+ }
+
+ /* Finally, record the termination entry */
+ dp->dst = entry_point;
+ dp->src = entry_type;
+ dp->len = 0;
+ dp++;
+ np++;
+
+ if (np != need_ptrs) {
+ dprintf("!!! np = %d : nmoves = %d, nzero = %d, desc_blocks = %d\n",
+ np, nmoves, nzero, desc_blocks);
+ }
+
+ rv = 0;
+
+bail:
+ /* This is safe only because free() doesn't use the bounce buffer!!!! */
+ if (moves)
+ syslinux_free_movelist(moves);
+ if (rxmap)
+ syslinux_free_memmap(rxmap);
+
+ if (rv)
+ return rv;
+
+ /* Actually do it... */
+ memset(&ireg, 0, sizeof ireg);
+ ireg.edi.l = descaddr;
+ ireg.esi.l = (addr_t) dbuf;
+ ireg.ecx.l = (addr_t) dp - (addr_t) dbuf;
+ ireg.edx.w[0] = bootflags;
+ ireg.eax.w[0] = 0x0024;
+ __intcall(0x22, &ireg, NULL);
+
+ return -1; /* Shouldn't have returned! */
+}
+
+/*
+ * Common helper routine: takes a memory map and blots out the
+ * zones which are used in the destination of a fraglist
+ */
+struct syslinux_memmap *syslinux_target_memmap(struct syslinux_movelist
+ *fraglist,
+ struct syslinux_memmap *memmap)
+{
+ struct syslinux_memmap *tmap;
+ struct syslinux_movelist *mp;
+
+ tmap = syslinux_dup_memmap(memmap);
+ if (!tmap)
+ return NULL;
+
+ for (mp = fraglist; mp; mp = mp->next) {
+ if (syslinux_add_memmap(&tmap, mp->dst, mp->len, SMT_ALLOC)) {
+ syslinux_free_memmap(tmap);
+ return NULL;
+ }
+ }
+
+ return tmap;
+}