/* ----------------------------------------------------------------------- * * * 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 #include #include #include #include #include #include #ifndef DEBUG # define DEBUG 0 #endif #define dprintf(f, ...) ((void)0) #define dprintf2(f, ...) ((void)0) #if DEBUG # include # 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; }