Add memtest support.
[syslinux-debian/hramrach.git] / com32 / lib / syslinux / movebits.c
blob63554012b2fd4746417caf8938b2df15a3d61784
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
13 * conditions:
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 * ----------------------------------------------------------------------- */
30 * movebits.c
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.
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>
50 #include <syslinux/movebits.h>
51 #include <dprintf.h>
53 static jmp_buf new_movelist_bail;
55 static struct syslinux_movelist *new_movelist(addr_t dst, addr_t src,
56 addr_t len)
58 struct syslinux_movelist *ml = malloc(sizeof(struct syslinux_movelist));
60 if (!ml)
61 longjmp(new_movelist_bail, 1);
63 ml->dst = dst;
64 ml->src = src;
65 ml->len = len;
66 ml->next = NULL;
68 return ml;
71 static struct syslinux_movelist *dup_movelist(struct syslinux_movelist *src)
73 struct syslinux_movelist *dst = NULL, **dstp = &dst, *ml;
75 while (src) {
76 ml = new_movelist(src->dst, src->src, src->len);
77 *dstp = ml;
78 dstp = &ml->next;
79 src = src->next;
82 return dst;
85 static void
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
99 **parentptr)
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);
111 m->next = ml->next;
112 ml->len = l;
113 ml->next = m;
115 parentptr = &ml->next;
116 ml = m; /* Continue processing the new node */
119 /* Split off the end */
120 if (ml->len > len) {
121 addr_t l = ml->len - len;
123 m = new_movelist(ml->dst + len, ml->src + len, l);
124 m->next = ml->next;
125 ml->len = len;
126 ml->next = m;
129 return parentptr;
132 static void delete_movelist(struct syslinux_movelist **parentptr)
134 struct syslinux_movelist *o = *parentptr;
135 *parentptr = o->next;
136 free(o);
139 static void free_movelist(struct syslinux_movelist **parentptr)
141 while (*parentptr)
142 delete_movelist(parentptr);
146 * Scan the freelist looking for a particular chunk of memory. Returns
147 * the memmap chunk containing to the first byte of the region.
149 static const struct syslinux_memmap *is_free_zone(const struct syslinux_memmap
150 *list, addr_t start,
151 addr_t len)
153 addr_t last, llast;
155 dprintf("f: 0x%08x bytes at 0x%08x\n", len, start);
157 last = start + len - 1;
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;
169 if (list->start > start)
170 return NULL; /* Invalid type in region */
172 list = list->next;
175 return NULL; /* Internal error? */
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.
182 static addr_t free_area(const struct syslinux_memmap *mmap,
183 addr_t len, addr_t * start)
185 const struct syslinux_memmap *best = NULL;
186 const struct syslinux_memmap *s;
187 addr_t slen, best_len = -1;
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;
201 if (best) {
202 *start = best->start;
203 return best_len;
204 } else {
205 return 0;
210 * Remove a chunk from the freelist
212 static void
213 allocate_from(struct syslinux_memmap **mmap, addr_t start, addr_t len)
215 syslinux_add_memmap(mmap, start, len, SMT_ALLOC);
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.
223 static void shuffle_dealias(struct syslinux_movelist **fraglist,
224 struct syslinux_movelist **postcopy)
226 struct syslinux_movelist *mp, **mpp, *mx, *np;
227 addr_t ps, pe, xs, xe, delta;
228 bool advance;
230 dprintf("Before alias resolution:\n");
231 syslinux_dump_movelist(*fraglist);
233 *postcopy = NULL;
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).
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);
247 * If there is any overlap between mx and mp, mp should be
248 * modified and possibly split.
250 xs = mx->src;
251 xe = mx->src + mx->len - 1;
253 dprintf("?: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe);
255 if (pe <= xs || ps >= xe)
256 continue; /* No overlap */
258 advance = false;
259 *mpp = mp->next; /* Remove from list */
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;
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;
283 assert(ps >= xs && pe <= xe);
285 dprintf("Overlap: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe);
287 mp->src = mx->dst + (ps - xs);
288 mp->next = *postcopy;
289 *postcopy = mp;
291 mp = *mpp;
293 if (!advance)
294 goto restart;
297 mpp = &mp->next;
298 restart:
302 dprintf("After alias resolution:\n");
303 syslinux_dump_movelist(*fraglist);
304 dprintf("Post-shuffle copies:\n");
305 syslinux_dump_movelist(*postcopy);
309 * The code to actually emit moving of a chunk into its final place.
311 static void
312 move_chunk(struct syslinux_movelist ***moves,
313 struct syslinux_memmap **mmap,
314 struct syslinux_movelist **fp, addr_t copylen)
316 addr_t copydst, copysrc;
317 addr_t freebase, freelen;
318 addr_t needlen;
319 int reverse;
320 struct syslinux_movelist *f = *fp, *mv;
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;
335 copydst = f->dst;
336 copysrc = f->src;
338 dprintf("Q: copylen = 0x%08x, needlen = 0x%08x\n", copylen, needlen);
340 if (copylen < needlen) {
341 if (reverse) {
342 copydst += (f->len - copylen);
343 copysrc += (f->len - copylen);
346 dprintf("X: 0x%08x bytes at 0x%08x -> 0x%08x\n",
347 copylen, copysrc, copydst);
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;
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;
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;
371 dprintf("F: 0x%08x bytes at 0x%08x\n", freelen, freebase);
373 add_freelist(mmap, freebase, freelen, SMT_FREE);
375 delete_movelist(fp);
379 * moves is computed from "frags" and "freemem". "space" lists
380 * free memory areas at our disposal, and is (src, cnt) only.
383 syslinux_compute_movelist(struct syslinux_movelist **moves,
384 struct syslinux_movelist *ifrags,
385 struct syslinux_memmap *memmap)
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;
402 dprintf("entering syslinux_compute_movelist()...\n");
404 if (setjmp(new_movelist_bail)) {
405 nomem:
406 dprintf("Out of working memory!\n");
407 goto bail;
410 *moves = NULL;
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;
418 frags = dup_movelist(ifrags);
420 /* Process one-to-many conditions */
421 shuffle_dealias(&frags, &postcopy);
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);
427 for (f = frags; f; f = f->next)
428 add_freelist(&mmap, f->src, f->len, SMT_ALLOC);
430 /* As long as there are unprocessed fragments in the chain... */
431 while ((fp = &frags, f = *fp)) {
433 dprintf("Current free list:\n");
434 syslinux_dump_memmap(mmap);
435 dprintf("Current frag list:\n");
436 syslinux_dump_movelist(frags);
438 /* Scan for fragments which can be discarded without action. */
439 if (f->src == f->dst) {
440 delete_movelist(fp);
441 continue;
443 op = &f->next;
444 while ((o = *op)) {
445 if (o->src == o->dst)
446 delete_movelist(op);
447 else
448 op = &o->next;
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" */
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;
484 /* Ok, bother. Need to do real work at least with one chunk. */
486 dprintf("@: 0x%08x bytes at 0x%08x -> 0x%08x\n",
487 f->len, f->src, f->dst);
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. */
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;
512 dprintf("need: base = 0x%08x, len = 0x%08x, "
513 "reverse = %d, cbyte = 0x%08x\n",
514 needbase, needlen, reverse, cbyte);
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;
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);
534 if (reverse)
535 allocate_from(&mmap, needbase + needlen - copylen, copylen);
536 else
537 allocate_from(&mmap, needbase, copylen);
539 goto move_chunk;
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) {
548 dprintf("O: 0x%08x bytes at 0x%08x -> 0x%08x\n",
549 o->len, o->src, o->dst);
551 if (!(o->src <= cbyte && o->src + o->len > cbyte))
552 continue; /* Not what we're looking for... */
554 /* Find somewhere to put it... */
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! */
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));
583 allocate_from(&mmap, copydst, copylen);
585 if (copylen < o->len) {
586 op = split_movelist(copysrc, copylen, op);
587 o = *op;
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;
596 o->src = copydst;
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);
604 if (copylen > needlen) {
605 add_freelist(&mmap, copysrc + needlen, copylen - needlen,
606 SMT_FREE);
607 copylen = needlen;
610 reverse = 0;
611 goto move_chunk;
613 dprintf("Cannot find the chunk containing the critical byte\n");
614 goto bail; /* Stuck! */
616 move_chunk:
617 move_chunk(&moves, &mmap, fp, copylen);
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;
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;
636 #ifdef TEST
638 #include <stdio.h>
640 int main(int argc, char *argv[])
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];
650 (void)argc;
652 memmap = syslinux_init_memmap();
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;
665 } else {
666 syslinux_add_memmap(&memmap, s, l, SMT_FREE);
670 fclose(f);
672 *fep = NULL;
674 dprintf("Input move list:\n");
675 syslinux_dump_movelist(frags);
676 dprintf("Input free list:\n");
677 syslinux_dump_memmap(memmap);
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(stdout, moves);
685 return 0;
689 #endif /* TEST */