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. 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
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;
169 if (list
->start
> start
)
170 return NULL
; /* Invalid type in region */
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
)
192 slen
= s
->next
->start
- s
->start
;
194 if (!best
|| best_len
> slen
) {
202 *start
= best
->start
;
210 * Remove a chunk from the freelist
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
;
230 dprintf("Before alias resolution:\n");
231 syslinux_dump_movelist(*fraglist
);
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).
240 while ((mp
= *mpp
)) {
241 dprintf("mp -> (%#x,%#x,%#x)\n", mp
->dst
, mp
->src
, mp
->len
);
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.
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 */
259 *mpp
= mp
->next
; /* Remove from list */
263 np
= new_movelist(mp
->dst
+ mp
->len
- delta
,
264 mp
->src
+ mp
->len
- delta
, delta
);
273 np
= new_movelist(mp
->dst
, ps
, delta
);
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
;
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.
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
;
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
;
326 } else if (f
->src
> f
->dst
&& (f
->src
- f
->dst
) < f
->len
) {
327 /* "Shift down" type overlap */
328 needlen
= f
->src
- f
->dst
;
338 dprintf("Q: copylen = 0x%08x, needlen = 0x%08x\n", copylen
, needlen
);
340 if (copylen
< needlen
) {
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? */
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
);
359 /* Figure out what memory we just freed up */
360 if (f
->dst
> f
->src
) {
362 freelen
= min(f
->len
, f
->dst
- f
->src
);
363 } else if (f
->src
>= f
->dst
+ f
->len
) {
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
);
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
;
402 dprintf("entering syslinux_compute_movelist()...\n");
404 if (setjmp(new_movelist_bail
)) {
406 dprintf("Out of working memory!\n");
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();
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
) {
445 if (o
->src
== o
->dst
)
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
);
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
;
465 cbyte
= o
->dst
; /* "Critical byte" */
470 cbyte
= o
->dst
; /* "Critical byte" */
473 if (is_free_zone(mmap
, needbase
, needlen
)) {
475 dprintf("!: 0x%08x bytes at 0x%08x -> 0x%08x\n",
476 f
->len
, f
->src
, f
->dst
);
479 allocate_from(&mmap
, needbase
, copylen
);
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
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
);
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
;
504 cbyte
= f
->dst
; /* "Critical byte" */
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);
518 ep_len
= ep
->next
->start
- ep
->start
;
520 avail
= needbase
+ needlen
- ep
->start
;
522 avail
= ep_len
- (needbase
- ep
->start
);
528 /* We can move at least part of this chunk into place without
530 dprintf("space: start 0x%08x, len 0x%08x, free 0x%08x\n",
531 ep
->start
, ep_len
, avail
);
532 copylen
= min(needlen
, avail
);
535 allocate_from(&mmap
, needbase
+ needlen
- copylen
, copylen
);
537 allocate_from(&mmap
, needbase
, copylen
);
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... */
561 } else if (free_area(mmap
, o
->len
, &fstart
)) {
562 /* We can move the whole chunk */
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 */
576 copysrc
= max(o
->src
, cbyte
+ 1 - flen
);
577 copylen
= cbyte
+ 1 - copysrc
;
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
);
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
);
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
,
613 dprintf("Cannot find the chunk containing the critical byte\n");
614 goto bail
; /* Stuck! */
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 */
628 syslinux_free_memmap(mmap
);
630 free_movelist(&frags
);
632 free_movelist(&postcopy
);
640 int main(int argc
, char *argv
[])
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
;
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) {
659 syslinux_add_memmap(&memmap
, d
, l
, SMT_ZERO
);
661 mv
= new_movelist(d
, s
, l
);
666 syslinux_add_memmap(&memmap
, s
, l
, SMT_FREE
);
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");
683 dprintf("Final move list:\n");
684 syslinux_dump_movelist(stdout
, moves
);