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 add_freelist(struct syslinux_memmap
**mmap
, addr_t start
,
102 addr_t len
, enum syslinux_memmap_types type
)
104 if (syslinux_add_memmap(mmap
, start
, len
, type
))
105 longjmp(new_movelist_bail
, 1);
109 * Take a chunk, entirely confined in **parentptr, and split it off so that
110 * it has its own structure.
112 static struct syslinux_movelist
**
113 split_movelist(addr_t start
, addr_t len
, struct syslinux_movelist
**parentptr
)
115 struct syslinux_movelist
*m
, *ml
= *parentptr
;
117 assert(start
>= ml
->src
);
118 assert(start
< ml
->src
+ml
->len
);
120 /* Split off the beginning */
121 if ( start
> ml
->src
) {
122 addr_t l
= start
- ml
->src
;
124 m
= new_movelist(ml
->dst
+l
, start
, ml
->len
-l
);
129 parentptr
= &ml
->next
;
130 ml
= m
; /* Continue processing the new node */
133 /* Split off the end */
134 if ( ml
->len
> len
) {
135 addr_t l
= ml
->len
- len
;
137 m
= new_movelist(ml
->dst
+len
, ml
->src
+len
, l
);
147 delete_movelist(struct syslinux_movelist
**parentptr
)
149 struct syslinux_movelist
*o
= *parentptr
;
150 *parentptr
= o
->next
;
155 free_movelist(struct syslinux_movelist
**parentptr
)
158 delete_movelist(parentptr
);
162 * Scan the freelist looking for a particular chunk of memory
164 static const struct syslinux_memmap
*
165 is_free_zone(const struct syslinux_memmap
*list
, addr_t start
, addr_t len
)
167 dprintf("f: 0x%08x bytes at 0x%08x\n", len
, start
);
173 while (list
->type
!= SMT_END
) {
174 llast
= list
->next
->start
-1;
175 if (list
->start
<= start
) {
177 /* Chunk has a single, well-defined type */
178 if (list
->type
== SMT_FREE
) {
179 dprintf("F: 0x%08x bytes at 0x%08x\n",
180 list
->next
->start
, list
->start
);
181 return list
; /* It's free */
183 return NULL
; /* Not free */
184 } else if (llast
>= start
) {
185 return NULL
; /* Crosses region boundary */
191 return NULL
; /* Internal error? */
195 * Scan the freelist looking for the smallest chunk of memory which
196 * can fit X bytes; returns the length of the block on success.
198 static addr_t
free_area(const struct syslinux_memmap
*mmap
,
199 addr_t len
, addr_t
*start
)
201 const struct syslinux_memmap
*best
= NULL
;
202 const struct syslinux_memmap
*s
;
203 addr_t slen
, best_len
= -1;
205 for (s
= mmap
; s
->type
!= SMT_END
; s
= s
->next
) {
206 slen
= s
->next
->start
- s
->start
;
208 if (!best
|| best_len
> slen
) {
216 *start
= best
->start
;
224 * Remove a chunk from the freelist
227 allocate_from(struct syslinux_memmap
**mmap
, addr_t start
, addr_t len
)
229 syslinux_add_memmap(mmap
, start
, len
, SMT_ALLOC
);
233 * The code to actually emit moving of a chunk into its final place.
236 move_chunk(struct syslinux_movelist
***moves
,
237 struct syslinux_memmap
**mmap
,
238 struct syslinux_movelist
**fp
, addr_t copylen
)
240 addr_t copydst
, copysrc
;
241 addr_t freebase
, freelen
;
244 struct syslinux_movelist
*f
= *fp
, *mv
;
246 if ( f
->src
< f
->dst
&& (f
->dst
- f
->src
) < f
->len
) {
247 /* "Shift up" type overlap */
248 needlen
= f
->dst
- f
->src
;
250 } else if ( f
->src
> f
->dst
&& (f
->src
- f
->dst
) < f
->len
) {
251 /* "Shift down" type overlap */
252 needlen
= f
->src
- f
->dst
;
262 dprintf("Q: copylen = 0x%08x, needlen = 0x%08x\n", copylen
, needlen
);
264 if ( copylen
< needlen
) {
266 copydst
+= (f
->len
-copylen
);
267 copysrc
+= (f
->len
-copylen
);
270 dprintf("X: 0x%08x bytes at 0x%08x -> 0x%08x\n",
271 copylen
, copysrc
, copydst
);
273 /* Didn't get all we wanted, so we have to split the chunk */
274 fp
= split_movelist(copysrc
, copylen
, fp
); /* Is this right? */
278 mv
= new_movelist(f
->dst
, f
->src
, f
->len
);
279 dprintf("A: 0x%08x bytes at 0x%08x -> 0x%08x\n",
280 mv
->len
, mv
->src
, mv
->dst
);
284 /* Figure out what memory we just freed up */
285 if ( f
->dst
> f
->src
) {
287 freelen
= min(f
->len
, f
->dst
-f
->src
);
288 } else if ( f
->src
>= f
->dst
+f
->len
) {
292 freelen
= f
->src
-f
->dst
;
293 freebase
= f
->dst
+f
->len
;
296 dprintf("F: 0x%08x bytes at 0x%08x\n", freelen
, freebase
);
298 add_freelist(mmap
, freebase
, freelen
, SMT_FREE
);
304 * moves is computed from "frags" and "freemem". "space" lists
305 * free memory areas at our disposal, and is (src, cnt) only.
309 syslinux_compute_movelist(struct syslinux_movelist
**moves
,
310 struct syslinux_movelist
*ifrags
,
311 struct syslinux_memmap
*memmap
)
313 struct syslinux_memmap
*mmap
= NULL
;
314 const struct syslinux_memmap
*mm
, *ep
;
315 struct syslinux_movelist
*frags
= NULL
;
316 struct syslinux_movelist
*mv
;
317 struct syslinux_movelist
*f
, **fp
;
318 struct syslinux_movelist
*o
, **op
;
319 addr_t needbase
, needlen
, copysrc
, copydst
, copylen
;
327 dprintf("entering syslinux_compute_movelist()...\n");
329 if (setjmp(new_movelist_bail
)) {
331 dprintf("Out of working memory!\n");
337 /* Create our memory map. Anything that is SMT_FREE or SMT_ZERO is
338 fair game, but mark anything used by source material as SMT_ALLOC. */
339 mmap
= syslinux_init_memmap();
343 frags
= dup_movelist(ifrags
);
345 for (mm
= memmap
; mm
->type
!= SMT_END
; mm
= mm
->next
)
346 add_freelist(&mmap
, mm
->start
, mm
->next
->start
- mm
->start
,
347 mm
->type
== SMT_ZERO
? SMT_FREE
: mm
->type
);
349 for (f
= frags
; f
; f
= f
->next
)
350 add_freelist(&mmap
, f
->src
, f
->len
, SMT_ALLOC
);
352 /* As long as there are unprocessed fragments in the chain... */
353 while ( (fp
= &frags
, f
= *fp
) ) {
356 dprintf("Current free list:\n");
357 syslinux_dump_memmap(stdout
, mmap
);
358 dprintf("Current frag list:\n");
359 syslinux_dump_movelist(stdout
, frags
);
362 /* Scan for fragments which can be discarded without action. */
363 if ( f
->src
== f
->dst
) {
368 while ( (o
= *op
) ) {
369 if ( o
->src
== o
->dst
)
375 /* Scan for fragments which can be immediately moved
376 to their final destination, if so handle them now */
377 for ( op
= fp
; (o
= *op
); op
= &o
->next
) {
378 if ( o
->src
< o
->dst
&& (o
->dst
- o
->src
) < o
->len
) {
379 /* "Shift up" type overlap */
380 needlen
= o
->dst
- o
->src
;
381 needbase
= o
->dst
+ (o
->len
- needlen
);
383 cbyte
= o
->dst
+ o
->len
- 1;
384 } else if ( o
->src
> o
->dst
&& (o
->src
- o
->dst
) < o
->len
) {
385 /* "Shift down" type overlap */
386 needlen
= o
->src
- o
->dst
;
389 cbyte
= o
->dst
; /* "Critical byte" */
394 cbyte
= o
->dst
; /* "Critical byte" */
397 if (is_free_zone(mmap
, needbase
, needlen
)) {
399 dprintf("!: 0x%08x bytes at 0x%08x -> 0x%08x\n",
400 f
->len
, f
->src
, f
->dst
);
403 allocate_from(&mmap
, needbase
, copylen
);
408 /* Ok, bother. Need to do real work at least with one chunk. */
410 dprintf("@: 0x%08x bytes at 0x%08x -> 0x%08x\n",
411 f
->len
, f
->src
, f
->dst
);
413 /* See if we can move this chunk into place by claiming
414 the destination, or in the case of partial overlap, the
417 if ( f
->src
< f
->dst
&& (f
->dst
- f
->src
) < f
->len
) {
418 /* "Shift up" type overlap */
419 needlen
= f
->dst
- f
->src
;
420 needbase
= f
->dst
+ (f
->len
- needlen
);
422 cbyte
= f
->dst
+ f
->len
- 1;
423 } else if ( f
->src
> f
->dst
&& (f
->src
- f
->dst
) < f
->len
) {
424 /* "Shift down" type overlap */
425 needlen
= f
->src
- f
->dst
;
428 cbyte
= f
->dst
; /* "Critical byte" */
436 dprintf("need: base = 0x%08x, len = 0x%08x, "
437 "reverse = %d, cbyte = 0x%08x\n",
438 needbase
, needlen
, reverse
, cbyte
);
440 ep
= is_free_zone(mmap
, cbyte
, 1);
442 ep_len
= ep
->next
->start
- ep
->start
;
444 avail
= needbase
+needlen
- ep
->start
;
446 avail
= ep_len
- (needbase
- ep
->start
);
452 /* We can move at least part of this chunk into place without
454 dprintf("space: start 0x%08x, len 0x%08x, free 0x%08x\n",
455 ep
->start
, ep_len
, avail
);
456 copylen
= min(needlen
, avail
);
459 allocate_from(&mmap
, needbase
+needlen
-copylen
, copylen
);
461 allocate_from(&mmap
, needbase
, copylen
);
466 /* At this point, we need to evict something out of our space.
467 Find the object occupying the critical byte of our target space,
468 and move it out (the whole object if we can, otherwise a subset.)
469 Then move a chunk of ourselves into place. */
470 for ( op
= &f
->next
, o
= *op
; o
; op
= &o
->next
, o
= *op
) {
472 dprintf("O: 0x%08x bytes at 0x%08x -> 0x%08x\n",
473 o
->len
, o
->src
, o
->dst
);
475 if ( !(o
->src
<= cbyte
&& o
->src
+o
->len
> cbyte
) )
476 continue; /* Not what we're looking for... */
478 /* Find somewhere to put it... */
480 if ( is_free_zone(mmap
, o
->dst
, o
->len
) ) {
481 /* Score! We can move it into place directly... */
485 } else if ( free_area(mmap
, o
->len
, &fstart
) ) {
486 /* We can move the whole chunk */
491 /* Well, copy as much as we can... */
492 if (syslinux_memmap_largest(mmap
, SMT_FREE
, &fstart
, &flen
)) {
493 dprintf("No free memory at all!\n");
494 goto bail
; /* Stuck! */
497 /* Make sure we include the critical byte */
500 copysrc
= max(o
->src
, cbyte
+1 - flen
);
501 copylen
= cbyte
+1 - copysrc
;
504 copylen
= min(flen
, o
->len
- (cbyte
-o
->src
));
507 allocate_from(&mmap
, copydst
, copylen
);
509 if ( copylen
< o
->len
) {
510 op
= split_movelist(copysrc
, copylen
, op
);
514 mv
= new_movelist(copydst
, copysrc
, copylen
);
515 dprintf("C: 0x%08x bytes at 0x%08x -> 0x%08x\n",
516 mv
->len
, mv
->src
, mv
->dst
);
522 if ( copylen
> needlen
) {
523 /* We don't need all the memory we freed up. Mark it free. */
524 if ( copysrc
< needbase
) {
525 add_freelist(&mmap
, copysrc
, needbase
-copysrc
, SMT_FREE
);
526 copylen
-= (needbase
-copysrc
);
528 if ( copylen
> needlen
) {
529 add_freelist(&mmap
, copysrc
+needlen
, copylen
-needlen
, SMT_FREE
);
536 dprintf("Cannot find the chunk containing the critical byte\n");
537 goto bail
; /* Stuck! */
540 move_chunk(&moves
, &mmap
, fp
, copylen
);
546 syslinux_free_memmap(mmap
);
548 free_movelist(&frags
);
556 int main(int argc
, char *argv
[])
559 unsigned long d
, s
, l
;
560 struct syslinux_movelist
*frags
;
561 struct syslinux_movelist
**fep
= &frags
;
562 struct syslinux_movelist
*space
;
563 struct syslinux_movelist
**sep
= &space
;
564 struct syslinux_movelist
*mv
, *moves
;
566 f
= fopen(argv
[1], "r");
567 while ( fscanf(f
, "%lx %lx %lx", &d
, &s
, &l
) == 3 ) {
569 mv
= new_movelist(d
, s
, l
);
573 mv
= new_movelist(0, s
, l
);
580 if ( syslinux_compute_movelist(&moves
, frags
, space
) ) {
581 printf("Failed to compute a move sequence\n");
584 for ( mv
= moves
; mv
; mv
= mv
->next
) {
585 printf("0x%08x bytes at 0x%08x -> 0x%08x\n",
586 mv
->len
, mv
->src
, mv
->dst
);