Annotation of Gnu-Mach/i386/i386at/biosmem.c, revision 1.1.1.2

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 <kern/assert.h>
                     22: #include <kern/debug.h>
                     23: #include <kern/macros.h>
                     24: #include <kern/printf.h>
                     25: #include <mach/vm_param.h>
                     26: #include <mach/xen.h>
                     27: #include <mach/machine/multiboot.h>
                     28: #include <sys/types.h>
                     29: #include <vm/vm_page.h>
                     30: 
1.1.1.2 ! root       31: #define DEBUG 0
        !            32: 
1.1       root       33: #define __boot
                     34: #define __bootdata
                     35: #define __init
                     36: 
                     37: #define boot_memmove    memmove
1.1.1.2 ! root       38: #define boot_panic(s)   panic("%s", s)
1.1       root       39: #define boot_strlen     strlen
                     40: 
                     41: #define BOOT_CGAMEM     phystokv(0xb8000)
                     42: #define BOOT_CGACHARS   (80 * 25)
                     43: #define BOOT_CGACOLOR   0x7
                     44: 
1.1.1.2 ! root       45: #define BIOSMEM_MAX_BOOT_DATA 64
        !            46: 
        !            47: /*
        !            48:  * Boot data descriptor.
        !            49:  *
        !            50:  * The start and end addresses must not be page-aligned, since there
        !            51:  * could be more than one range inside a single page.
        !            52:  */
        !            53: struct biosmem_boot_data {
        !            54:     phys_addr_t start;
        !            55:     phys_addr_t end;
        !            56:     boolean_t temporary;
        !            57: };
        !            58: 
        !            59: /*
        !            60:  * Sorted array of boot data descriptors.
        !            61:  */
        !            62: static struct biosmem_boot_data biosmem_boot_data_array[BIOSMEM_MAX_BOOT_DATA]
        !            63:     __bootdata;
        !            64: static unsigned int biosmem_nr_boot_data __bootdata;
1.1       root       65: 
                     66: /*
                     67:  * Maximum number of entries in the BIOS memory map.
                     68:  *
                     69:  * Because of adjustments of overlapping ranges, the memory map can grow
                     70:  * to twice this size.
                     71:  */
                     72: #define BIOSMEM_MAX_MAP_SIZE 128
                     73: 
                     74: /*
                     75:  * Memory range types.
                     76:  */
                     77: #define BIOSMEM_TYPE_AVAILABLE  1
                     78: #define BIOSMEM_TYPE_RESERVED   2
                     79: #define BIOSMEM_TYPE_ACPI       3
                     80: #define BIOSMEM_TYPE_NVS        4
                     81: #define BIOSMEM_TYPE_UNUSABLE   5
                     82: #define BIOSMEM_TYPE_DISABLED   6
                     83: 
                     84: /*
                     85:  * Memory map entry.
                     86:  */
                     87: struct biosmem_map_entry {
                     88:     uint64_t base_addr;
                     89:     uint64_t length;
                     90:     unsigned int type;
                     91: };
                     92: 
                     93: /*
                     94:  * Memory map built from the information passed by the boot loader.
                     95:  *
                     96:  * If the boot loader didn't pass a valid memory map, a simple map is built
                     97:  * based on the mem_lower and mem_upper multiboot fields.
                     98:  */
                     99: static struct biosmem_map_entry biosmem_map[BIOSMEM_MAX_MAP_SIZE * 2]
                    100:     __bootdata;
                    101: static unsigned int biosmem_map_size __bootdata;
                    102: 
                    103: /*
1.1.1.2 ! root      104:  * Contiguous block of physical memory.
        !           105:  */
        !           106: struct biosmem_segment {
        !           107:     phys_addr_t start;
        !           108:     phys_addr_t end;
        !           109: };
        !           110: 
        !           111: /*
1.1       root      112:  * Physical segment boundaries.
                    113:  */
                    114: static struct biosmem_segment biosmem_segments[VM_PAGE_MAX_SEGS] __bootdata;
                    115: 
                    116: /*
                    117:  * Boundaries of the simple bootstrap heap.
                    118:  *
                    119:  * This heap is located above BIOS memory.
                    120:  */
1.1.1.2 ! root      121: static phys_addr_t biosmem_heap_start __bootdata;
        !           122: static phys_addr_t biosmem_heap_bottom __bootdata;
        !           123: static phys_addr_t biosmem_heap_top __bootdata;
        !           124: static phys_addr_t biosmem_heap_end __bootdata;
        !           125: 
        !           126: /*
        !           127:  * Boot allocation policy.
        !           128:  *
        !           129:  * Top-down allocations are normally preferred to avoid unnecessarily
        !           130:  * filling the DMA segment.
        !           131:  */
        !           132: static boolean_t biosmem_heap_topdown __bootdata;
1.1       root      133: 
1.1.1.2 ! root      134: static char biosmem_panic_inval_boot_data[] __bootdata
        !           135:     = "biosmem: invalid boot data";
        !           136: static char biosmem_panic_too_many_boot_data[] __bootdata
        !           137:     = "biosmem: too many boot data ranges";
        !           138: static char biosmem_panic_too_big_msg[] __bootdata
