|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 2010-2014 Richard Braun. ! 3: * ! 4: * This program is free software: you can redistribute it and/or modify ! 5: * it under the terms of the GNU General Public License as published by ! 6: * the Free Software Foundation, either version 2 of the License, or ! 7: * (at your option) any later version. ! 8: * ! 9: * This program is distributed in the hope that it will be useful, ! 10: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 12: * GNU General Public License for more details. ! 13: * ! 14: * You should have received a copy of the GNU General Public License ! 15: * along with this program. If not, see <http://www.gnu.org/licenses/>. ! 16: */ ! 17: ! 18: #include <string.h> ! 19: #include <i386/model_dep.h> ! 20: #include <i386at/biosmem.h> ! 21: #include <i386at/elf.h> ! 22: #include <kern/assert.h> ! 23: #include <kern/debug.h> ! 24: #include <kern/macros.h> ! 25: #include <kern/printf.h> ! 26: #include <mach/vm_param.h> ! 27: #include <mach/xen.h> ! 28: #include <mach/machine/multiboot.h> ! 29: #include <sys/types.h> ! 30: #include <vm/vm_page.h> ! 31: ! 32: #define __boot ! 33: #define __bootdata ! 34: #define __init ! 35: ! 36: #define boot_memmove memmove ! 37: #define boot_panic panic ! 38: #define boot_strlen strlen ! 39: ! 40: #define BOOT_CGAMEM phystokv(0xb8000) ! 41: #define BOOT_CGACHARS (80 * 25) ! 42: #define BOOT_CGACOLOR 0x7 ! 43: ! 44: extern char _start, _end; ! 45: ! 46: /* ! 47: * Maximum number of entries in the BIOS memory map. ! 48: * ! 49: * Because of adjustments of overlapping ranges, the memory map can grow ! 50: * to twice this size. ! 51: */ ! 52: #define BIOSMEM_MAX_MAP_SIZE 128 ! 53: ! 54: /* ! 55: * Memory range types. ! 56: */ ! 57: #define BIOSMEM_TYPE_AVAILABLE 1 ! 58: #define BIOSMEM_TYPE_RESERVED 2 ! 59: #define BIOSMEM_TYPE_ACPI 3 ! 60: #define BIOSMEM_TYPE_NVS 4 ! 61: #define BIOSMEM_TYPE_UNUSABLE 5 ! 62: #define BIOSMEM_TYPE_DISABLED 6 ! 63: ! 64: /* ! 65: * Memory map entry. ! 66: */ ! 67: struct biosmem_map_entry { ! 68: uint64_t base_addr; ! 69: uint64_t length; ! 70: unsigned int type; ! 71: }; ! 72: ! 73: /* ! 74: * Contiguous block of physical memory. ! 75: * ! 76: * Tha "available" range records what has been passed to the VM system as ! 77: * available inside the segment. ! 78: */ ! 79: struct biosmem_segment { ! 80: phys_addr_t start; ! 81: phys_addr_t end; ! 82: phys_addr_t avail_start; ! 83: phys_addr_t avail_end; ! 84: }; ! 85: ! 86: /* ! 87: * Memory map built from the information passed by the boot loader. ! 88: * ! 89: * If the boot loader didn't pass a valid memory map, a simple map is built ! 90: * based on the mem_lower and mem_upper multiboot fields. ! 91: */ ! 92: static struct biosmem_map_entry biosmem_map[BIOSMEM_MAX_MAP_SIZE * 2] ! 93: __bootdata; ! 94: static unsigned int biosmem_map_size __bootdata; ! 95: ! 96: /* ! 97: * Physical segment boundaries. ! 98: */ ! 99: static struct biosmem_segment biosmem_segments[VM_PAGE_MAX_SEGS] __bootdata; ! 100: ! 101: /* ! 102: * Boundaries of the simple bootstrap heap. ! 103: * ! 104: * This heap is located above BIOS memory. ! 105: */ ! 106: static uint32_t biosmem_heap_start __bootdata; ! 107: static uint32_t biosmem_heap_cur __bootdata; ! 108: static uint32_t biosmem_heap_end __bootdata; ! 109: ! 110: static char biosmem_panic_toobig_msg[] __bootdata ! 111: = "biosmem: too many memory map entries"; ! 112: #ifndef MACH_HYP ! 113: static char biosmem_panic_setup_msg[] __bootdata ! 114: = "biosmem: unable to set up the early memory allocator"; ! 115: #endif /* MACH_HYP */ ! 116: static char biosmem_panic_noseg_msg[] __bootdata ! 117: = "biosmem: unable to find any memory segment"; ! 118: static char biosmem_panic_inval_msg[] __bootdata ! 119: = "biosmem: attempt to allocate 0 page"; ! 120: static char biosmem_panic_nomem_msg[] __bootdata ! 121: = "biosmem: unable to allocate memory"; ! 122: ! 123: #ifndef MACH_HYP ! 124: ! 125: static void __boot ! 126: biosmem_map_build(const struct multiboot_raw_info *mbi) ! 127: { ! 128: struct multiboot_raw_mmap_entry *mb_entry, *mb_end; ! 129: struct biosmem_map_entry *start, *entry, *end; ! 130: unsigned long addr; ! 131: ! 132: addr = phystokv(mbi->mmap_addr); ! 133: mb_entry = (struct multiboot_raw_mmap_entry *)addr; ! 134: mb_end = (struct multiboot_raw_mmap_entry *)(addr + mbi->mmap_length); ! 135: start = biosmem_map; ! 136: entry = start; ! 137: end = entry + BIOSMEM_MAX_MAP_SIZE; ! 138: ! 139: while ((mb_entry < mb_end) && (entry < end)) { ! 140: entry->base_addr = mb_entry->base_addr; ! 141: entry->length = mb_entry->length; ! 142: entry->type = mb_entry->type; ! 143: ! 144: mb_entry = (void *)mb_entry + sizeof(mb_entry->size) + mb_entry->size; ! 145: entry++; ! 146: } ! 147: ! 148: biosmem_map_size = entry - start; ! 149: } ! 150: ! 151: static void __boot ! 152: biosmem_map_build_simple(const struct multiboot_raw_info *mbi) ! 153: { ! 154: struct biosmem_map_entry *entry; ! 155: ! 156: entry = biosmem_map; ! 157: entry->base_addr = 0; ! 158: entry->length = mbi->mem_lower << 10; ! 159: entry->type = BIOSMEM_TYPE_AVAILABLE; ! 160: ! 161: entry++; ! 162: entry->base_addr = BIOSMEM_END; ! 163: entry->length = mbi->mem_upper << 10; ! 164: entry->type = BIOSMEM_TYPE_AVAILABLE; ! 165: ! 166: biosmem_map_size = 2; ! 167: } ! 168: ! 169: #endif /* MACH_HYP */ ! 170: ! 171: static int __boot ! 172: biosmem_map_entry_is_invalid(const struct biosmem_map_entry *entry) ! 173: { ! 174: return (entry->base_addr + entry->length) <= entry->base_addr; ! 175: } ! 176: ! 177: static void __boot ! 178: biosmem_map_filter(void) ! 179: { ! 180: struct biosmem_map_entry *entry; ! 181: unsigned int i; ! 182: ! 183: i = 0; ! 184: ! 185: while (i < biosmem_map_size) { ! 186: entry = &biosmem_map[i]; ! 187: ! 188: if (biosmem_map_entry_is_invalid(entry)) { ! 189: biosmem_map_size--; ! 190: boot_memmove(entry, entry + 1, ! 191: (biosmem_map_size - i) * sizeof(*entry)); ! 192: continue; ! 193: } ! 194: ! 195: i++; ! 196: } ! 197: } ! 198: ! 199: static void __boot ! 200: biosmem_map_sort(void) ! 201: { ! 202: struct biosmem_map_entry tmp; ! 203: unsigned int i, j; ! 204: ! 205: /* ! 206: * Simple insertion sort. ! 207: */ ! 208: for (i = 1; i < biosmem_map_size; i++) { ! 209: tmp = biosmem_map[i]; ! 210: ! 211: for (j = i - 1; j < i; j--) { ! 212: if (biosmem_map[j].base_addr < tmp.base_addr) ! 213: break; ! 214: ! 215: biosmem_map[j + 1] = biosmem_map[j]; ! 216: } ! 217: ! 218: biosmem_map[j + 1] = tmp; ! 219: } ! 220: } ! 221: ! 222: static void __boot ! 223: biosmem_map_adjust(void) ! 224: { ! 225: struct biosmem_map_entry tmp, *a, *b, *first, *second; ! 226: uint64_t a_end, b_end, last_end; ! 227: unsigned int i, j, last_type; ! 228: ! 229: biosmem_map_filter(); ! 230: ! 231: /* ! 232: * Resolve overlapping areas, giving priority to most restrictive ! 233: * (i.e. numerically higher) types. ! 234: */ ! 235: for (i = 0; i < biosmem_map_size; i++) { ! 236: a = &biosmem_map[i]; ! 237: a_end = a->base_addr + a->length; ! 238: ! 239: j = i + 1; ! 240: ! 241: while (j < biosmem_map_size) { ! 242: b = &biosmem_map[j]; ! 243: b_end = b->base_addr + b->length; ! 244: ! 245: if ((a->base_addr >= b_end) || (a_end <= b->base_addr)) { ! 246: j++; ! 247: continue; ! 248: } ! 249: ! 250: if (a->base_addr < b->base_addr) { ! 251: first = a; ! 252: second = b; ! 253: } else { ! 254: first = b; ! 255: second = a; ! 256: } ! 257: ! 258: if (a_end > b_end) { ! 259: last_end = a_end; ! 260: last_type = a->type; ! 261: } else { ! 262: last_end = b_end; ! 263: last_type = b->type; ! 264: } ! 265: ! 266: tmp.base_addr = second->base_addr; ! 267: tmp.length = MIN(a_end, b_end) - tmp.base_addr; ! 268: tmp.type = MAX(a->type, b->type); ! 269: first->length = tmp.base_addr - first->base_addr; ! 270: second->base_addr += tmp.length; ! 271: second->length = last_end - second->base_addr; ! 272: second->type = last_type; ! 273: ! 274: /* ! 275: * Filter out invalid entries. ! 276: */ ! 277: if (biosmem_map_entry_is_invalid(a) ! 278: && biosmem_map_entry_is_invalid(b)) { ! 279: *a = tmp; ! 280: biosmem_map_size--; ! 281: memmove(b, b + 1, (biosmem_map_size - j) * sizeof(*b)); ! 282: continue; ! 283: } else if (biosmem_map_entry_is_invalid(a)) { ! 284: *a = tmp; ! 285: j++; ! 286: continue; ! 287: } else if (biosmem_map_entry_is_invalid(b)) { ! 288: *b = tmp; ! 289: j++; ! 290: continue; ! 291: } ! 292: ! 293: if (tmp.type == a->type) ! 294: first = a; ! 295: else if (tmp.type == b->type) ! 296: first = b; ! 297: else { ! 298: ! 299: /* ! 300: * If the overlapping area can't be merged with one of its ! 301: * neighbors, it must be added as a new entry. ! 302: */ ! 303: ! 304: if (biosmem_map_size >= ARRAY_SIZE(biosmem_map)) ! 305: boot_panic(biosmem_panic_toobig_msg); ! 306: ! 307: biosmem_map[biosmem_map_size] = tmp; ! 308: biosmem_map_size++; ! 309: j++; ! 310: continue; ! 311: } ! 312: ! 313: if (first->base_addr > tmp.base_addr) ! 314: first->base_addr = tmp.base_addr; ! 315: ! 316: first->length += tmp.length; ! 317: j++; ! 318: } ! 319: } ! 320: ! 321: biosmem_map_sort(); ! 322: } ! 323: ! 324: static int __boot ! 325: biosmem_map_find_avail(phys_addr_t *phys_start, phys_addr_t *phys_end) ! 326: { ! 327: const struct biosmem_map_entry *entry, *map_end; ! 328: phys_addr_t seg_start, seg_end; ! 329: uint64_t start, end; ! 330: ! 331: seg_start = (phys_addr_t)-1; ! 332: seg_end = (phys_addr_t)-1; ! 333: map_end = biosmem_map + biosmem_map_size; ! 334: ! 335: for (entry = biosmem_map; entry < map_end; entry++) { ! 336: if (entry->type != BIOSMEM_TYPE_AVAILABLE) ! 337: continue; ! 338: ! 339: start = vm_page_round(entry->base_addr); ! 340: ! 341: if (start >= *phys_end) ! 342: break; ! 343: ! 344: end = vm_page_trunc(entry->base_addr + entry->length); ! 345: ! 346: if ((start < end) && (start < *phys_end) && (end > *phys_start)) { ! 347: if (seg_start == (phys_addr_t)-1) ! 348: seg_start = start; ! 349: ! 350: seg_end = end; ! 351: } ! 352: } ! 353: ! 354: if ((seg_start == (phys_addr_t)-1) || (seg_end == (phys_addr_t)-1)) ! 355: return -1; ! 356: ! 357: if (seg_start > *phys_start) ! 358: *phys_start = seg_start; ! 359: ! 360: if (seg_end < *phys_end) ! 361: *phys_end = seg_end; ! 362: ! 363: return 0; ! 364: } ! 365: ! 366: static void __boot ! 367: biosmem_set_segment(unsigned int seg_index, phys_addr_t start, phys_addr_t end) ! 368: { ! 369: biosmem_segments[seg_index].start = start; ! 370: biosmem_segments[seg_index].end = end; ! 371: } ! 372: ! 373: static phys_addr_t __boot ! 374: biosmem_segment_end(unsigned int seg_index) ! 375: { ! 376: return biosmem_segments[seg_index].end; ! 377: } ! 378: ! 379: static phys_addr_t __boot ! 380: biosmem_segment_size(unsigned int seg_index) ! 381: { ! 382: return biosmem_segments[seg_index].end - biosmem_segments[seg_index].start; ! 383: } ! 384: ! 385: #ifndef MACH_HYP ! 386: ! 387: static void __boot ! 388: biosmem_save_cmdline_sizes(struct multiboot_raw_info *mbi) ! 389: { ! 390: struct multiboot_raw_module *mod; ! 391: uint32_t i, va; ! 392: ! 393: if (mbi->flags & MULTIBOOT_LOADER_CMDLINE) { ! 394: va = phystokv(mbi->cmdline); ! 395: mbi->unused0 = boot_strlen((char *)va) + 1; ! 396: } ! 397: ! 398: if (mbi->flags & MULTIBOOT_LOADER_MODULES) { ! 399: unsigned long addr; ! 400: ! 401: addr = phystokv(mbi->mods_addr); ! 402: ! 403: for (i = 0; i < mbi->mods_count; i++) { ! 404: mod = (struct multiboot_raw_module *)addr + i; ! 405: va = phystokv(mod->string); ! 406: mod->reserved = boot_strlen((char *)va) + 1; ! 407: } ! 408: } ! 409: } ! 410: ! 411: static void __boot ! 412: biosmem_find_boot_data_update(uint32_t min, uint32_t *start, uint32_t *end, ! 413: uint32_t data_start, uint32_t data_end) ! 414: { ! 415: if ((min <= data_start) && (data_start < *start)) { ! 416: *start = data_start; ! 417: *end = data_end; ! 418: } ! 419: } ! 420: ! 421: /* ! 422: * Find the first boot data in the given range, and return their containing ! 423: * area (start address is returned directly, end address is returned in end). ! 424: * The following are considered boot data : ! 425: * - the kernel ! 426: * - the kernel command line ! 427: * - the module table ! 428: * - the modules ! 429: * - the modules command lines ! 430: * - the ELF section header table ! 431: * - the ELF .shstrtab, .symtab and .strtab sections ! 432: * ! 433: * If no boot data was found, 0 is returned, and the end address isn't set. ! 434: */ ! 435: static uint32_t __boot ! 436: biosmem_find_boot_data(const struct multiboot_raw_info *mbi, uint32_t min, ! 437: uint32_t max, uint32_t *endp) ! 438: { ! 439: struct multiboot_raw_module *mod; ! 440: struct elf_shdr *shdr; ! 441: uint32_t i, start, end = end; ! 442: unsigned long tmp; ! 443: ! 444: start = max; ! 445: ! 446: biosmem_find_boot_data_update(min, &start, &end, _kvtophys(&_start), ! 447: _kvtophys(&_end)); ! 448: ! 449: if ((mbi->flags & MULTIBOOT_LOADER_CMDLINE) && (mbi->cmdline != 0)) ! 450: biosmem_find_boot_data_update(min, &start, &end, mbi->cmdline, ! 451: mbi->cmdline + mbi->unused0); ! 452: ! 453: if (mbi->flags & MULTIBOOT_LOADER_MODULES) { ! 454: i = mbi->mods_count * sizeof(struct multiboot_raw_module); ! 455: biosmem_find_boot_data_update(min, &start, &end, mbi->mods_addr, ! 456: mbi->mods_addr + i); ! 457: tmp = phystokv(mbi->mods_addr); ! 458: ! 459: for (i = 0; i < mbi->mods_count; i++) { ! 460: mod = (struct multiboot_raw_module *)tmp + i; ! 461: biosmem_find_boot_data_update(min, &start, &end, mod->mod_start, ! 462: mod->mod_end); ! 463: ! 464: if (mod->string != 0) ! 465: biosmem_find_boot_data_update(min, &start, &end, mod->string, ! 466: mod->string + mod->reserved); ! 467: } ! 468: } ! 469: ! 470: if (mbi->flags & MULTIBOOT_LOADER_SHDR) { ! 471: tmp = mbi->shdr_num * mbi->shdr_size; ! 472: biosmem_find_boot_data_update(min, &start, &end, mbi->shdr_addr, ! 473: mbi->shdr_addr + tmp); ! 474: tmp = phystokv(mbi->shdr_addr); ! 475: ! 476: for (i = 0; i < mbi->shdr_num; i++) { ! 477: shdr = (struct elf_shdr *)(tmp + (i * mbi->shdr_size)); ! 478: ! 479: if ((shdr->type != ELF_SHT_SYMTAB) ! 480: && (shdr->type != ELF_SHT_STRTAB)) ! 481: continue; ! 482: ! 483: biosmem_find_boot_data_update(min, &start, &end, shdr->addr, ! 484: shdr->addr + shdr->size); ! 485: } ! 486: } ! 487: ! 488: if (start == max) ! 489: return 0; ! 490: ! 491: *endp = end; ! 492: return start; ! 493: } ! 494: ! 495: static void __boot ! 496: biosmem_setup_allocator(struct multiboot_raw_info *mbi) ! 497: { ! 498: uint32_t heap_start, heap_end, max_heap_start, max_heap_end; ! 499: uint32_t mem_end, next; ! 500: ! 501: /* ! 502: * Find some memory for the heap. Look for the largest unused area in ! 503: * upper memory, carefully avoiding all boot data. ! 504: */ ! 505: mem_end = vm_page_trunc((mbi->mem_upper + 1024) << 10); ! 506: ! 507: #ifndef __LP64__ ! 508: if (mem_end > VM_PAGE_DIRECTMAP_LIMIT) ! 509: mem_end = VM_PAGE_DIRECTMAP_LIMIT; ! 510: #endif /* __LP64__ */ ! 511: ! 512: max_heap_start = 0; ! 513: max_heap_end = 0; ! 514: next = BIOSMEM_END; ! 515: ! 516: do { ! 517: heap_start = next; ! 518: heap_end = biosmem_find_boot_data(mbi, heap_start, mem_end, &next); ! 519: ! 520: if (heap_end == 0) { ! 521: heap_end = mem_end; ! 522: next = 0; ! 523: } ! 524: ! 525: if ((heap_end - heap_start) > (max_heap_end - max_heap_start)) { ! 526: max_heap_start = heap_start; ! 527: max_heap_end = heap_end; ! 528: } ! 529: } while (next != 0); ! 530: ! 531: max_heap_start = vm_page_round(max_heap_start); ! 532: max_heap_end = vm_page_trunc(max_heap_end); ! 533: ! 534: if (max_heap_start >= max_heap_end) ! 535: boot_panic(biosmem_panic_setup_msg); ! 536: ! 537: biosmem_heap_start = max_heap_start; ! 538: biosmem_heap_end = max_heap_end; ! 539: biosmem_heap_cur = biosmem_heap_end; ! 540: } ! 541: ! 542: #endif /* MACH_HYP */ ! 543: ! 544: static void __boot ! 545: biosmem_bootstrap_common(void) ! 546: { ! 547: phys_addr_t phys_start, phys_end, last_addr; ! 548: int error; ! 549: ! 550: biosmem_map_adjust(); ! 551: ! 552: phys_start = BIOSMEM_BASE; ! 553: phys_end = VM_PAGE_DMA_LIMIT; ! 554: error = biosmem_map_find_avail(&phys_start, &phys_end); ! 555: ! 556: if (error) ! 557: boot_panic(biosmem_panic_noseg_msg); ! 558: ! 559: biosmem_set_segment(VM_PAGE_SEG_DMA, phys_start, phys_end); ! 560: last_addr = phys_end; ! 561: ! 562: phys_start = VM_PAGE_DMA_LIMIT; ! 563: #ifdef VM_PAGE_DMA32_LIMIT ! 564: phys_end = VM_PAGE_DMA32_LIMIT; ! 565: error = biosmem_map_find_avail(&phys_start, &phys_end); ! 566: ! 567: if (error) ! 568: goto out; ! 569: ! 570: biosmem_set_segment(VM_PAGE_SEG_DMA32, phys_start, phys_end); ! 571: last_addr = phys_end; ! 572: ! 573: phys_start = VM_PAGE_DMA32_LIMIT; ! 574: #endif /* VM_PAGE_DMA32_LIMIT */ ! 575: phys_end = VM_PAGE_DIRECTMAP_LIMIT; ! 576: error = biosmem_map_find_avail(&phys_start, &phys_end); ! 577: ! 578: if (error) ! 579: goto out; ! 580: ! 581: biosmem_set_segment(VM_PAGE_SEG_DIRECTMAP, phys_start, phys_end); ! 582: last_addr = phys_end; ! 583: ! 584: phys_start = VM_PAGE_DIRECTMAP_LIMIT; ! 585: phys_end = VM_PAGE_HIGHMEM_LIMIT; ! 586: error = biosmem_map_find_avail(&phys_start, &phys_end); ! 587: ! 588: if (error) ! 589: goto out; ! 590: ! 591: biosmem_set_segment(VM_PAGE_SEG_HIGHMEM, phys_start, phys_end); ! 592: ! 593: out: ! 594: /* XXX phys_last_addr must be part of the direct physical mapping */ ! 595: phys_last_addr = last_addr; ! 596: } ! 597: ! 598: #ifdef MACH_HYP ! 599: ! 600: void ! 601: biosmem_xen_bootstrap(void) ! 602: { ! 603: struct biosmem_map_entry *entry; ! 604: ! 605: entry = biosmem_map; ! 606: entry->base_addr = 0; ! 607: entry->length = boot_info.nr_pages << PAGE_SHIFT; ! 608: entry->type = BIOSMEM_TYPE_AVAILABLE; ! 609: ! 610: biosmem_map_size = 1; ! 611: ! 612: biosmem_bootstrap_common(); ! 613: ! 614: biosmem_heap_start = _kvtophys(boot_info.pt_base) ! 615: + (boot_info.nr_pt_frames + 3) * 0x1000; ! 616: biosmem_heap_end = boot_info.nr_pages << PAGE_SHIFT; ! 617: ! 618: #ifndef __LP64__ ! 619: /* TODO Check that this actually makes sense */ ! 620: if (biosmem_heap_end > VM_PAGE_DIRECTMAP_LIMIT) ! 621: biosmem_heap_end = VM_PAGE_DIRECTMAP_LIMIT; ! 622: #endif /* __LP64__ */ ! 623: ! 624: /* ! 625: * XXX Allocation on Xen must be bottom-up : ! 626: * At the "start of day", only 512k are available after the boot ! 627: * data. The pmap module then creates a 4g mapping so all physical ! 628: * memory is available, but it uses this allocator to do so. ! 629: * Therefore, it must return pages from this small 512k regions ! 630: * first. ! 631: */ ! 632: biosmem_heap_cur = biosmem_heap_start; ! 633: } ! 634: ! 635: #else /* MACH_HYP */ ! 636: ! 637: void __boot ! 638: biosmem_bootstrap(struct multiboot_raw_info *mbi) ! 639: { ! 640: if (mbi->flags & MULTIBOOT_LOADER_MMAP) ! 641: biosmem_map_build(mbi); ! 642: else ! 643: biosmem_map_build_simple(mbi); ! 644: ! 645: biosmem_bootstrap_common(); ! 646: ! 647: /* ! 648: * The kernel and modules command lines will be memory mapped later ! 649: * during initialization. Their respective sizes must be saved. ! 650: */ ! 651: biosmem_save_cmdline_sizes(mbi); ! 652: biosmem_setup_allocator(mbi); ! 653: } ! 654: ! 655: #endif /* MACH_HYP */ ! 656: ! 657: unsigned long __boot ! 658: biosmem_bootalloc(unsigned int nr_pages) ! 659: { ! 660: unsigned long addr, size; ! 661: ! 662: assert(!vm_page_ready()); ! 663: ! 664: size = vm_page_ptoa(nr_pages); ! 665: ! 666: if (size == 0) ! 667: boot_panic(biosmem_panic_inval_msg); ! 668: ! 669: #ifdef MACH_HYP ! 670: addr = biosmem_heap_cur; ! 671: #else /* MACH_HYP */ ! 672: /* Top-down allocation to avoid unnecessarily filling DMA segments */ ! 673: addr = biosmem_heap_cur - size; ! 674: #endif /* MACH_HYP */ ! 675: ! 676: if ((addr < biosmem_heap_start) || (addr > biosmem_heap_cur)) ! 677: boot_panic(biosmem_panic_nomem_msg); ! 678: ! 679: #ifdef MACH_HYP ! 680: biosmem_heap_cur += size; ! 681: #else /* MACH_HYP */ ! 682: biosmem_heap_cur = addr; ! 683: #endif /* MACH_HYP */ ! 684: ! 685: return addr; ! 686: } ! 687: ! 688: phys_addr_t __boot ! 689: biosmem_directmap_size(void) ! 690: { ! 691: if (biosmem_segment_size(VM_PAGE_SEG_DIRECTMAP) != 0) ! 692: return biosmem_segment_end(VM_PAGE_SEG_DIRECTMAP); ! 693: else if (biosmem_segment_size(VM_PAGE_SEG_DMA32) != 0) ! 694: return biosmem_segment_end(VM_PAGE_SEG_DMA32); ! 695: else ! 696: return biosmem_segment_end(VM_PAGE_SEG_DMA); ! 697: } ! 698: ! 699: static const char * __init ! 700: biosmem_type_desc(unsigned int type) ! 701: { ! 702: switch (type) { ! 703: case BIOSMEM_TYPE_AVAILABLE: ! 704: return "available"; ! 705: case BIOSMEM_TYPE_RESERVED: ! 706: return "reserved"; ! 707: case BIOSMEM_TYPE_ACPI: ! 708: return "ACPI"; ! 709: case BIOSMEM_TYPE_NVS: ! 710: return "ACPI NVS"; ! 711: case BIOSMEM_TYPE_UNUSABLE: ! 712: return "unusable"; ! 713: default: ! 714: return "unknown (reserved)"; ! 715: } ! 716: } ! 717: ! 718: static void __init ! 719: biosmem_map_show(void) ! 720: { ! 721: const struct biosmem_map_entry *entry, *end; ! 722: ! 723: printf("biosmem: physical memory map:\n"); ! 724: ! 725: for (entry = biosmem_map, end = entry + biosmem_map_size; ! 726: entry < end; ! 727: entry++) ! 728: printf("biosmem: %018llx:%018llx, %s\n", entry->base_addr, ! 729: entry->base_addr + entry->length, ! 730: biosmem_type_desc(entry->type)); ! 731: ! 732: printf("biosmem: heap: %x-%x\n", biosmem_heap_start, biosmem_heap_end); ! 733: } ! 734: ! 735: static void __init ! 736: biosmem_load_segment(struct biosmem_segment *seg, uint64_t max_phys_end, ! 737: phys_addr_t phys_start, phys_addr_t phys_end, ! 738: phys_addr_t avail_start, phys_addr_t avail_end) ! 739: { ! 740: unsigned int seg_index; ! 741: ! 742: seg_index = seg - biosmem_segments; ! 743: ! 744: if (phys_end > max_phys_end) { ! 745: if (max_phys_end <= phys_start) { ! 746: printf("biosmem: warning: segment %s physically unreachable, " ! 747: "not loaded\n", vm_page_seg_name(seg_index)); ! 748: return; ! 749: } ! 750: ! 751: printf("biosmem: warning: segment %s truncated to %#llx\n", ! 752: vm_page_seg_name(seg_index), max_phys_end); ! 753: phys_end = max_phys_end; ! 754: } ! 755: ! 756: if ((avail_start < phys_start) || (avail_start >= phys_end)) ! 757: avail_start = phys_start; ! 758: ! 759: if ((avail_end <= phys_start) || (avail_end > phys_end)) ! 760: avail_end = phys_end; ! 761: ! 762: seg->avail_start = avail_start; ! 763: seg->avail_end = avail_end; ! 764: vm_page_load(seg_index, phys_start, phys_end, avail_start, avail_end); ! 765: } ! 766: ! 767: void __init ! 768: biosmem_setup(void) ! 769: { ! 770: struct biosmem_segment *seg; ! 771: unsigned int i; ! 772: ! 773: biosmem_map_show(); ! 774: ! 775: for (i = 0; i < ARRAY_SIZE(biosmem_segments); i++) { ! 776: if (biosmem_segment_size(i) == 0) ! 777: break; ! 778: ! 779: seg = &biosmem_segments[i]; ! 780: biosmem_load_segment(seg, VM_PAGE_HIGHMEM_LIMIT, seg->start, seg->end, ! 781: biosmem_heap_start, biosmem_heap_cur); ! 782: } ! 783: } ! 784: ! 785: static void __init ! 786: biosmem_free_usable_range(phys_addr_t start, phys_addr_t end) ! 787: { ! 788: struct vm_page *page; ! 789: ! 790: printf("biosmem: release to vm_page: %llx-%llx (%lluk)\n", ! 791: (unsigned long long)start, (unsigned long long)end, ! 792: (unsigned long long)((end - start) >> 10)); ! 793: ! 794: while (start < end) { ! 795: page = vm_page_lookup_pa(start); ! 796: assert(page != NULL); ! 797: vm_page_manage(page); ! 798: start += PAGE_SIZE; ! 799: } ! 800: } ! 801: ! 802: static void __init ! 803: biosmem_free_usable_update_start(phys_addr_t *start, phys_addr_t res_start, ! 804: phys_addr_t res_end) ! 805: { ! 806: if ((*start >= res_start) && (*start < res_end)) ! 807: *start = res_end; ! 808: } ! 809: ! 810: static phys_addr_t __init ! 811: biosmem_free_usable_start(phys_addr_t start) ! 812: { ! 813: const struct biosmem_segment *seg; ! 814: unsigned int i; ! 815: ! 816: biosmem_free_usable_update_start(&start, _kvtophys(&_start), ! 817: _kvtophys(&_end)); ! 818: biosmem_free_usable_update_start(&start, biosmem_heap_start, ! 819: biosmem_heap_end); ! 820: ! 821: for (i = 0; i < ARRAY_SIZE(biosmem_segments); i++) { ! 822: seg = &biosmem_segments[i]; ! 823: biosmem_free_usable_update_start(&start, seg->avail_start, ! 824: seg->avail_end); ! 825: } ! 826: ! 827: return start; ! 828: } ! 829: ! 830: static int __init ! 831: biosmem_free_usable_reserved(phys_addr_t addr) ! 832: { ! 833: const struct biosmem_segment *seg; ! 834: unsigned int i; ! 835: ! 836: if ((addr >= _kvtophys(&_start)) ! 837: && (addr < _kvtophys(&_end))) ! 838: return 1; ! 839: ! 840: if ((addr >= biosmem_heap_start) && (addr < biosmem_heap_end)) ! 841: return 1; ! 842: ! 843: for (i = 0; i < ARRAY_SIZE(biosmem_segments); i++) { ! 844: seg = &biosmem_segments[i]; ! 845: ! 846: if ((addr >= seg->avail_start) && (addr < seg->avail_end)) ! 847: return 1; ! 848: } ! 849: ! 850: return 0; ! 851: } ! 852: ! 853: static phys_addr_t __init ! 854: biosmem_free_usable_end(phys_addr_t start, phys_addr_t entry_end) ! 855: { ! 856: while (start < entry_end) { ! 857: if (biosmem_free_usable_reserved(start)) ! 858: break; ! 859: ! 860: start += PAGE_SIZE; ! 861: } ! 862: ! 863: return start; ! 864: } ! 865: ! 866: static void __init ! 867: biosmem_free_usable_entry(phys_addr_t start, phys_addr_t end) ! 868: { ! 869: phys_addr_t entry_end; ! 870: ! 871: entry_end = end; ! 872: ! 873: for (;;) { ! 874: start = biosmem_free_usable_start(start); ! 875: ! 876: if (start >= entry_end) ! 877: return; ! 878: ! 879: end = biosmem_free_usable_end(start, entry_end); ! 880: biosmem_free_usable_range(start, end); ! 881: start = end; ! 882: } ! 883: } ! 884: ! 885: void __init ! 886: biosmem_free_usable(void) ! 887: { ! 888: struct biosmem_map_entry *entry; ! 889: uint64_t start, end; ! 890: unsigned int i; ! 891: ! 892: for (i = 0; i < biosmem_map_size; i++) { ! 893: entry = &biosmem_map[i]; ! 894: ! 895: if (entry->type != BIOSMEM_TYPE_AVAILABLE) ! 896: continue; ! 897: ! 898: start = vm_page_round(entry->base_addr); ! 899: ! 900: if (start >= VM_PAGE_HIGHMEM_LIMIT) ! 901: break; ! 902: ! 903: end = vm_page_trunc(entry->base_addr + entry->length); ! 904: ! 905: if (start < BIOSMEM_BASE) ! 906: start = BIOSMEM_BASE; ! 907: ! 908: biosmem_free_usable_entry(start, end); ! 909: } ! 910: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.