|
|
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
|
|
|
4e7deb |
index ee97d761d1e..a6e169bd003 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
|
|
|
4e7deb |
index 421dd410e50..03d53498c51 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
|