1.1       root      139:     = "biosmem: too many memory map entries";
                    140: #ifndef MACH_HYP
                    141: static char biosmem_panic_setup_msg[] __bootdata
                    142:     = "biosmem: unable to set up the early memory allocator";
                    143: #endif /* MACH_HYP */
                    144: static char biosmem_panic_noseg_msg[] __bootdata
                    145:     = "biosmem: unable to find any memory segment";
                    146: static char biosmem_panic_inval_msg[] __bootdata
                    147:     = "biosmem: attempt to allocate 0 page";
                    148: static char biosmem_panic_nomem_msg[] __bootdata
                    149:     = "biosmem: unable to allocate memory";
                    150: 
1.1.1.2 ! root      151: void __boot
        !           152: biosmem_register_boot_data(phys_addr_t start, phys_addr_t end,
        !           153:                            boolean_t temporary)
        !           154: {
        !           155:     unsigned int i;
        !           156: 
        !           157:     if (start >= end) {
        !           158:         boot_panic(biosmem_panic_inval_boot_data);
        !           159:     }
        !           160: 
        !           161:     if (biosmem_nr_boot_data == ARRAY_SIZE(biosmem_boot_data_array)) {
        !           162:         boot_panic(biosmem_panic_too_many_boot_data);
        !           163:     }
        !           164: 
        !           165:     for (i = 0; i < biosmem_nr_boot_data; i++) {
        !           166:         /* Check if the new range overlaps */
        !           167:         if ((end > biosmem_boot_data_array[i].start)
        !           168:              && (start < biosmem_boot_data_array[i].end)) {
        !           169: 
        !           170:             /*
        !           171:              * If it does, check whether it's part of another range.
        !           172:              * For example, this applies to debugging symbols directly
        !           173:              * taken from the kernel image.
        !           174:              */
        !           175:             if ((start >= biosmem_boot_data_array[i].start)
        !           176:                 && (end <= biosmem_boot_data_array[i].end)) {
        !           177: 
        !           178:                 /*
        !           179:                  * If it's completely included, make sure that a permanent
        !           180:                  * range remains permanent.
        !           181:                  *
        !           182:                  * XXX This means that if one big range is first registered
        !           183:                  * as temporary, and a smaller range inside of it is
        !           184:                  * registered as permanent, the bigger range becomes
        !           185:                  * permanent. It's not easy nor useful in practice to do
        !           186:                  * better than that.
        !           187:                  */
        !           188:                 if (biosmem_boot_data_array[i].temporary != temporary) {
        !           189:                     biosmem_boot_data_array[i].temporary = FALSE;
        !           190:                 }
        !           191: 
        !           192:                 return;
        !           193:             }
        !           194: 
        !           195:             boot_panic(biosmem_panic_inval_boot_data);
        !           196:         }
        !           197: 
        !           198:         if (end <= biosmem_boot_data_array[i].start) {
        !           199:             break;
        !           200:         }
        !           201:     }
        !           202: 
        !           203:     boot_memmove(&biosmem_boot_data_array[i + 1],
        !           204:                  &biosmem_boot_data_array[i],
        !           205:                  (biosmem_nr_boot_data - i) * sizeof(*biosmem_boot_data_array));
        !           206: 
        !           207:     biosmem_boot_data_array[i].start = start;
        !           208:     biosmem_boot_data_array[i].end = end;
        !           209:     biosmem_boot_data_array[i].temporary = temporary;
        !           210:     biosmem_nr_boot_data++;
        !           211: }
        !           212: 
        !           213: static void __init
        !           214: biosmem_unregister_boot_data(phys_addr_t start, phys_addr_t end)
        !           215: {
        !           216:     unsigned int i;
        !           217: 
        !           218:     if (start >= end) {
        !           219:         panic("%s", biosmem_panic_inval_boot_data);
        !           220:     }
        !           221: 
        !           222:     assert(biosmem_nr_boot_data != 0);
        !           223: 
        !           224:     for (i = 0; biosmem_nr_boot_data; i++) {
        !           225:         if ((start == biosmem_boot_data_array[i].start)
        !           226:             && (end == biosmem_boot_data_array[i].end)) {
        !           227:             break;
        !           228:         }
        !           229:     }
        !           230: 
        !           231:     if (i == biosmem_nr_boot_data) {
        !           232:         return;
        !           233:     }
        !           234: 
        !           235: #if DEBUG
        !           236:     printf("biosmem: unregister boot data: %llx:%llx\n",
        !           237:            (unsigned long long)biosmem_boot_data_array[i].start,
        !           238:            (unsigned long long)biosmem_boot_data_array[i].end);
        !           239: #endif /* DEBUG */
        !           240: 
        !           241:     biosmem_nr_boot_data--;
        !           242: 
        !           243:     boot_memmove(&biosmem_boot_data_array[i],
        !           244:                  &biosmem_boot_data_array[i + 1],
        !           245:                  (biosmem_nr_boot_data - i) * sizeof(*biosmem_boot_data_array));
        !           246: }
        !           247: 
