Blame SOURCES/0482-ieee1275-claim-more-memory.patch

f20682
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
f20682
From: Daniel Axtens <dja@axtens.net>
f20682
Date: Wed, 15 Apr 2020 23:28:29 +1000
f20682
Subject: [PATCH] ieee1275: claim more memory
f20682
f20682
On powerpc-ieee1275, we are running out of memory trying to verify
f20682
anything. This is because:
f20682
f20682
 - we have to load an entire file into memory to verify it. This is
f20682
   extremely difficult to change with appended signatures.
f20682
 - We only have 32MB of heap.
f20682
 - Distro kernels are now often around 30MB.
f20682
f20682
So we want to claim more memory from OpenFirmware for our heap.
f20682
f20682
There are some complications:
f20682
f20682
 - The grub mm code isn't the only thing that will make claims on
f20682
   memory from OpenFirmware:
f20682
f20682
    * PFW/SLOF will have claimed some for their own use.
f20682
f20682
    * The ieee1275 loader will try to find other bits of memory that we
f20682
      haven't claimed to place the kernel and initrd when we go to boot.
f20682
f20682
    * Once we load Linux, it will also try to claim memory. It claims
f20682
      memory without any reference to /memory/available, it just starts
f20682
      at min(top of RMO, 768MB) and works down. So we need to avoid this
f20682
      area. See arch/powerpc/kernel/prom_init.c as of v5.11.
f20682
f20682
 - The smallest amount of memory a ppc64 KVM guest can have is 256MB.
f20682
   It doesn't work with distro kernels but can work with custom kernels.
f20682
   We should maintain support for that. (ppc32 can boot with even less,
f20682
   and we shouldn't break that either.)
f20682
f20682
 - Even if a VM has more memory, the memory OpenFirmware makes available
f20682
   as Real Memory Area can be restricted. A freshly created LPAR on a
f20682
   PowerVM machine is likely to have only 256MB available to OpenFirmware
f20682
   even if it has many gigabytes of memory allocated.
f20682
f20682
EFI systems will attempt to allocate 1/4th of the available memory,
f20682
clamped to between 1M and 1600M. That seems like a good sort of
f20682
approach, we just need to figure out if 1/4 is the right fraction
f20682
for us.
f20682
f20682
We don't know in advance how big the kernel and initrd are going to be,
f20682
which makes figuring out how much memory we can take a bit tricky.
f20682
f20682
To figure out how much memory we should leave unused, I looked at:
f20682
f20682
 - an Ubuntu 20.04.1 ppc64le pseries KVM guest:
f20682
    vmlinux: ~30MB
f20682
    initrd:  ~50MB
f20682
f20682
 - a RHEL8.2 ppc64le pseries KVM guest:
f20682
    vmlinux: ~30MB
f20682
    initrd:  ~30MB
f20682
f20682
Ubuntu VMs struggle to boot with just 256MB under SLOF.
f20682
RHEL likewise has a higher minimum supported memory figure.
f20682
So lets first consider a distro kernel and 512MB of addressible memory.
f20682
(This is the default case for anything booting under PFW.) Say we lose
f20682
131MB to PFW (based on some tests). This leaves us 381MB. 1/4 of 381MB
f20682
is ~95MB. That should be enough to verify a 30MB vmlinux and should
f20682
leave plenty of space to load Linux and the initrd.
f20682
f20682
If we consider 256MB of RMA under PFW, we have just 125MB remaining. 1/4
f20682
of that is a smidge under 32MB, which gives us very poor odds of verifying
f20682
a distro-sized kernel. However, if we need 80MB just to put the kernel
f20682
and initrd in memory, we can't claim any more than 45MB anyway. So 1/4
f20682
will do. We'll come back to this later.
f20682
f20682
grub is always built as a 32-bit binary, even if it's loading a ppc64
f20682
kernel. So we can't address memory beyond 4GB. This gives a natural cap
f20682
of 1GB for powerpc-ieee1275.
f20682
f20682
Also apply this 1/4 approach to i386-ieee1275, but keep the 32MB cap.
f20682
f20682
make check still works for both i386 and powerpc and I've booted
f20682
powerpc grub with this change under SLOF and PFW.
f20682
f20682
Signed-off-by: Daniel Axtens <dja@axtens.net>
f20682
---
f20682
 grub-core/kern/ieee1275/init.c | 81 +++++++++++++++++++++++++++++++++---------
f20682
 docs/grub-dev.texi             |  6 ++--
f20682
 2 files changed, 69 insertions(+), 18 deletions(-)
f20682
f20682
diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
b32e65
index ee97d761d..a6e169bd0 100644
f20682
--- a/grub-core/kern/ieee1275/init.c
f20682
+++ b/grub-core/kern/ieee1275/init.c
f20682
@@ -42,11 +42,12 @@
f20682
 #include <grub/machine/kernel.h>
f20682
 #endif
f20682
 
f20682
-/* The maximum heap size we're going to claim */
f20682
+/* The maximum heap size we're going to claim. Not used by sparc.
f20682
+   We allocate 1/4 of the available memory under 4G, up to this limit. */
f20682
 #ifdef __i386__
f20682
 #define HEAP_MAX_SIZE		(unsigned long) (64 * 1024 * 1024)
f20682
-#else
f20682
-#define HEAP_MAX_SIZE		(unsigned long) (32 * 1024 * 1024)
f20682
+#else // __powerpc__
f20682
+#define HEAP_MAX_SIZE		(unsigned long) (1 * 1024 * 1024 * 1024)
f20682
 #endif
f20682
 
f20682
 extern char _end[];
f20682
@@ -143,16 +144,45 @@ grub_claim_heap (void)
f20682
 				 + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000);
