diff options
Diffstat (limited to 'contrib/syslinux/latest/com32/lib/syslinux/shuffle.c')
-rw-r--r-- | contrib/syslinux/latest/com32/lib/syslinux/shuffle.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/contrib/syslinux/latest/com32/lib/syslinux/shuffle.c b/contrib/syslinux/latest/com32/lib/syslinux/shuffle.c new file mode 100644 index 0000000..6b5a601 --- /dev/null +++ b/contrib/syslinux/latest/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, ®, ®); + + 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; +} |