1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom
11 * the Software is furnished to do so, subject to the following
14 * The above copyright notice and this permission notice shall
15 * be included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
26 * ----------------------------------------------------------------------- */
31 * Utility function to take a list of memory areas to shuffle and
32 * convert it to a set of shuffle operations.
34 * Note: a lot of the functions in this file deal with "parent pointers",
35 * which are pointers to a pointer to a list, or part of the list.
36 * They can be pointers to a variable holding the list root pointer,
37 * or pointers to a next field of a previous entry.
47 #include <syslinux/movebits.h>
59 # define dprintf printf
61 # define dprintf(...) ((void)0)
64 #define min(x,y) ((x) < (y) ? (x) : (y))
65 #define max(x,y) ((x) > (y) ? (x) : (y))
67 static jmp_buf new_movelist_bail;
69 static struct syslinux_movelist *
70 new_movelist(addr_t dst, addr_t src, addr_t len)
72 struct syslinux_movelist *ml = malloc(sizeof(struct syslinux_movelist));
75 longjmp(new_movelist_bail, 1);
85 static struct syslinux_movelist *
86 dup_movelist(struct syslinux_movelist *src)
88 struct syslinux_movelist *dst = NULL, **dstp = &dst, *ml;
91 ml = new_movelist(src->dst, src->src, src->len);
101 * Take a chunk, entirely confined in **parentptr, and split it off so that
102 * it has its own structure.
104 static struct syslinux_movelist **
105 split_movelist(addr_t start, addr_t len, struct syslinux_movelist **parentptr)
107 struct syslinux_movelist *m, *ml = *parentptr;
109 assert(start >= ml->src);
110 assert(start < ml->src+ml->len);
112 /* Split off the beginning */
113 if ( start > ml->src ) {
114 addr_t l = start - ml->src;
116 m = new_movelist(ml->dst+l, start, ml->len-l);
121 parentptr = &ml->next;
122 ml = m; /* Continue processing the new node */
125 /* Split off the end */
126 if ( ml->len > len ) {
127 addr_t l = ml->len - len;
129 m = new_movelist(ml->dst+len, ml->src+len, l);
139 delete_movelist(struct syslinux_movelist **parentptr)
141 struct syslinux_movelist *o = *parentptr;
142 *parentptr = o->next;
147 free_movelist(struct syslinux_movelist **parentptr)
150 delete_movelist(parentptr);
154 * Scan the freelist looking for a particular chunk of memory
156 static struct syslinux_movelist **
157 is_free_zone(addr_t start, addr_t len, struct syslinux_movelist **space)
159 struct syslinux_movelist *s;
161 while ( (s = *space) ) {
162 if ( s->src <= start && s->src+s->len >= start+len )
171 * Scan the freelist looking for the smallest chunk of memory which
174 static struct syslinux_movelist **
175 free_area(addr_t len, struct syslinux_movelist **space)
177 struct syslinux_movelist **best = NULL;
178 struct syslinux_movelist *s;
180 while ( (s = *space) ) {
181 if ( s->len >= len ) {
182 if ( !best || (*best)->len > s->len )
193 * Scan the freelist looking for the largest chunk of memory,
196 static struct syslinux_movelist **
197 free_area_max(struct syslinux_movelist **space)
199 struct syslinux_movelist **best = NULL;
200 struct syslinux_movelist *s;
202 while ( (s = *space) ) {
203 if ( !best || s->len > (*best)->len )
212 * Remove a chunk from the freelist
215 allocate_from(addr_t start, addr_t len, struct syslinux_movelist **parentptr)
217 struct syslinux_movelist *c = *parentptr;
219 assert(c->src <= start);
220 assert(c->src+c->len >= start+len);
222 if ( c->src != start || c->len != len ) {
223 parentptr = split_movelist(start, len, parentptr);
227 *parentptr = c->next;
233 * Remove any chunk from the freelist which is already
234 * claimed by source data. This somewhat frivolously
235 * assumes that the fragments are either completely
236 * covered by a free zone or not covered at all.
239 tidy_freelist(struct syslinux_movelist **frags,
240 struct syslinux_movelist **space)
242 struct syslinux_movelist **sep;
243 struct syslinux_movelist *f;
245 while ( (f = *frags) ) {
246 if ( (sep = is_free_zone(f->src, f->len, space)) )
247 allocate_from(f->src, f->len, sep);
254 * moves is computed from "frags" and "freemem". "space" lists
255 * free memory areas at our disposal, and is (src, cnt) only.
259 syslinux_compute_movelist(struct syslinux_movelist **moves,
260 struct syslinux_movelist *ifrags,
261 struct syslinux_memmap *memmap)
263 struct syslinux_memmap *mmap = NULL, *mm;
264 struct syslinux_movelist *frags = NULL;
265 struct syslinux_movelist *mv, *space;
266 struct syslinux_movelist *f, **fp, **ep;
267 struct syslinux_movelist *o, **op;
268 addr_t needbase, needlen, copysrc, copydst, copylen;
269 addr_t freebase, freelen;
270 addr_t mstart, avail;
277 dprintf("entering syslinux_compute_movelist()...\n");
279 if (setjmp(new_movelist_bail)) {
280 dprintf("Out of working memory!\n");
286 /* Mark any memory that's in use by source material busy in the memory map */
287 mmap = syslinux_dup_memmap(memmap);
291 frags = dup_movelist(ifrags);
294 dprintf("Initial memory map:\n");
295 syslinux_dump_memmap(stdout, mmap);
298 for (f = frags; f; f = f->next) {
299 if (syslinux_add_memmap(&mmap, f->src, f->len, SMT_ALLOC)) {
300 dprintf("Cannot generate free map\n");
306 dprintf("Adjusted memory map:\n");
307 syslinux_dump_memmap(stdout, mmap);
310 /* Compute the freelist in movelist format. */
316 /* Yes, we really do want to run through the loop on SMT_END */
317 for (mm = mmap; mm; mm = mm->next) {
318 /* We can safely use to-be-zeroed memory as a scratch area. */
319 if (mmap->type == SMT_FREE || mmap->type == SMT_ZERO) {
322 mstart = mmap->start;
326 f = new_movelist(0, mstart, mmap->start - mstart);
340 dprintf("Current free list:\n");
341 syslinux_dump_movelist(stdout, space);
342 dprintf("Current frag list:\n");
343 syslinux_dump_movelist(stdout, frags);
347 while ( (f = *fp) ) {
348 dprintf("@: 0x%08x bytes at 0x%08x -> 0x%08x\n",
349 f->len, f->src, f->dst);
351 if ( f->src == f->dst ) {
356 /* See if we can move this chunk into place by claiming
357 the destination, or in the case of partial overlap, the
363 dprintf("need: base = 0x%08x, len = 0x%08x\n", needbase, needlen);
366 cbyte = f->dst; /* "Critical byte" */
367 if ( f->src < f->dst && (f->dst - f->src) < f->len ) {
368 /* "Shift up" type overlap */
369 needlen = f->dst - f->src;
370 needbase = f->dst + (f->len - needlen);
371 cbyte = f->dst + f->len - 1;
373 } else if ( f->src > f->dst && (f->src - f->dst) < f->len ) {
374 /* "Shift down" type overlap */
376 needlen = f->src - f->dst;
379 dprintf("need: base = 0x%08x, len = 0x%08x, "
380 "reverse = %d, cbyte = 0x%08x\n",
381 needbase, needlen, reverse, cbyte);
383 ep = is_free_zone(cbyte, 1, &space);
386 avail = needbase+needlen - (*ep)->src;
388 avail = (*ep)->len - (needbase - (*ep)->src);
394 /* We can move at least part of this chunk into place without
396 dprintf("space: start 0x%08x, len 0x%08x, free 0x%08x\n",
397 (*ep)->src, (*ep)->len, avail);
398 copylen = min(needlen, avail);
401 allocate_from(needbase+needlen-copylen, copylen, ep);
403 allocate_from(needbase, copylen, ep);
408 /* At this point, we need to evict something out of our space.
409 Find the object occupying the critical byte of our target space,
410 and move it out (the whole object if we can, otherwise a subset.)
411 Then move a chunk of ourselves into place. */
412 for ( op = &f->next, o = *op ; o ; op = &o->next, o = *op ) {
414 dprintf("O: 0x%08x bytes at 0x%08x -> 0x%08x\n",
415 o->len, o->src, o->dst);
417 if ( !(o->src <= cbyte && o->src+o->len > cbyte) )
418 continue; /* Not what we're looking for... */
420 /* Find somewhere to put it... */
422 if ( (ep = is_free_zone(o->dst, o->len, &space)) ) {
423 /* Score! We can move it into place directly... */
426 } else if ( (ep = free_area(o->len, &space)) ) {
427 /* We can move the whole chunk */
428 copydst = (*ep)->src;
431 /* Well, copy as much as we can... */
432 ep = free_area_max(&space);
434 dprintf("No free memory at all!\n");
435 goto bail; /* Stuck! */
438 /* Make sure we include the critical byte */
439 copydst = (*ep)->src;
441 copysrc = max(o->src, cbyte+1 - (*ep)->len);
442 copylen = cbyte+1 - copysrc;
445 copylen = min((*ep)->len, o->len - (cbyte-o->src));
448 allocate_from(copydst, copylen, ep);
450 if ( copylen < o->len ) {
451 op = split_movelist(copysrc, copylen, op);
455 mv = new_movelist(copydst, copysrc, copylen);
456 dprintf("C: 0x%08x bytes at 0x%08x -> 0x%08x\n",
457 mv->len, mv->src, mv->dst);
463 if ( copylen > needlen ) {
464 /* We don't need all the memory we freed up. Mark it free. */
465 if ( copysrc < needbase ) {
466 mv = new_movelist(0, copysrc, needbase-copysrc);
469 copylen -= (needbase-copysrc);
471 if ( copylen > needlen ) {
472 mv = new_movelist(0, copysrc+needlen, copylen-needlen);
481 dprintf("Cannot find the chunk containing the critical byte\n");
482 goto bail; /* Stuck! */
485 /* We're allowed to move the chunk into place now. */
490 dprintf("Q: copylen = 0x%08x, needlen = 0x%08x\n", copylen, needlen);
492 if ( copylen < needlen ) {
494 copydst += (f->len-copylen);
495 copysrc += (f->len-copylen);
498 dprintf("X: 0x%08x bytes at 0x%08x -> 0x%08x\n",
499 copylen, copysrc, copydst);
501 /* Didn't get all we wanted, so we have to split the chunk */
502 fp = split_movelist(copysrc, copylen, fp); /* Is this right? */
506 mv = new_movelist(f->dst, f->src, f->len);
507 dprintf("A: 0x%08x bytes at 0x%08x -> 0x%08x\n",
508 mv->len, mv->src, mv->dst);
512 /* Figure out what memory we just freed up */
513 if ( f->dst > f->src ) {
515 freelen = min(f->len, f->dst-f->src);
516 } else if ( f->src >= f->dst+f->len ) {
520 freelen = f->src-f->dst;
521 freebase = f->dst+f->len;
524 dprintf("F: 0x%08x bytes at 0x%08x\n", freelen, freebase);
526 mv = new_movelist(0, freebase, freelen);
531 again = 1; /* At least one chunk was moved */
538 syslinux_free_memmap(mmap);
540 free_movelist(&frags);
548 int main(int argc, char *argv[])
551 unsigned long d, s, l;
552 struct syslinux_movelist *frags;
553 struct syslinux_movelist **fep = &frags;
554 struct syslinux_movelist *space;
555 struct syslinux_movelist **sep = &space;
556 struct syslinux_movelist *mv, *moves;
558 f = fopen(argv[1], "r");
559 while ( fscanf(f, "%lx %lx %lx", &d, &s, &l) == 3 ) {
561 mv = new_movelist(d, s, l);
565 mv = new_movelist(0, s, l);
572 if ( syslinux_compute_movelist(&moves, frags, space) ) {
573 printf("Failed to compute a move sequence\n");
576 for ( mv = moves ; mv ; mv = mv->next ) {
577 printf("0x%08x bytes at 0x%08x -> 0x%08x\n",
578 mv->len, mv->src, mv->dst);