1.1       root      248: #ifndef MACH_HYP
                    249: 
                    250: static void __boot
                    251: biosmem_map_build(const struct multiboot_raw_info *mbi)
                    252: {
                    253:     struct multiboot_raw_mmap_entry *mb_entry, *mb_end;
                    254:     struct biosmem_map_entry *start, *entry, *end;
                    255:     unsigned long addr;
                    256: 
                    257:     addr = phystokv(mbi->mmap_addr);
                    258:     mb_entry = (struct multiboot_raw_mmap_entry *)addr;
                    259:     mb_end = (struct multiboot_raw_mmap_entry *)(addr + mbi->mmap_length);
                    260:     start = biosmem_map;
                    261:     entry = start;
                    262:     end = entry + BIOSMEM_MAX_MAP_SIZE;
                    263: 
                    264:     while ((mb_entry < mb_end) && (entry < end)) {
                    265:         entry->base_addr = mb_entry->base_addr;
                    266:         entry->length = mb_entry->length;
                    267:         entry->type = mb_entry->type;
                    268: 
                    269:         mb_entry = (void *)mb_entry + sizeof(mb_entry->size) + mb_entry->size;
                    270:         entry++;
                    271:     }
                    272: 
                    273:     biosmem_map_size = entry - start;
                    274: }
                    275: 
                    276: static void __boot
                    277: biosmem_map_build_simple(const struct multiboot_raw_info *mbi)
                    278: {
                    279:     struct biosmem_map_entry *entry;
                    280: 
                    281:     entry = biosmem_map;
                    282:     entry->base_addr = 0;
                    283:     entry->length = mbi->mem_lower << 10;
                    284:     entry->type = BIOSMEM_TYPE_AVAILABLE;
                    285: 
                    286:     entry++;
                    287:     entry->base_addr = BIOSMEM_END;
                    288:     entry->length = mbi->mem_upper << 10;
                    289:     entry->type = BIOSMEM_TYPE_AVAILABLE;
                    290: 
                    291:     biosmem_map_size = 2;
                    292: }
                    293: 
                    294: #endif /* MACH_HYP */
                    295: 
                    296: static int __boot
                    297: biosmem_map_entry_is_invalid(const struct biosmem_map_entry *entry)
                    298: {
                    299:     return (entry->base_addr + entry->length) <= entry->base_addr;
                    300: }
                    301: 
                    302: static void __boot
                    303: biosmem_map_filter(void)
                    304: {
                    305:     struct biosmem_map_entry *entry;
                    306:     unsigned int i;
                    307: 
                    308:     i = 0;
                    309: 
                    310:     while (i < biosmem_map_size) {
                    311:         entry = &biosmem_map[i];
                    312: 
                    313:         if (biosmem_map_entry_is_invalid(entry)) {
                    314:             biosmem_map_size--;
                    315:             boot_memmove(entry, entry + 1,
                    316:                          (biosmem_map_size - i) * sizeof(*entry));
                    317:             continue;
                    318:         }
                    319: 
                    320:         i++;
                    321:     }
                    322: }
                    323: 
                    324: static void __boot
                    325: biosmem_map_sort(void)
                    326: {
                    327:     struct biosmem_map_entry tmp;
                    328:     unsigned int i, j;
                    329: 
                    330:     /*
                    331:      * Simple insertion sort.
                    332:      */
                    333:     for (i = 1; i < biosmem_map_size; i++) {
                    334:         tmp = biosmem_map[i];
                    335: 
                    336:         for (j = i - 1; j < i; j--) {
                    337:             if (biosmem_map[j].base_addr < tmp.base_addr)
                    338:                 break;
                    339: 
                    340:             biosmem_map[j + 1] = biosmem_map[j];
                    341:         }
                    342: 
                    343:         biosmem_map[j + 1] = tmp;
                    344:     }
                    345: }
                    346: 
                    347: static void __boot
                    348: biosmem_map_adjust(void)
                    349: {
                    350:     struct biosmem_map_entry tmp, *a, *b, *first, *second;
                    351:     uint64_t a_end, b_end, last_end;
                    352:     unsigned int i, j, last_type;
                    353: 
                    354:     biosmem_map_filter();
                    355: 
                    356:     /*
                    357:      * Resolve overlapping areas, giving priority to most restrictive
                    358:      * (i.e. numerically higher) types.
                    359:      */
                    360:     for (i = 0; i < biosmem_map_size; i++) {
                    361:         a = &biosmem_map[i];
                    362:         a_end = a->base_addr + a->length;
                    363: 
                    364:         j = i + 1;
                    365: 
                    366:         while (j < biosmem_map_size) {
                    367:             b = &biosmem_map[j];
                    368:             b_end = b->base_addr + b->length;
                    369: 
                    370:             if ((a->base_addr >= b_end) || (a_end <= b->base_addr)) {
                    371:                 j++;
                    372:                 continue;
                    373:             }
                    374: 
                    375:             if (a->base_addr < b->base_addr) {
                    376:                 first = a;
                    377:                 second = b;
                    378:             } else {
                    379:                 first = b;
                    380:                 second = a;
                    381:             }
                    382: 
                    383:             if (a_end > b_end) {
                    384:                 last_end = a_end;
                    385:                 last_type = a->type;
                    386:             } else {
                    387:                 last_end = b_end;
                    388:                 last_type = b->type;
                    389:             }
                    390: 
                    391:             tmp.base_addr = second->base_addr;
                    392:             tmp.length = MIN(a_end, b_end) - tmp.base_addr;
                    393:             tmp.type = MAX(a->type, b->type);
                    394:             first->length = tmp.base_addr - first->base_addr;
                    395:             second->base_addr += tmp.length;
                    396:             second->length = last_end - second->base_addr;
                    397:             second->type = last_type;
                    398: 
                    399:             /*
                    400:              * Filter out invalid entries.
                    401:              */
                    402:             if (biosmem_map_entry_is_invalid(a)
                    403:                 && biosmem_map_entry_is_invalid(b)) {
                    404:                 *a = tmp;
                    405:                 biosmem_map_size--;
                    406:                 memmove(b, b + 1, (biosmem_map_size - j) * sizeof(*b));
                    407:                 continue;
                    408:             } else if (biosmem_map_entry_is_invalid(a)) {
                    409:                 *a = tmp;
                    410:                 j++;
                    411:                 continue;
                    412:             } else if (biosmem_map_entry_is_invalid(b)) {
                    413:                 *b = tmp;
                    414:                 j++;
                    415:                 continue;
                    416:             }
                    417: 
                    418:             if (tmp.type == a->type)
                    419:                 first = a;
                    420:             else if (tmp.type == b->type)
                    421:                 first = b;
                    422:             else {
                    423: 
                    424:                 /*
                    425:                  * If the overlapping area can't be merged with one of its
                    426:                  * neighbors, it must be added as a new entry.
                    427:                  */
                    428: 
                    429:                 if (biosmem_map_size >= ARRAY_SIZE(biosmem_map))
1.1.1.2 ! root      430:                     boot_panic(biosmem_panic_too_big_msg);
1.1       root      431: 
                    432:                 biosmem_map[biosmem_map_size] = tmp;
                    433:                 biosmem_map_size++;
                    434:                 j++;
                    435:                 continue;
                    436:             }
                    437: 
                    438:             if (first->base_addr > tmp.base_addr)
                    439:                 first->base_addr = tmp.base_addr;
                    440: 
                    441:             first->length += tmp.length;
                    442:             j++;
                    443:         }
                    444:     }
                    445: 
                    446:     biosmem_map_sort();
                    447: }
                    448: 
