1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007 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);
84 * Take a chunk, entirely confined in **parentptr, and split it off so that
85 * it has its own structure.
87 static struct syslinux_movelist
**
88 split_movelist(addr_t start
, addr_t len
, struct syslinux_movelist
**parentptr
)
90 struct syslinux_movelist
*m
, *ml
= *parentptr
;
92 assert(start
>= ml
->src
);
93 assert(start
< ml
->src
+ml
->len
);
95 /* Split off the beginning */
96 if ( start
> ml
->src
) {
97 addr_t l
= start
- ml
->src
;
99 m
= new_movelist(ml
->dst
+l
, start
, ml
->len
-l
);
104 parentptr
= &ml
->next
;
105 ml
= m
; /* Continue processing the new node */
108 /* Split off the end */
109 if ( ml
->len
> len
) {
110 addr_t l
= ml
->len
- len
;
112 m
= new_movelist(ml
->dst
+len
, ml
->src
+len
, l
);
123 delete_movelist(struct syslinux_movelist
**parentptr
)
125 struct syslinux_movelist
*o
= *parentptr
;
126 *parentptr
= o
->next
;
132 * Scan the freelist looking for a particular chunk of memory
134 static struct syslinux_movelist
**
135 is_free_zone(addr_t start
, addr_t len
, struct syslinux_movelist
**space
)
137 struct syslinux_movelist
*s
;
139 while ( (s
= *space
) ) {
140 if ( s
->src
<= start
&& s
->src
+s
->len
>= start
+len
)
149 * Scan the freelist looking for the smallest chunk of memory which
152 static struct syslinux_movelist
**
153 free_area(addr_t len
, struct syslinux_movelist
**space
)
155 struct syslinux_movelist
**best
= NULL
;
156 struct syslinux_movelist
*s
;
158 while ( (s
= *space
) ) {
159 if ( s
->len
>= len
) {
160 if ( !best
|| (*best
)->len
> s
->len
)
171 * Scan the freelist looking for the largest chunk of memory,
174 static struct syslinux_movelist
**
175 free_area_max(struct syslinux_movelist
**space
)
177 struct syslinux_movelist
**best
= NULL
;
178 struct syslinux_movelist
*s
;
180 while ( (s
= *space
) ) {
181 if ( !best
|| s
->len
> (*best
)->len
)
190 * Remove a chunk from the freelist
193 allocate_from(addr_t start
, addr_t len
, struct syslinux_movelist
**parentptr
)
195 struct syslinux_movelist
*c
= *parentptr
;
197 assert(c
->src
<= start
);
198 assert(c
->src
+c
->len
>= start
+len
);
200 if ( c
->src
!= start
|| c
->len
!= len
) {
201 parentptr
= split_movelist(start
, len
, parentptr
);
205 *parentptr
= c
->next
;
211 * Remove any chunk from the freelist which is already
212 * claimed by source data. This somewhat frivolously
213 * assumes that the fragments are either completely
214 * covered by a free zone or not covered at all.
217 tidy_freelist(struct syslinux_movelist
**frags
,
218 struct syslinux_movelist
**space
)
220 struct syslinux_movelist
**sep
;
221 struct syslinux_movelist
*f
;
223 while ( (f
= *frags
) ) {
224 if ( (sep
= is_free_zone(f
->src
, f
->len
, space
)) )
225 allocate_from(f
->src
, f
->len
, sep
);
232 * moves is computed from "frags" and "freemem". "space" lists
233 * free memory areas at our disposal, and is (src, cnt) only.
237 syslinux_compute_movelist(struct syslinux_movelist
**moves
,
238 struct syslinux_movelist
*frags
,
239 struct syslinux_memmap
*memmap
)
241 struct syslinux_memmap
*mmap
= NULL
, *mm
;
242 struct syslinux_movelist
*mv
, *space
;
243 struct syslinux_movelist
*f
, **fp
, **ep
;
244 struct syslinux_movelist
*o
, **op
;
245 addr_t needbase
, needlen
, copysrc
, copydst
, copylen
;
246 addr_t freebase
, freelen
;
251 dprintf("entering syslinux_compute_movelist()...\n");
253 if (setjmp(new_movelist_bail
))
258 /* Mark any memory that's in use by source material busy in the memory map */
259 mmap
= syslinux_dup_memmap(memmap
);
264 dprintf("Initial memory map:\n");
265 syslinux_dump_memmap(stdout
, mmap
);
268 for (f
= frags
; f
; f
= f
->next
) {
269 if (syslinux_add_memmap(&mmap
, f
->src
, f
->len
, SMT_ALLOC
))
274 dprintf("Adjusted memory map:\n");
275 syslinux_dump_memmap(stdout
, mmap
);
278 /* Compute the freelist in movelist format. */
284 /* Yes, we really do want to run through the loop on SMT_END */
285 for (mm
= mmap
; mm
; mm
= mm
->next
) {
286 /* We can safely use to-be-zeroed memory as a scratch area. */
287 if (mmap
->type
== SMT_FREE
|| mmap
->type
== SMT_ZERO
) {
290 mstart
= mmap
->start
;
294 f
= new_movelist(0, mstart
, mmap
->start
- mstart
);
305 dprintf("Computed free list:\n");
306 syslinux_dump_movelist(stdout
, space
);
309 for ( fp
= &frags
, f
= *fp
; f
; fp
= &f
->next
, f
= *fp
) {
310 dprintf("@: 0x%08x bytes at 0x%08x -> 0x%08x\n",
311 f
->len
, f
->src
, f
->dst
);
313 if ( f
->src
== f
->dst
) {
314 //delete_movelist(fp); /* Already in the right place! */
318 /* See if we can move this chunk into place by claiming
319 the destination, or in the case of partial overlap, the
325 dprintf("need: base = 0x%08x, len = 0x%08x\n", needbase
, needlen
);
327 if ( f
->src
< f
->dst
&& (f
->dst
- f
->src
) < f
->len
) {
328 /* "Shift up" type overlap */
329 needlen
= f
->dst
- f
->src
;
330 needbase
= f
->dst
+ (f
->len
- needlen
);
331 } else if ( f
->src
> f
->dst
&& (f
->src
- f
->dst
) < f
->len
) {
332 /* "Shift down" type overlap */
334 needlen
= f
->src
- f
->dst
;
337 if ( (ep
= is_free_zone(needbase
, 1, &space
)) ) {
338 /* We can move at least part of this chunk into place without further ado */
339 copylen
= min(needlen
, (*ep
)->len
);
340 allocate_from(needbase
, copylen
, ep
);
344 /* At this point, we need to evict something out of our space.
345 Find the object occupying the first byte of our target space,
346 and move it out (the whole object if we can, otherwise a subset.)
347 Then move a chunk of ourselves into place. */
348 for ( op
= &f
->next
, o
= *op
; o
; op
= &o
->next
, o
= *op
) {
350 dprintf("O: 0x%08x bytes at 0x%08x -> 0x%08x\n",
351 o
->len
, o
->src
, o
->dst
);
353 if ( !(o
->src
<= needbase
&& o
->src
+o
->len
> needbase
) )
354 continue; /* Not what we're looking for... */
356 /* Find somewhere to put it... */
357 if ( (ep
= free_area(o
->len
, &space
)) ) {
358 /* We got what we wanted... */
359 copydst
= (*ep
)->src
;
362 ep
= free_area_max(&space
);
364 return -1; /* Stuck! */
365 copydst
= (*ep
)->src
;
366 copylen
= (*ep
)->len
;
368 allocate_from(copydst
, copylen
, ep
);
370 if ( copylen
>= o
->len
- (needbase
-o
->src
) ) {
371 copysrc
= o
->src
+ (o
->len
- copylen
);
376 if ( copylen
< o
->len
) {
377 op
= split_movelist(copysrc
, copylen
, op
);
381 mv
= new_movelist(copydst
, copysrc
, copylen
);
382 dprintf("C: 0x%08x bytes at 0x%08x -> 0x%08x\n",
383 mv
->len
, mv
->src
, mv
->dst
);
389 if ( copylen
> needlen
) {
390 /* We don't need all the memory we freed up. Mark it free. */
391 if ( copysrc
< needbase
) {
392 mv
= new_movelist(0, copysrc
, needbase
-copysrc
);
395 copylen
-= (needbase
-copysrc
);
397 if ( copylen
> needlen
) {
398 mv
= new_movelist(0, copysrc
+needlen
, copylen
-needlen
);
406 return -1; /* Stuck! */
409 /* We're allowed to move the chunk into place now. */
411 if ( copylen
< needlen
) {
412 /* Didn't get all we wanted, so we have to split the chunk */
413 fp
= split_movelist(f
->src
, copylen
+(needbase
-f
->dst
), fp
);
417 mv
= new_movelist(f
->dst
, f
->src
, f
->len
);
418 dprintf("A: 0x%08x bytes at 0x%08x -> 0x%08x\n",
419 mv
->len
, mv
->src
, mv
->dst
);
423 /* Figure out what memory we just freed up */
424 if ( f
->dst
> f
->src
) {
426 freelen
= min(f
->len
, f
->dst
-f
->src
);
427 } else if ( f
->src
>= f
->dst
+f
->len
) {
431 freelen
= f
->src
-f
->dst
;
432 freebase
= f
->dst
+f
->len
;
435 mv
= new_movelist(0, freebase
, freelen
);
443 syslinux_free_memmap(mmap
);
451 int main(int argc
, char *argv
[])
454 unsigned long d
, s
, l
;
455 struct syslinux_movelist
*frags
;
456 struct syslinux_movelist
**fep
= &frags
;
457 struct syslinux_movelist
*space
;
458 struct syslinux_movelist
**sep
= &space
;
459 struct syslinux_movelist
*mv
, *moves
;
461 f
= fopen(argv
[1], "r");
462 while ( fscanf(f
, "%lx %lx %lx", &d
, &s
, &l
) == 3 ) {
464 mv
= new_movelist(d
, s
, l
);
468 mv
= new_movelist(0, s
, l
);
475 if ( syslinux_compute_movelist(&moves
, frags
, space
) ) {
476 printf("Failed to compute a move sequence\n");
479 for ( mv
= moves
; mv
; mv
= mv
->next
) {
480 printf("0x%08x bytes at 0x%08x -> 0x%08x\n",
481 mv
->len
, mv
->src
, mv
->dst
);