1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009 Intel Corporation; author: H. Peter Anvin
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
15 * The above copyright notice and this permission notice shall
16 * be included in all copies or substantial portions of the Software.
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.
27 * ----------------------------------------------------------------------- */
32 * Utility function to take a list of memory areas to shuffle and
33 * convert it to a set of shuffle operations.
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.
50 #include <syslinux/movebits.h>
53 static jmp_buf new_movelist_bail;
55 static struct syslinux_movelist *new_movelist(addr_t dst, addr_t src,
58 struct syslinux_movelist *ml = malloc(sizeof(struct syslinux_movelist));
61 longjmp(new_movelist_bail, 1);
71 static struct syslinux_movelist *dup_movelist(struct syslinux_movelist *src)
73 struct syslinux_movelist *dst = NULL, **dstp = &dst, *ml;
76 ml = new_movelist(src->dst, src->src, src->len);
86 add_freelist(struct syslinux_memmap **mmap, addr_t start,
87 addr_t len, enum syslinux_memmap_types type)
89 if (syslinux_add_memmap(mmap, start, len, type))
90 longjmp(new_movelist_bail, 1);
94 * Take a chunk, entirely confined in **parentptr, and split it off so that
95 * it has its own structure.
97 static struct syslinux_movelist **split_movelist(addr_t start, addr_t len,
98 struct syslinux_movelist
101 struct syslinux_movelist *m, *ml = *parentptr;
103 assert(start >= ml->src);
104 assert(start < ml->src + ml->len);
106 /* Split off the beginning */
107 if (start > ml->src) {
108 addr_t l = start - ml->src;
110 m = new_movelist(ml->dst + l, start, ml->len - l);
115 parentptr = &ml->next;
116 ml = m; /* Continue processing the new node */
119 /* Split off the end */
121 addr_t l = ml->len - len;
123 m = new_movelist(ml->dst + len, ml->src + len, l);
132 static void delete_movelist(struct syslinux_movelist **parentptr)
134 struct syslinux_movelist *o = *parentptr;
135 *parentptr = o->next;
139 static void free_movelist(struct syslinux_movelist **parentptr)
142 delete_movelist(parentptr);
146 * Scan the freelist looking for a particular chunk of memory
148 static const struct syslinux_memmap *is_free_zone(const struct syslinux_memmap
152 dprintf("f: 0x%08x bytes at 0x%08x\n", len, start);
156 last = start + len - 1;
158 while (list->type != SMT_END) {
159 llast = list->next->start - 1;
160 if (list->start <= start) {
162 /* Chunk has a single, well-defined type */
163 if (list->type == SMT_FREE) {
164 dprintf("F: 0x%08x bytes at 0x%08x\n",
165 list->next->start, list->start);
166 return list; /* It's free */
168 return NULL; /* Not free */
169 } else if (llast >= start) {
170 return NULL; /* Crosses region boundary */
176 return NULL; /* Internal error? */
180 * Scan the freelist looking for the smallest chunk of memory which
181 * can fit X bytes; returns the length of the block on success.
183 static addr_t free_area(const struct syslinux_memmap *mmap,
184 addr_t len, addr_t * start)
186 const struct syslinux_memmap *best = NULL;
187 const struct syslinux_memmap *s;
188 addr_t slen, best_len = -1;
190 for (s = mmap; s->type != SMT_END; s = s->next) {
191 if (s->type != SMT_FREE)
193 slen = s->next->start - s->start;
195 if (!best || best_len > slen) {
203 *start = best->start;
211 * Remove a chunk from the freelist
214 allocate_from(struct syslinux_memmap **mmap, addr_t start, addr_t len)
216 syslinux_add_memmap(mmap, start, len, SMT_ALLOC);
220 * Find chunks of a movelist which are one-to-many (one source, multiple
221 * destinations.) Those chunks can get turned into post-shuffle copies,
222 * to avoid confusing the shuffler.
224 static void shuffle_dealias(struct syslinux_movelist **fraglist,
225 struct syslinux_movelist **postcopy)
227 struct syslinux_movelist *mp, **mpp, *mx, *np;
228 addr_t ps, pe, xs, xe, delta;
232 dprintf("Before alias resolution:\n");
233 syslinux_dump_movelist(stdout, *fraglist);
239 * Note: as written, this is an O(n^2) algorithm; by producing a list
240 * sorted by destination address we could reduce it to O(n log n).
243 while ((mp = *mpp)) {
244 dprintf("mp -> (%#x,%#x,%#x)\n", mp->dst, mp->src, mp->len);
246 pe = mp->src + mp->len - 1;
247 for (mx = *fraglist; mx != mp; mx = mx->next) {
248 dprintf("mx -> (%#x,%#x,%#x)\n", mx->dst, mx->src, mx->len);
250 * If there is any overlap between mx and mp, mp should be
251 * modified and possibly split.
254 xe = mx->src + mx->len - 1;
256 dprintf("?: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe);
258 if (pe <= xs || ps >= xe)
259 continue; /* No overlap */
262 *mpp = mp->next; /* Remove from list */
266 np = new_movelist(mp->dst + mp->len - delta,
267 mp->src + mp->len - delta, delta);
276 np = new_movelist(mp->dst, ps, delta);
286 assert(ps >= xs && pe <= xe);
288 dprintf("Overlap: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe);
290 mp->src = mx->dst + (ps - xs);
291 mp->next = *postcopy;
306 dprintf("After alias resolution:\n");
307 syslinux_dump_movelist(stdout, *fraglist);
308 dprintf("Post-shuffle copies:\n");
309 syslinux_dump_movelist(stdout, *postcopy);
314 * The code to actually emit moving of a chunk into its final place.
317 move_chunk(struct syslinux_movelist ***moves,
318 struct syslinux_memmap **mmap,
319 struct syslinux_movelist **fp, addr_t copylen)
321 addr_t copydst, copysrc;
322 addr_t freebase, freelen;
325 struct syslinux_movelist *f = *fp, *mv;
327 if (f->src < f->dst && (f->dst - f->src) < f->len) {
328 /* "Shift up" type overlap */
329 needlen = f->dst - f->src;
331 } else if (f->src > f->dst && (f->src - f->dst) < f->len) {
332 /* "Shift down" type overlap */
333 needlen = f->src - f->dst;
343 dprintf("Q: copylen = 0x%08x, needlen = 0x%08x\n", copylen, needlen);
345 if (copylen < needlen) {
347 copydst += (f->len - copylen);
348 copysrc += (f->len - copylen);
351 dprintf("X: 0x%08x bytes at 0x%08x -> 0x%08x\n",
352 copylen, copysrc, copydst);
354 /* Didn't get all we wanted, so we have to split the chunk */
355 fp = split_movelist(copysrc, copylen, fp); /* Is this right? */
359 mv = new_movelist(f->dst, f->src, f->len);
360 dprintf("A: 0x%08x bytes at 0x%08x -> 0x%08x\n", mv->len, mv->src, mv->dst);
364 /* Figure out what memory we just freed up */
365 if (f->dst > f->src) {
367 freelen = min(f->len, f->dst - f->src);
368 } else if (f->src >= f->dst + f->len) {
372 freelen = f->src - f->dst;
373 freebase = f->dst + f->len;
376 dprintf("F: 0x%08x bytes at 0x%08x\n", freelen, freebase);
378 add_freelist(mmap, freebase, freelen, SMT_FREE);
384 * moves is computed from "frags" and "freemem". "space" lists
385 * free memory areas at our disposal, and is (src, cnt) only.
388 syslinux_compute_movelist(struct syslinux_movelist **moves,
389 struct syslinux_movelist *ifrags,
390 struct syslinux_memmap *memmap)
392 struct syslinux_memmap *mmap = NULL;
393 const struct syslinux_memmap *mm, *ep;
394 struct syslinux_movelist *frags = NULL;
395 struct syslinux_movelist *postcopy = NULL;
396 struct syslinux_movelist *mv;
397 struct syslinux_movelist *f, **fp;
398 struct syslinux_movelist *o, **op;
399 addr_t needbase, needlen, copysrc, copydst, copylen;
407 dprintf("entering syslinux_compute_movelist()...\n");
409 if (setjmp(new_movelist_bail)) {
411 dprintf("Out of working memory!\n");
417 /* Create our memory map. Anything that is SMT_FREE or SMT_ZERO is
418 fair game, but mark anything used by source material as SMT_ALLOC. */
419 mmap = syslinux_init_memmap();
423 frags = dup_movelist(ifrags);
425 /* Process one-to-many conditions */
426 shuffle_dealias(&frags, &postcopy);
428 for (mm = memmap; mm->type != SMT_END; mm = mm->next)
429 add_freelist(&mmap, mm->start, mm->next->start - mm->start,
430 mm->type == SMT_ZERO ? SMT_FREE : mm->type);
432 for (f = frags; f; f = f->next)
433 add_freelist(&mmap, f->src, f->len, SMT_ALLOC);
435 /* As long as there are unprocessed fragments in the chain... */
436 while ((fp = &frags, f = *fp)) {
439 dprintf("Current free list:\n");
440 syslinux_dump_memmap(stdout, mmap);
441 dprintf("Current frag list:\n");
442 syslinux_dump_movelist(stdout, frags);
445 /* Scan for fragments which can be discarded without action. */
446 if (f->src == f->dst) {
452 if (o->src == o->dst)
458 /* Scan for fragments which can be immediately moved
459 to their final destination, if so handle them now */
460 for (op = fp; (o = *op); op = &o->next) {
461 if (o->src < o->dst && (o->dst - o->src) < o->len) {
462 /* "Shift up" type overlap */
463 needlen = o->dst - o->src;
464 needbase = o->dst + (o->len - needlen);
466 cbyte = o->dst + o->len - 1;
467 } else if (o->src > o->dst && (o->src - o->dst) < o->len) {
468 /* "Shift down" type overlap */
469 needlen = o->src - o->dst;
472 cbyte = o->dst; /* "Critical byte" */
477 cbyte = o->dst; /* "Critical byte" */
480 if (is_free_zone(mmap, needbase, needlen)) {
482 dprintf("!: 0x%08x bytes at 0x%08x -> 0x%08x\n",
483 f->len, f->src, f->dst);
486 allocate_from(&mmap, needbase, copylen);
491 /* Ok, bother. Need to do real work at least with one chunk. */
493 dprintf("@: 0x%08x bytes at 0x%08x -> 0x%08x\n",
494 f->len, f->src, f->dst);
496 /* See if we can move this chunk into place by claiming
497 the destination, or in the case of partial overlap, the
500 if (f->src < f->dst && (f->dst - f->src) < f->len) {
501 /* "Shift up" type overlap */
502 needlen = f->dst - f->src;
503 needbase = f->dst + (f->len - needlen);
505 cbyte = f->dst + f->len - 1;
506 } else if (f->src > f->dst && (f->src - f->dst) < f->len) {
507 /* "Shift down" type overlap */
508 needlen = f->src - f->dst;
511 cbyte = f->dst; /* "Critical byte" */
519 dprintf("need: base = 0x%08x, len = 0x%08x, "
520 "reverse = %d, cbyte = 0x%08x\n",
521 needbase, needlen, reverse, cbyte);
523 ep = is_free_zone(mmap, cbyte, 1);
525 ep_len = ep->next->start - ep->start;
527 avail = needbase + needlen - ep->start;
529 avail = ep_len - (needbase - ep->start);
535 /* We can move at least part of this chunk into place without
537 dprintf("space: start 0x%08x, len 0x%08x, free 0x%08x\n",
538 ep->start, ep_len, avail);
539 copylen = min(needlen, avail);
542 allocate_from(&mmap, needbase + needlen - copylen, copylen);
544 allocate_from(&mmap, needbase, copylen);
549 /* At this point, we need to evict something out of our space.
550 Find the object occupying the critical byte of our target space,
551 and move it out (the whole object if we can, otherwise a subset.)
552 Then move a chunk of ourselves into place. */
553 for (op = &f->next, o = *op; o; op = &o->next, o = *op) {
555 dprintf("O: 0x%08x bytes at 0x%08x -> 0x%08x\n",
556 o->len, o->src, o->dst);
558 if (!(o->src <= cbyte && o->src + o->len > cbyte))
559 continue; /* Not what we're looking for... */
561 /* Find somewhere to put it... */
563 if (is_free_zone(mmap, o->dst, o->len)) {
564 /* Score! We can move it into place directly... */
568 } else if (free_area(mmap, o->len, &fstart)) {
569 /* We can move the whole chunk */
574 /* Well, copy as much as we can... */
575 if (syslinux_memmap_largest(mmap, SMT_FREE, &fstart, &flen)) {
576 dprintf("No free memory at all!\n");
577 goto bail; /* Stuck! */
580 /* Make sure we include the critical byte */
583 copysrc = max(o->src, cbyte + 1 - flen);
584 copylen = cbyte + 1 - copysrc;
587 copylen = min(flen, o->len - (cbyte - o->src));
590 allocate_from(&mmap, copydst, copylen);
592 if (copylen < o->len) {
593 op = split_movelist(copysrc, copylen, op);
597 mv = new_movelist(copydst, copysrc, copylen);
598 dprintf("C: 0x%08x bytes at 0x%08x -> 0x%08x\n",
599 mv->len, mv->src, mv->dst);
605 if (copylen > needlen) {
606 /* We don't need all the memory we freed up. Mark it free. */
607 if (copysrc < needbase) {
608 add_freelist(&mmap, copysrc, needbase - copysrc, SMT_FREE);
609 copylen -= (needbase - copysrc);
611 if (copylen > needlen) {
612 add_freelist(&mmap, copysrc + needlen, copylen - needlen,
620 dprintf("Cannot find the chunk containing the critical byte\n");
621 goto bail; /* Stuck! */
624 move_chunk(&moves, &mmap, fp, copylen);
627 /* Finally, append the postcopy chain to the end of the moves list */
628 for (op = moves; (o = *op); op = &o->next) ; /* Locate the end of the list */
635 syslinux_free_memmap(mmap);
637 free_movelist(&frags);
639 free_movelist(&postcopy);
647 int main(int argc, char *argv[])
650 unsigned long d, s, l;
651 struct syslinux_movelist *frags;
652 struct syslinux_movelist **fep = &frags;
653 struct syslinux_movelist *mv, *moves;
654 struct syslinux_memmap *memmap;
659 memmap = syslinux_init_memmap();
661 f = fopen(argv[1], "r");
662 while (fgets(line, sizeof line, f) != NULL) {
663 if (sscanf(line, "%lx %lx %lx", &s, &d, &l) == 3) {
666 syslinux_add_memmap(&memmap, d, l, SMT_ZERO);
668 mv = new_movelist(d, s, l);
673 syslinux_add_memmap(&memmap, s, l, SMT_FREE);
681 printf("Input move list:\n");
682 syslinux_dump_movelist(stdout, frags);
683 printf("Input free list:\n");
684 syslinux_dump_memmap(stdout, memmap);
686 if (syslinux_compute_movelist(&moves, frags, memmap)) {
687 printf("Failed to compute a move sequence\n");
690 printf("Final move list:\n");
691 syslinux_dump_movelist(stdout, moves);