1.1.1.2 ! root      449: /*
        !           450:  * Find addresses of physical memory within a given range.
        !           451:  *
        !           452:  * This function considers the memory map with the [*phys_start, *phys_end]
        !           453:  * range on entry, and returns the lowest address of physical memory
        !           454:  * in *phys_start, and the highest address of unusable memory immediately
        !           455:  * following physical memory in *phys_end.
        !           456:  *
        !           457:  * These addresses are normally used to establish the range of a segment.
        !           458:  */
1.1       root      459: static int __boot
                    460: biosmem_map_find_avail(phys_addr_t *phys_start, phys_addr_t *phys_end)
                    461: {
                    462:     const struct biosmem_map_entry *entry, *map_end;
                    463:     phys_addr_t seg_start, seg_end;
                    464:     uint64_t start, end;
                    465: 
                    466:     seg_start = (phys_addr_t)-1;
                    467:     seg_end = (phys_addr_t)-1;
                    468:     map_end = biosmem_map + biosmem_map_size;
                    469: 
                    470:     for (entry = biosmem_map; entry < map_end; entry++) {
                    471:         if (entry->type != BIOSMEM_TYPE_AVAILABLE)
                    472:             continue;
                    473: 
                    474:         start = vm_page_round(entry->base_addr);
                    475: 
                    476:         if (start >= *phys_end)
                    477:             break;
                    478: 
                    479:         end = vm_page_trunc(entry->base_addr + entry->length);
                    480: 
                    481:         if ((start < end) && (start < *phys_end) && (end > *phys_start)) {
                    482:             if (seg_start == (phys_addr_t)-1)
                    483:                 seg_start = start;
                    484: 
                    485:             seg_end = end;
                    486:         }
                    487:     }
                    488: 
                    489:     if ((seg_start == (phys_addr_t)-1) || (seg_end == (phys_addr_t)-1))
                    490:         return -1;
                    491: 
                    492:     if (seg_start > *phys_start)
                    493:         *phys_start = seg_start;
                    494: 
                    495:     if (seg_end < *phys_end)
                    496:         *phys_end = seg_end;
                    497: 
                    498:     return 0;
                    499: }
                    500: 
                    501: static void __boot
                    502: biosmem_set_segment(unsigned int seg_index, phys_addr_t start, phys_addr_t end)
                    503: {
                    504:     biosmem_segments[seg_index].start = start;
                    505:     biosmem_segments[seg_index].end = end;
                    506: }
                    507: 
                    508: static phys_addr_t __boot
                    509: biosmem_segment_end(unsigned int seg_index)
                    510: {
                    511:     return biosmem_segments[seg_index].end;
                    512: }
                    513: 
                    514: static phys_addr_t __boot
                    515: biosmem_segment_size(unsigned int seg_index)
                    516: {
                    517:     return biosmem_segments[seg_index].end - biosmem_segments[seg_index].start;
                    518: }
                    519: 
1.1.1.2 ! root      520: static int __boot
        !           521: biosmem_find_avail_clip(phys_addr_t *avail_start, phys_addr_t *avail_end,
        !           522:                         phys_addr_t data_start, phys_addr_t data_end)
