Blob Blame History Raw
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Fri, 29 Jul 2022 15:57:57 -0400
Subject: [PATCH] efi: make the default arena most of ram

Currently when populating the initial memory arena on EFI systems, we
count the available regions below GRUB_EFI_MAX_ALLOCATION_ADDRESS from
the EFI memory map and then allocates one quarter of that for our arena.

Because many systems come up without IOMMUs, we currently set
GRUB_EFI_MAX_ALLOCATION_ADDRESS to 0x7fffffff, i.e. all addresses
allocated must be below 2G[0].  Due to firmware and other
considerations, this makes the most memory we can possibly have in our
arena 512M.

Because our EFI loader doesn't get kernel and initrd memory from grub's
allocator, but rather reserves it directly from UEFI and then simply
marks those as allocated if they're within grub's arena, it was
historically possible to have initrds that are larger than 512M, because
we could use any memory region below 4G, without concern for grub's
choice of arena size.

Unfortunately, when we switched to using the "verifiers" API (and thus
the file_filter_t API) to do measurement of kernel and initrd, this
introduced a pattern that allocates the entire file when we call
grub_file_open(), and buffers it to pass to the filter.  This results in
needing to have enough space for the initramfs in the grub arena.

This is bad.

Since it's unlikely you're going to do anything *other* than loading a
kernel and initramfs that takes much of the available free memory from
UEFI, this patch introduces a workaround by changing the amount we give
to the arena be three quarters of the available memory, rather than one
quarter, thus changing our theoretical initrd limit to 1.5G.  In
practice, it may still be smaller than that depending on allocation
fragmentation, but generally it will be most of it.

Note that this doesn't fix the underlying flaw, which is that there is
no safe way to do the validation correctly using the "verifiers" system
with the current file API without buffering the whole file before
grub_file_read() is ever called, and thus you can't set an allocation
policy for the initial buffer of the file at all, so unless we raise the
allocation limit to >4G, it can't be allocated in the big region.

[0] I'm not sure there was a good reason not to pick 4G, but even if we
    had, at least one common firmware routes the first 2G of physical
    RAM to 0x0, and any additional memory starting at 0x100000000.

Related: rhbz#2112134

Signed-off-by: Peter Jones <pjones@redhat.com>
(cherry picked from commit 005a0aaaad2a00a1fa1e60d94cc4fd5407c22e7d)
---
 grub-core/kern/efi/mm.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
index 88364d764c..0288eab361 100644
--- a/grub-core/kern/efi/mm.c
+++ b/grub-core/kern/efi/mm.c
@@ -738,10 +738,10 @@ grub_efi_mm_init (void)
   filtered_memory_map_end = filter_memory_map (memory_map, filtered_memory_map,
 					       desc_size, memory_map_end);
 
-  /* By default, request a quarter of the available memory.  */
+  /* By default, request three quarters of the available memory.  */
   total_pages = get_total_pages (filtered_memory_map, desc_size,
 				 filtered_memory_map_end);
-  required_pages = (total_pages >> 2);
+  required_pages = (total_pages >> 1) + (total_pages >> 2);
   if (required_pages < BYTES_TO_PAGES (MIN_HEAP_SIZE))
     required_pages = BYTES_TO_PAGES (MIN_HEAP_SIZE);
   else if (required_pages > BYTES_TO_PAGES (MAX_HEAP_SIZE))