Home | History | Annotate | Download | only in syslinux
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
      4  *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
      5  *
      6  *   Permission is hereby granted, free of charge, to any person
      7  *   obtaining a copy of this software and associated documentation
      8  *   files (the "Software"), to deal in the Software without
      9  *   restriction, including without limitation the rights to use,
     10  *   copy, modify, merge, publish, distribute, sublicense, and/or
     11  *   sell copies of the Software, and to permit persons to whom
     12  *   the Software is furnished to do so, subject to the following
     13  *   conditions:
     14  *
     15  *   The above copyright notice and this permission notice shall
     16  *   be included in all copies or substantial portions of the Software.
     17  *
     18  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     20  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     22  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     23  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     24  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     25  *   OTHER DEALINGS IN THE SOFTWARE.
     26  *
     27  * ----------------------------------------------------------------------- */
     28 
     29 /*
     30  * shuffle.c
     31  *
     32  * Common code for "shuffle and boot" operation; generates a shuffle list
     33  * and puts it in the bounce buffer.  Returns the number of shuffle
     34  * descriptors.
     35  */
     36 
     37 #include <stdlib.h>
     38 #include <string.h>
     39 #include <inttypes.h>
     40 #include <com32.h>
     41 #include <core.h>
     42 #include <minmax.h>
     43 #include <dprintf.h>
     44 #include <syslinux/movebits.h>
     45 #include <klibc/compiler.h>
     46 #include <syslinux/boot.h>
     47 
     48 struct shuffle_descriptor {
     49     uint32_t dst, src, len;
     50 };
     51 
     52 /*
     53  * Allocate descriptor memory in these chunks; if this is large we may
     54  * waste memory, if it is small we may get slow convergence.
     55  */
     56 #define DESC_BLOCK_SIZE	256
     57 
     58 int syslinux_do_shuffle(struct syslinux_movelist *fraglist,
     59 			struct syslinux_memmap *memmap,
     60 			addr_t entry_point, addr_t entry_type,
     61 			uint16_t bootflags)
     62 {
     63     int rv = -1;
     64     struct syslinux_movelist *moves = NULL, *mp;
     65     struct syslinux_memmap *rxmap = NULL, *ml;
     66     struct shuffle_descriptor *dp, *dbuf;
     67     int np;
     68     int desc_blocks, need_blocks;
     69     int need_ptrs;
     70     addr_t desczone, descfree, descaddr;
     71     int nmoves, nzero;
     72 
     73 #ifndef __FIRMWARE_BIOS__
     74     errno = ENOSYS;
     75     return -1;			/* Not supported at this time*/
     76 #endif
     77 
     78     descaddr = 0;
     79     dp = dbuf = NULL;
     80 
     81     /* Count the number of zero operations */
     82     nzero = 0;
     83     for (ml = memmap; ml->type != SMT_END; ml = ml->next) {
     84 	if (ml->type == SMT_ZERO)
     85 	    nzero++;
     86     }
     87 
     88     /* Find the largest contiguous region unused by input *and* output;
     89        this is where we put the move descriptor list and safe area */
     90 
     91     rxmap = syslinux_dup_memmap(memmap);
     92     if (!rxmap)
     93 	goto bail;
     94     /* Avoid using the low 1 MB for the shuffle area -- this avoids
     95        possible interference with the real mode code or stack */
     96     if (syslinux_add_memmap(&rxmap, 0, 1024 * 1024, SMT_RESERVED))
     97 	goto bail;
     98     for (mp = fraglist; mp; mp = mp->next) {
     99 	if (syslinux_add_memmap(&rxmap, mp->src, mp->len, SMT_ALLOC) ||
    100 	    syslinux_add_memmap(&rxmap, mp->dst, mp->len, SMT_ALLOC))
    101 	    goto bail;
    102     }
    103     if (syslinux_memmap_largest(rxmap, SMT_FREE, &desczone, &descfree))
    104 	goto bail;
    105 
    106     syslinux_free_memmap(rxmap);
    107 
    108     dprintf("desczone = 0x%08x, descfree = 0x%08x\n", desczone, descfree);
    109 
    110     rxmap = syslinux_dup_memmap(memmap);
    111     if (!rxmap)
    112 	goto bail;
    113 
    114     desc_blocks = (nzero + DESC_BLOCK_SIZE - 1) / DESC_BLOCK_SIZE;
    115     for (;;) {
    116 	/* We want (desc_blocks) allocation blocks, plus the terminating
    117 	   descriptor, plus the shuffler safe area. */
    118 	addr_t descmem = desc_blocks *
    119 	    sizeof(struct shuffle_descriptor) * DESC_BLOCK_SIZE
    120 	    + sizeof(struct shuffle_descriptor)
    121 	    + syslinux_shuffler_size();
    122 
    123 	descaddr = (desczone + descfree - descmem) & ~3;
    124 
    125 	if (descaddr < desczone)
    126 	    goto bail;		/* No memory block large enough */
    127 
    128 	/* Mark memory used by shuffle descriptors as reserved */
    129 	if (syslinux_add_memmap(&rxmap, descaddr, descmem, SMT_RESERVED))
    130 	    goto bail;
    131 
    132 #if DEBUG > 1
    133 	syslinux_dump_movelist(fraglist);
    134 #endif
    135 
    136 	if (syslinux_compute_movelist(&moves, fraglist, rxmap))
    137 	    goto bail;
    138 
    139 	nmoves = 0;
    140 	for (mp = moves; mp; mp = mp->next)
    141 	    nmoves++;
    142 
    143 	need_blocks = (nmoves + nzero + DESC_BLOCK_SIZE - 1) / DESC_BLOCK_SIZE;
    144 
    145 	if (desc_blocks >= need_blocks)
    146 	    break;		/* Sufficient memory, yay */
    147 
    148 	desc_blocks = need_blocks;	/* Try again... */
    149     }
    150 
    151 #if DEBUG > 1
    152     dprintf("Final movelist:\n");
    153     syslinux_dump_movelist(moves);
    154 #endif
    155 
    156     syslinux_free_memmap(rxmap);
    157     rxmap = NULL;
    158 
    159     need_ptrs = nmoves + nzero + 1;
    160     dbuf = malloc(need_ptrs * sizeof(struct shuffle_descriptor));
    161     if (!dbuf)
    162 	goto bail;
    163 
    164 #if DEBUG
    165     {
    166 	addr_t descoffs = descaddr - (addr_t) dbuf;
    167 
    168 	dprintf("nmoves = %d, nzero = %d, dbuf = %p, offs = 0x%08x\n",
    169 		nmoves, nzero, dbuf, descoffs);
    170     }
    171 #endif
    172 
    173     /* Copy the move sequence into the descriptor buffer */
    174     np = 0;
    175     dp = dbuf;
    176     for (mp = moves; mp; mp = mp->next) {
    177 	dp->dst = mp->dst;
    178 	dp->src = mp->src;
    179 	dp->len = mp->len;
    180 	dprintf2("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len);
    181 	dp++;
    182 	np++;
    183     }
    184 
    185     /* Copy bzero operations into the descriptor buffer */
    186     for (ml = memmap; ml->type != SMT_END; ml = ml->next) {
    187 	if (ml->type == SMT_ZERO) {
    188 	    dp->dst = ml->start;
    189 	    dp->src = (addr_t) - 1;	/* bzero region */
    190 	    dp->len = ml->next->start - ml->start;
    191 	    dprintf2("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len);
    192 	    dp++;
    193 	    np++;
    194 	}
    195     }
    196 
    197     /* Finally, record the termination entry */
    198     dp->dst = entry_point;
    199     dp->src = entry_type;
    200     dp->len = 0;
    201     dp++;
    202     np++;
    203 
    204     if (np != need_ptrs) {
    205 	dprintf("!!! np = %d : nmoves = %d, nzero = %d, desc_blocks = %d\n",
    206 		np, nmoves, nzero, desc_blocks);
    207     }
    208 
    209     rv = 0;
    210 
    211 bail:
    212     /* This is safe only because free() doesn't use the bounce buffer!!!! */
    213     if (moves)
    214 	syslinux_free_movelist(moves);
    215     if (rxmap)
    216 	syslinux_free_memmap(rxmap);
    217 
    218     if (rv)
    219 	return rv;
    220 
    221     /* Actually do it... */
    222     bios_do_shuffle_and_boot(bootflags, descaddr, dbuf,
    223 			     (size_t)dp - (size_t)dbuf);
    224 
    225     return -1;			/* Shouldn't have returned! */
    226 }
    227 
    228 /*
    229  * Common helper routine: takes a memory map and blots out the
    230  * zones which are used in the destination of a fraglist
    231  */
    232 struct syslinux_memmap *syslinux_target_memmap(struct syslinux_movelist
    233 					       *fraglist,
    234 					       struct syslinux_memmap *memmap)
    235 {
    236     struct syslinux_memmap *tmap;
    237     struct syslinux_movelist *mp;
    238 
    239     tmap = syslinux_dup_memmap(memmap);
    240     if (!tmap)
    241 	return NULL;
    242 
    243     for (mp = fraglist; mp; mp = mp->next) {
    244 	if (syslinux_add_memmap(&tmap, mp->dst, mp->len, SMT_ALLOC)) {
    245 	    syslinux_free_memmap(tmap);
    246 	    return NULL;
    247 	}
    248     }
    249 
    250     return tmap;
    251 }
    252