1.1       root      523: {
1.1.1.2 ! root      524:     phys_addr_t orig_end;
1.1       root      525: 
1.1.1.2 ! root      526:     assert(data_start < data_end);
1.1       root      527: 
1.1.1.2 ! root      528:     orig_end = data_end;
        !           529:     data_start = vm_page_trunc(data_start);
        !           530:     data_end = vm_page_round(data_end);
1.1       root      531: 
1.1.1.2 ! root      532:     if (data_end < orig_end) {
        !           533:         boot_panic(biosmem_panic_inval_boot_data);
        !           534:     }
1.1       root      535: 
1.1.1.2 ! root      536:     if ((data_end <= *avail_start) || (data_start >= *avail_end)) {
        !           537:         return 0;
1.1       root      538:     }
                    539: 
1.1.1.2 ! root      540:     if (data_start > *avail_start) {
        !           541:         *avail_end = data_start;
        !           542:     } else {
        !           543:         if (data_end >= *avail_end) {
        !           544:             return -1;
        !           545:         }
        !           546: 
        !           547:         *avail_start = data_end;
1.1       root      548:     }
1.1.1.2 ! root      549: 
        !           550:     return 0;
1.1       root      551: }
                    552: 
                    553: /*
1.1.1.2 ! root      554:  * Find available memory in the given range.
        !           555:  *
        !           556:  * The search starts at the given start address, up to the given end address.
        !           557:  * If a range is found, it is stored through the avail_startp and avail_endp
        !           558:  * pointers.
1.1       root      559:  *
1.1.1.2 ! root      560:  * The range boundaries are page-aligned on return.
1.1       root      561:  */
1.1.1.2 ! root      562: static int __boot
        !           563: biosmem_find_avail(phys_addr_t start, phys_addr_t end,
        !           564:                    phys_addr_t *avail_start, phys_addr_t *avail_end)
1.1       root      565: {
1.1.1.2 ! root      566:     phys_addr_t orig_start;
        !           567:     unsigned int i;
        !           568:     int error;
1.1       root      569: 
1.1.1.2 ! root      570:     assert(start <= end);
1.1       root      571: 
1.1.1.2 ! root      572:     orig_start = start;
        !           573:     start = vm_page_round(start);
        !           574:     end = vm_page_trunc(end);
1.1       root      575: 
1.1.1.2 ! root      576:     if ((start < orig_start) || (start >= end)) {
        !           577:         return -1;
1.1       root      578:     }
                    579: 
1.1.1.2 ! root      580:     *avail_start = start;
        !           581:     *avail_end = end;
1.1       root      582: 
1.1.1.2 ! root      583:     for (i = 0; i < biosmem_nr_boot_data; i++) {
        !           584:         error = biosmem_find_avail_clip(avail_start, avail_end,
        !           585:                                         biosmem_boot_data_array[i].start,
        !           586:                                         biosmem_boot_data_array[i].end);
1.1       root      587: 
1.1.1.2 ! root      588:         if (error) {
        !           589:             return -1;
1.1       root      590:         }
                    591:     }
                    592: 
1.1.1.2 ! root      593:     return 0;
1.1       root      594: }
                    595: 
1.1.1.2 ! root      596: #ifndef MACH_HYP
        !           597: 
