[haiku-commits] Change in haiku[master]: bootloader: Split memory map handling into add/remove passes.

  • From: Gerrit <review@xxxxxxxxxxxxxxxxxxx>
  • To: waddlesplash <waddlesplash@xxxxxxxxx>, haiku-commits@xxxxxxxxxxxxx
  • Date: Mon, 25 May 2020 23:42:00 +0000

From Michael Lotz <mmlr@xxxxxxxx>:

Michael Lotz has uploaded this change for review. ( 
https://review.haiku-os.org/c/haiku/+/2814 ;)


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

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.
---
M headers/private/kernel/boot/addr_range.h
M src/system/boot/loader/kernel_args.cpp
M src/system/boot/platform/bios_ia32/mmu.cpp
M src/system/boot/platform/efi/arch/x86_64/arch_mmu.cpp
4 files changed, 119 insertions(+), 41 deletions(-)



  git pull ssh://git.haiku-os.org:22/haiku refs/changes/14/2814/1

diff --git a/headers/private/kernel/boot/addr_range.h 
b/headers/private/kernel/boot/addr_range.h
index 80d0c78..a6ef173 100644
--- a/headers/private/kernel/boot/addr_range.h
+++ b/headers/private/kernel/boot/addr_range.h
@@ -28,9 +28,13 @@
        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 a5ddc2b..c17ae44 100644
--- a/src/system/boot/loader/kernel_args.cpp
+++ b/src/system/boot/loader/kernel_args.cpp
@@ -248,6 +248,16 @@
 }


+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)
 {
@@ -282,6 +292,23 @@


 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)
 {
        return insert_address_range(gKernelArgs.physical_allocated_range,
diff --git a/src/system/boot/platform/bios_ia32/mmu.cpp 
b/src/system/boot/platform/bios_ia32/mmu.cpp
index 4897521..d99db7f 100644
--- a/src/system/boot/platform/bios_ia32/mmu.cpp
+++ b/src/system/boot/platform/bios_ia32/mmu.cpp
@@ -664,51 +664,68 @@
        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 @@
                        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 63c61b4..bc6eb69 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 @@
        // 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 @@
                        // 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 @@
                }
                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 @@
                }
        }

+       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);

--
To view, visit https://review.haiku-os.org/c/haiku/+/2814
To unsubscribe, or for help writing mail filters, visit 
https://review.haiku-os.org/settings

Gerrit-Project: haiku
Gerrit-Branch: master
Gerrit-Change-Id: Ie7991d2c4dcd988edac2995b3a7efc509fa0f4a3
Gerrit-Change-Number: 2814
Gerrit-PatchSet: 1
Gerrit-Owner: Michael Lotz <mmlr@xxxxxxxx>
Gerrit-MessageType: newchange

Other related posts:

  • » [haiku-commits] Change in haiku[master]: bootloader: Split memory map handling into add/remove passes. - Gerrit