syslinux/com32/lib/syslinux/shuffle.c
<<
>>
Prefs
   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 <minmax.h>
  42#include <dprintf.h>
  43#include <syslinux/movebits.h>
  44#include <klibc/compiler.h>
  45
  46struct shuffle_descriptor {
  47    uint32_t dst, src, len;
  48};
  49
  50static int shuffler_size;
  51
  52static void __constructor __syslinux_get_shuffer_size(void)
  53{
  54    static com32sys_t reg;
  55
  56    reg.eax.w[0] = 0x0023;
  57    __intcall(0x22, &reg, &reg);
  58
  59    shuffler_size = (reg.eflags.l & EFLAGS_CF) ? 2048 : reg.ecx.w[0];
  60}
  61
  62/*
  63 * Allocate descriptor memory in these chunks; if this is large we may
  64 * waste memory, if it is small we may get slow convergence.
  65 */
  66#define DESC_BLOCK_SIZE 256
  67
  68int syslinux_do_shuffle(struct syslinux_movelist *fraglist,
  69                        struct syslinux_memmap *memmap,
  70                        addr_t entry_point, addr_t entry_type,
  71                        uint16_t bootflags)
  72{
  73    int rv = -1;
  74    struct syslinux_movelist *moves = NULL, *mp;
  75    struct syslinux_memmap *rxmap = NULL, *ml;
  76    struct shuffle_descriptor *dp, *dbuf;
  77    int np;
  78    int desc_blocks, need_blocks;
  79    int need_ptrs;
  80    addr_t desczone, descfree, descaddr;
  81    int nmoves, nzero;
  82    com32sys_t ireg;
  83
  84    descaddr = 0;
  85    dp = dbuf = NULL;
  86
  87    /* Count the number of zero operations */
  88    nzero = 0;
  89    for (ml = memmap; ml->type != SMT_END; ml = ml->next) {
  90        if (ml->type == SMT_ZERO)
  91            nzero++;
  92    }
  93
  94    /* Find the largest contiguous region unused by input *and* output;
  95       this is where we put the move descriptor list and safe area */
  96
  97    rxmap = syslinux_dup_memmap(memmap);
  98    if (!rxmap)
  99        goto bail;
 100    /* Avoid using the low 1 MB for the shuffle area -- this avoids
 101       possible interference with the real mode code or stack */
 102    if (syslinux_add_memmap(&rxmap, 0, 1024 * 1024, SMT_RESERVED))
 103        goto bail;
 104    for (mp = fraglist; mp; mp = mp->next) {
 105        if (syslinux_add_memmap(&rxmap, mp->src, mp->len, SMT_ALLOC) ||
 106            syslinux_add_memmap(&rxmap, mp->dst, mp->len, SMT_ALLOC))
 107            goto bail;
 108    }
 109    if (syslinux_memmap_largest(rxmap, SMT_FREE, &desczone, &descfree))
 110        goto bail;
 111
 112    syslinux_free_memmap(rxmap);
 113
 114    dprintf("desczone = 0x%08x, descfree = 0x%08x\n", desczone, descfree);
 115
 116    rxmap = syslinux_dup_memmap(memmap);
 117    if (!rxmap)
 118        goto bail;
 119
 120    desc_blocks = (nzero + DESC_BLOCK_SIZE - 1) / DESC_BLOCK_SIZE;
 121    for (;;) {
 122        /* We want (desc_blocks) allocation blocks, plus the terminating
 123           descriptor, plus the shuffler safe area. */
 124        addr_t descmem = desc_blocks *
 125            sizeof(struct shuffle_descriptor) * DESC_BLOCK_SIZE
 126            + sizeof(struct shuffle_descriptor) + shuffler_size;
 127
 128        descaddr = (desczone + descfree - descmem) & ~3;
 129
 130        if (descaddr < desczone)
 131            goto bail;          /* No memory block large enough */
 132
 133        /* Mark memory used by shuffle descriptors as reserved */
 134        if (syslinux_add_memmap(&rxmap, descaddr, descmem, SMT_RESERVED))
 135            goto bail;
 136
 137#if DEBUG > 1
 138        syslinux_dump_movelist(stdout, fraglist);
 139#endif
 140
 141        if (syslinux_compute_movelist(&moves, fraglist, rxmap))
 142            goto bail;
 143
 144        nmoves = 0;
 145        for (mp = moves; mp; mp = mp->next)
 146            nmoves++;
 147
 148        need_blocks = (nmoves + nzero + DESC_BLOCK_SIZE - 1) / DESC_BLOCK_SIZE;
 149
 150        if (desc_blocks >= need_blocks)
 151            break;              /* Sufficient memory, yay */
 152
 153        desc_blocks = need_blocks;      /* Try again... */
 154    }
 155
 156#if DEBUG > 1
 157    dprintf("Final movelist:\n");
 158    syslinux_dump_movelist(stdout, moves);
 159#endif
 160
 161    syslinux_free_memmap(rxmap);
 162    rxmap = NULL;
 163
 164    need_ptrs = nmoves + nzero + 1;
 165    dbuf = malloc(need_ptrs * sizeof(struct shuffle_descriptor));
 166    if (!dbuf)
 167        goto bail;
 168
 169#if DEBUG
 170    {
 171        addr_t descoffs = descaddr - (addr_t) dbuf;
 172
 173        dprintf("nmoves = %d, nzero = %d, dbuf = %p, offs = 0x%08x\n",
 174                nmoves, nzero, dbuf, descoffs);
 175    }
 176#endif
 177
 178    /* Copy the move sequence into the descriptor buffer */
 179    np = 0;
 180    dp = dbuf;
 181    for (mp = moves; mp; mp = mp->next) {
 182        dp->dst = mp->dst;
 183        dp->src = mp->src;
 184        dp->len = mp->len;
 185        dprintf2("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len);
 186        dp++;
 187        np++;
 188    }
 189
 190    /* Copy bzero operations into the descriptor buffer */
 191    for (ml = memmap; ml->type != SMT_END; ml = ml->next) {
 192        if (ml->type == SMT_ZERO) {
 193            dp->dst = ml->start;
 194            dp->src = (addr_t) - 1;     /* bzero region */
 195            dp->len = ml->next->start - ml->start;
 196            dprintf2("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len);
 197            dp++;
 198            np++;
 199        }
 200    }
 201
 202    /* Finally, record the termination entry */
 203    dp->dst = entry_point;
 204    dp->src = entry_type;
 205    dp->len = 0;
 206    dp++;
 207    np++;
 208
 209    if (np != need_ptrs) {
 210        dprintf("!!! np = %d : nmoves = %d, nzero = %d, desc_blocks = %d\n",
 211                np, nmoves, nzero, desc_blocks);
 212    }
 213
 214    rv = 0;
 215
 216bail:
 217    /* This is safe only because free() doesn't use the bounce buffer!!!! */
 218    if (moves)
 219        syslinux_free_movelist(moves);
 220    if (rxmap)
 221        syslinux_free_memmap(rxmap);
 222
 223    if (rv)
 224        return rv;
 225
 226    /* Actually do it... */
 227    memset(&ireg, 0, sizeof ireg);
 228    ireg.edi.l = descaddr;
 229    ireg.esi.l = (addr_t) dbuf;
 230    ireg.ecx.l = (addr_t) dp - (addr_t) dbuf;
 231    ireg.edx.w[0] = bootflags;
 232    ireg.eax.w[0] = 0x0024;
 233    __intcall(0x22, &ireg, NULL);
 234
 235    return -1;                  /* Shouldn't have returned! */
 236}
 237
 238/*
 239 * Common helper routine: takes a memory map and blots out the
 240 * zones which are used in the destination of a fraglist
 241 */
 242struct syslinux_memmap *syslinux_target_memmap(struct syslinux_movelist
 243                                               *fraglist,
 244                                               struct syslinux_memmap *memmap)
 245{
 246    struct syslinux_memmap *tmap;
 247    struct syslinux_movelist *mp;
 248
 249    tmap = syslinux_dup_memmap(memmap);
 250    if (!tmap)
 251        return NULL;
 252
 253    for (mp = fraglist; mp; mp = mp->next) {
 254        if (syslinux_add_memmap(&tmap, mp->dst, mp->len, SMT_ALLOC)) {
 255            syslinux_free_memmap(tmap);
 256            return NULL;
 257        }
 258    }
 259
 260    return tmap;
 261}
 262
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.