1.1       root      598: static void __boot
1.1.1.2 ! root      599: biosmem_setup_allocator(const struct multiboot_raw_info *mbi)
1.1       root      600: {
1.1.1.2 ! root      601:     phys_addr_t heap_start, heap_end, max_heap_start, max_heap_end;
        !           602:     phys_addr_t start, end;
        !           603:     int error;
1.1       root      604: 
                    605:     /*
                    606:      * Find some memory for the heap. Look for the largest unused area in
                    607:      * upper memory, carefully avoiding all boot data.
                    608:      */
1.1.1.2 ! root      609:     end = vm_page_trunc((mbi->mem_upper + 1024) << 10);
1.1       root      610: 
                    611: #ifndef __LP64__
1.1.1.2 ! root      612:     if (end > VM_PAGE_DIRECTMAP_LIMIT)
        !           613:         end = VM_PAGE_DIRECTMAP_LIMIT;
1.1       root      614: #endif /* __LP64__ */
                    615: 
                    616:     max_heap_start = 0;
                    617:     max_heap_end = 0;
1.1.1.2 ! root      618:     start = BIOSMEM_END;
1.1       root      619: 
1.1.1.2 ! root      620:     for (;;) {
        !           621:         error = biosmem_find_avail(start, end, &heap_start, &heap_end);
        !           622: 
        !           623:         if (error) {
        !           624:             break;
1.1       root      625:         }
                    626: 
                    627:         if ((heap_end - heap_start) > (max_heap_end - max_heap_start)) {
                    628:             max_heap_start = heap_start;
                    629:             max_heap_end = heap_end;
                    630:         }
                    631: 
1.1.1.2 ! root      632:         start = heap_end;
        !           633:     }
1.1       root      634: 
                    635:     if (max_heap_start >= max_heap_end)
                    636:         boot_panic(biosmem_panic_setup_msg);
                    637: 
                    638:     biosmem_heap_start = max_heap_start;
                    639:     biosmem_heap_end = max_heap_end;
1.1.1.2 ! root      640:     biosmem_heap_bottom = biosmem_heap_start;
        !           641:     biosmem_heap_top = biosmem_heap_end;
        !           642:     biosmem_heap_topdown = TRUE;
        !           643: 
        !           644:     /* Prevent biosmem_free_usable() from releasing the heap */
        !           645:     biosmem_register_boot_data(biosmem_heap_start, biosmem_heap_end, FALSE);
1.1       root      646: }
                    647: 
                    648: #endif /* MACH_HYP */
                    649: 
                    650: static void __boot
                    651: biosmem_bootstrap_common(void)
                    652: {
1.1.1.2 ! root      653:     phys_addr_t phys_start, phys_end;
1.1       root      654:     int error;
                    655: 
                    656:     biosmem_map_adjust();
                    657: 
                    658:     phys_start = BIOSMEM_BASE;
                    659:     phys_end = VM_PAGE_DMA_LIMIT;
                    660:     error = biosmem_map_find_avail(&phys_start, &phys_end);
                    661: 
                    662:     if (error)
                    663:         boot_panic(biosmem_panic_noseg_msg);
                    664: 
                    665:     biosmem_set_segment(VM_PAGE_SEG_DMA, phys_start, phys_end);
                    666: 
                    667:     phys_start = VM_PAGE_DMA_LIMIT;
                    668: #ifdef VM_PAGE_DMA32_LIMIT
                    669:     phys_end = VM_PAGE_DMA32_LIMIT;
                    670:     error = biosmem_map_find_avail(&phys_start, &phys_end);
                    671: 
                    672:     if (error)
1.1.1.2 ! root      673:         return;
1.1       root      674: 
                    675:     biosmem_set_segment(VM_PAGE_SEG_DMA32, phys_start, phys_end);
                    676: 
                    677:     phys_start = VM_PAGE_DMA32_LIMIT;
                    678: #endif /* VM_PAGE_DMA32_LIMIT */
                    679:     phys_end = VM_PAGE_DIRECTMAP_LIMIT;
                    680:     error = biosmem_map_find_avail(&phys_start, &phys_end);
                    681: 
                    682:     if (error)
1.1.1.2 ! root      683:         return;
1.1       root      684: 
                    685:     biosmem_set_segment(VM_PAGE_SEG_DIRECTMAP, phys_start, phys_end);
                    686: 
                    687:     phys_start = VM_PAGE_DIRECTMAP_LIMIT;
                    688:     phys_end = VM_PAGE_HIGHMEM_LIMIT;
                    689:     error = biosmem_map_find_avail(&phys_start, &phys_end);
                    690: 
                    691:     if (error)
1.1.1.2 ! root      692:         return;
1.1       root      693: 
                    694:     biosmem_set_segment(VM_PAGE_SEG_HIGHMEM, phys_start, phys_end);
                    695: }
                    696: 
                    697: #ifdef MACH_HYP
                    698: 
                    699: void
                    700: biosmem_xen_bootstrap(void)
                    701: {
                    702:     struct biosmem_map_entry *entry;
                    703: 
                    704:     entry = biosmem_map;
                    705:     entry->base_addr = 0;
                    706:     entry->length = boot_info.nr_pages << PAGE_SHIFT;
                    707:     entry->type = BIOSMEM_TYPE_AVAILABLE;
                    708: 
                    709:     biosmem_map_size = 1;
                    710: 
                    711:     biosmem_bootstrap_common();
                    712: 
                    713:     biosmem_heap_start = _kvtophys(boot_info.pt_base)
                    714:                          + (boot_info.nr_pt_frames + 3) * 0x1000;
                    715:     biosmem_heap_end = boot_info.nr_pages << PAGE_SHIFT;
                    716: 
                    717: #ifndef __LP64__
                    718:     if (biosmem_heap_end > VM_PAGE_DIRECTMAP_LIMIT)
                    719:         biosmem_heap_end = VM_PAGE_DIRECTMAP_LIMIT;
                    720: #endif /* __LP64__ */
                    721: 
1.1.1.2 ! root      722:     biosmem_heap_bottom = biosmem_heap_start;
        !           723:     biosmem_heap_top = biosmem_heap_end;
        !           724: 
1.1       root      725:     /*
1.1.1.2 ! root      726:      * XXX Allocation on Xen are initially bottom-up :
1.1       root      727:      * At the "start of day", only 512k are available after the boot
                    728:      * data. The pmap module then creates a 4g mapping so all physical
                    729:      * memory is available, but it uses this allocator to do so.
                    730:      * Therefore, it must return pages from this small 512k regions
                    731:      * first.
                    732:      */
1.1.1.2 ! root      733:     biosmem_heap_topdown = FALSE;
        !           734: 
        !           735:     /*
        !           736:      * Prevent biosmem_free_usable() from releasing the Xen boot information
        !           737:      * and the heap.
        !           738:      */
        !           739:     biosmem_register_boot_data(0, biosmem_heap_end, FALSE);
1.1       root      740: }
                    741: 
                    742: #else /* MACH_HYP */
                    743: 
                    744: void __boot
