Home | History | Annotate | Download | only in syslinux
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2007-2008 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  * movebits.c
     31  *
     32  * Utility function to take a list of memory areas to shuffle and
     33  * convert it to a set of shuffle operations.
     34  *
     35  * Note: a lot of the functions in this file deal with "parent pointers",
     36  * which are pointers to a pointer to a list, or part of the list.
     37  * They can be pointers to a variable holding the list root pointer,
     38  * or pointers to a next field of a previous entry.
     39  */
     40 
     41 #include <assert.h>
     42 #include <stdio.h>
     43 #include <errno.h>
     44 #include <stdlib.h>
     45 #include <inttypes.h>
     46 #include <setjmp.h>
     47 #include <minmax.h>
     48 #include <stdbool.h>
     49 
     50 #include <syslinux/movebits.h>
     51 #include <dprintf.h>
     52 
     53 static jmp_buf new_movelist_bail;
     54 
     55 static struct syslinux_movelist *new_movelist(addr_t dst, addr_t src,
     56 					      addr_t len)
     57 {
     58     struct syslinux_movelist *ml = malloc(sizeof(struct syslinux_movelist));
     59 
     60     if (!ml)
     61 	longjmp(new_movelist_bail, 1);
     62 
     63     ml->dst = dst;
     64     ml->src = src;
     65     ml->len = len;
     66     ml->next = NULL;
     67 
     68     return ml;
     69 }
     70 
     71 static struct syslinux_movelist *dup_movelist(struct syslinux_movelist *src)
     72 {
     73     struct syslinux_movelist *dst = NULL, **dstp = &dst, *ml;
     74 
     75     while (src) {
     76 	ml = new_movelist(src->dst, src->src, src->len);
     77 	*dstp = ml;
     78 	dstp = &ml->next;
     79 	src = src->next;
     80     }
     81 
     82     return dst;
     83 }
     84 
     85 static void
     86 add_freelist(struct syslinux_memmap **mmap, addr_t start,
     87 	     addr_t len, enum syslinux_memmap_types type)
     88 {
     89     if (syslinux_add_memmap(mmap, start, len, type))
     90 	longjmp(new_movelist_bail, 1);
     91 }
     92 
     93 /*
     94  * Take a chunk, entirely confined in **parentptr, and split it off so that
     95  * it has its own structure.
     96  */
     97 static struct syslinux_movelist **split_movelist(addr_t start, addr_t len,
     98 						 struct syslinux_movelist
     99 						 **parentptr)
    100 {
    101     struct syslinux_movelist *m, *ml = *parentptr;
    102 
    103     assert(start >= ml->src);
    104     assert(start < ml->src + ml->len);
    105 
    106     /* Split off the beginning */
    107     if (start > ml->src) {
    108 	addr_t l = start - ml->src;
    109 
    110 	m = new_movelist(ml->dst + l, start, ml->len - l);
    111 	m->next = ml->next;
    112 	ml->len = l;
    113 	ml->next = m;
    114 
    115 	parentptr = &ml->next;
    116 	ml = m;			/* Continue processing the new node */
    117     }
    118 
    119     /* Split off the end */
    120     if (ml->len > len) {
    121 	addr_t l = ml->len - len;
    122 
    123 	m = new_movelist(ml->dst + len, ml->src + len, l);
    124 	m->next = ml->next;
    125 	ml->len = len;
    126 	ml->next = m;
    127     }
    128 
    129     return parentptr;
    130 }
    131 
    132 static void delete_movelist(struct syslinux_movelist **parentptr)
    133 {
    134     struct syslinux_movelist *o = *parentptr;
    135     *parentptr = o->next;
    136     free(o);
    137 }
    138 
    139 static void free_movelist(struct syslinux_movelist **parentptr)
    140 {
    141     while (*parentptr)
    142 	delete_movelist(parentptr);
    143 }
    144 
    145 /*
    146  * Scan the freelist looking for a particular chunk of memory.  Returns
    147  * the memmap chunk containing to the first byte of the region.
    148  */
    149 static const struct syslinux_memmap *is_free_zone(const struct syslinux_memmap
    150 						  *list, addr_t start,
    151 						  addr_t len)
    152 {
    153     addr_t last, llast;
    154 
    155     dprintf("f: 0x%08x bytes at 0x%08x\n", len, start);
    156 
    157     last = start + len - 1;
    158 
    159     while (list->type != SMT_END) {
    160 	if (list->start <= start) {
    161 	    const struct syslinux_memmap *ilist = list;
    162 	    while (valid_terminal_type(list->type)) {
    163 		llast = list->next->start - 1;
    164 		if (llast >= last)
    165 		    return ilist;
    166 		list = list->next;
    167 	    }
    168 
    169 	    if (list->start > start)
    170 		return NULL;	/* Invalid type in region */
    171 	}
    172 	list = list->next;
    173     }
    174 
    175     return NULL;		/* Internal error? */
    176 }
    177 
    178 /*
    179  * Scan the freelist looking for the smallest chunk of memory which
    180  * can fit X bytes; returns the length of the block on success.
    181  */
    182 static addr_t free_area(const struct syslinux_memmap *mmap,
    183 			addr_t len, addr_t * start)
    184 {
    185     const struct syslinux_memmap *best = NULL;
    186     const struct syslinux_memmap *s;
    187     addr_t slen, best_len = -1;
    188 
    189     for (s = mmap; s->type != SMT_END; s = s->next) {
    190 	if (s->type != SMT_FREE)
    191 	    continue;
    192 	slen = s->next->start - s->start;
    193 	if (slen >= len) {
    194 	    if (!best || best_len > slen) {
    195 		best = s;
    196 		best_len = slen;
    197 	    }
    198 	}
    199     }
    200 
    201     if (best) {
    202 	*start = best->start;
    203 	return best_len;
    204     } else {
    205 	return 0;
    206     }
    207 }
    208 
    209 /*
    210  * Remove a chunk from the freelist
    211  */
    212 static void
    213 allocate_from(struct syslinux_memmap **mmap, addr_t start, addr_t len)
    214 {
    215     syslinux_add_memmap(mmap, start, len, SMT_ALLOC);
    216 }
    217 
    218 /*
    219  * Find chunks of a movelist which are one-to-many (one source, multiple
    220  * destinations.)  Those chunks can get turned into post-shuffle copies,
    221  * to avoid confusing the shuffler.
    222  */
    223 static void shuffle_dealias(struct syslinux_movelist **fraglist,
    224 			    struct syslinux_movelist **postcopy)
    225 {
    226     struct syslinux_movelist *mp, **mpp, *mx, *np;
    227     addr_t ps, pe, xs, xe, delta;
    228     bool advance;
    229 
    230     dprintf("Before alias resolution:\n");
    231     syslinux_dump_movelist(*fraglist);
    232 
    233     *postcopy = NULL;
    234 
    235     /*
    236      * Note: as written, this is an O(n^2) algorithm; by producing a list
    237      * sorted by destination address we could reduce it to O(n log n).
    238      */
    239     mpp = fraglist;
    240     while ((mp = *mpp)) {
    241 	dprintf("mp -> (%#x,%#x,%#x)\n", mp->dst, mp->src, mp->len);
    242 	ps = mp->src;
    243 	pe = mp->src + mp->len - 1;
    244 	for (mx = *fraglist; mx != mp; mx = mx->next) {
    245 	    dprintf("mx -> (%#x,%#x,%#x)\n", mx->dst, mx->src, mx->len);
    246 	    /*
    247 	     * If there is any overlap between mx and mp, mp should be
    248 	     * modified and possibly split.
    249 	     */
    250 	    xs = mx->src;
    251 	    xe = mx->src + mx->len - 1;
    252 
    253 	    dprintf("?: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe);
    254 
    255 	    if (pe <= xs || ps >= xe)
    256 		continue;	/* No overlap */
    257 
    258 	    advance = false;
    259 	    *mpp = mp->next;	/* Remove from list */
    260 
    261 	    if (pe > xe) {
    262 		delta = pe - xe;
    263 		np = new_movelist(mp->dst + mp->len - delta,
    264 				  mp->src + mp->len - delta, delta);
    265 		mp->len -= delta;
    266 		pe = xe;
    267 		np->next = *mpp;
    268 		*mpp = np;
    269 		advance = true;
    270 	    }
    271 	    if (ps < xs) {
    272 		delta = xs - ps;
    273 		np = new_movelist(mp->dst, ps, delta);
    274 		mp->src += delta;
    275 		ps = mp->src;
    276 		mp->dst += delta;
    277 		mp->len -= delta;
    278 		np->next = *mpp;
    279 		*mpp = np;
    280 		advance = true;
    281 	    }
    282 
    283 	    assert(ps >= xs && pe <= xe);
    284 
    285 	    dprintf("Overlap: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe);
    286 
    287 	    mp->src = mx->dst + (ps - xs);
    288 	    mp->next = *postcopy;
    289 	    *postcopy = mp;
    290 
    291 	    mp = *mpp;
    292 
    293 	    if (!advance)
    294 		goto restart;
    295 	}
    296 
    297 	mpp = &mp->next;
    298 restart:
    299 	;
    300     }
    301 
    302     dprintf("After alias resolution:\n");
    303     syslinux_dump_movelist(*fraglist);
    304     dprintf("Post-shuffle copies:\n");
    305     syslinux_dump_movelist(*postcopy);
    306 }
    307 
    308 /*
    309  * The code to actually emit moving of a chunk into its final place.
    310  */
    311 static void
    312 move_chunk(struct syslinux_movelist ***moves,
    313 	   struct syslinux_memmap **mmap,
    314 	   struct syslinux_movelist **fp, addr_t copylen)
    315 {
    316     addr_t copydst, copysrc;
    317     addr_t freebase, freelen;
    318     addr_t needlen;
    319     int reverse;
    320     struct syslinux_movelist *f = *fp, *mv;
    321 
    322     if (f->src < f->dst && (f->dst - f->src) < f->len) {
    323 	/* "Shift up" type overlap */
    324 	needlen = f->dst - f->src;
    325 	reverse = 1;
    326     } else if (f->src > f->dst && (f->src - f->dst) < f->len) {
    327 	/* "Shift down" type overlap */
    328 	needlen = f->src - f->dst;
    329 	reverse = 0;
    330     } else {
    331 	needlen = f->len;
    332 	reverse = 0;
    333     }
    334 
    335     copydst = f->dst;
    336     copysrc = f->src;
    337 
    338     dprintf("Q: copylen = 0x%08x, needlen = 0x%08x\n", copylen, needlen);
    339 
    340     if (copylen < needlen) {
    341 	if (reverse) {
    342 	    copydst += (f->len - copylen);
    343 	    copysrc += (f->len - copylen);
    344 	}
    345 
    346 	dprintf("X: 0x%08x bytes at 0x%08x -> 0x%08x\n",
    347 		copylen, copysrc, copydst);
    348 
    349 	/* Didn't get all we wanted, so we have to split the chunk */
    350 	fp = split_movelist(copysrc, copylen, fp);	/* Is this right? */
    351 	f = *fp;
    352     }
    353 
    354     mv = new_movelist(f->dst, f->src, f->len);
    355     dprintf("A: 0x%08x bytes at 0x%08x -> 0x%08x\n", mv->len, mv->src, mv->dst);
    356     **moves = mv;
    357     *moves = &mv->next;
    358 
    359     /* Figure out what memory we just freed up */
    360     if (f->dst > f->src) {
    361 	freebase = f->src;
    362 	freelen = min(f->len, f->dst - f->src);
    363     } else if (f->src >= f->dst + f->len) {
    364 	freebase = f->src;
    365 	freelen = f->len;
    366     } else {
    367 	freelen = f->src - f->dst;
    368 	freebase = f->dst + f->len;
    369     }
    370 
    371     dprintf("F: 0x%08x bytes at 0x%08x\n", freelen, freebase);
    372 
    373     add_freelist(mmap, freebase, freelen, SMT_FREE);
    374 
    375     delete_movelist(fp);
    376 }
    377 
    378 /*
    379  * moves is computed from "frags" and "freemem".  "space" lists
    380  * free memory areas at our disposal, and is (src, cnt) only.
    381  */
    382 int
    383 syslinux_compute_movelist(struct syslinux_movelist **moves,
    384 			  struct syslinux_movelist *ifrags,
    385 			  struct syslinux_memmap *memmap)
    386 {
    387     struct syslinux_memmap *mmap = NULL;
    388     const struct syslinux_memmap *mm, *ep;
    389     struct syslinux_movelist *frags = NULL;
    390     struct syslinux_movelist *postcopy = NULL;
    391     struct syslinux_movelist *mv;
    392     struct syslinux_movelist *f, **fp;
    393     struct syslinux_movelist *o, **op;
    394     addr_t needbase, needlen, copysrc, copydst, copylen;
    395     addr_t avail;
    396     addr_t fstart, flen;
    397     addr_t cbyte;
    398     addr_t ep_len;
    399     int rv = -1;
    400     int reverse;
    401 
    402     dprintf("entering syslinux_compute_movelist()...\n");
    403 
    404     if (setjmp(new_movelist_bail)) {
    405 nomem:
    406 	dprintf("Out of working memory!\n");
    407 	goto bail;
    408     }
    409 
    410     *moves = NULL;
    411 
    412     /* Create our memory map.  Anything that is SMT_FREE or SMT_ZERO is
    413        fair game, but mark anything used by source material as SMT_ALLOC. */
    414     mmap = syslinux_init_memmap();
    415     if (!mmap)
    416 	goto nomem;
    417 
    418     frags = dup_movelist(ifrags);
    419 
    420     /* Process one-to-many conditions */
    421     shuffle_dealias(&frags, &postcopy);
    422 
    423     for (mm = memmap; mm->type != SMT_END; mm = mm->next)
    424 	add_freelist(&mmap, mm->start, mm->next->start - mm->start,
    425 		     mm->type == SMT_ZERO ? SMT_FREE : mm->type);
    426 
    427     for (f = frags; f; f = f->next)
    428 	add_freelist(&mmap, f->src, f->len, SMT_ALLOC);
    429 
    430     /* As long as there are unprocessed fragments in the chain... */
    431     while ((fp = &frags, f = *fp)) {
    432 
    433 	dprintf("Current free list:\n");
    434 	syslinux_dump_memmap(mmap);
    435 	dprintf("Current frag list:\n");
    436 	syslinux_dump_movelist(frags);
    437 
    438 	/* Scan for fragments which can be discarded without action. */
    439 	if (f->src == f->dst) {
    440 	    delete_movelist(fp);
    441 	    continue;
    442 	}
    443 	op = &f->next;
    444 	while ((o = *op)) {
    445 	    if (o->src == o->dst)
    446 		delete_movelist(op);
    447 	    else
    448 		op = &o->next;
    449 	}
    450 
    451 	/* Scan for fragments which can be immediately moved
    452 	   to their final destination, if so handle them now */
    453 	for (op = fp; (o = *op); op = &o->next) {
    454 	    if (o->src < o->dst && (o->dst - o->src) < o->len) {
    455 		/* "Shift up" type overlap */
    456 		needlen = o->dst - o->src;
    457 		needbase = o->dst + (o->len - needlen);
    458 		reverse = 1;
    459 		cbyte = o->dst + o->len - 1;
    460 	    } else if (o->src > o->dst && (o->src - o->dst) < o->len) {
    461 		/* "Shift down" type overlap */
    462 		needlen = o->src - o->dst;
    463 		needbase = o->dst;
    464 		reverse = 0;
    465 		cbyte = o->dst;	/* "Critical byte" */
    466 	    } else {
    467 		needlen = o->len;
    468 		needbase = o->dst;
    469 		reverse = 0;
    470 		cbyte = o->dst;	/* "Critical byte" */
    471 	    }
    472 
    473 	    if (is_free_zone(mmap, needbase, needlen)) {
    474 		fp = op, f = o;
    475 		dprintf("!: 0x%08x bytes at 0x%08x -> 0x%08x\n",
    476 			f->len, f->src, f->dst);
    477 		copysrc = f->src;
    478 		copylen = needlen;
    479 		allocate_from(&mmap, needbase, copylen);
    480 		goto move_chunk;
    481 	    }
    482 	}
    483 
    484 	/* Ok, bother.  Need to do real work at least with one chunk. */
    485 
    486 	dprintf("@: 0x%08x bytes at 0x%08x -> 0x%08x\n",
    487 		f->len, f->src, f->dst);
    488 
    489 	/* See if we can move this chunk into place by claiming
    490 	   the destination, or in the case of partial overlap, the
    491 	   missing portion. */
    492 
    493 	if (f->src < f->dst && (f->dst - f->src) < f->len) {
    494 	    /* "Shift up" type overlap */
    495 	    needlen = f->dst - f->src;
    496 	    needbase = f->dst + (f->len - needlen);
    497 	    reverse = 1;
    498 	    cbyte = f->dst + f->len - 1;
    499 	} else if (f->src > f->dst && (f->src - f->dst) < f->len) {
    500 	    /* "Shift down" type overlap */
    501 	    needlen = f->src - f->dst;
    502 	    needbase = f->dst;
    503 	    reverse = 0;
    504 	    cbyte = f->dst;	/* "Critical byte" */
    505 	} else {
    506 	    needlen = f->len;
    507 	    needbase = f->dst;
    508 	    reverse = 0;
    509 	    cbyte = f->dst;
    510 	}
    511 
    512 	dprintf("need: base = 0x%08x, len = 0x%08x, "
    513 		"reverse = %d, cbyte = 0x%08x\n",
    514 		needbase, needlen, reverse, cbyte);
    515 
    516 	ep = is_free_zone(mmap, cbyte, 1);
    517 	if (ep) {
    518 	    ep_len = ep->next->start - ep->start;
    519 	    if (reverse)
    520 		avail = needbase + needlen - ep->start;
    521 	    else
    522 		avail = ep_len - (needbase - ep->start);
    523 	} else {
    524 	    avail = 0;
    525 	}
    526 
    527 	if (avail) {
    528 	    /* We can move at least part of this chunk into place without
    529 	       further ado */
    530 	    dprintf("space: start 0x%08x, len 0x%08x, free 0x%08x\n",
    531 		    ep->start, ep_len, avail);
    532 	    copylen = min(needlen, avail);
    533 
    534 	    if (reverse)
    535 		allocate_from(&mmap, needbase + needlen - copylen, copylen);
    536 	    else
    537 		allocate_from(&mmap, needbase, copylen);
    538 
    539 	    goto move_chunk;
    540 	}
    541 
    542 	/* At this point, we need to evict something out of our space.
    543 	   Find the object occupying the critical byte of our target space,
    544 	   and move it out (the whole object if we can, otherwise a subset.)
    545 	   Then move a chunk of ourselves into place. */
    546 	for (op = &f->next, o = *op; o; op = &o->next, o = *op) {
    547 
    548 	    dprintf("O: 0x%08x bytes at 0x%08x -> 0x%08x\n",
    549 		    o->len, o->src, o->dst);
    550 
    551 	    if (!(o->src <= cbyte && o->src + o->len > cbyte))
    552 		continue;	/* Not what we're looking for... */
    553 
    554 	    /* Find somewhere to put it... */
    555 
    556 	    if (is_free_zone(mmap, o->dst, o->len)) {
    557 		/* Score!  We can move it into place directly... */
    558 		copydst = o->dst;
    559 		copysrc = o->src;
    560 		copylen = o->len;
    561 	    } else if (free_area(mmap, o->len, &fstart)) {
    562 		/* We can move the whole chunk */
    563 		copydst = fstart;
    564 		copysrc = o->src;
    565 		copylen = o->len;
    566 	    } else {
    567 		/* Well, copy as much as we can... */
    568 		if (syslinux_memmap_largest(mmap, SMT_FREE, &fstart, &flen)) {
    569 		    dprintf("No free memory at all!\n");
    570 		    goto bail;	/* Stuck! */
    571 		}
    572 
    573 		/* Make sure we include the critical byte */
    574 		copydst = fstart;
    575 		if (reverse) {
    576 		    copysrc = max(o->src, cbyte + 1 - flen);
    577 		    copylen = cbyte + 1 - copysrc;
    578 		} else {
    579 		    copysrc = cbyte;
    580 		    copylen = min(flen, o->len - (cbyte - o->src));
    581 		}
    582 	    }
    583 	    allocate_from(&mmap, copydst, copylen);
    584 
    585 	    if (copylen < o->len) {
    586 		op = split_movelist(copysrc, copylen, op);
    587 		o = *op;
    588 	    }
    589 
    590 	    mv = new_movelist(copydst, copysrc, copylen);
    591 	    dprintf("C: 0x%08x bytes at 0x%08x -> 0x%08x\n",
    592 		    mv->len, mv->src, mv->dst);
    593 	    *moves = mv;
    594 	    moves = &mv->next;
    595 
    596 	    o->src = copydst;
    597 
    598 	    if (copylen > needlen) {
    599 		/* We don't need all the memory we freed up.  Mark it free. */
    600 		if (copysrc < needbase) {
    601 		    add_freelist(&mmap, copysrc, needbase - copysrc, SMT_FREE);
    602 		    copylen -= (needbase - copysrc);
    603 		}
    604 		if (copylen > needlen) {
    605 		    add_freelist(&mmap, copysrc + needlen, copylen - needlen,
    606 				 SMT_FREE);
    607 		    copylen = needlen;
    608 		}
    609 	    }
    610 	    reverse = 0;
    611 	    goto move_chunk;
    612 	}
    613 	dprintf("Cannot find the chunk containing the critical byte\n");
    614 	goto bail;		/* Stuck! */
    615 
    616 move_chunk:
    617 	move_chunk(&moves, &mmap, fp, copylen);
    618     }
    619 
    620     /* Finally, append the postcopy chain to the end of the moves list */
    621     for (op = moves; (o = *op); op = &o->next) ;	/* Locate the end of the list */
    622     *op = postcopy;
    623     postcopy = NULL;
    624 
    625     rv = 0;
    626 bail:
    627     if (mmap)
    628 	syslinux_free_memmap(mmap);
    629     if (frags)
    630 	free_movelist(&frags);
    631     if (postcopy)
    632 	free_movelist(&postcopy);
    633     return rv;
    634 }
    635 
    636 #ifdef TEST
    637 
    638 #include <stdio.h>
    639 
    640 int main(int argc, char *argv[])
    641 {
    642     FILE *f;
    643     unsigned long d, s, l;
    644     struct syslinux_movelist *frags;
    645     struct syslinux_movelist **fep = &frags;
    646     struct syslinux_movelist *mv, *moves;
    647     struct syslinux_memmap *memmap;
    648     char line[BUFSIZ];
    649 
    650     (void)argc;
    651 
    652     memmap = syslinux_init_memmap();
    653 
    654     f = fopen(argv[1], "r");
    655     while (fgets(line, sizeof line, f) != NULL) {
    656 	if (sscanf(line, "%lx %lx %lx", &s, &d, &l) == 3) {
    657 	    if (d) {
    658 		if (s == -1UL) {
    659 		    syslinux_add_memmap(&memmap, d, l, SMT_ZERO);
    660 		} else {
    661 		    mv = new_movelist(d, s, l);
    662 		    *fep = mv;
    663 		    fep = &mv->next;
    664 		}
    665 	    } else {
    666 		syslinux_add_memmap(&memmap, s, l, SMT_FREE);
    667 	    }
    668 	}
    669     }
    670     fclose(f);
    671 
    672     *fep = NULL;
    673 
    674     dprintf("Input move list:\n");
    675     syslinux_dump_movelist(frags);
    676     dprintf("Input free list:\n");
    677     syslinux_dump_memmap(memmap);
    678 
    679     if (syslinux_compute_movelist(&moves, frags, memmap)) {
    680 	printf("Failed to compute a move sequence\n");
    681 	return 1;
    682     } else {
    683 	dprintf("Final move list:\n");
    684 	syslinux_dump_movelist(moves);
    685 	return 0;
    686     }
    687 }
    688 
    689 #endif /* TEST */
    690