f20682
 }
f20682
 #else
f20682
-/* Helper for grub_claim_heap.  */
f20682
+/* Helper for grub_claim_heap on powerpc. */
f20682
+static int
f20682
+heap_size (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
f20682
+	   void *data)
f20682
+{
f20682
+  grub_uint32_t total = *(grub_uint32_t *)data;
f20682
+
f20682
+  if (type != GRUB_MEMORY_AVAILABLE)
f20682
+    return 0;
f20682
+
f20682
+  /* Do not consider memory beyond 4GB */
f20682
+  if (addr > 0xffffffffUL)
f20682
+    return 0;
f20682
+
f20682
+  if (addr + len > 0xffffffffUL)
f20682
+    len = 0xffffffffUL - addr;
f20682
+
f20682
+  total += len;
f20682
+  *(grub_uint32_t *)data = total;
f20682
+
f20682
+  return 0;
f20682
+}
f20682
+
f20682
 static int
f20682
 heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
f20682
 	   void *data)
f20682
 {
f20682
-  unsigned long *total = data;
f20682
+  grub_uint32_t total = *(grub_uint32_t *)data;
f20682
 
f20682
   if (type != GRUB_MEMORY_AVAILABLE)
f20682
     return 0;
f20682
 
f20682
+  /* Do not consider memory beyond 4GB */
f20682
+  if (addr > 0xffffffffUL)
f20682
+    return 0;
f20682
+
f20682
+  if (addr + len > 0xffffffffUL)
f20682
+    len = 0xffffffffUL - addr;
f20682
+
f20682
   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM))
f20682
     {
f20682
       if (addr + len <= 0x180000)
f20682
@@ -166,10 +196,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
f20682
     }
f20682
   len -= 1; /* Required for some firmware.  */
f20682
 