1.1.1.2 ! root      745: biosmem_bootstrap(const struct multiboot_raw_info *mbi)
1.1       root      746: {
                    747:     if (mbi->flags & MULTIBOOT_LOADER_MMAP)
                    748:         biosmem_map_build(mbi);
                    749:     else
                    750:         biosmem_map_build_simple(mbi);
                    751: 
                    752:     biosmem_bootstrap_common();
                    753:     biosmem_setup_allocator(mbi);
                    754: }
                    755: 
                    756: #endif /* MACH_HYP */
                    757: 
                    758: unsigned long __boot
                    759: biosmem_bootalloc(unsigned int nr_pages)
                    760: {
                    761:     unsigned long addr, size;
                    762: 
                    763:     size = vm_page_ptoa(nr_pages);
                    764: 
                    765:     if (size == 0)
                    766:         boot_panic(biosmem_panic_inval_msg);
                    767: 
1.1.1.2 ! root      768:     if (biosmem_heap_topdown) {
        !           769:         addr = biosmem_heap_top - size;
1.1       root      770: 
1.1.1.2 ! root      771:         if ((addr < biosmem_heap_start) || (addr > biosmem_heap_top)) {
        !           772:             boot_panic(biosmem_panic_nomem_msg);
        !           773:         }
1.1       root      774: 
1.1.1.2 ! root      775:         biosmem_heap_top = addr;
        !           776:     } else {
        !           777:         unsigned long end;
        !           778: 
        !           779:         addr = biosmem_heap_bottom;
        !           780:         end = addr + size;
        !           781: 
        !           782:         if ((end > biosmem_heap_end) || (end < biosmem_heap_bottom)) {
        !           783:             boot_panic(biosmem_panic_nomem_msg);
        !           784:         }
        !           785: 
        !           786:         biosmem_heap_bottom = end;
        !           787:     }
1.1       root      788: 
                    789:     return addr;
                    790: }
                    791: 
                    792: phys_addr_t __boot
1.1.1.2 ! root      793: biosmem_directmap_end(void)
1.1       root      794: {
                    795:     if (biosmem_segment_size(VM_PAGE_SEG_DIRECTMAP) != 0)
                    796:         return biosmem_segment_end(VM_PAGE_SEG_DIRECTMAP);
                    797:     else if (biosmem_segment_size(VM_PAGE_SEG_DMA32) != 0)
                    798:         return biosmem_segment_end(VM_PAGE_SEG_DMA32);
                    799:     else
                    800:         return biosmem_segment_end(VM_PAGE_SEG_DMA);
                    801: }
                    802: 
                    803: static const char * __init
                    804: biosmem_type_desc(unsigned int type)
                    805: {
                    806:     switch (type) {
                    807:     case BIOSMEM_TYPE_AVAILABLE:
                    808:         return "available";
                    809:     case BIOSMEM_TYPE_RESERVED:
                    810:         return "reserved";
                    811:     case BIOSMEM_TYPE_ACPI:
                    812:         return "ACPI";
                    813:     case BIOSMEM_TYPE_NVS:
                    814:         return "ACPI NVS";
                    815:     case BIOSMEM_TYPE_UNUSABLE:
                    816:         return "unusable";
                    817:     default:
                    818:         return "unknown (reserved)";
                    819:     }
                    820: }
                    821: 
                    822: static void __init
                    823: biosmem_map_show(void)
                    824: {
                    825:     const struct biosmem_map_entry *entry, *end;
                    826: 
                    827:     printf("biosmem: physical memory map:\n");
                    828: 
                    829:     for (entry = biosmem_map, end = entry + biosmem_map_size;
                    830:          entry < end;
                    831:          entry++)
                    832:         printf("biosmem: %018llx:%018llx, %s\n", entry->base_addr,
                    833:                entry->base_addr + entry->length,
                    834:                biosmem_type_desc(entry->type));
                    835: 
1.1.1.2 ! root      836: #if DEBUG
        !           837:     printf("biosmem: heap: %llx:%llx\n",
        !           838:            (unsigned long long)biosmem_heap_start,
        !           839:            (unsigned long long)biosmem_heap_end);
        !           840: #endif
1.1       root      841: }
                    842: 
                    843: static void __init
