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

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