f20682
-  /* Never exceed HEAP_MAX_SIZE  */
f20682
-  if (*total + len > HEAP_MAX_SIZE)
f20682
-    len = HEAP_MAX_SIZE - *total;
f20682
-
f20682
   /* In theory, firmware should already prevent this from happening by not
f20682
      listing our own image in /memory/available.  The check below is intended
f20682
      as a safeguard in case that doesn't happen.  However, it doesn't protect
f20682
@@ -181,6 +207,18 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
f20682
       len = 0;
f20682
     }
f20682
 
f20682
+  /* If this block contains 0x30000000 (768MB), do not claim below that.
f20682
+     Linux likes to claim memory at min(RMO top, 768MB) and works down
f20682
+     without reference to /memory/available. */
f20682
+  if ((addr < 0x30000000) && ((addr + len) > 0x30000000))
f20682
+    {
f20682
+      len = len - (0x30000000 - addr);
f20682
+      addr = 0x30000000;
f20682
+    }
f20682
+
f20682
+  if (len > total)
f20682
+    len = total;
f20682
+
f20682
   if (len)
f20682
     {
f20682
       grub_err_t err;
f20682
@@ -189,10 +227,12 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
f20682
       if (err)
f20682
 	return err;
f20682
       grub_mm_init_region ((void *) (grub_addr_t) addr, len);
f20682
+      total -= len;
f20682
     }
f20682
 
f20682
-  *total += len;
f20682
-  if (*total >= HEAP_MAX_SIZE)
f20682
+  *(grub_uint32_t *)data = total;
f20682
+
f20682
+  if (total == 0)
f20682
     return 1;
f20682
 
f20682
   return 0;
f20682
@@ -201,13 +241,22 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
f20682
 static void 
f20682
 grub_claim_heap (void)
f20682
 {
f20682
-  unsigned long total = 0;
f20682
+  grub_uint32_t total = 0;
f20682
 
f20682
   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
f20682
-    heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
f20682
-	       1, &total);
f20682
-  else
f20682
-    grub_machine_mmap_iterate (heap_init, &total);
f20682
+    {
f20682
+      heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
f20682
+		 1, &total);
f20682
+      return;
f20682
+    }
f20682
+
f20682
+  grub_machine_mmap_iterate (heap_size, &total);
f20682
+
f20682
+  total = total / 4;
f20682
+  if (total > HEAP_MAX_SIZE)
f20682
+    total = HEAP_MAX_SIZE;
f20682
+
f20682
+  grub_machine_mmap_iterate (heap_init, &total);
f20682
 }
f20682
 #endif
f20682
 
f20682
diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
b32e65
index 421dd410e..03d53498c 100644
f20682
--- a/docs/grub-dev.texi
f20682
+++ b/docs/grub-dev.texi
f20682
@@ -930,7 +930,9 @@ space is limited to 4GiB. GRUB allocates pages from EFI for its heap, at most
f20682
 1.6 GiB.
f20682
 
f20682
 On i386-ieee1275 and powerpc-ieee1275 GRUB uses same stack as IEEE1275.
f20682
-It allocates at most 32MiB for its heap.
f20682
+
f20682
+On i386-ieee1275, GRUB allocates at most 32MiB for its heap. On
f20682
+powerpc-ieee1275, GRUB allocates up to 1GiB.
f20682
 
f20682
 On sparc64-ieee1275 stack is 256KiB and heap is 2MiB.
f20682
 
f20682
@@ -958,7 +960,7 @@ In short:
f20682
 @item i386-qemu               @tab 60 KiB  @tab < 4 GiB
f20682
 @item *-efi                   @tab ?       @tab < 1.6 GiB
f20682
 @item i386-ieee1275           @tab ?       @tab < 32 MiB
f20682
-@item powerpc-ieee1275        @tab ?       @tab < 32 MiB
f20682
+@item powerpc-ieee1275        @tab ?       @tab < 1 GiB
f20682
 @item sparc64-ieee1275        @tab 256KiB  @tab 2 MiB
f20682
 @item arm-uboot               @tab 256KiB  @tab 2 MiB
f20682
 @item mips(el)-qemu_mips      @tab 2MiB    @tab 253 MiB