bootloader: Split memory map handling into add/remove passes.

The memory map may be unordered and include overlapping ranges. To make
sure that nothing gets included as usable that should actually be
excluded, first scan for all usable ranges and add them, then remove
anything unusable from these ranges again.

To calculate the amount of unusable memory, count the total after the
first pass and then subtract the total after the second. This way, only
unusable ranges that actually overlap physical memory (and therefore
reduce the amount of usable memory) get excluded.

Note that the explicit ignore of the ACPI reclaim memory is subsumed by
the above. We still don't want to add this region to the usable memory
map, as that would allow the kernel to allocate pages into that region,
possibly corrupting ACPI tables before they were used. We also don't
want to add it as an allocated range, as it is not guaranteed that ACPI
is done with the tables before the unused bootloader ranges are freed in
the kernel.

Also add the missing unusable memory amount from ignoring the first MiB
of memory in the EFI loader.

May fix #16056 although it is not certain that graphics memory ranges
are actually included in the memory map.

Change-Id: Ie7991d2c4dcd988edac2995b3a7efc509fa0f4a3
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2814
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Michael Lotz
2020-05-26 04:04:35 +00:00
committed by waddlesplash
parent 6c53279f5b
commit d750211a65
4 changed files with 119 additions and 41 deletions
+4
View File
@@ -28,9 +28,13 @@ bool get_free_address_range(addr_range* ranges, uint32 numRanges, uint64 base,
uint64 size, uint64* _rangeBase); uint64 size, uint64* _rangeBase);
bool is_address_range_covered(addr_range* ranges, uint32 numRanges, uint64 base, bool is_address_range_covered(addr_range* ranges, uint32 numRanges, uint64 base,
uint64 size); uint64 size);
uint64 total_address_ranges_size(addr_range* ranges, uint32 numRanges);
void sort_address_ranges(addr_range* ranges, uint32 numRanges); void sort_address_ranges(addr_range* ranges, uint32 numRanges);
status_t insert_physical_memory_range(uint64 start, uint64 size); status_t insert_physical_memory_range(uint64 start, uint64 size);
status_t remove_physical_memory_range(uint64 start, uint64 size);
uint64 total_physical_memory();
status_t insert_physical_allocated_range(uint64 start, uint64 size); status_t insert_physical_allocated_range(uint64 start, uint64 size);
status_t insert_virtual_allocated_range(uint64 start, uint64 size); status_t insert_virtual_allocated_range(uint64 start, uint64 size);
void ignore_physical_memory_ranges_beyond_4gb(); void ignore_physical_memory_ranges_beyond_4gb();
+27
View File
@@ -248,6 +248,16 @@ is_address_range_covered(addr_range* ranges, uint32 numRanges, uint64 base,
} }
extern "C" uint64
total_address_ranges_size(addr_range* ranges, uint32 numRanges)
{
uint64 total = 0;
for (uint32 i = 0; i < numRanges; i++)
total += ranges[i].size;
return total;
}
void void
sort_address_ranges(addr_range* ranges, uint32 numRanges) sort_address_ranges(addr_range* ranges, uint32 numRanges)
{ {
@@ -281,6 +291,23 @@ insert_physical_memory_range(uint64 start, uint64 size)
} }
status_t
remove_physical_memory_range(uint64 start, uint64 size)
{
return remove_address_range(gKernelArgs.physical_memory_range,
&gKernelArgs.num_physical_memory_ranges, MAX_PHYSICAL_MEMORY_RANGE,
start, size);
}
uint64
total_physical_memory()
{
return total_address_ranges_size(gKernelArgs.physical_memory_range,
gKernelArgs.num_physical_memory_ranges);
}
status_t status_t
insert_physical_allocated_range(uint64 start, uint64 size) insert_physical_allocated_range(uint64 start, uint64 size)
{ {
+58 -40
View File
@@ -664,51 +664,68 @@ mmu_init(void)
if (extMemoryCount > 0) { if (extMemoryCount > 0) {
gKernelArgs.num_physical_memory_ranges = 0; gKernelArgs.num_physical_memory_ranges = 0;
// first scan: add all usable ranges
for (uint32 i = 0; i < extMemoryCount; i++) { for (uint32 i = 0; i < extMemoryCount; i++) {
// Type 1 is available memory // Type 1 is available memory
if (extMemoryBlock[i].type == 1) { if (extMemoryBlock[i].type != 1)
uint64 base = extMemoryBlock[i].base_addr; continue;
uint64 length = extMemoryBlock[i].length;
uint64 end = base + length;
// round everything up to page boundaries, exclusive of pages uint64 base = extMemoryBlock[i].base_addr;
// it partially occupies uint64 length = extMemoryBlock[i].length;
base = ROUNDUP(base, B_PAGE_SIZE); uint64 end = base + length;
end = ROUNDDOWN(end, B_PAGE_SIZE);
// We ignore all memory beyond 4 GB, if phys_addr_t is only // round everything up to page boundaries, exclusive of pages
// 32 bit wide. // it partially occupies
#if B_HAIKU_PHYSICAL_BITS == 32 base = ROUNDUP(base, B_PAGE_SIZE);
if (end > 0x100000000ULL) end = ROUNDDOWN(end, B_PAGE_SIZE);
end = 0x100000000ULL;
#endif
// Also ignore memory below 1 MB. Apparently some BIOSes fail to // We ignore all memory beyond 4 GB, if phys_addr_t is only
// provide the correct range type for some ranges (cf. #1925). // 32 bit wide.
// Later in the kernel we will reserve the range 0x0 - 0xa0000 #if B_HAIKU_PHYSICAL_BITS == 32
// and apparently 0xa0000 - 0x100000 never contain usable if (end > 0x100000000ULL)
// memory, so we don't lose anything by doing that. end = 0x100000000ULL;
if (base < 0x100000) #endif
base = 0x100000;
gKernelArgs.ignored_physical_memory // Also ignore memory below 1 MB. Apparently some BIOSes fail to
+= length - (max_c(end, base) - base); // provide the correct range type for some ranges (cf. #1925).
// Later in the kernel we will reserve the range 0x0 - 0xa0000
// and apparently 0xa0000 - 0x100000 never contain usable
// memory, so we don't lose anything by doing that.
if (base < 0x100000)
base = 0x100000;
if (end <= base) gKernelArgs.ignored_physical_memory
continue; += length - (max_c(end, base) - base);
status_t status = insert_physical_memory_range(base, end - base); if (end <= base)
if (status == B_ENTRY_NOT_FOUND) { continue;
panic("mmu_init(): Failed to add physical memory range "
"%#" B_PRIx64 " - %#" B_PRIx64 " : all %d entries are " status_t status = insert_physical_memory_range(base, end - base);
"used already!\n", base, end, MAX_PHYSICAL_MEMORY_RANGE); if (status == B_ENTRY_NOT_FOUND) {
} else if (status != B_OK) { panic("mmu_init(): Failed to add physical memory range "
panic("mmu_init(): Failed to add physical memory range " "%#" B_PRIx64 " - %#" B_PRIx64 " : all %d entries are "
"%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end); "used already!\n", base, end, MAX_PHYSICAL_MEMORY_RANGE);
} } else if (status != B_OK) {
} else if (extMemoryBlock[i].type == 3) { panic("mmu_init(): Failed to add physical memory range "
// ACPI reclaim -- physical memory we could actually use later "%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end);
gKernelArgs.ignored_physical_memory += extMemoryBlock[i].length; }
}
uint64 initialPhysicalMemory = total_physical_memory();
// second scan: remove everything reserved that may overlap
for (uint32 i = 0; i < extMemoryCount; i++) {
if (extMemoryBlock[i].type == 1)
continue;
uint64 base = extMemoryBlock[i].base_addr;
uint64 end = ROUNDUP(base + extMemoryBlock[i].length, B_PAGE_SIZE);
base = ROUNDDOWN(base, B_PAGE_SIZE);
status_t status = remove_physical_memory_range(base, end - base);
if (status != B_OK) {
panic("mmu_init(): Failed to remove physical memory range "
"%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end);
} }
} }
@@ -725,11 +742,12 @@ mmu_init(void)
uint64 size = gKernelArgs.physical_memory_range[i].size; uint64 size = gKernelArgs.physical_memory_range[i].size;
if (size < 64 * 1024) { if (size < 64 * 1024) {
uint64 start = gKernelArgs.physical_memory_range[i].start; uint64 start = gKernelArgs.physical_memory_range[i].start;
remove_address_range(gKernelArgs.physical_memory_range, remove_physical_memory_range(start, size);
&gKernelArgs.num_physical_memory_ranges,
MAX_PHYSICAL_MEMORY_RANGE, start, size);
} }
} }
gKernelArgs.ignored_physical_memory
+= initialPhysicalMemory - total_physical_memory();
} else { } else {
bios_regs regs; bios_regs regs;
@@ -64,6 +64,8 @@ arch_mmu_post_efi_setup(size_t memory_map_size,
// EFI regions. // EFI regions.
addr_t addr = (addr_t)memory_map; addr_t addr = (addr_t)memory_map;
gKernelArgs.num_physical_memory_ranges = 0; gKernelArgs.num_physical_memory_ranges = 0;
// First scan: Add all usable ranges
for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) { for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) {
efi_memory_descriptor *entry efi_memory_descriptor *entry
= (efi_memory_descriptor *)(addr + i * descriptor_size); = (efi_memory_descriptor *)(addr + i * descriptor_size);
@@ -77,10 +79,15 @@ arch_mmu_post_efi_setup(size_t memory_map_size,
// Ignore memory below 1MB and above 512GB. // Ignore memory below 1MB and above 512GB.
uint64_t base = entry->PhysicalStart; uint64_t base = entry->PhysicalStart;
uint64_t end = entry->PhysicalStart + entry->NumberOfPages * 4096; uint64_t end = entry->PhysicalStart + entry->NumberOfPages * 4096;
uint64_t originalSize = end - base;
if (base < 0x100000) if (base < 0x100000)
base = 0x100000; base = 0x100000;
if (end > (512ull * 1024 * 1024 * 1024)) if (end > (512ull * 1024 * 1024 * 1024))
end = 512ull * 1024 * 1024 * 1024; end = 512ull * 1024 * 1024 * 1024;
gKernelArgs.ignored_physical_memory
+= originalSize - (max_c(end, base) - base);
if (base >= end) if (base >= end)
break; break;
uint64_t size = end - base; uint64_t size = end - base;
@@ -94,7 +101,6 @@ arch_mmu_post_efi_setup(size_t memory_map_size,
} }
case EfiACPIReclaimMemory: case EfiACPIReclaimMemory:
// ACPI reclaim -- physical memory we could actually use later // ACPI reclaim -- physical memory we could actually use later
gKernelArgs.ignored_physical_memory += entry->NumberOfPages * 4096;
break; break;
case EfiRuntimeServicesCode: case EfiRuntimeServicesCode:
case EfiRuntimeServicesData: case EfiRuntimeServicesData:
@@ -103,6 +109,29 @@ arch_mmu_post_efi_setup(size_t memory_map_size,
} }
} }
uint64_t initialPhysicalMemory = total_physical_memory();
// Second scan: Remove everything reserved that may overlap
for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) {
efi_memory_descriptor *entry
= (efi_memory_descriptor *)(addr + i * descriptor_size);
switch (entry->Type) {
case EfiLoaderCode:
case EfiLoaderData:
case EfiBootServicesCode:
case EfiBootServicesData:
case EfiConventionalMemory:
break;
default:
uint64_t base = entry->PhysicalStart;
uint64_t end = entry->PhysicalStart + entry->NumberOfPages * 4096;
remove_physical_memory_range(base, end - base);
}
}
gKernelArgs.ignored_physical_memory
+= initialPhysicalMemory - total_physical_memory();
// Sort the address ranges. // Sort the address ranges.
sort_address_ranges(gKernelArgs.physical_memory_range, sort_address_ranges(gKernelArgs.physical_memory_range,
gKernelArgs.num_physical_memory_ranges); gKernelArgs.num_physical_memory_ranges);