diff --git a/headers/private/kernel/boot/addr_range.h b/headers/private/kernel/boot/addr_range.h index 80d0c7823f..a6ef1737b2 100644 --- a/headers/private/kernel/boot/addr_range.h +++ b/headers/private/kernel/boot/addr_range.h @@ -28,9 +28,13 @@ bool get_free_address_range(addr_range* ranges, uint32 numRanges, uint64 base, uint64 size, uint64* _rangeBase); bool is_address_range_covered(addr_range* ranges, uint32 numRanges, uint64 base, uint64 size); +uint64 total_address_ranges_size(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 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_virtual_allocated_range(uint64 start, uint64 size); void ignore_physical_memory_ranges_beyond_4gb(); diff --git a/src/system/boot/loader/kernel_args.cpp b/src/system/boot/loader/kernel_args.cpp index a5ddc2bb63..c17ae44c24 100644 --- a/src/system/boot/loader/kernel_args.cpp +++ b/src/system/boot/loader/kernel_args.cpp @@ -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 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 insert_physical_allocated_range(uint64 start, uint64 size) { diff --git a/src/system/boot/platform/bios_ia32/mmu.cpp b/src/system/boot/platform/bios_ia32/mmu.cpp index 4897521ea9..d99db7f3ba 100644 --- a/src/system/boot/platform/bios_ia32/mmu.cpp +++ b/src/system/boot/platform/bios_ia32/mmu.cpp @@ -664,51 +664,68 @@ mmu_init(void) if (extMemoryCount > 0) { gKernelArgs.num_physical_memory_ranges = 0; + // first scan: add all usable ranges for (uint32 i = 0; i < extMemoryCount; i++) { // Type 1 is available memory - if (extMemoryBlock[i].type == 1) { - uint64 base = extMemoryBlock[i].base_addr; - uint64 length = extMemoryBlock[i].length; - uint64 end = base + length; + if (extMemoryBlock[i].type != 1) + continue; - // round everything up to page boundaries, exclusive of pages - // it partially occupies - base = ROUNDUP(base, B_PAGE_SIZE); - end = ROUNDDOWN(end, B_PAGE_SIZE); + uint64 base = extMemoryBlock[i].base_addr; + uint64 length = extMemoryBlock[i].length; + uint64 end = base + length; - // We ignore all memory beyond 4 GB, if phys_addr_t is only - // 32 bit wide. - #if B_HAIKU_PHYSICAL_BITS == 32 - if (end > 0x100000000ULL) - end = 0x100000000ULL; - #endif + // round everything up to page boundaries, exclusive of pages + // it partially occupies + base = ROUNDUP(base, B_PAGE_SIZE); + end = ROUNDDOWN(end, B_PAGE_SIZE); - // Also ignore memory below 1 MB. Apparently some BIOSes fail to - // 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; + // We ignore all memory beyond 4 GB, if phys_addr_t is only + // 32 bit wide. + #if B_HAIKU_PHYSICAL_BITS == 32 + if (end > 0x100000000ULL) + end = 0x100000000ULL; + #endif - gKernelArgs.ignored_physical_memory - += length - (max_c(end, base) - base); + // Also ignore memory below 1 MB. Apparently some BIOSes fail to + // 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) - continue; + gKernelArgs.ignored_physical_memory + += length - (max_c(end, base) - base); - status_t status = insert_physical_memory_range(base, end - base); - if (status == B_ENTRY_NOT_FOUND) { - panic("mmu_init(): Failed to add physical memory range " - "%#" B_PRIx64 " - %#" B_PRIx64 " : all %d entries are " - "used already!\n", base, end, MAX_PHYSICAL_MEMORY_RANGE); - } else if (status != B_OK) { - panic("mmu_init(): Failed to add physical memory range " - "%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end); - } - } else if (extMemoryBlock[i].type == 3) { - // ACPI reclaim -- physical memory we could actually use later - gKernelArgs.ignored_physical_memory += extMemoryBlock[i].length; + if (end <= base) + continue; + + status_t status = insert_physical_memory_range(base, end - base); + if (status == B_ENTRY_NOT_FOUND) { + panic("mmu_init(): Failed to add physical memory range " + "%#" B_PRIx64 " - %#" B_PRIx64 " : all %d entries are " + "used already!\n", base, end, MAX_PHYSICAL_MEMORY_RANGE); + } else if (status != B_OK) { + panic("mmu_init(): Failed to add physical memory range " + "%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end); + } + } + + 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; if (size < 64 * 1024) { uint64 start = gKernelArgs.physical_memory_range[i].start; - remove_address_range(gKernelArgs.physical_memory_range, - &gKernelArgs.num_physical_memory_ranges, - MAX_PHYSICAL_MEMORY_RANGE, start, size); + remove_physical_memory_range(start, size); } } + + gKernelArgs.ignored_physical_memory + += initialPhysicalMemory - total_physical_memory(); } else { bios_regs regs; diff --git a/src/system/boot/platform/efi/arch/x86_64/arch_mmu.cpp b/src/system/boot/platform/efi/arch/x86_64/arch_mmu.cpp index 63c61b465a..bc6eb69aa2 100644 --- a/src/system/boot/platform/efi/arch/x86_64/arch_mmu.cpp +++ b/src/system/boot/platform/efi/arch/x86_64/arch_mmu.cpp @@ -64,6 +64,8 @@ arch_mmu_post_efi_setup(size_t memory_map_size, // EFI regions. addr_t addr = (addr_t)memory_map; gKernelArgs.num_physical_memory_ranges = 0; + + // First scan: Add all usable ranges for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) { efi_memory_descriptor *entry = (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. uint64_t base = entry->PhysicalStart; uint64_t end = entry->PhysicalStart + entry->NumberOfPages * 4096; + uint64_t originalSize = end - base; if (base < 0x100000) base = 0x100000; if (end > (512ull * 1024 * 1024 * 1024)) end = 512ull * 1024 * 1024 * 1024; + + gKernelArgs.ignored_physical_memory + += originalSize - (max_c(end, base) - base); + if (base >= end) break; uint64_t size = end - base; @@ -94,7 +101,6 @@ arch_mmu_post_efi_setup(size_t memory_map_size, } case EfiACPIReclaimMemory: // ACPI reclaim -- physical memory we could actually use later - gKernelArgs.ignored_physical_memory += entry->NumberOfPages * 4096; break; case EfiRuntimeServicesCode: 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_address_ranges(gKernelArgs.physical_memory_range, gKernelArgs.num_physical_memory_ranges);