1.1.1.2 ! root      844: biosmem_load_segment(struct biosmem_segment *seg, uint64_t max_phys_end)
1.1       root      845: {
1.1.1.2 ! root      846:     phys_addr_t phys_start, phys_end, avail_start, avail_end;
1.1       root      847:     unsigned int seg_index;
                    848: 
1.1.1.2 ! root      849:     phys_start = seg->start;
        !           850:     phys_end = seg->end;
1.1       root      851:     seg_index = seg - biosmem_segments;
                    852: 
                    853:     if (phys_end > max_phys_end) {
                    854:         if (max_phys_end <= phys_start) {
                    855:             printf("biosmem: warning: segment %s physically unreachable, "
                    856:                    "not loaded\n", vm_page_seg_name(seg_index));
                    857:             return;
                    858:         }
                    859: 
                    860:         printf("biosmem: warning: segment %s truncated to %#llx\n",
                    861:                vm_page_seg_name(seg_index), max_phys_end);
                    862:         phys_end = max_phys_end;
                    863:     }
                    864: 
1.1.1.2 ! root      865:     vm_page_load(seg_index, phys_start, phys_end);
1.1       root      866: 
1.1.1.2 ! root      867:     /*
        !           868:      * Clip the remaining available heap to fit it into the loaded
        !           869:      * segment if possible.
        !           870:      */
1.1       root      871: 
1.1.1.2 ! root      872:     if ((biosmem_heap_top > phys_start) && (biosmem_heap_bottom < phys_end)) {
        !           873:         if (biosmem_heap_bottom >= phys_start) {
        !           874:             avail_start = biosmem_heap_bottom;
        !           875:         } else {
        !           876:             avail_start = phys_start;
        !           877:         }
        !           878: 
        !           879:         if (biosmem_heap_top <= phys_end) {
        !           880:             avail_end = biosmem_heap_top;
        !           881:         } else {
        !           882:             avail_end = phys_end;
        !           883:         }
        !           884: 
        !           885:         vm_page_load_heap(seg_index, avail_start, avail_end);
        !           886:     }
1.1       root      887: }
                    888: 
                    889: void __init
                    890: biosmem_setup(void)
                    891: {
                    892:     struct biosmem_segment *seg;
                    893:     unsigned int i;
                    894: 
                    895:     biosmem_map_show();
                    896: 
                    897:     for (i = 0; i < ARRAY_SIZE(biosmem_segments); i++) {
                    898:         if (biosmem_segment_size(i) == 0)
                    899:             break;
                    900: 
                    901:         seg = &biosmem_segments[i];
1.1.1.2 ! root      902:         biosmem_load_segment(seg, VM_PAGE_HIGHMEM_LIMIT);
        !           903:     }
        !           904: }
        !           905: 
        !           906: static void __init
        !           907: biosmem_unregister_temporary_boot_data(void)
        !           908: {
        !           909:     struct biosmem_boot_data *data;
        !           910:     unsigned int i;
        !           911: 
        !           912:     for (i = 0; i < biosmem_nr_boot_data; i++) {
        !           913:         data = &biosmem_boot_data_array[i];
        !           914: 
        !           915:         if (!data->temporary) {
        !           916:             continue;
        !           917:         }
        !           918: 
        !           919:         biosmem_unregister_boot_data(data->start, data->end);
        !           920:         i = (unsigned int)-1;
1.1       root      921:     }
                    922: }
                    923: 
                    924: static void __init
                    925: biosmem_free_usable_range(phys_addr_t start, phys_addr_t end)
                    926: {
                    927:     struct vm_page *page;
                    928: 
1.1.1.2 ! root      929: #if DEBUG
        !           930:     printf("biosmem: release to vm_page: %llx:%llx (%lluk)\n",
1.1       root      931:            (unsigned long long)start, (unsigned long long)end,
                    932:            (unsigned long long)((end - start) >> 10));
1.1.1.2 ! root      933: #endif
1.1       root      934: 
                    935:     while (start < end) {
                    936:         page = vm_page_lookup_pa(start);
                    937:         assert(page != NULL);
                    938:         vm_page_manage(page);
                    939:         start += PAGE_SIZE;
                    940:     }
                    941: }
                    942: 
                    943: static void __init
                    944: biosmem_free_usable_entry(phys_addr_t start, phys_addr_t end)
                    945: {
1.1.1.2 ! root      946:     phys_addr_t avail_start, avail_end;
        !           947:     int error;
1.1       root      948: 
                    949:     for (;;) {
1.1.1.2 ! root      950:         error = biosmem_find_avail(start, end, &avail_start, &avail_end);
1.1       root      951: 
1.1.1.2 ! root      952:         if (error) {
        !           953:             break;
        !           954:         }
1.1       root      955: 
1.1.1.2 ! root      956:         biosmem_free_usable_range(avail_start, avail_end);
        !           957:         start = avail_end;
1.1       root      958:     }
                    959: }
                    960: 
                    961: void __init
                    962: biosmem_free_usable(void)
                    963: {
                    964:     struct biosmem_map_entry *entry;
                    965:     uint64_t start, end;
                    966:     unsigned int i;
                    967: 
1.1.1.2 ! root      968:     biosmem_unregister_temporary_boot_data();
        !           969: 
1.1       root      970:     for (i = 0; i < biosmem_map_size; i++) {
                    971:         entry = &biosmem_map[i];
                    972: 
                    973:         if (entry->type != BIOSMEM_TYPE_AVAILABLE)
                    974:             continue;
                    975: 
                    976:         start = vm_page_round(entry->base_addr);
                    977: 
                    978:         if (start >= VM_PAGE_HIGHMEM_LIMIT)
                    979:             break;
                    980: 
                    981:         end = vm_page_trunc(entry->base_addr + entry->length);
                    982: 
1.1.1.2 ! root      983:         if (end > VM_PAGE_HIGHMEM_LIMIT) {
        !           984:             end = VM_PAGE_HIGHMEM_LIMIT;
        !           985:         }
        !           986: 
1.1       root      987:         if (start < BIOSMEM_BASE)
                    988:             start = BIOSMEM_BASE;
                    989: 
1.1.1.2 ! root      990:         if (start >= end) {
        !           991:             continue;
        !           992:         }
        !           993: 
1.1       root      994:         biosmem_free_usable_entry(start, end);
                    995:     }
                    996: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.