Adding upstream version 3.62+dfsg.
[syslinux-debian/hramrach.git] / com32 / lib / syslinux / movebits.c
blob9ec2c8c4d29fe9a4670f0db2a06f607609cd6b7e
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
12 * conditions:
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 * ----------------------------------------------------------------------- */
29 * movebits.c
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.
40 #include <assert.h>
41 #include <stdio.h>
42 #include <errno.h>
43 #include <stdlib.h>
44 #include <inttypes.h>
45 #include <setjmp.h>
47 #include <syslinux/movebits.h>
49 #ifdef TEST
50 # define DEBUG 1
51 #else
52 # define DEBUG 0
53 #endif
55 #if DEBUG
56 # include <stdio.h>
57 # define dprintf printf
58 #else
59 # define dprintf(...) ((void)0)
60 #endif
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));
72 if ( !ml )
73 longjmp(new_movelist_bail, 1);
75 ml->dst = dst;
76 ml->src = src;
77 ml->len = len;
78 ml->next = NULL;
80 return ml;
83 static struct syslinux_movelist *
84 dup_movelist(struct syslinux_movelist *src)
86 struct syslinux_movelist *dst = NULL, **dstp = &dst, *ml;
88 while (src) {
89 ml = new_movelist(src->dst, src->src, src->len);
90 *dstp = ml;
91 dstp = &ml->next;
92 src = src->next;
95 return dst;
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);
115 m->next = ml->next;
116 ml->len = l;
117 ml->next = m;
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);
128 m->next = ml->next;
129 ml->len = len;
130 ml->next = m;
133 return parentptr;
136 static void
137 delete_movelist(struct syslinux_movelist **parentptr)
139 struct syslinux_movelist *o = *parentptr;
140 *parentptr = o->next;
141 free(o);
144 static void
145 free_movelist(struct syslinux_movelist **parentptr)
147 while (*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 )
161 return space;
162 space = &s->next;
165 return NULL;
169 * Scan the freelist looking for the smallest chunk of memory which
170 * can fit X bytes
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 )
181 best = space;
183 space = &s->next;
186 return best;
191 * Scan the freelist looking for the largest chunk of memory,
192 * returns parent ptr
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 )
202 best = space;
203 space = &s->next;
206 return best;
210 * Remove a chunk from the freelist
212 static void
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);
222 c = *parentptr;
225 *parentptr = c->next;
226 free(c);
229 #if 0
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.
236 static void
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);
246 frags = &f->next;
249 #endif
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;
268 addr_t mstart;
269 int m_ok;
270 int rv = -1;
272 dprintf("entering syslinux_compute_movelist()...\n");
274 if (setjmp(new_movelist_bail))
275 goto bail;
277 *moves = NULL;
279 /* Mark any memory that's in use by source material busy in the memory map */
280 mmap = syslinux_dup_memmap(memmap);
281 if (!mmap)
282 goto bail;
284 frags = dup_movelist(ifrags);
286 #if DEBUG
287 dprintf("Initial memory map:\n");
288 syslinux_dump_memmap(stdout, mmap);
289 #endif
291 for (f = frags; f; f = f->next) {
292 if (syslinux_add_memmap(&mmap, f->src, f->len, SMT_ALLOC))
293 goto bail;
296 #if DEBUG
297 dprintf("Adjusted memory map:\n");
298 syslinux_dump_memmap(stdout, mmap);
299 #endif
301 /* Compute the freelist in movelist format. */
302 space = NULL;
303 ep = &space;
304 mstart = -1;
305 m_ok = 0;
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) {
311 if (!m_ok) {
312 m_ok = 1;
313 mstart = mmap->start;
315 } else {
316 if (m_ok) {
317 f = new_movelist(0, mstart, mmap->start - mstart);
318 *ep = f;
319 ep = &f->next;
320 m_ok = 0;
324 mmap = mmap->next;
327 #if DEBUG
328 dprintf("Computed free list:\n");
329 syslinux_dump_movelist(stdout, space);
330 #endif
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! */
338 continue;
341 /* See if we can move this chunk into place by claiming
342 the destination, or in the case of partial overlap, the
343 missing portion. */
345 needbase = f->dst;
346 needlen = f->len;
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 */
356 needbase = f->dst;
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);
364 goto move_chunk;
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;
383 copylen = o->len;
384 } else {
385 ep = free_area_max(&space);
386 if ( !ep )
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);
395 } else {
396 copysrc = o->src;
399 if ( copylen < o->len ) {
400 op = split_movelist(copysrc, copylen, op);
401 o = *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);
407 *moves = mv;
408 moves = &mv->next;
410 o->src = copydst;
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);
416 mv->next = space;
417 space = mv;
418 copylen -= (needbase-copysrc);
420 if ( copylen > needlen ) {
421 mv = new_movelist(0, copysrc+needlen, copylen-needlen);
422 mv->next = space;
423 space = mv;
424 copylen = needlen;
427 goto move_chunk;
429 goto bail; /* Stuck! */
431 move_chunk:
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);
437 f = *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);
443 *moves = mv;
444 moves = &mv->next;
446 /* Figure out what memory we just freed up */
447 if ( f->dst > f->src ) {
448 freebase = f->src;
449 freelen = min(f->len, f->dst-f->src);
450 } else if ( f->src >= f->dst+f->len ) {
451 freebase = f->src;
452 freelen = f->len;
453 } else {
454 freelen = f->src-f->dst;
455 freebase = f->dst+f->len;
458 mv = new_movelist(0, freebase, freelen);
459 mv->next = space;
460 space = mv;
463 rv = 0;
464 bail:
465 if (mmap)
466 syslinux_free_memmap(mmap);
467 if (frags)
468 free_movelist(&frags);
469 return rv;
472 #ifdef TEST
474 #include <stdio.h>
476 int main(int argc, char *argv[])
478 FILE *f;
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 ) {
488 if ( d ) {
489 mv = new_movelist(d, s, l);
490 *fep = mv;
491 fep = &mv->next;
492 } else {
493 mv = new_movelist(0, s, l);
494 *sep = mv;
495 sep = &mv->next;
498 fclose(f);
500 if ( syslinux_compute_movelist(&moves, frags, space) ) {
501 printf("Failed to compute a move sequence\n");
502 return 1;
503 } else {
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);
508 return 0;
512 #endif /* TEST */