/* ----------------------------------------------------------------------- * * * Copyright 2007-2008 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. * * ----------------------------------------------------------------------- */ /* * 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 #include #include #include #include #include #include #ifndef DEBUG # define DEBUG 0 #endif #if DEBUG # include # define dprintf printf #else # define dprintf(f, ...) ((void)0) #endif struct shuffle_descriptor { uint32_t dst, src, len; }; static int desc_block_size; static void __constructor __syslinux_get_desc_block_size(void) { static com32sys_t reg; reg.eax.w[0] = 0x0011; __intcall(0x22, ®, ®); desc_block_size = (reg.eflags.l & EFLAGS_CF) ? 64 : reg.ecx.w[0]; } /* Allocate descriptor memory in these chunks */ #define DESC_BLOCK_SIZE desc_block_size int syslinux_prepare_shuffle(struct syslinux_movelist *fraglist, struct syslinux_memmap *memmap) { struct syslinux_movelist *moves = NULL, *mp; struct syslinux_memmap *rxmap = NULL, *ml; struct shuffle_descriptor *dp, *dbuf; int np, nb, rv = -1; int desc_blocks, need_blocks; addr_t desczone, descfree, descaddr, descoffs; int nmoves, nzero; struct shuffle_descriptor primaries[2]; /* 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 larges contiguous region unused by input *and* output; this is where we put the move descriptor list */ rxmap = syslinux_dup_memmap(memmap); if (!rxmap) 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)/(DESC_BLOCK_SIZE-1); for (;;) { addr_t descmem = desc_blocks* sizeof(struct shuffle_descriptor)*DESC_BLOCK_SIZE; if (descfree < descmem) goto bail; /* No memory block large enough */ /* Mark memory used by shuffle descriptors as reserved */ descaddr = desczone + descfree - descmem; if (syslinux_add_memmap(&rxmap, descaddr, descmem, SMT_RESERVED)) goto bail; #if DEBUG 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); if (desc_blocks >= need_blocks) break; /* Sufficient memory, yay */ desc_blocks = need_blocks; /* Try again... */ } #if DEBUG dprintf("Final movelist:\n"); syslinux_dump_movelist(stdout, moves); #endif syslinux_free_memmap(rxmap); rxmap = NULL; dbuf = malloc((nmoves+nzero+desc_blocks)*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; nb = 0; dp = dbuf; for (mp = moves; mp; mp = mp->next) { if (nb == DESC_BLOCK_SIZE-1) { dp->dst = -1; /* Load new descriptors */ dp->src = (addr_t)(dp+1) + descoffs; dp->len = sizeof(*dp)*min(nmoves, DESC_BLOCK_SIZE); dprintf("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len); dp++; np++; nb = 0; } dp->dst = mp->dst; dp->src = mp->src; dp->len = mp->len; dprintf("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len); dp++; np++; nb++; } /* Copy bzero operations into the descriptor buffer */ for (ml = memmap; ml->type != SMT_END; ml = ml->next) { if (ml->type == SMT_ZERO) { if (nb == DESC_BLOCK_SIZE-1) { dp->dst = (addr_t)-1; /* Load new descriptors */ dp->src = (addr_t)(dp+1) + descoffs; dp->len = sizeof(*dp)*min(nmoves, DESC_BLOCK_SIZE); dprintf("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len); dp++; np++; nb = 0; } dp->dst = ml->start; dp->src = (addr_t)-1; /* bzero region */ dp->len = ml->next->start - ml->start; dprintf("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len); dp++; np++; nb++; } } /* Set up the primary descriptors in the bounce buffer. The first one moves the descriptor list into its designated safe zone, the second one loads the first descriptor block. */ dp = primaries; dp->dst = descaddr; dp->src = (addr_t)dbuf; dp->len = np*sizeof(*dp); dprintf("< %08x %08x %08x >\n", dp->dst, dp->src, dp->len); dp++; dp->dst = (addr_t)-1; dp->src = descaddr; dp->len = sizeof(*dp)*min(np, DESC_BLOCK_SIZE); dprintf("< %08x %08x %08x >\n", dp->dst, dp->src, dp->len); dp++; memcpy(__com32.cs_bounce, primaries, 2*sizeof(*dp)); rv = 2; /* Always two primaries */ 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); return rv; }