Index: sys/amd64/amd64/machdep.c =================================================================== --- sys/amd64/amd64/machdep.c +++ sys/amd64/amd64/machdep.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include @@ -173,7 +174,7 @@ static caddr_t native_parse_preload_data(u_int64_t); /* Native function to fetch and parse the e820 map */ -static void native_parse_memmap(caddr_t, vm_paddr_t *, int *); +static void native_parse_memmap(caddr_t); /* Default init_ops implementation. */ struct init_ops init_ops = { @@ -197,9 +198,6 @@ int cold = 1; -long Maxmem = 0; -long realmem = 0; - struct kva_md_info kmi; struct region_descriptor r_idt; @@ -633,77 +631,11 @@ u_int basemem; -static int -add_physmap_entry(uint64_t base, uint64_t length, vm_paddr_t *physmap, - int *physmap_idxp) -{ - int i, insert_idx, physmap_idx; - - physmap_idx = *physmap_idxp; - - if (length == 0) - return (1); - - /* - * Find insertion point while checking for overlap. Start off by - * assuming the new entry will be added to the end. - * - * NB: physmap_idx points to the next free slot. - */ - insert_idx = physmap_idx; - for (i = 0; i <= physmap_idx; i += 2) { - if (base < physmap[i + 1]) { - if (base + length <= physmap[i]) { - insert_idx = i; - break; - } - if (boothowto & RB_VERBOSE) - printf( - "Overlapping memory regions, ignoring second region\n"); - return (1); - } - } - - /* See if we can prepend to the next entry. */ - if (insert_idx <= physmap_idx && base + length == physmap[insert_idx]) { - physmap[insert_idx] = base; - return (1); - } - - /* See if we can append to the previous entry. */ - if (insert_idx > 0 && base == physmap[insert_idx - 1]) { - physmap[insert_idx - 1] += length; - return (1); - } - - physmap_idx += 2; - *physmap_idxp = physmap_idx; - if (physmap_idx == PHYS_AVAIL_ENTRIES) { - printf( - "Too many segments in the physical address map, giving up\n"); - return (0); - } - - /* - * Move the last 'N' entries down to make room for the new - * entry if needed. - */ - for (i = (physmap_idx - 2); i > insert_idx; i -= 2) { - physmap[i] = physmap[i - 2]; - physmap[i + 1] = physmap[i - 1]; - } - - /* Insert the new entry. */ - physmap[insert_idx] = base; - physmap[insert_idx + 1] = base + length; - return (1); -} - void -bios_add_smap_entries(struct bios_smap *smapbase, u_int32_t smapsize, - vm_paddr_t *physmap, int *physmap_idx) +bios_add_smap_entries(struct bios_smap *smapbase, u_int32_t smapsize) { struct bios_smap *smap, *smapend; + int error; smapend = (struct bios_smap *)((uintptr_t)smapbase + smapsize); @@ -715,20 +647,22 @@ if (smap->type != SMAP_TYPE_MEMORY) continue; - if (!add_physmap_entry(smap->base, smap->length, physmap, - physmap_idx)) + error = physmem_hardware_region(smap->base, smap->length); + if (error == E2BIG) { + printf( + "Too many holes in the physical address space, giving up\n"); break; + } } } static void -add_efi_map_entries(struct efi_map_header *efihdr, vm_paddr_t *physmap, - int *physmap_idx) +add_efi_map_entries(struct efi_map_header *efihdr) { struct efi_md *map, *p; const char *type; size_t efisz; - int ndesc, i; + int error, i, ndesc; static const char *types[] = { "Reserved", @@ -813,14 +747,18 @@ continue; } - if (!add_physmap_entry(p->md_phys, (p->md_pages * PAGE_SIZE), - physmap, physmap_idx)) + error = physmem_hardware_region(p->md_phys, + (p->md_pages * PAGE_SIZE)); + if (error == E2BIG) { + printf( + "Too many holes in the physical address space, giving up\n"); break; + } } } static void -native_parse_memmap(caddr_t kmdp, vm_paddr_t *physmap, int *physmap_idx) +native_parse_memmap(caddr_t kmdp) { struct bios_smap *smap; struct efi_map_header *efihdr; @@ -842,21 +780,110 @@ panic("No BIOS smap or EFI map info from loader!"); if (efihdr != NULL) { - add_efi_map_entries(efihdr, physmap, physmap_idx); + add_efi_map_entries(efihdr); strlcpy(bootmethod, "UEFI", sizeof(bootmethod)); } else { size = *((u_int32_t *)smap - 1); - bios_add_smap_entries(smap, size, physmap, physmap_idx); + bios_add_smap_entries(smap, size); strlcpy(bootmethod, "BIOS", sizeof(bootmethod)); } } #define PAGES_PER_GB (1024 * 1024 * 1024 / PAGE_SIZE) +static int +getmemsize_memtest(vm_paddr_t pa, vm_paddr_t end __unused, void *data) +{ + int count, error, *page_counter, tmp; + int *ptr = (int *)CADDR1; + bool bad; + + /* Skip partial pages. */ + if ((pa % PAGE_SIZE) != 0 || (end % PAGE_SIZE) != 0) + return (0); + page_counter = (int *)data; + bad = false; + + /* + * Print a "." every GB to show we're making + * progress. + */ + count = ++(*page_counter); + if ((count % PAGES_PER_GB) == 0) + printf("."); + + /* + * map page into kernel: valid, read/write,non-cacheable + */ + *CMAP1 = pa | PG_V | PG_RW | PG_NC_PWT | PG_NC_PCD; + invltlb(); + + tmp = *(int *)ptr; + /* + * Test for alternating 1's and 0's + */ + *(volatile int *)ptr = 0xaaaaaaaa; + if (*(volatile int *)ptr != 0xaaaaaaaa) { + bad = true; + goto out; + } + /* + * Test for alternating 0's and 1's + */ + *(volatile int *)ptr = 0x55555555; + if (*(volatile int *)ptr != 0x55555555) { + bad = true; + goto out; + } + /* + * Test for all 1's + */ + *(volatile int *)ptr = 0xffffffff; + if (*(volatile int *)ptr != 0xffffffff) { + bad = true; + goto out; + } + /* + * Test for all 0's + */ + *(volatile int *)ptr = 0x0; + if (*(volatile int *)ptr != 0x0) { + bad = true; + goto out; + } + +out: + error = 0; + if (bad) { + error = physmem_exclude_region(pa, PAGE_SIZE, + EXFLAG_NOALLOC | EXFLAG_NODUMP); + } + + /* + * Restore original value. + */ + *(int *)ptr = tmp; + + /* + * We'll give up on memtesting if we've maxed out exregions. + */ + if (error == E2BIG) { + /* + * Still in the middle of a line, suppress the urge to break it. + */ + printf( + "too many errors, giving up"); + } else { + error = 0; + } + + return (error); +} + /* - * Populate the (physmap) array with base/bound pairs describing the - * available physical memory in the system, then test this memory and - * build the phys_avail array describing the actually-available memory. + * Populate physmem regions describing the available physical memory in the + * system, optionally test this memory, then build the phys_avail array + * describing the actually-available memory. * * Total memory size may be set by the kernel environment variable * hw.physmem or the compile-time define MAXMEM. @@ -866,10 +893,8 @@ static void getmemsize(caddr_t kmdp, u_int64_t first) { - int i, physmap_idx, pa_indx, da_indx; - vm_paddr_t pa, physmap[PHYS_AVAIL_ENTRIES]; - u_long physmem_start, physmem_tunable, memtest; - pt_entry_t *pte; + size_t pa_indx; + u_long physmem_maxpage, physmem_start, physmem_tunable, memtest; quad_t dcons_addr, dcons_size; int page_counter; @@ -879,28 +904,7 @@ */ vm_phys_early_add_seg((vm_paddr_t)kernphys, trunc_page(first)); - bzero(physmap, sizeof(physmap)); - physmap_idx = 0; - - init_ops.parse_memmap(kmdp, physmap, &physmap_idx); - physmap_idx -= 2; - - /* - * Find the 'base memory' segment for SMP - */ - basemem = 0; - for (i = 0; i <= physmap_idx; i += 2) { - if (physmap[i] <= 0xA0000) { - basemem = physmap[i + 1] / 1024; - break; - } - } - if (basemem == 0 || basemem > 640) { - if (bootverbose) - printf( - "Memory map doesn't contain a basemem segment, faking it"); - basemem = 640; - } + init_ops.parse_memmap(kmdp); /* * Maxmem isn't the "maximum memory", it's one larger than the @@ -908,15 +912,20 @@ * called something like "Maxphyspage". We may adjust this * based on ``hw.physmem'' and the results of the memory test. */ - Maxmem = atop(physmap[physmap_idx + 1]); - + physmem_maxpage = physmem_max_page(); #ifdef MAXMEM Maxmem = MAXMEM / 4; #endif - - if (TUNABLE_ULONG_FETCH("hw.physmem", &physmem_tunable)) + if (TUNABLE_LONG_FETCH("hw.physmem", &physmem_tunable)) Maxmem = atop(physmem_tunable); + /* + * Don't allow MAXMEM or hw.physmem to extend the amount of memory + * in the system. + */ + if (Maxmem == 0 || Maxmem > physmem_maxpage) + Maxmem = physmem_maxpage; + /* * The boot memory test is disabled by default, as it takes a * significant amount of time on large-memory systems, and is @@ -929,17 +938,6 @@ memtest = 0; TUNABLE_ULONG_FETCH("hw.memtest.tests", &memtest); - /* - * Don't allow MAXMEM or hw.physmem to extend the amount of memory - * in the system. - */ - if (Maxmem > atop(physmap[physmap_idx + 1])) - Maxmem = atop(physmap[physmap_idx + 1]); - - if (atop(physmap[physmap_idx + 1]) != Maxmem && - (boothowto & RB_VERBOSE)) - printf("Physical memory use set to %ldK\n", Maxmem * 4); - /* call pmap initialization to make new kernel address space */ pmap_bootstrap(&first); @@ -952,161 +950,49 @@ */ physmem_start = (vm_guest > VM_GUEST_NO ? 1 : 16) << PAGE_SHIFT; TUNABLE_ULONG_FETCH("hw.physmem.start", &physmem_start); - if (physmap[0] < physmem_start) { - if (physmem_start < PAGE_SIZE) - physmap[0] = PAGE_SIZE; - else if (physmem_start >= physmap[1]) - physmap[0] = round_page(physmap[1] - PAGE_SIZE); - else - physmap[0] = round_page(physmem_start); - } - pa_indx = 0; - da_indx = 1; - phys_avail[pa_indx++] = physmap[0]; - phys_avail[pa_indx] = physmap[0]; - dump_avail[da_indx] = physmap[0]; - pte = CMAP1; + + physmem_exclude_region(0, physmem_start, + EXFLAG_NOALLOC | EXFLAG_NODUMP); /* - * Get dcons buffer address + * Block dcons buffer. */ - if (getenv_quad("dcons.addr", &dcons_addr) == 0 || - getenv_quad("dcons.size", &dcons_size) == 0) - dcons_addr = 0; + if (getenv_quad("dcons.addr", &dcons_addr) != 0 && + getenv_quad("dcons.size", &dcons_size) != 0) + physmem_exclude_region(dcons_addr, dcons_size, EXFLAG_NOALLOC); /* - * physmap is in bytes, so when converting to page boundaries, - * round up the start address and round down the end address. + * Block kernel memory. */ - page_counter = 0; - if (memtest != 0) - printf("Testing system memory"); - for (i = 0; i <= physmap_idx; i += 2) { - vm_paddr_t end; - - end = ptoa((vm_paddr_t)Maxmem); - if (physmap[i + 1] < end) - end = trunc_page(physmap[i + 1]); - for (pa = round_page(physmap[i]); pa < end; pa += PAGE_SIZE) { - int tmp, page_bad, full; - int *ptr = (int *)CADDR1; - - full = FALSE; - /* - * block out kernel memory as not available. - */ - if (pa >= (vm_paddr_t)kernphys && pa < first) - goto do_dump_avail; - - /* - * block out dcons buffer - */ - if (dcons_addr > 0 - && pa >= trunc_page(dcons_addr) - && pa < dcons_addr + dcons_size) - goto do_dump_avail; + physmem_exclude_region(kernphys, first - kernphys, EXFLAG_NOALLOC); - page_bad = FALSE; - if (memtest == 0) - goto skip_memtest; + if (memtest != 0) { + page_counter = 0; + printf("Testing system memory"); + physmem_foreach_page(getmemsize_memtest, &page_counter, 0); - /* - * Print a "." every GB to show we're making - * progress. - */ - page_counter++; - if ((page_counter % PAGES_PER_GB) == 0) - printf("."); + *CMAP1 = 0; + invltlb(); + printf("\n"); + } - /* - * map page into kernel: valid, read/write,non-cacheable - */ - *pte = pa | PG_V | PG_RW | PG_NC_PWT | PG_NC_PCD; - invltlb(); + physmem_init_kernel_globals(&pa_indx, NULL); + pa_indx--; - tmp = *(int *)ptr; - /* - * Test for alternating 1's and 0's - */ - *(volatile int *)ptr = 0xaaaaaaaa; - if (*(volatile int *)ptr != 0xaaaaaaaa) - page_bad = TRUE; - /* - * Test for alternating 0's and 1's - */ - *(volatile int *)ptr = 0x55555555; - if (*(volatile int *)ptr != 0x55555555) - page_bad = TRUE; - /* - * Test for all 1's - */ - *(volatile int *)ptr = 0xffffffff; - if (*(volatile int *)ptr != 0xffffffff) - page_bad = TRUE; - /* - * Test for all 0's - */ - *(volatile int *)ptr = 0x0; - if (*(volatile int *)ptr != 0x0) - page_bad = TRUE; - /* - * Restore original value. - */ - *(int *)ptr = tmp; + /* + * Find the 'base memory' segment for SMP + */ + basemem = 0; + /* phys_avail is sorted. */ + if (phys_avail[0] <= 0xA0000) + basemem = phys_avail[1] / 1024; -skip_memtest: - /* - * Adjust array of valid/good pages. - */ - if (page_bad == TRUE) - continue; - /* - * If this good page is a continuation of the - * previous set of good pages, then just increase - * the end pointer. Otherwise start a new chunk. - * Note that "end" points one higher than end, - * making the range >= start and < end. - * If we're also doing a speculative memory - * test and we at or past the end, bump up Maxmem - * so that we keep going. The first bad page - * will terminate the loop. - */ - if (phys_avail[pa_indx] == pa) { - phys_avail[pa_indx] += PAGE_SIZE; - } else { - pa_indx++; - if (pa_indx == PHYS_AVAIL_ENTRIES) { - printf( - "Too many holes in the physical address space, giving up\n"); - pa_indx--; - full = TRUE; - goto do_dump_avail; - } - phys_avail[pa_indx++] = pa; /* start */ - phys_avail[pa_indx] = pa + PAGE_SIZE; /* end */ - } - physmem++; -do_dump_avail: - if (dump_avail[da_indx] == pa) { - dump_avail[da_indx] += PAGE_SIZE; - } else { - da_indx++; - if (da_indx == PHYS_AVAIL_ENTRIES) { - da_indx--; - goto do_next; - } - dump_avail[da_indx++] = pa; /* start */ - dump_avail[da_indx] = pa + PAGE_SIZE; /* end */ - } -do_next: - if (full) - break; - } + if (basemem == 0 || basemem > 640) { + if (bootverbose) + printf( + "Memory map doesn't contain a basemem segment, faking it"); + basemem = 640; } - *pte = 0; - invltlb(); - if (memtest != 0) - printf("\n"); /* * XXX @@ -1121,8 +1007,13 @@ phys_avail[pa_indx--] = 0; } + /* Collect the final Maxmem. */ Maxmem = atop(phys_avail[pa_indx]); + if (bootverbose) + printf("Physical memory use set to %ldK\n", Maxmem * 4); + + /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(msgbufsize); Index: sys/amd64/include/pc/bios.h =================================================================== --- sys/amd64/include/pc/bios.h +++ sys/amd64/include/pc/bios.h @@ -87,8 +87,7 @@ int bios_oem_strings(struct bios_oem *oem, u_char *buffer, size_t maxlen); uint32_t bios_sigsearch(uint32_t start, u_char *sig, int siglen, int paralen, int sigofs); -void bios_add_smap_entries(struct bios_smap *smapbase, u_int32_t smapsize, - vm_paddr_t *physmap, int *physmap_idx); +void bios_add_smap_entries(struct bios_smap *smapbase, u_int32_t smapsize); #endif #endif /* _MACHINE_PC_BIOS_H_ */ Index: sys/conf/files.amd64 =================================================================== --- sys/conf/files.amd64 +++ sys/conf/files.amd64 @@ -345,6 +345,7 @@ isa/vga_isa.c optional vga kern/imgact_aout.c optional compat_aout kern/link_elf_obj.c standard +kern/subr_physmem.c standard # # IA32 binary support # Index: sys/x86/include/init.h =================================================================== --- sys/x86/include/init.h +++ sys/x86/include/init.h @@ -40,7 +40,7 @@ caddr_t (*parse_preload_data)(u_int64_t); void (*early_clock_source_init)(void); void (*early_delay)(int); - void (*parse_memmap)(caddr_t, vm_paddr_t *, int *); + void (*parse_memmap)(caddr_t); }; extern struct init_ops init_ops; Index: sys/x86/xen/pv.c =================================================================== --- sys/x86/xen/pv.c +++ sys/x86/xen/pv.c @@ -93,7 +93,7 @@ /*--------------------------- Forward Declarations ---------------------------*/ static caddr_t xen_pvh_parse_preload_data(uint64_t); -static void xen_pvh_parse_memmap(caddr_t, vm_paddr_t *, int *); +static void xen_pvh_parse_memmap(caddr_t); /*---------------------------- Extern Declarations ---------------------------*/ /* @@ -391,7 +391,7 @@ } static void -xen_pvh_parse_memmap(caddr_t kmdp, vm_paddr_t *physmap, int *physmap_idx) +xen_pvh_parse_memmap(caddr_t kmdp) { struct xen_memory_map memmap; u_int32_t size; @@ -409,5 +409,5 @@ size = memmap.nr_entries * sizeof(xen_smap[0]); - bios_add_smap_entries(xen_smap, size, physmap, physmap_idx); + bios_add_smap_entries(xen_smap, size); }