[e16e8f2] | 1 | /* ----------------------------------------------------------------------- * |
---|
| 2 | * |
---|
| 3 | * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved |
---|
| 4 | * Copyright 2009 Intel Corporation; author: H. Peter Anvin |
---|
| 5 | * |
---|
| 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 |
---|
| 13 | * conditions: |
---|
| 14 | * |
---|
| 15 | * The above copyright notice and this permission notice shall |
---|
| 16 | * be included in all copies or substantial portions of the Software. |
---|
| 17 | * |
---|
| 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. |
---|
| 26 | * |
---|
| 27 | * ----------------------------------------------------------------------- */ |
---|
| 28 | |
---|
| 29 | /* |
---|
| 30 | * movebits.c |
---|
| 31 | * |
---|
| 32 | * Utility function to take a list of memory areas to shuffle and |
---|
| 33 | * convert it to a set of shuffle operations. |
---|
| 34 | * |
---|
| 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. |
---|
| 39 | */ |
---|
| 40 | |
---|
| 41 | #include <assert.h> |
---|
| 42 | #include <stdio.h> |
---|
| 43 | #include <errno.h> |
---|
| 44 | #include <stdlib.h> |
---|
| 45 | #include <inttypes.h> |
---|
| 46 | #include <setjmp.h> |
---|
| 47 | #include <minmax.h> |
---|
| 48 | #include <stdbool.h> |
---|
| 49 | |
---|
| 50 | #include <syslinux/movebits.h> |
---|
| 51 | #include <dprintf.h> |
---|
| 52 | |
---|
| 53 | static jmp_buf new_movelist_bail; |
---|
| 54 | |
---|
| 55 | static struct syslinux_movelist *new_movelist(addr_t dst, addr_t src, |
---|
| 56 | addr_t len) |
---|
| 57 | { |
---|
| 58 | struct syslinux_movelist *ml = malloc(sizeof(struct syslinux_movelist)); |
---|
| 59 | |
---|
| 60 | if (!ml) |
---|
| 61 | longjmp(new_movelist_bail, 1); |
---|
| 62 | |
---|
| 63 | ml->dst = dst; |
---|
| 64 | ml->src = src; |
---|
| 65 | ml->len = len; |
---|
| 66 | ml->next = NULL; |
---|
| 67 | |
---|
| 68 | return ml; |
---|
| 69 | } |
---|
| 70 | |
---|
| 71 | static struct syslinux_movelist *dup_movelist(struct syslinux_movelist *src) |
---|
| 72 | { |
---|
| 73 | struct syslinux_movelist *dst = NULL, **dstp = &dst, *ml; |
---|
| 74 | |
---|
| 75 | while (src) { |
---|
| 76 | ml = new_movelist(src->dst, src->src, src->len); |
---|
| 77 | *dstp = ml; |
---|
| 78 | dstp = &ml->next; |
---|
| 79 | src = src->next; |
---|
| 80 | } |
---|
| 81 | |
---|
| 82 | return dst; |
---|
| 83 | } |
---|
| 84 | |
---|
| 85 | static void |
---|
| 86 | add_freelist(struct syslinux_memmap **mmap, addr_t start, |
---|
| 87 | addr_t len, enum syslinux_memmap_types type) |
---|
| 88 | { |
---|
| 89 | if (syslinux_add_memmap(mmap, start, len, type)) |
---|
| 90 | longjmp(new_movelist_bail, 1); |
---|
| 91 | } |
---|
| 92 | |
---|
| 93 | /* |
---|
| 94 | * Take a chunk, entirely confined in **parentptr, and split it off so that |
---|
| 95 | * it has its own structure. |
---|
| 96 | */ |
---|
| 97 | static struct syslinux_movelist **split_movelist(addr_t start, addr_t len, |
---|
| 98 | struct syslinux_movelist |
---|
| 99 | **parentptr) |
---|
| 100 | { |
---|
| 101 | struct syslinux_movelist *m, *ml = *parentptr; |
---|
| 102 | |
---|
| 103 | assert(start >= ml->src); |
---|
| 104 | assert(start < ml->src + ml->len); |
---|
| 105 | |
---|
| 106 | /* Split off the beginning */ |
---|
| 107 | if (start > ml->src) { |
---|
| 108 | addr_t l = start - ml->src; |
---|
| 109 | |
---|
| 110 | m = new_movelist(ml->dst + l, start, ml->len - l); |
---|
| 111 | m->next = ml->next; |
---|
| 112 | ml->len = l; |
---|
| 113 | ml->next = m; |
---|
| 114 | |
---|
| 115 | parentptr = &ml->next; |
---|
| 116 | ml = m; /* Continue processing the new node */ |
---|
| 117 | } |
---|
| 118 | |
---|
| 119 | /* Split off the end */ |
---|
| 120 | if (ml->len > len) { |
---|
| 121 | addr_t l = ml->len - len; |
---|
| 122 | |
---|
| 123 | m = new_movelist(ml->dst + len, ml->src + len, l); |
---|
| 124 | m->next = ml->next; |
---|
| 125 | ml->len = len; |
---|
| 126 | ml->next = m; |
---|
| 127 | } |
---|
| 128 | |
---|
| 129 | return parentptr; |
---|
| 130 | } |
---|
| 131 | |
---|
| 132 | static void delete_movelist(struct syslinux_movelist **parentptr) |
---|
| 133 | { |
---|
| 134 | struct syslinux_movelist *o = *parentptr; |
---|
| 135 | *parentptr = o->next; |
---|
| 136 | free(o); |
---|
| 137 | } |
---|
| 138 | |
---|
| 139 | static void free_movelist(struct syslinux_movelist **parentptr) |
---|
| 140 | { |
---|
| 141 | while (*parentptr) |
---|
| 142 | delete_movelist(parentptr); |
---|
| 143 | } |
---|
| 144 | |
---|
| 145 | /* |
---|
| 146 | * Scan the freelist looking for a particular chunk of memory. Returns |
---|
| 147 | * the memmap chunk containing to the first byte of the region. |
---|
| 148 | */ |
---|
| 149 | static const struct syslinux_memmap *is_free_zone(const struct syslinux_memmap |
---|
| 150 | *list, addr_t start, |
---|
| 151 | addr_t len) |
---|
| 152 | { |
---|
| 153 | addr_t last, llast; |
---|
| 154 | |
---|
| 155 | dprintf("f: 0x%08x bytes at 0x%08x\n", len, start); |
---|
| 156 | |
---|
| 157 | last = start + len - 1; |
---|
| 158 | |
---|
| 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; |
---|
| 164 | if (llast >= last) |
---|
| 165 | return ilist; |
---|
| 166 | list = list->next; |
---|
| 167 | } |
---|
| 168 | |
---|
| 169 | if (list->start > start) |
---|
| 170 | return NULL; /* Invalid type in region */ |
---|
| 171 | } |
---|
| 172 | list = list->next; |
---|
| 173 | } |
---|
| 174 | |
---|
| 175 | return NULL; /* Internal error? */ |
---|
| 176 | } |
---|
| 177 | |
---|
| 178 | /* |
---|
| 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. |
---|
| 181 | */ |
---|
| 182 | static addr_t free_area(const struct syslinux_memmap *mmap, |
---|
| 183 | addr_t len, addr_t * start) |
---|
| 184 | { |
---|
| 185 | const struct syslinux_memmap *best = NULL; |
---|
| 186 | const struct syslinux_memmap *s; |
---|
| 187 | addr_t slen, best_len = -1; |
---|
| 188 | |
---|
| 189 | for (s = mmap; s->type != SMT_END; s = s->next) { |
---|
| 190 | if (s->type != SMT_FREE) |
---|
| 191 | continue; |
---|
| 192 | slen = s->next->start - s->start; |
---|
| 193 | if (slen >= len) { |
---|
| 194 | if (!best || best_len > slen) { |
---|
| 195 | best = s; |
---|
| 196 | best_len = slen; |
---|
| 197 | } |
---|
| 198 | } |
---|
| 199 | } |
---|
| 200 | |
---|
| 201 | if (best) { |
---|
| 202 | *start = best->start; |
---|
| 203 | return best_len; |
---|
| 204 | } else { |
---|
| 205 | return 0; |
---|
| 206 | } |
---|
| 207 | } |
---|
| 208 | |
---|
| 209 | /* |
---|
| 210 | * Remove a chunk from the freelist |
---|
| 211 | */ |
---|
| 212 | static void |
---|
| 213 | allocate_from(struct syslinux_memmap **mmap, addr_t start, addr_t len) |
---|
| 214 | { |
---|
| 215 | syslinux_add_memmap(mmap, start, len, SMT_ALLOC); |
---|
| 216 | } |
---|
| 217 | |
---|
| 218 | /* |
---|
| 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. |
---|
| 222 | */ |
---|
| 223 | static void shuffle_dealias(struct syslinux_movelist **fraglist, |
---|
| 224 | struct syslinux_movelist **postcopy) |
---|
| 225 | { |
---|
| 226 | struct syslinux_movelist *mp, **mpp, *mx, *np; |
---|
| 227 | addr_t ps, pe, xs, xe, delta; |
---|
| 228 | bool advance; |
---|
| 229 | |
---|
| 230 | dprintf("Before alias resolution:\n"); |
---|
| 231 | syslinux_dump_movelist(*fraglist); |
---|
| 232 | |
---|
| 233 | *postcopy = NULL; |
---|
| 234 | |
---|
| 235 | /* |
---|
| 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). |
---|
| 238 | */ |
---|
| 239 | mpp = fraglist; |
---|
| 240 | while ((mp = *mpp)) { |
---|
| 241 | dprintf("mp -> (%#x,%#x,%#x)\n", mp->dst, mp->src, mp->len); |
---|
| 242 | ps = mp->src; |
---|
| 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); |
---|
| 246 | /* |
---|
| 247 | * If there is any overlap between mx and mp, mp should be |
---|
| 248 | * modified and possibly split. |
---|
| 249 | */ |
---|
| 250 | xs = mx->src; |
---|
| 251 | xe = mx->src + mx->len - 1; |
---|
| 252 | |
---|
| 253 | dprintf("?: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe); |
---|
| 254 | |
---|
| 255 | if (pe <= xs || ps >= xe) |
---|
| 256 | continue; /* No overlap */ |
---|
| 257 | |
---|
| 258 | advance = false; |
---|
| 259 | *mpp = mp->next; /* Remove from list */ |
---|
| 260 | |
---|
| 261 | if (pe > xe) { |
---|
| 262 | delta = pe - xe; |
---|
| 263 | np = new_movelist(mp->dst + mp->len - delta, |
---|
| 264 | mp->src + mp->len - delta, delta); |
---|
| 265 | mp->len -= delta; |
---|
| 266 | pe = xe; |
---|
| 267 | np->next = *mpp; |
---|
| 268 | *mpp = np; |
---|
| 269 | advance = true; |
---|
| 270 | } |
---|
| 271 | if (ps < xs) { |
---|
| 272 | delta = xs - ps; |
---|
| 273 | np = new_movelist(mp->dst, ps, delta); |
---|
| 274 | mp->src += delta; |
---|
| 275 | ps = mp->src; |
---|
| 276 | mp->dst += delta; |
---|
| 277 | mp->len -= delta; |
---|
| 278 | np->next = *mpp; |
---|
| 279 | *mpp = np; |
---|
| 280 | advance = true; |
---|
| 281 | } |
---|
| 282 | |
---|
| 283 | assert(ps >= xs && pe <= xe); |
---|
| 284 | |
---|
| 285 | dprintf("Overlap: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe); |
---|
| 286 | |
---|
| 287 | mp->src = mx->dst + (ps - xs); |
---|
| 288 | mp->next = *postcopy; |
---|
| 289 | *postcopy = mp; |
---|
| 290 | |
---|
| 291 | mp = *mpp; |
---|
| 292 | |
---|
| 293 | if (!advance) |
---|
| 294 | goto restart; |
---|
| 295 | } |
---|
| 296 | |
---|
| 297 | mpp = &mp->next; |
---|
| 298 | restart: |
---|
| 299 | ; |
---|
| 300 | } |
---|
| 301 | |
---|
| 302 | dprintf("After alias resolution:\n"); |
---|
| 303 | syslinux_dump_movelist(*fraglist); |
---|
| 304 | dprintf("Post-shuffle copies:\n"); |
---|
| 305 | syslinux_dump_movelist(*postcopy); |
---|
| 306 | } |
---|
| 307 | |
---|
| 308 | /* |
---|
| 309 | * The code to actually emit moving of a chunk into its final place. |
---|
| 310 | */ |
---|
| 311 | static void |
---|
| 312 | move_chunk(struct syslinux_movelist ***moves, |
---|
| 313 | struct syslinux_memmap **mmap, |
---|
| 314 | struct syslinux_movelist **fp, addr_t copylen) |
---|
| 315 | { |
---|
| 316 | addr_t copydst, copysrc; |
---|
| 317 | addr_t freebase, freelen; |
---|
| 318 | addr_t needlen; |
---|
| 319 | int reverse; |
---|
| 320 | struct syslinux_movelist *f = *fp, *mv; |
---|
| 321 | |
---|
| 322 | if (f->src < f->dst && (f->dst - f->src) < f->len) { |
---|
| 323 | /* "Shift up" type overlap */ |
---|
| 324 | needlen = f->dst - f->src; |
---|
| 325 | reverse = 1; |
---|
| 326 | } else if (f->src > f->dst && (f->src - f->dst) < f->len) { |
---|
| 327 | /* "Shift down" type overlap */ |
---|
| 328 | needlen = f->src - f->dst; |
---|
| 329 | reverse = 0; |
---|
| 330 | } else { |
---|
| 331 | needlen = f->len; |
---|
| 332 | reverse = 0; |
---|
| 333 | } |
---|
| 334 | |
---|
| 335 | copydst = f->dst; |
---|
| 336 | copysrc = f->src; |
---|
| 337 | |
---|
| 338 | dprintf("Q: copylen = 0x%08x, needlen = 0x%08x\n", copylen, needlen); |
---|
| 339 | |
---|
| 340 | if (copylen < needlen) { |
---|
| 341 | if (reverse) { |
---|
| 342 | copydst += (f->len - copylen); |
---|
| 343 | copysrc += (f->len - copylen); |
---|
| 344 | } |
---|
| 345 | |
---|
| 346 | dprintf("X: 0x%08x bytes at 0x%08x -> 0x%08x\n", |
---|
| 347 | copylen, copysrc, copydst); |
---|
| 348 | |
---|
| 349 | /* Didn't get all we wanted, so we have to split the chunk */ |
---|
| 350 | fp = split_movelist(copysrc, copylen, fp); /* Is this right? */ |
---|
| 351 | f = *fp; |
---|
| 352 | } |
---|
| 353 | |
---|
| 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); |
---|
| 356 | **moves = mv; |
---|
| 357 | *moves = &mv->next; |
---|
| 358 | |
---|
| 359 | /* Figure out what memory we just freed up */ |
---|
| 360 | if (f->dst > f->src) { |
---|
| 361 | freebase = f->src; |
---|
| 362 | freelen = min(f->len, f->dst - f->src); |
---|
| 363 | } else if (f->src >= f->dst + f->len) { |
---|
| 364 | freebase = f->src; |
---|
| 365 | freelen = f->len; |
---|
| 366 | } else { |
---|
| 367 | freelen = f->src - f->dst; |
---|
| 368 | freebase = f->dst + f->len; |
---|
| 369 | } |
---|
| 370 | |
---|
| 371 | dprintf("F: 0x%08x bytes at 0x%08x\n", freelen, freebase); |
---|
| 372 | |
---|
| 373 | add_freelist(mmap, freebase, freelen, SMT_FREE); |
---|
| 374 | |
---|
| 375 | delete_movelist(fp); |
---|
| 376 | } |
---|
| 377 | |
---|
| 378 | /* |
---|
| 379 | * moves is computed from "frags" and "freemem". "space" lists |
---|
| 380 | * free memory areas at our disposal, and is (src, cnt) only. |
---|
| 381 | */ |
---|
| 382 | int |
---|
| 383 | syslinux_compute_movelist(struct syslinux_movelist **moves, |
---|
| 384 | struct syslinux_movelist *ifrags, |
---|
| 385 | struct syslinux_memmap *memmap) |
---|
| 386 | { |
---|
| 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; |
---|
| 395 | addr_t avail; |
---|
| 396 | addr_t fstart, flen; |
---|
| 397 | addr_t cbyte; |
---|
| 398 | addr_t ep_len; |
---|
| 399 | int rv = -1; |
---|
| 400 | int reverse; |
---|
| 401 | |
---|
| 402 | dprintf("entering syslinux_compute_movelist()...\n"); |
---|
| 403 | |
---|
| 404 | if (setjmp(new_movelist_bail)) { |
---|
| 405 | nomem: |
---|
| 406 | dprintf("Out of working memory!\n"); |
---|
| 407 | goto bail; |
---|
| 408 | } |
---|
| 409 | |
---|
| 410 | *moves = NULL; |
---|
| 411 | |
---|
| 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(); |
---|
| 415 | if (!mmap) |
---|
| 416 | goto nomem; |
---|
| 417 | |
---|
| 418 | frags = dup_movelist(ifrags); |
---|
| 419 | |
---|
| 420 | /* Process one-to-many conditions */ |
---|
| 421 | shuffle_dealias(&frags, &postcopy); |
---|
| 422 | |
---|
| 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); |
---|
| 426 | |
---|
| 427 | for (f = frags; f; f = f->next) |
---|
| 428 | add_freelist(&mmap, f->src, f->len, SMT_ALLOC); |
---|
| 429 | |
---|
| 430 | /* As long as there are unprocessed fragments in the chain... */ |
---|
| 431 | while ((fp = &frags, f = *fp)) { |
---|
| 432 | |
---|
| 433 | dprintf("Current free list:\n"); |
---|
| 434 | syslinux_dump_memmap(mmap); |
---|
| 435 | dprintf("Current frag list:\n"); |
---|
| 436 | syslinux_dump_movelist(frags); |
---|
| 437 | |
---|
| 438 | /* Scan for fragments which can be discarded without action. */ |
---|
| 439 | if (f->src == f->dst) { |
---|
| 440 | delete_movelist(fp); |
---|
| 441 | continue; |
---|
| 442 | } |
---|
| 443 | op = &f->next; |
---|
| 444 | while ((o = *op)) { |
---|
| 445 | if (o->src == o->dst) |
---|
| 446 | delete_movelist(op); |
---|
| 447 | else |
---|
| 448 | op = &o->next; |
---|
| 449 | } |
---|
| 450 | |
---|
| 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); |
---|
| 458 | reverse = 1; |
---|
| 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; |
---|
| 463 | needbase = o->dst; |
---|
| 464 | reverse = 0; |
---|
| 465 | cbyte = o->dst; /* "Critical byte" */ |
---|
| 466 | } else { |
---|
| 467 | needlen = o->len; |
---|
| 468 | needbase = o->dst; |
---|
| 469 | reverse = 0; |
---|
| 470 | cbyte = o->dst; /* "Critical byte" */ |
---|
| 471 | } |
---|
| 472 | |
---|
| 473 | if (is_free_zone(mmap, needbase, needlen)) { |
---|
| 474 | fp = op, f = o; |
---|
| 475 | dprintf("!: 0x%08x bytes at 0x%08x -> 0x%08x\n", |
---|
| 476 | f->len, f->src, f->dst); |
---|
| 477 | copysrc = f->src; |
---|
| 478 | copylen = needlen; |
---|
| 479 | allocate_from(&mmap, needbase, copylen); |
---|
| 480 | goto move_chunk; |
---|
| 481 | } |
---|
| 482 | } |
---|
| 483 | |
---|
| 484 | /* Ok, bother. Need to do real work at least with one chunk. */ |
---|
| 485 | |
---|
| 486 | dprintf("@: 0x%08x bytes at 0x%08x -> 0x%08x\n", |
---|
| 487 | f->len, f->src, f->dst); |
---|
| 488 | |
---|
| 489 | /* See if we can move this chunk into place by claiming |
---|
| 490 | the destination, or in the case of partial overlap, the |
---|
| 491 | missing portion. */ |
---|
| 492 | |
---|
| 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); |
---|
| 497 | reverse = 1; |
---|
| 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; |
---|
| 502 | needbase = f->dst; |
---|
| 503 | reverse = 0; |
---|
| 504 | cbyte = f->dst; /* "Critical byte" */ |
---|
| 505 | } else { |
---|
| 506 | needlen = f->len; |
---|
| 507 | needbase = f->dst; |
---|
| 508 | reverse = 0; |
---|
| 509 | cbyte = f->dst; |
---|
| 510 | } |
---|
| 511 | |
---|
| 512 | dprintf("need: base = 0x%08x, len = 0x%08x, " |
---|
| 513 | "reverse = %d, cbyte = 0x%08x\n", |
---|
| 514 | needbase, needlen, reverse, cbyte); |
---|
| 515 | |
---|
| 516 | ep = is_free_zone(mmap, cbyte, 1); |
---|
| 517 | if (ep) { |
---|
| 518 | ep_len = ep->next->start - ep->start; |
---|
| 519 | if (reverse) |
---|
| 520 | avail = needbase + needlen - ep->start; |
---|
| 521 | else |
---|
| 522 | avail = ep_len - (needbase - ep->start); |
---|
| 523 | } else { |
---|
| 524 | avail = 0; |
---|
| 525 | } |
---|
| 526 | |
---|
| 527 | if (avail) { |
---|
| 528 | /* We can move at least part of this chunk into place without |
---|
| 529 | further ado */ |
---|
| 530 | dprintf("space: start 0x%08x, len 0x%08x, free 0x%08x\n", |
---|
| 531 | ep->start, ep_len, avail); |
---|
| 532 | copylen = min(needlen, avail); |
---|
| 533 | |
---|
| 534 | if (reverse) |
---|
| 535 | allocate_from(&mmap, needbase + needlen - copylen, copylen); |
---|
| 536 | else |
---|
| 537 | allocate_from(&mmap, needbase, copylen); |
---|
| 538 | |
---|
| 539 | goto move_chunk; |
---|
| 540 | } |
---|
| 541 | |
---|
| 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) { |
---|
| 547 | |
---|
| 548 | dprintf("O: 0x%08x bytes at 0x%08x -> 0x%08x\n", |
---|
| 549 | o->len, o->src, o->dst); |
---|
| 550 | |
---|
| 551 | if (!(o->src <= cbyte && o->src + o->len > cbyte)) |
---|
| 552 | continue; /* Not what we're looking for... */ |
---|
| 553 | |
---|
| 554 | /* Find somewhere to put it... */ |
---|
| 555 | |
---|
| 556 | if (is_free_zone(mmap, o->dst, o->len)) { |
---|
| 557 | /* Score! We can move it into place directly... */ |
---|
| 558 | copydst = o->dst; |
---|
| 559 | copysrc = o->src; |
---|
| 560 | copylen = o->len; |
---|
| 561 | } else if (free_area(mmap, o->len, &fstart)) { |
---|
| 562 | /* We can move the whole chunk */ |
---|
| 563 | copydst = fstart; |
---|
| 564 | copysrc = o->src; |
---|
| 565 | copylen = o->len; |
---|
| 566 | } else { |
---|
| 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! */ |
---|
| 571 | } |
---|
| 572 | |
---|
| 573 | /* Make sure we include the critical byte */ |
---|
| 574 | copydst = fstart; |
---|
| 575 | if (reverse) { |
---|
| 576 | copysrc = max(o->src, cbyte + 1 - flen); |
---|
| 577 | copylen = cbyte + 1 - copysrc; |
---|
| 578 | } else { |
---|
| 579 | copysrc = cbyte; |
---|
| 580 | copylen = min(flen, o->len - (cbyte - o->src)); |
---|
| 581 | } |
---|
| 582 | } |
---|
| 583 | allocate_from(&mmap, copydst, copylen); |
---|
| 584 | |
---|
| 585 | if (copylen < o->len) { |
---|
| 586 | op = split_movelist(copysrc, copylen, op); |
---|
| 587 | o = *op; |
---|
| 588 | } |
---|
| 589 | |
---|
| 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); |
---|
| 593 | *moves = mv; |
---|
| 594 | moves = &mv->next; |
---|
| 595 | |
---|
| 596 | o->src = copydst; |
---|
| 597 | |
---|
| 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); |
---|
| 603 | } |
---|
| 604 | if (copylen > needlen) { |
---|
| 605 | add_freelist(&mmap, copysrc + needlen, copylen - needlen, |
---|
| 606 | SMT_FREE); |
---|
| 607 | copylen = needlen; |
---|
| 608 | } |
---|
| 609 | } |
---|
| 610 | reverse = 0; |
---|
| 611 | goto move_chunk; |
---|
| 612 | } |
---|
| 613 | dprintf("Cannot find the chunk containing the critical byte\n"); |
---|
| 614 | goto bail; /* Stuck! */ |
---|
| 615 | |
---|
| 616 | move_chunk: |
---|
| 617 | move_chunk(&moves, &mmap, fp, copylen); |
---|
| 618 | } |
---|
| 619 | |
---|
| 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 */ |
---|
| 622 | *op = postcopy; |
---|
| 623 | postcopy = NULL; |
---|
| 624 | |
---|
| 625 | rv = 0; |
---|
| 626 | bail: |
---|
| 627 | if (mmap) |
---|
| 628 | syslinux_free_memmap(mmap); |
---|
| 629 | if (frags) |
---|
| 630 | free_movelist(&frags); |
---|
| 631 | if (postcopy) |
---|
| 632 | free_movelist(&postcopy); |
---|
| 633 | return rv; |
---|
| 634 | } |
---|
| 635 | |
---|
| 636 | #ifdef TEST |
---|
| 637 | |
---|
| 638 | #include <stdio.h> |
---|
| 639 | |
---|
| 640 | int main(int argc, char *argv[]) |
---|
| 641 | { |
---|
| 642 | FILE *f; |
---|
| 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; |
---|
| 648 | char line[BUFSIZ]; |
---|
| 649 | |
---|
| 650 | (void)argc; |
---|
| 651 | |
---|
| 652 | memmap = syslinux_init_memmap(); |
---|
| 653 | |
---|
| 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) { |
---|
| 657 | if (d) { |
---|
| 658 | if (s == -1UL) { |
---|
| 659 | syslinux_add_memmap(&memmap, d, l, SMT_ZERO); |
---|
| 660 | } else { |
---|
| 661 | mv = new_movelist(d, s, l); |
---|
| 662 | *fep = mv; |
---|
| 663 | fep = &mv->next; |
---|
| 664 | } |
---|
| 665 | } else { |
---|
| 666 | syslinux_add_memmap(&memmap, s, l, SMT_FREE); |
---|
| 667 | } |
---|
| 668 | } |
---|
| 669 | } |
---|
| 670 | fclose(f); |
---|
| 671 | |
---|
| 672 | *fep = NULL; |
---|
| 673 | |
---|
| 674 | dprintf("Input move list:\n"); |
---|
| 675 | syslinux_dump_movelist(frags); |
---|
| 676 | dprintf("Input free list:\n"); |
---|
| 677 | syslinux_dump_memmap(memmap); |
---|
| 678 | |
---|
| 679 | if (syslinux_compute_movelist(&moves, frags, memmap)) { |
---|
| 680 | printf("Failed to compute a move sequence\n"); |
---|
| 681 | return 1; |
---|
| 682 | } else { |
---|
| 683 | dprintf("Final move list:\n"); |
---|
| 684 | syslinux_dump_movelist(moves); |
---|
| 685 | return 0; |
---|
| 686 | } |
---|
| 687 | } |
---|
| 688 | |
---|
| 689 | #endif /* TEST */ |
---|