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>
57 # define dprintf printf
59 # define dprintf(...) ((void)0)
62 #define min(x,y) ((x) < (y) ? (x) : (y))
63 #define max(x,y) ((x) > (y) ? (x) : (y))
65 static jmp_buf new_movelist_bail
;
67 static struct syslinux_movelist
*
68 new_movelist(addr_t dst
, addr_t src
, addr_t len
)
70 struct syslinux_movelist
*ml
= malloc(sizeof(struct syslinux_movelist
));
73 longjmp(new_movelist_bail
, 1);
83 static struct syslinux_movelist
*
84 dup_movelist(struct syslinux_movelist
*src
)
86 struct syslinux_movelist
*dst
= NULL
, **dstp
= &dst
, *ml
;
89 ml
= new_movelist(src
->dst
, src
->src
, src
->len
);
99 * Take a chunk, entirely confined in **parentptr, and split it off so that
100 * it has its own structure.
102 static struct syslinux_movelist
**
103 split_movelist(addr_t start
, addr_t len
, struct syslinux_movelist
**parentptr
)
105 struct syslinux_movelist
*m
, *ml
= *parentptr
;
107 assert(start
>= ml
->src
);
108 assert(start
< ml
->src
+ml
->len
);
110 /* Split off the beginning */
111 if ( start
> ml
->src
) {
112 addr_t l
= start
- ml
->src
;
114 m
= new_movelist(ml
->dst
+l
, start
, ml
->len
-l
);
119 parentptr
= &ml
->next
;
120 ml
= m
; /* Continue processing the new node */
123 /* Split off the end */
124 if ( ml
->len
> len
) {
125 addr_t l
= ml
->len
- len
;
127 m
= new_movelist(ml
->dst
+len
, ml
->src
+len
, l
);
137 delete_movelist(struct syslinux_movelist
**parentptr
)
139 struct syslinux_movelist
*o
= *parentptr
;
140 *parentptr
= o
->next
;
145 free_movelist(struct syslinux_movelist
**parentptr
)
148 delete_movelist(parentptr
);
152 * Scan the freelist looking for a particular chunk of memory
154 static struct syslinux_movelist
**
155 is_free_zone(addr_t start
, addr_t len
, struct syslinux_movelist
**space
)
157 struct syslinux_movelist
*s
;
159 while ( (s
= *space
) ) {
160 if ( s
->src
<= start
&& s
->src
+s
->len
>= start
+len
)
169 * Scan the freelist looking for the smallest chunk of memory which
172 static struct syslinux_movelist
**
173 free_area(addr_t len
, struct syslinux_movelist
**space
)
175 struct syslinux_movelist
**best
= NULL
;
176 struct syslinux_movelist
*s
;
178 while ( (s
= *space
) ) {
179 if ( s
->len
>= len
) {
180 if ( !best
|| (*best
)->len
> s
->len
)
191 * Scan the freelist looking for the largest chunk of memory,
194 static struct syslinux_movelist
**
195 free_area_max(struct syslinux_movelist
**space
)
197 struct syslinux_movelist
**best
= NULL
;
198 struct syslinux_movelist
*s
;
200 while ( (s
= *space
) ) {
201 if ( !best
|| s
->len
> (*best
)->len
)
210 * Remove a chunk from the freelist
213 allocate_from(addr_t start
, addr_t len
, struct syslinux_movelist
**parentptr
)
215 struct syslinux_movelist
*c
= *parentptr
;
217 assert(c
->src
<= start
);
218 assert(c
->src
+c
->len
>= start
+len
);
220 if ( c
->src
!= start
|| c
->len
!= len
) {
221 parentptr
= split_movelist(start
, len
, parentptr
);
225 *parentptr
= c
->next
;
231 * Remove any chunk from the freelist which is already
232 * claimed by source data. This somewhat frivolously
233 * assumes that the fragments are either completely
234 * covered by a free zone or not covered at all.
237 tidy_freelist(struct syslinux_movelist
**frags
,
238 struct syslinux_movelist
**space
)
240 struct syslinux_movelist
**sep
;
241 struct syslinux_movelist
*f
;
243 while ( (f
= *frags
) ) {
244 if ( (sep
= is_free_zone(f
->src
, f
->len
, space
)) )
245 allocate_from(f
->src
, f
->len
, sep
);
252 * moves is computed from "frags" and "freemem". "space" lists
253 * free memory areas at our disposal, and is (src, cnt) only.
257 syslinux_compute_movelist(struct syslinux_movelist
**moves
,
258 struct syslinux_movelist
*ifrags
,
259 struct syslinux_memmap
*memmap
)
261 struct syslinux_memmap
*mmap
= NULL
, *mm
;
262 struct syslinux_movelist
*frags
= NULL
;
263 struct syslinux_movelist
*mv
, *space
;
264 struct syslinux_movelist
*f
, **fp
, **ep
;
265 struct syslinux_movelist
*o
, **op
;
266 addr_t needbase
, needlen
, copysrc
, copydst
, copylen
;
267 addr_t freebase
, freelen
;
272 dprintf("entering syslinux_compute_movelist()...\n");
274 if (setjmp(new_movelist_bail
))
279 /* Mark any memory that's in use by source material busy in the memory map */
280 mmap
= syslinux_dup_memmap(memmap
);
284 frags
= dup_movelist(ifrags
);
287 dprintf("Initial memory map:\n");
288 syslinux_dump_memmap(stdout
, mmap
);
291 for (f
= frags
; f
; f
= f
->next
) {
292 if (syslinux_add_memmap(&mmap
, f
->src
, f
->len
, SMT_ALLOC
))
297 dprintf("Adjusted memory map:\n");
298 syslinux_dump_memmap(stdout
, mmap
);
301 /* Compute the freelist in movelist format. */
307 /* Yes, we really do want to run through the loop on SMT_END */
308 for (mm
= mmap
; mm
; mm
= mm
->next
) {
309 /* We can safely use to-be-zeroed memory as a scratch area. */
310 if (mmap
->type
== SMT_FREE
|| mmap
->type
== SMT_ZERO
) {
313 mstart
= mmap
->start
;
317 f
= new_movelist(0, mstart
, mmap
->start
- mstart
);
328 dprintf("Computed free list:\n");
329 syslinux_dump_movelist(stdout
, space
);
332 for ( fp
= &frags
, f
= *fp
; f
; fp
= &f
->next
, f
= *fp
) {
333 dprintf("@: 0x%08x bytes at 0x%08x -> 0x%08x\n",
334 f
->len
, f
->src
, f
->dst
);
336 if ( f
->src
== f
->dst
) {
337 //delete_movelist(fp); /* Already in the right place! */
341 /* See if we can move this chunk into place by claiming
342 the destination, or in the case of partial overlap, the
348 dprintf("need: base = 0x%08x, len = 0x%08x\n", needbase
, needlen
);
350 if ( f
->src
< f
->dst
&& (f
->dst
- f
->src
) < f
->len
) {
351 /* "Shift up" type overlap */
352 needlen
= f
->dst
- f
->src
;
353 needbase
= f
->dst
+ (f
->len
- needlen
);
354 } else if ( f
->src
> f
->dst
&& (f
->src
- f
->dst
) < f
->len
) {
355 /* "Shift down" type overlap */
357 needlen
= f
->src
- f
->dst
;
360 if ( (ep
= is_free_zone(needbase
, 1, &space
)) ) {
361 /* We can move at least part of this chunk into place without further ado */
362 copylen
= min(needlen
, (*ep
)->len
);
363 allocate_from(needbase
, copylen
, ep
);
367 /* At this point, we need to evict something out of our space.
368 Find the object occupying the first byte of our target space,
369 and move it out (the whole object if we can, otherwise a subset.)
370 Then move a chunk of ourselves into place. */
371 for ( op
= &f
->next
, o
= *op
; o
; op
= &o
->next
, o
= *op
) {
373 dprintf("O: 0x%08x bytes at 0x%08x -> 0x%08x\n",
374 o
->len
, o
->src
, o
->dst
);
376 if ( !(o
->src
<= needbase
&& o
->src
+o
->len
> needbase
) )
377 continue; /* Not what we're looking for... */
379 /* Find somewhere to put it... */
380 if ( (ep
= free_area(o
->len
, &space
)) ) {
381 /* We got what we wanted... */
382 copydst
= (*ep
)->src
;
385 ep
= free_area_max(&space
);
387 goto bail
; /* Stuck! */
388 copydst
= (*ep
)->src
;
389 copylen
= (*ep
)->len
;
391 allocate_from(copydst
, copylen
, ep
);
393 if ( copylen
>= o
->len
- (needbase
-o
->src
) ) {
394 copysrc
= o
->src
+ (o
->len
- copylen
);
399 if ( copylen
< o
->len
) {
400 op
= split_movelist(copysrc
, copylen
, op
);
404 mv
= new_movelist(copydst
, copysrc
, copylen
);
405 dprintf("C: 0x%08x bytes at 0x%08x -> 0x%08x\n",
406 mv
->len
, mv
->src
, mv
->dst
);
412 if ( copylen
> needlen
) {
413 /* We don't need all the memory we freed up. Mark it free. */
414 if ( copysrc
< needbase
) {
415 mv
= new_movelist(0, copysrc
, needbase
-copysrc
);
418 copylen
-= (needbase
-copysrc
);
420 if ( copylen
> needlen
) {
421 mv
= new_movelist(0, copysrc
+needlen
, copylen
-needlen
);
429 goto bail
; /* Stuck! */
432 /* We're allowed to move the chunk into place now. */
434 if ( copylen
< needlen
) {
435 /* Didn't get all we wanted, so we have to split the chunk */
436 fp
= split_movelist(f
->src
, copylen
+(needbase
-f
->dst
), fp
);
440 mv
= new_movelist(f
->dst
, f
->src
, f
->len
);
441 dprintf("A: 0x%08x bytes at 0x%08x -> 0x%08x\n",
442 mv
->len
, mv
->src
, mv
->dst
);
446 /* Figure out what memory we just freed up */
447 if ( f
->dst
> f
->src
) {
449 freelen
= min(f
->len
, f
->dst
-f
->src
);
450 } else if ( f
->src
>= f
->dst
+f
->len
) {
454 freelen
= f
->src
-f
->dst
;
455 freebase
= f
->dst
+f
->len
;
458 mv
= new_movelist(0, freebase
, freelen
);
466 syslinux_free_memmap(mmap
);
468 free_movelist(&frags
);
476 int main(int argc
, char *argv
[])
479 unsigned long d
, s
, l
;
480 struct syslinux_movelist
*frags
;
481 struct syslinux_movelist
**fep
= &frags
;
482 struct syslinux_movelist
*space
;
483 struct syslinux_movelist
**sep
= &space
;
484 struct syslinux_movelist
*mv
, *moves
;
486 f
= fopen(argv
[1], "r");
487 while ( fscanf(f
, "%lx %lx %lx", &d
, &s
, &l
) == 3 ) {
489 mv
= new_movelist(d
, s
, l
);
493 mv
= new_movelist(0, s
, l
);
500 if ( syslinux_compute_movelist(&moves
, frags
, space
) ) {
501 printf("Failed to compute a move sequence\n");
504 for ( mv
= moves
; mv
; mv
= mv
->next
) {
505 printf("0x%08x bytes at 0x%08x -> 0x%08x\n",
506 mv
->len
, mv
->src
, mv
->dst
);