Pull in allocator fixes from upstream

Resolves: #2156419
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
This commit is contained in:
Robbie Harwood 2023-02-01 19:51:45 +00:00
parent 501956fdc0
commit 433335e50c
47 changed files with 1529 additions and 104 deletions

View File

@ -1,74 +0,0 @@
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))

View File

@ -0,0 +1,184 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Thu, 25 Nov 2021 02:22:46 +1100
Subject: [PATCH] mm: Clarify grub_real_malloc()
When iterating through the singly linked list of free blocks,
grub_real_malloc() uses p and q for the current and previous blocks
respectively. This isn't super clear, so swap to using prev and cur.
This makes another quirk more obvious. The comment at the top of
grub_real_malloc() might lead you to believe that the function will
allocate from *first if there is space in that block.
It actually doesn't do that, and it can't do that with the current
data structures. If we used up all of *first, we would need to change
the ->next of the previous block to point to *first->next, but we
can't do that because it's a singly linked list and we don't have
access to *first's previous block.
What grub_real_malloc() actually does is set *first to the initial
previous block, and *first->next is the block we try to allocate
from. That allows us to keep all the data structures consistent.
Document that.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
(cherry picked from commit 246ad6a44c281bb13486ddea0a26bb661db73106)
(cherry picked from commit b590e0181a94b3d48ca0c537565c946fad8e6a4f)
---
grub-core/kern/mm.c | 76 +++++++++++++++++++++++++++++------------------------
1 file changed, 41 insertions(+), 35 deletions(-)
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index d8c8377578..fb20e93acf 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -178,13 +178,20 @@ grub_mm_init_region (void *addr, grub_size_t size)
}
/* Allocate the number of units N with the alignment ALIGN from the ring
- buffer starting from *FIRST. ALIGN must be a power of two. Both N and
- ALIGN are in units of GRUB_MM_ALIGN. Return a non-NULL if successful,
- otherwise return NULL. */
+ * buffer given in *FIRST. ALIGN must be a power of two. Both N and
+ * ALIGN are in units of GRUB_MM_ALIGN. Return a non-NULL if successful,
+ * otherwise return NULL.
+ *
+ * Note: because in certain circumstances we need to adjust the ->next
+ * pointer of the previous block, we iterate over the singly linked
+ * list with the pair (prev, cur). *FIRST is our initial previous, and
+ * *FIRST->next is our initial current pointer. So we will actually
+ * allocate from *FIRST->next first and *FIRST itself last.
+ */
static void *
grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
{
- grub_mm_header_t p, q;
+ grub_mm_header_t cur, prev;
/* When everything is allocated side effect is that *first will have alloc
magic marked, meaning that there is no room in this region. */
@@ -192,24 +199,24 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
return 0;
/* Try to search free slot for allocation in this memory region. */
- for (q = *first, p = q->next; ; q = p, p = p->next)
+ for (prev = *first, cur = prev->next; ; prev = cur, cur = cur->next)
{
grub_off_t extra;
- extra = ((grub_addr_t) (p + 1) >> GRUB_MM_ALIGN_LOG2) & (align - 1);
+ extra = ((grub_addr_t) (cur + 1) >> GRUB_MM_ALIGN_LOG2) & (align - 1);
if (extra)
extra = align - extra;
- if (! p)
+ if (! cur)
grub_fatal ("null in the ring");
- if (p->magic != GRUB_MM_FREE_MAGIC)
- grub_fatal ("free magic is broken at %p: 0x%x", p, p->magic);
+ if (cur->magic != GRUB_MM_FREE_MAGIC)
+ grub_fatal ("free magic is broken at %p: 0x%x", cur, cur->magic);
- if (p->size >= n + extra)
+ if (cur->size >= n + extra)
{
- extra += (p->size - extra - n) & (~(align - 1));
- if (extra == 0 && p->size == n)
+ extra += (cur->size - extra - n) & (~(align - 1));
+ if (extra == 0 && cur->size == n)
{
/* There is no special alignment requirement and memory block
is complete match.
@@ -222,9 +229,9 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
| alloc, size=n | |
+---------------+ v
*/
- q->next = p->next;
+ prev->next = cur->next;
}
- else if (align == 1 || p->size == n + extra)
+ else if (align == 1 || cur->size == n + extra)
{
/* There might be alignment requirement, when taking it into
account memory block fits in.
@@ -241,23 +248,22 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
| alloc, size=n | |
+---------------+ v
*/
-
- p->size -= n;
- p += p->size;
+ cur->size -= n;
+ cur += cur->size;
}
else if (extra == 0)
{
grub_mm_header_t r;
- r = p + extra + n;
+ r = cur + extra + n;
r->magic = GRUB_MM_FREE_MAGIC;
- r->size = p->size - extra - n;
- r->next = p->next;
- q->next = r;
+ r->size = cur->size - extra - n;
+ r->next = cur->next;
+ prev->next = r;
- if (q == p)
+ if (prev == cur)
{
- q = r;
+ prev = r;
r->next = r;
}
}
@@ -284,32 +290,32 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
*/
grub_mm_header_t r;
- r = p + extra + n;
+ r = cur + extra + n;
r->magic = GRUB_MM_FREE_MAGIC;
- r->size = p->size - extra - n;
- r->next = p;
+ r->size = cur->size - extra - n;
+ r->next = cur;
- p->size = extra;
- q->next = r;
- p += extra;
+ cur->size = extra;
+ prev->next = r;
+ cur += extra;
}
- p->magic = GRUB_MM_ALLOC_MAGIC;
- p->size = n;
+ cur->magic = GRUB_MM_ALLOC_MAGIC;
+ cur->size = n;
/* Mark find as a start marker for next allocation to fasten it.
This will have side effect of fragmenting memory as small
pieces before this will be un-used. */
/* So do it only for chunks under 64K. */
if (n < (0x8000 >> GRUB_MM_ALIGN_LOG2)
- || *first == p)
- *first = q;
+ || *first == cur)
+ *first = prev;
- return p + 1;
+ return cur + 1;
}
/* Search was completed without result. */
- if (p == *first)
+ if (cur == *first)
break;
}

View File

@ -0,0 +1,33 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Thu, 25 Nov 2021 02:22:47 +1100
Subject: [PATCH] mm: grub_real_malloc(): Make small allocs comment match code
Small allocations move the region's *first pointer. The comment
says that this happens for allocations under 64K. The code says
it's for allocations under 32K. Commit 45bf8b3a7549 changed the
code intentionally: make the comment match.
Fixes: 45bf8b3a7549 (* grub-core/kern/mm.c (grub_real_malloc): Decrease cut-off of moving the)
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
(cherry picked from commit a847895a8d000bdf27ad4d4326f883a0eed769ca)
(cherry picked from commit 16f329218ced9302554bde95084e6a2ad308733b)
---
grub-core/kern/mm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index fb20e93acf..db7e0b2a5b 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -306,7 +306,7 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
/* Mark find as a start marker for next allocation to fasten it.
This will have side effect of fragmenting memory as small
pieces before this will be un-used. */
- /* So do it only for chunks under 64K. */
+ /* So do it only for chunks under 32K. */
if (n < (0x8000 >> GRUB_MM_ALIGN_LOG2)
|| *first == cur)
*first = prev;

View File

@ -0,0 +1,121 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Thu, 25 Nov 2021 02:22:48 +1100
Subject: [PATCH] mm: Document grub_free()
The grub_free() possesses a surprising number of quirks, and also
uses single-letter variable names confusingly to iterate through
the free list.
Document what's going on.
Use prev and cur to iterate over the free list.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
(cherry picked from commit 1f8d0b01738e49767d662d6426af3570a64565f0)
(cherry picked from commit 6fd181e46a16f0219b586ce0bd44928a3f58dec8)
---
grub-core/kern/mm.c | 63 ++++++++++++++++++++++++++++++++++-------------------
1 file changed, 41 insertions(+), 22 deletions(-)
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index db7e0b2a5b..0351171cf9 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -446,54 +446,73 @@ grub_free (void *ptr)
}
else
{
- grub_mm_header_t q, s;
+ grub_mm_header_t cur, prev;
#if 0
- q = r->first;
+ cur = r->first;
do
{
grub_printf ("%s:%d: q=%p, q->size=0x%x, q->magic=0x%x\n",
- GRUB_FILE, __LINE__, q, q->size, q->magic);
- q = q->next;
+ GRUB_FILE, __LINE__, cur, cur->size, cur->magic);
+ cur = cur->next;
}
- while (q != r->first);
+ while (cur != r->first);
#endif
-
- for (s = r->first, q = s->next; q <= p || q->next >= p; s = q, q = s->next)
+ /* Iterate over all blocks in the free ring.
+ *
+ * The free ring is arranged from high addresses to low
+ * addresses, modulo wraparound.
+ *
+ * We are looking for a block with a higher address than p or
+ * whose next address is lower than p.
+ */
+ for (prev = r->first, cur = prev->next; cur <= p || cur->next >= p;
+ prev = cur, cur = prev->next)
{
- if (q->magic != GRUB_MM_FREE_MAGIC)
- grub_fatal ("free magic is broken at %p: 0x%x", q, q->magic);
+ if (cur->magic != GRUB_MM_FREE_MAGIC)
+ grub_fatal ("free magic is broken at %p: 0x%x", cur, cur->magic);
- if (q <= q->next && (q > p || q->next < p))
+ /* Deal with wrap-around */
+ if (cur <= cur->next && (cur > p || cur->next < p))
break;
}
+ /* mark p as free and insert it between cur and cur->next */
p->magic = GRUB_MM_FREE_MAGIC;
- p->next = q->next;
- q->next = p;
+ p->next = cur->next;
+ cur->next = p;
+ /*
+ * If the block we are freeing can be merged with the next
+ * free block, do that.
+ */
if (p->next + p->next->size == p)
{
p->magic = 0;
p->next->size += p->size;
- q->next = p->next;
+ cur->next = p->next;
p = p->next;
}
- r->first = q;
+ r->first = cur;
- if (q == p + p->size)
+ /* Likewise if can be merged with the preceeding free block */
+ if (cur == p + p->size)
{
- q->magic = 0;
- p->size += q->size;
- if (q == s)
- s = p;
- s->next = p;
- q = s;
+ cur->magic = 0;
+ p->size += cur->size;
+ if (cur == prev)
+ prev = p;
+ prev->next = p;
+ cur = prev;
}
- r->first = q;
+ /*
+ * Set r->first such that the just free()d block is tried first.
+ * (An allocation is tried from *first->next, and cur->next == p.)
+ */
+ r->first = cur;
}
}

View File

@ -0,0 +1,74 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Thu, 25 Nov 2021 02:22:49 +1100
Subject: [PATCH] mm: Document grub_mm_init_region()
The grub_mm_init_region() does some things that seem magical, especially
around region merging. Make it a bit clearer.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
(cherry picked from commit 246d69b7ea619fc1e77dcc5960e37aea45a9808c)
(cherry picked from commit 1eb53ad7659ee1d8141fe31138d0df9873b09182)
---
grub-core/kern/mm.c | 31 ++++++++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index 0351171cf9..1cbf98c7ab 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -128,23 +128,52 @@ grub_mm_init_region (void *addr, grub_size_t size)
if (((grub_addr_t) addr + 0x1000) > ~(grub_addr_t) size)
size = ((grub_addr_t) -0x1000) - (grub_addr_t) addr;
+ /* Attempt to merge this region with every existing region */
for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p)
+ /*
+ * Is the new region immediately below an existing region? That
+ * is, is the address of the memory we're adding now (addr) + size
+ * of the memory we're adding (size) + the bytes we couldn't use
+ * at the start of the region we're considering (q->pre_size)
+ * equal to the address of q? In other words, does the memory
+ * looks like this?
+ *
+ * addr q
+ * |----size-----|-q->pre_size-|<q region>|
+ */
if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
{
+ /*
+ * Yes, we can merge the memory starting at addr into the
+ * existing region from below. Align up addr to GRUB_MM_ALIGN
+ * so that our new region has proper alignment.
+ */
r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
+ /* Copy the region data across */
*r = *q;
+ /* Consider all the new size as pre-size */
r->pre_size += size;
-
+
+ /*
+ * If we have enough pre-size to create a block, create a
+ * block with it. Mark it as allocated and pass it to
+ * grub_free (), which will sort out getting it into the free
+ * list.
+ */
if (r->pre_size >> GRUB_MM_ALIGN_LOG2)
{
h = (grub_mm_header_t) (r + 1);
+ /* block size is pre-size converted to cells */
h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2);
h->magic = GRUB_MM_ALLOC_MAGIC;
+ /* region size grows by block size converted back to bytes */
r->size += h->size << GRUB_MM_ALIGN_LOG2;
+ /* adjust pre_size to be accurate */
r->pre_size &= (GRUB_MM_ALIGN - 1);
*p = r;
grub_free (h + 1);
}
+ /* Replace the old region with the new region */
*p = r;
return;
}

View File

@ -0,0 +1,79 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Thu, 25 Nov 2021 02:22:45 +1100
Subject: [PATCH] mm: Document GRUB internal memory management structures
I spent more than a trivial quantity of time figuring out pre_size and
whether a memory region's size contains the header cell or not.
Document the meanings of all the properties. Hopefully now no-one else
has to figure it out!
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
(cherry picked from commit a6c5c52ccffd2674d43db25fb4baa9c528526aa0)
(cherry picked from commit 222eb8897c25581e63a352633528fc130d151a64)
---
include/grub/mm_private.h | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h
index c2c4cb1511..203533cc3d 100644
--- a/include/grub/mm_private.h
+++ b/include/grub/mm_private.h
@@ -21,15 +21,27 @@
#include <grub/mm.h>
+/* For context, see kern/mm.c */
+
/* Magic words. */
#define GRUB_MM_FREE_MAGIC 0x2d3c2808
#define GRUB_MM_ALLOC_MAGIC 0x6db08fa4
+/* A header describing a block of memory - either allocated or free */
typedef struct grub_mm_header
{
+ /*
+ * The 'next' free block in this region's circular free list.
+ * Only meaningful if the block is free.
+ */
struct grub_mm_header *next;
+ /* The block size, not in bytes but the number of cells of
+ * GRUB_MM_ALIGN bytes. Includes the header cell.
+ */
grub_size_t size;
+ /* either free or alloc magic, depending on the block type. */
grub_size_t magic;
+ /* pad to cell size: see the top of kern/mm.c. */
#if GRUB_CPU_SIZEOF_VOID_P == 4
char padding[4];
#elif GRUB_CPU_SIZEOF_VOID_P == 8
@@ -48,11 +60,27 @@ typedef struct grub_mm_header
#define GRUB_MM_ALIGN (1 << GRUB_MM_ALIGN_LOG2)
+/* A region from which we can make allocations. */
typedef struct grub_mm_region
{
+ /* The first free block in this region. */
struct grub_mm_header *first;
+
+ /*
+ * The next region in the linked list of regions. Regions are initially
+ * sorted in order of increasing size, but can grow, in which case the
+ * ordering may not be preserved.
+ */
struct grub_mm_region *next;
+
+ /*
+ * A grub_mm_region will always be aligned to cell size. The pre-size is
+ * the number of bytes we were given but had to skip in order to get that
+ * alignment.
+ */
grub_size_t pre_size;
+
+ /* How many bytes are in this region? (free and allocated) */
grub_size_t size;
}
*grub_mm_region_t;

View File

@ -0,0 +1,57 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Thu, 21 Apr 2022 15:24:14 +1000
Subject: [PATCH] mm: Assert that we preserve header vs region alignment
grub_mm_region_init() does:
h = (grub_mm_header_t) (r + 1);
where h is a grub_mm_header_t and r is a grub_mm_region_t.
Cells are supposed to be GRUB_MM_ALIGN aligned, but while grub_mm_dump
ensures this vs the region header, grub_mm_region_init() does not.
It's better to be explicit than implicit here: rather than changing
grub_mm_region_init() to ALIGN_UP(), require that the struct is
explicitly a multiple of the header size.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Tested-by: Patrick Steinhardt <ps@pks.im>
(cherry picked from commit 1df8fe66c57087eb33bd6dc69f786ed124615aa7)
(cherry picked from commit f4d39686e773274e690ada441f5e128ac3c4ac06)
---
include/grub/mm_private.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h
index 203533cc3d..a688b92a83 100644
--- a/include/grub/mm_private.h
+++ b/include/grub/mm_private.h
@@ -20,6 +20,7 @@
#define GRUB_MM_PRIVATE_H 1
#include <grub/mm.h>
+#include <grub/misc.h>
/* For context, see kern/mm.c */
@@ -89,4 +90,17 @@ typedef struct grub_mm_region
extern grub_mm_region_t EXPORT_VAR (grub_mm_base);
#endif
+static inline void
+grub_mm_size_sanity_check (void) {
+ /* Ensure we preserve alignment when doing h = (grub_mm_header_t) (r + 1). */
+ COMPILE_TIME_ASSERT ((sizeof (struct grub_mm_region) %
+ sizeof (struct grub_mm_header)) == 0);
+
+ /*
+ * GRUB_MM_ALIGN is supposed to represent cell size, and a mm_header is
+ * supposed to be 1 cell.
+ */
+ COMPILE_TIME_ASSERT (sizeof (struct grub_mm_header) == GRUB_MM_ALIGN);
+}
+
#endif

View File

@ -0,0 +1,204 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Thu, 21 Apr 2022 15:24:15 +1000
Subject: [PATCH] mm: When adding a region, merge with region after as well as
before
On x86_64-efi (at least) regions seem to be added from top down. The mm
code will merge a new region with an existing region that comes
immediately before the new region. This allows larger allocations to be
satisfied that would otherwise be the case.
On powerpc-ieee1275, however, regions are added from bottom up. So if
we add 3x 32MB regions, we can still only satisfy a 32MB allocation,
rather than the 96MB allocation we might otherwise be able to satisfy.
* Define 'post_size' as being bytes lost to the end of an allocation
due to being given weird sizes from firmware that are not multiples
of GRUB_MM_ALIGN.
* Allow merging of regions immediately _after_ existing regions, not
just before. As with the other approach, we create an allocated
block to represent the new space and the pass it to grub_free() to
get the metadata right.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Tested-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Tested-by: Patrick Steinhardt <ps@pks.im>
(cherry picked from commit 052e6068be622ff53f1238b449c300dbd0a8abcd)
(cherry picked from commit 9b7b9820e37b6828ea9b7da15f8da952a53a3df5)
---
grub-core/kern/mm.c | 128 +++++++++++++++++++++++++++++-----------------
include/grub/mm_private.h | 9 ++++
2 files changed, 91 insertions(+), 46 deletions(-)
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index 1cbf98c7ab..7be33e23bf 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -130,53 +130,88 @@ grub_mm_init_region (void *addr, grub_size_t size)
/* Attempt to merge this region with every existing region */
for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p)
- /*
- * Is the new region immediately below an existing region? That
- * is, is the address of the memory we're adding now (addr) + size
- * of the memory we're adding (size) + the bytes we couldn't use
- * at the start of the region we're considering (q->pre_size)
- * equal to the address of q? In other words, does the memory
- * looks like this?
- *
- * addr q
- * |----size-----|-q->pre_size-|<q region>|
- */
- if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
- {
- /*
- * Yes, we can merge the memory starting at addr into the
- * existing region from below. Align up addr to GRUB_MM_ALIGN
- * so that our new region has proper alignment.
- */
- r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
- /* Copy the region data across */
- *r = *q;
- /* Consider all the new size as pre-size */
- r->pre_size += size;
+ {
+ /*
+ * Is the new region immediately below an existing region? That
+ * is, is the address of the memory we're adding now (addr) + size
+ * of the memory we're adding (size) + the bytes we couldn't use
+ * at the start of the region we're considering (q->pre_size)
+ * equal to the address of q? In other words, does the memory
+ * looks like this?
+ *
+ * addr q
+ * |----size-----|-q->pre_size-|<q region>|
+ */
+ if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
+ {
+ /*
+ * Yes, we can merge the memory starting at addr into the
+ * existing region from below. Align up addr to GRUB_MM_ALIGN
+ * so that our new region has proper alignment.
+ */
+ r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
+ /* Copy the region data across */
+ *r = *q;
+ /* Consider all the new size as pre-size */
+ r->pre_size += size;
- /*
- * If we have enough pre-size to create a block, create a
- * block with it. Mark it as allocated and pass it to
- * grub_free (), which will sort out getting it into the free
- * list.
- */
- if (r->pre_size >> GRUB_MM_ALIGN_LOG2)
- {
- h = (grub_mm_header_t) (r + 1);
- /* block size is pre-size converted to cells */
- h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2);
- h->magic = GRUB_MM_ALLOC_MAGIC;
- /* region size grows by block size converted back to bytes */
- r->size += h->size << GRUB_MM_ALIGN_LOG2;
- /* adjust pre_size to be accurate */
- r->pre_size &= (GRUB_MM_ALIGN - 1);
- *p = r;
- grub_free (h + 1);
- }
- /* Replace the old region with the new region */
- *p = r;
- return;
- }
+ /*
+ * If we have enough pre-size to create a block, create a
+ * block with it. Mark it as allocated and pass it to
+ * grub_free (), which will sort out getting it into the free
+ * list.
+ */
+ if (r->pre_size >> GRUB_MM_ALIGN_LOG2)
+ {
+ h = (grub_mm_header_t) (r + 1);
+ /* block size is pre-size converted to cells */
+ h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2);
+ h->magic = GRUB_MM_ALLOC_MAGIC;
+ /* region size grows by block size converted back to bytes */
+ r->size += h->size << GRUB_MM_ALIGN_LOG2;
+ /* adjust pre_size to be accurate */
+ r->pre_size &= (GRUB_MM_ALIGN - 1);
+ *p = r;
+ grub_free (h + 1);
+ }
+ /* Replace the old region with the new region */
+ *p = r;
+ return;
+ }
+
+ /*
+ * Is the new region immediately above an existing region? That
+ * is:
+ * q addr
+ * |<q region>|-q->post_size-|----size-----|
+ */
+ if ((grub_uint8_t *) q + sizeof (*q) + q->size + q->post_size ==
+ (grub_uint8_t *) addr)
+ {
+ /*
+ * Yes! Follow a similar pattern to above, but simpler.
+ * Our header starts at address - post_size, which should align us
+ * to a cell boundary.
+ *
+ * Cast to (void *) first to avoid the following build error:
+ * kern/mm.c: In function grub_mm_init_region:
+ * kern/mm.c:211:15: error: cast increases required alignment of target type [-Werror=cast-align]
+ * 211 | h = (grub_mm_header_t) ((grub_uint8_t *) addr - q->post_size);
+ * | ^
+ * It is safe to do that because proper alignment is enforced in grub_mm_size_sanity_check().
+ */
+ h = (grub_mm_header_t)(void *) ((grub_uint8_t *) addr - q->post_size);
+ /* our size is the allocated size plus post_size, in cells */
+ h->size = (size + q->post_size) >> GRUB_MM_ALIGN_LOG2;
+ h->magic = GRUB_MM_ALLOC_MAGIC;
+ /* region size grows by block size converted back to bytes */
+ q->size += h->size << GRUB_MM_ALIGN_LOG2;
+ /* adjust new post_size to be accurate */
+ q->post_size = (q->post_size + size) & (GRUB_MM_ALIGN - 1);
+ grub_free (h + 1);
+ return;
+ }
+ }
/* Allocate a region from the head. */
r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
@@ -195,6 +230,7 @@ grub_mm_init_region (void *addr, grub_size_t size)
r->first = h;
r->pre_size = (grub_addr_t) r - (grub_addr_t) addr;
r->size = (h->size << GRUB_MM_ALIGN_LOG2);
+ r->post_size = size - r->size;
/* Find where to insert this region. Put a smaller one before bigger ones,
to prevent fragmentation. */
diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h
index a688b92a83..96c2d816be 100644
--- a/include/grub/mm_private.h
+++ b/include/grub/mm_private.h
@@ -81,8 +81,17 @@ typedef struct grub_mm_region
*/
grub_size_t pre_size;
+ /*
+ * Likewise, the post-size is the number of bytes we wasted at the end
+ * of the allocation because it wasn't a multiple of GRUB_MM_ALIGN
+ */
+ grub_size_t post_size;
+
/* How many bytes are in this region? (free and allocated) */
grub_size_t size;
+
+ /* pad to a multiple of cell size */
+ char padding[3 * GRUB_CPU_SIZEOF_VOID_P];
}
*grub_mm_region_t;

View File

@ -0,0 +1,72 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Thu, 21 Apr 2022 15:24:16 +1000
Subject: [PATCH] mm: Debug support for region operations
This is handy for debugging. Enable with "set debug=regions".
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Tested-by: Patrick Steinhardt <ps@pks.im>
(cherry picked from commit 8afa5ef45b797ba5d8147ceee85ac2c59dcc7f09)
(cherry picked from commit 4eb79abc52d35f40afc21e241561cf1dd8251a7b)
---
grub-core/kern/mm.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index 7be33e23bf..38bfb01df9 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -115,9 +115,8 @@ grub_mm_init_region (void *addr, grub_size_t size)
grub_mm_header_t h;
grub_mm_region_t r, *p, q;
-#if 0
- grub_printf ("Using memory for heap: start=%p, end=%p\n", addr, addr + (unsigned int) size);
-#endif
+ grub_dprintf ("regions", "Using memory for heap: start=%p, end=%p\n",
+ addr, (char *) addr + (unsigned int) size);
/* Exclude last 4K to avoid overflows. */
/* If addr + 0x1000 overflows then whole region is in excluded zone. */
@@ -142,8 +141,14 @@ grub_mm_init_region (void *addr, grub_size_t size)
* addr q
* |----size-----|-q->pre_size-|<q region>|
*/
+ grub_dprintf ("regions", "Can we extend into region above?"
+ " %p + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " ?=? %p\n",
+ (grub_uint8_t *) addr, size, q->pre_size, (grub_uint8_t *) q);
if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
{
+ grub_dprintf ("regions", "Yes: extending a region: (%p -> %p) -> (%p -> %p)\n",
+ q, (grub_uint8_t *) q + sizeof (*q) + q->size,
+ addr, (grub_uint8_t *) q + sizeof (*q) + q->size);
/*
* Yes, we can merge the memory starting at addr into the
* existing region from below. Align up addr to GRUB_MM_ALIGN
@@ -185,9 +190,15 @@ grub_mm_init_region (void *addr, grub_size_t size)
* q addr
* |<q region>|-q->post_size-|----size-----|
*/
+ grub_dprintf ("regions", "Can we extend into region below?"
+ " %p + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " ?=? %p\n",
+ (grub_uint8_t *) q, sizeof(*q), q->size, q->post_size, (grub_uint8_t *) addr);
if ((grub_uint8_t *) q + sizeof (*q) + q->size + q->post_size ==
(grub_uint8_t *) addr)
{
+ grub_dprintf ("regions", "Yes: extending a region: (%p -> %p) -> (%p -> %p)\n",
+ q, (grub_uint8_t *) q + sizeof (*q) + q->size,
+ q, (grub_uint8_t *) addr + size);
/*
* Yes! Follow a similar pattern to above, but simpler.
* Our header starts at address - post_size, which should align us
@@ -213,6 +224,8 @@ grub_mm_init_region (void *addr, grub_size_t size)
}
}
+ grub_dprintf ("regions", "No: considering a new region at %p of size %" PRIxGRUB_SIZE "\n",
+ addr, size);
/* Allocate a region from the head. */
r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);

View File

@ -0,0 +1,81 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Thu, 21 Apr 2022 15:24:17 +1000
Subject: [PATCH] mm: Drop unused unloading of modules on OOM
In grub_memalign(), there's a commented section which would allow for
unloading of unneeded modules in case where there is not enough free
memory available to satisfy a request. Given that this code is never
compiled in, let's remove it together with grub_dl_unload_unneeded().
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Tested-by: Patrick Steinhardt <ps@pks.im>
(cherry picked from commit 139fd9b134a01e0b5fe0ebefafa7f48d1ffb6d60)
(cherry picked from commit 81267ca5387486ede52e0462a71555bc6d181085)
---
grub-core/kern/dl.c | 20 --------------------
grub-core/kern/mm.c | 8 --------
include/grub/dl.h | 1 -
3 files changed, 29 deletions(-)
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
index d5de80186f..ab9101a5ad 100644
--- a/grub-core/kern/dl.c
+++ b/grub-core/kern/dl.c
@@ -998,23 +998,3 @@ grub_dl_unload (grub_dl_t mod)
grub_free (mod);
return 1;
}
-
-/* Unload unneeded modules. */
-void
-grub_dl_unload_unneeded (void)
-{
- /* Because grub_dl_remove modifies the list of modules, this
- implementation is tricky. */
- grub_dl_t p = grub_dl_head;
-
- while (p)
- {
- if (grub_dl_unload (p))
- {
- p = grub_dl_head;
- continue;
- }
-
- p = p->next;
- }
-}
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index 38bfb01df9..1825dc8289 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -444,14 +444,6 @@ grub_memalign (grub_size_t align, grub_size_t size)
count++;
goto again;
-#if 0
- case 1:
- /* Unload unneeded modules. */
- grub_dl_unload_unneeded ();
- count++;
- goto again;
-#endif
-
default:
break;
}
diff --git a/include/grub/dl.h b/include/grub/dl.h
index 45ac8e339f..6bc2560bf0 100644
--- a/include/grub/dl.h
+++ b/include/grub/dl.h
@@ -206,7 +206,6 @@ grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name);
grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size);
int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod);
-extern void grub_dl_unload_unneeded (void);
extern int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
extern int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
extern int EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod);

View File

@ -0,0 +1,131 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Thu, 21 Apr 2022 15:24:18 +1000
Subject: [PATCH] mm: Allow dynamically requesting additional memory regions
Currently, all platforms will set up their heap on initialization of the
platform code. While this works mostly fine, it poses some limitations
on memory management on us. Most notably, allocating big chunks of
memory in the gigabyte range would require us to pre-request this many
bytes from the firmware and add it to the heap from the beginning on
some platforms like EFI. As this isn't needed for most configurations,
it is inefficient and may even negatively impact some usecases when,
e.g., chainloading. Nonetheless, allocating big chunks of memory is
required sometimes, where one example is the upcoming support for the
Argon2 key derival function in LUKS2.
In order to avoid pre-allocating big chunks of memory, this commit
implements a runtime mechanism to add more pages to the system. When
a given allocation cannot be currently satisfied, we'll call a given
callback set up by the platform's own memory management subsystem,
asking it to add a memory area with at least "n" bytes. If this
succeeds, we retry searching for a valid memory region, which should
now succeed.
If this fails, we try asking for "n" bytes, possibly spread across
multiple regions, in hopes that region merging means that we end up
with enough memory for things to work out.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Tested-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Tested-by: Patrick Steinhardt <ps@pks.im>
(cherry picked from commit 887f98f0db43e33fba4ec1f85e42fae1185700bc)
(cherry picked from commit 8f3238a7dc482497e76b5ef6fbeb04f84d9a9aaa)
---
grub-core/kern/mm.c | 30 ++++++++++++++++++++++++++++++
include/grub/mm.h | 18 ++++++++++++++++++
2 files changed, 48 insertions(+)
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index 1825dc8289..f2e27f263b 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -28,6 +28,9 @@
- multiple regions may be used as free space. They may not be
contiguous.
+ - if existing regions are insufficient to satisfy an allocation, a new
+ region can be requested from firmware.
+
Regions are managed by a singly linked list, and the meta information is
stored in the beginning of each region. Space after the meta information
is used to allocate memory.
@@ -81,6 +84,7 @@
grub_mm_region_t grub_mm_base;
+grub_mm_add_region_func_t grub_mm_add_region_fn;
/* Get a header from the pointer PTR, and set *P and *R to a pointer
to the header and a pointer to its region, respectively. PTR must
@@ -444,6 +448,32 @@ grub_memalign (grub_size_t align, grub_size_t size)
count++;
goto again;
+ case 1:
+ /* Request additional pages, contiguous */
+ count++;
+
+ if (grub_mm_add_region_fn != NULL &&
+ grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_CONSECUTIVE) == GRUB_ERR_NONE)
+ goto again;
+
+ /* fallthrough */
+
+ case 2:
+ /* Request additional pages, anything at all */
+ count++;
+
+ if (grub_mm_add_region_fn != NULL)
+ {
+ /*
+ * Try again even if this fails, in case it was able to partially
+ * satisfy the request
+ */
+ grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_NONE);
+ goto again;
+ }
+
+ /* fallthrough */
+
default:
break;
}
diff --git a/include/grub/mm.h b/include/grub/mm.h
index d81623d226..7c6f925ffd 100644
--- a/include/grub/mm.h
+++ b/include/grub/mm.h
@@ -20,6 +20,7 @@
#ifndef GRUB_MM_H
#define GRUB_MM_H 1
+#include <grub/err.h>
#include <grub/types.h>
#include <grub/symbol.h>
#include <grub/err.h>
@@ -29,6 +30,23 @@
# define NULL ((void *) 0)
#endif
+#define GRUB_MM_ADD_REGION_NONE 0
+#define GRUB_MM_ADD_REGION_CONSECUTIVE (1 << 0)
+
+/*
+ * Function used to request memory regions of `grub_size_t` bytes. The second
+ * parameter is a bitfield of `GRUB_MM_ADD_REGION` flags.
+ */
+typedef grub_err_t (*grub_mm_add_region_func_t) (grub_size_t, unsigned int);
+
+/*
+ * Set this function pointer to enable adding memory-regions at runtime in case
+ * a memory allocation cannot be satisfied with existing regions.
+ */
+#ifndef GRUB_MACHINE_EMU
+extern grub_mm_add_region_func_t EXPORT_VAR(grub_mm_add_region_fn);
+#endif
+
void grub_mm_init_region (void *addr, grub_size_t size);
void *EXPORT_FUNC(grub_calloc) (grub_size_t nmemb, grub_size_t size);
void *EXPORT_FUNC(grub_malloc) (grub_size_t size);

View File

@ -0,0 +1,104 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Thu, 21 Apr 2022 15:24:19 +1000
Subject: [PATCH] kern/efi/mm: Always request a fixed number of pages on init
When initializing the EFI memory subsystem, we will by default request
a quarter of the available memory, bounded by a minimum/maximum value.
Given that we're about to extend the EFI memory system to dynamically
request additional pages from the firmware as required, this scaling of
requested memory based on available memory will not make a lot of sense
anymore.
Remove this logic as a preparatory patch such that we'll instead defer
to the runtime memory allocator. Note that ideally, we'd want to change
this after dynamic requesting of pages has been implemented for the EFI
platform. But because we'll need to split up initialization of the
memory subsystem and the request of pages from the firmware, we'd have
to duplicate quite some logic at first only to remove it afterwards
again. This seems quite pointless, so we instead have patches slightly
out of order.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Tested-by: Patrick Steinhardt <ps@pks.im>
(cherry picked from commit 938c3760b8c0fca759140be48307179b50107ff6)
(cherry picked from commit 90e793a2ef3bc35609cb1b605ebd1fae15dd4b2d)
---
grub-core/kern/efi/mm.c | 35 +++--------------------------------
1 file changed, 3 insertions(+), 32 deletions(-)
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
index 88364d764c..f574edabef 100644
--- a/grub-core/kern/efi/mm.c
+++ b/grub-core/kern/efi/mm.c
@@ -38,9 +38,8 @@
a multiplier of 4KB. */
#define MEMORY_MAP_SIZE 0x3000
-/* The minimum and maximum heap size for GRUB itself. */
-#define MIN_HEAP_SIZE 0x100000
-#define MAX_HEAP_SIZE (1600 * 0x100000)
+/* The default heap size for GRUB itself in bytes. */
+#define DEFAULT_HEAP_SIZE 0x100000
static void *finish_mmap_buf = 0;
static grub_efi_uintn_t finish_mmap_size = 0;
@@ -514,23 +513,6 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
return filtered_desc;
}
-/* Return the total number of pages. */
-static grub_efi_uint64_t
-get_total_pages (grub_efi_memory_descriptor_t *memory_map,
- grub_efi_uintn_t desc_size,
- grub_efi_memory_descriptor_t *memory_map_end)
-{
- grub_efi_memory_descriptor_t *desc;
- grub_efi_uint64_t total = 0;
-
- for (desc = memory_map;
- desc < memory_map_end;
- desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
- total += desc->num_pages;
-
- return total;
-}
-
/* Add memory regions. */
static void
add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
@@ -695,8 +677,6 @@ grub_efi_mm_init (void)
grub_efi_memory_descriptor_t *filtered_memory_map_end;
grub_efi_uintn_t map_size;
grub_efi_uintn_t desc_size;
- grub_efi_uint64_t total_pages;
- grub_efi_uint64_t required_pages;
int mm_status;
grub_nx_init ();
@@ -738,22 +718,13 @@ 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. */
- total_pages = get_total_pages (filtered_memory_map, desc_size,
- filtered_memory_map_end);
- required_pages = (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))
- required_pages = BYTES_TO_PAGES (MAX_HEAP_SIZE);
-
/* Sort the filtered descriptors, so that GRUB can allocate pages
from smaller regions. */
sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end);
/* Allocate memory regions for GRUB's memory management. */
add_memory_regions (filtered_memory_map, desc_size,
- filtered_memory_map_end, required_pages);
+ filtered_memory_map_end, BYTES_TO_PAGES (DEFAULT_HEAP_SIZE));
#if 0
/* For debug. */

View File

@ -0,0 +1,86 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Thu, 21 Apr 2022 15:24:20 +1000
Subject: [PATCH] kern/efi/mm: Extract function to add memory regions
In preparation of support for runtime-allocating additional memory
region, this patch extracts the function to retrieve the EFI memory
map and add a subset of it to GRUB's own memory regions.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Tested-by: Patrick Steinhardt <ps@pks.im>
(cherry picked from commit 96a7ea29e3cb61b6c2302e260e8e6a6117e17fa3)
[rharwood: backport around our nx]
(cherry picked from commit 4a85c48111f5e516cbc000bf4a7bc989b36d623c)
---
grub-core/kern/efi/mm.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
index f574edabef..9c9f9ac0c5 100644
--- a/grub-core/kern/efi/mm.c
+++ b/grub-core/kern/efi/mm.c
@@ -668,8 +668,8 @@ grub_nx_init (void)
}
}
-void
-grub_efi_mm_init (void)
+static grub_err_t
+grub_efi_mm_add_regions (grub_size_t required_bytes)
{
grub_efi_memory_descriptor_t *memory_map;
grub_efi_memory_descriptor_t *memory_map_end;
@@ -684,7 +684,7 @@ grub_efi_mm_init (void)
/* Prepare a memory region to store two memory maps. */
memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
if (! memory_map)
- grub_fatal ("cannot allocate memory");
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for memory map");
/* Obtain descriptors for available memory. */
map_size = MEMORY_MAP_SIZE;
@@ -702,14 +702,14 @@ grub_efi_mm_init (void)
memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (map_size));
if (! memory_map)
- grub_fatal ("cannot allocate memory");
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for new memory map");
mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0,
&desc_size, 0);
}
if (mm_status < 0)
- grub_fatal ("cannot get memory map");
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "error fetching memory map from EFI");
memory_map_end = NEXT_MEMORY_DESCRIPTOR (memory_map, map_size);
@@ -724,7 +724,7 @@ grub_efi_mm_init (void)
/* Allocate memory regions for GRUB's memory management. */
add_memory_regions (filtered_memory_map, desc_size,
- filtered_memory_map_end, BYTES_TO_PAGES (DEFAULT_HEAP_SIZE));
+ filtered_memory_map_end, BYTES_TO_PAGES (required_bytes));
#if 0
/* For debug. */
@@ -742,6 +742,15 @@ grub_efi_mm_init (void)
/* Release the memory maps. */
grub_efi_free_pages ((grub_addr_t) memory_map,
2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_efi_mm_init (void)
+{
+ if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE) != GRUB_ERR_NONE)
+ grub_fatal ("%s", grub_errmsg);
}
#if defined (__aarch64__) || defined (__arm__) || defined (__riscv)

View File

@ -0,0 +1,88 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Thu, 21 Apr 2022 15:24:21 +1000
Subject: [PATCH] kern/efi/mm: Pass up errors from add_memory_regions()
The function add_memory_regions() is currently only called on system
initialization to allocate a fixed amount of pages. As such, it didn't
need to return any errors: in case it failed, we cannot proceed anyway.
This will change with the upcoming support for requesting more memory
from the firmware at runtime, where it doesn't make sense anymore to
fail hard.
Refactor the function to return an error to prepare for this. Note that
this does not change the behaviour when initializing the memory system
because grub_efi_mm_init() knows to call grub_fatal() in case
grub_efi_mm_add_regions() returns an error.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Tested-by: Patrick Steinhardt <ps@pks.im>
(cherry picked from commit 15a015698921240adc1ac266a3b5bc5fcbd81521)
(cherry picked from commit c39acabc11d98ba18c6724eeb446508da7410c68)
---
grub-core/kern/efi/mm.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
index 9c9f9ac0c5..29cf178aa9 100644
--- a/grub-core/kern/efi/mm.c
+++ b/grub-core/kern/efi/mm.c
@@ -514,7 +514,7 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
}
/* Add memory regions. */
-static void
+static grub_err_t
add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
grub_efi_uintn_t desc_size,
grub_efi_memory_descriptor_t *memory_map_end,
@@ -542,9 +542,9 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
GRUB_EFI_ALLOCATE_ADDRESS,
GRUB_EFI_LOADER_CODE);
if (! addr)
- grub_fatal ("cannot allocate conventional memory %p with %u pages",
- (void *) ((grub_addr_t) start),
- (unsigned) pages);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Memory starting at %p (%u pages) marked as free, but EFI would not allocate",
+ (void *) ((grub_addr_t) start), (unsigned) pages);
grub_mm_init_region (addr, PAGES_TO_BYTES (pages));
@@ -554,7 +554,11 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
}
if (required_pages > 0)
- grub_fatal ("too little memory");
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate all requested memory: %" PRIuGRUB_UINT64_T " pages still required after iterating EFI memory map",
+ required_pages);
+
+ return GRUB_ERR_NONE;
}
void
@@ -677,6 +681,7 @@ grub_efi_mm_add_regions (grub_size_t required_bytes)
grub_efi_memory_descriptor_t *filtered_memory_map_end;
grub_efi_uintn_t map_size;
grub_efi_uintn_t desc_size;
+ grub_err_t err;
int mm_status;
grub_nx_init ();
@@ -723,8 +728,11 @@ grub_efi_mm_add_regions (grub_size_t required_bytes)
sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end);
/* Allocate memory regions for GRUB's memory management. */
- add_memory_regions (filtered_memory_map, desc_size,
- filtered_memory_map_end, BYTES_TO_PAGES (required_bytes));
+ err = add_memory_regions (filtered_memory_map, desc_size,
+ filtered_memory_map_end,
+ BYTES_TO_PAGES (required_bytes));
+ if (err != GRUB_ERR_NONE)
+ return err;
#if 0
/* For debug. */

View File

@ -0,0 +1,76 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Thu, 21 Apr 2022 15:24:22 +1000
Subject: [PATCH] kern/efi/mm: Implement runtime addition of pages
Adjust the interface of grub_efi_mm_add_regions() to take a set of
GRUB_MM_ADD_REGION_* flags, which most notably is currently only the
GRUB_MM_ADD_REGION_CONSECUTIVE flag. This allows us to set the function
up as callback for the memory subsystem and have it call out to us in
case there's not enough pages available in the current heap.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Tested-by: Patrick Steinhardt <ps@pks.im>
(cherry picked from commit 1df2934822df4c1170dde069d97cfbf7a9572bba)
(cherry picked from commit d2c744833aafcd84485e7b1faebaade5bfe7db7a)
---
grub-core/kern/efi/mm.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
index 29cf178aa9..fc7561bbf7 100644
--- a/grub-core/kern/efi/mm.c
+++ b/grub-core/kern/efi/mm.c
@@ -518,7 +518,8 @@ static grub_err_t
add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
grub_efi_uintn_t desc_size,
grub_efi_memory_descriptor_t *memory_map_end,
- grub_efi_uint64_t required_pages)
+ grub_efi_uint64_t required_pages,
+ unsigned int flags)
{
grub_efi_memory_descriptor_t *desc;
@@ -532,6 +533,10 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
start = desc->physical_start;
pages = desc->num_pages;
+
+ if (pages < required_pages && (flags & GRUB_MM_ADD_REGION_CONSECUTIVE))
+ continue;
+
if (pages > required_pages)
{
start += PAGES_TO_BYTES (pages - required_pages);
@@ -673,7 +678,7 @@ grub_nx_init (void)
}
static grub_err_t
-grub_efi_mm_add_regions (grub_size_t required_bytes)
+grub_efi_mm_add_regions (grub_size_t required_bytes, unsigned int flags)
{
grub_efi_memory_descriptor_t *memory_map;
grub_efi_memory_descriptor_t *memory_map_end;
@@ -730,7 +735,8 @@ grub_efi_mm_add_regions (grub_size_t required_bytes)
/* Allocate memory regions for GRUB's memory management. */
err = add_memory_regions (filtered_memory_map, desc_size,
filtered_memory_map_end,
- BYTES_TO_PAGES (required_bytes));
+ BYTES_TO_PAGES (required_bytes),
+ flags);
if (err != GRUB_ERR_NONE)
return err;
@@ -757,8 +763,9 @@ grub_efi_mm_add_regions (grub_size_t required_bytes)
void
grub_efi_mm_init (void)
{
- if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE) != GRUB_ERR_NONE)
+ if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE, GRUB_MM_ADD_REGION_NONE) != GRUB_ERR_NONE)
grub_fatal ("%s", grub_errmsg);
+ grub_mm_add_region_fn = grub_efi_mm_add_regions;
}
#if defined (__aarch64__) || defined (__arm__) || defined (__riscv)

View File

@ -0,0 +1,32 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Tue, 20 Sep 2022 00:30:30 +1000
Subject: [PATCH] efi: Increase default memory allocation to 32 MiB
We have multiple reports of things being slower with a 1 MiB initial static
allocation, and a report (more difficult to nail down) of a boot failure
as a result of the smaller initial allocation.
Make the initial memory allocation 32 MiB.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
(cherry picked from commit 75e38e86e7d9202f050b093f20500d9ad4c6dad9)
(cherry picked from commit 7cae9dcf90a9db0cdfae144356fe97e5391862e9)
---
grub-core/kern/efi/mm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
index fc7561bbf7..fde92ac9a5 100644
--- a/grub-core/kern/efi/mm.c
+++ b/grub-core/kern/efi/mm.c
@@ -39,7 +39,7 @@
#define MEMORY_MAP_SIZE 0x3000
/* The default heap size for GRUB itself in bytes. */
-#define DEFAULT_HEAP_SIZE 0x100000
+#define DEFAULT_HEAP_SIZE 0x2000000
static void *finish_mmap_buf = 0;
static grub_efi_uintn_t finish_mmap_size = 0;

View File

@ -0,0 +1,58 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zhang Boyang <zhangboyang.id@gmail.com>
Date: Sat, 15 Oct 2022 22:15:11 +0800
Subject: [PATCH] mm: Try invalidate disk caches last when out of memory
Every heap grow will cause all disk caches invalidated which decreases
performance severely. This patch moves disk cache invalidation code to
the last of memory squeezing measures. So, disk caches are released only
when there are no other ways to get free memory.
Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Reviewed-by: Patrick Steinhardt <ps@pks.im>
(cherry picked from commit 17975d10a80e2457e5237f87fa58a7943031983e)
(cherry picked from commit a048b2280ae6a2cf90fd5d2960823843e17e4d66)
---
grub-core/kern/mm.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index f2e27f263b..da1ac9427c 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -443,12 +443,6 @@ grub_memalign (grub_size_t align, grub_size_t size)
switch (count)
{
case 0:
- /* Invalidate disk caches. */
- grub_disk_cache_invalidate_all ();
- count++;
- goto again;
-
- case 1:
/* Request additional pages, contiguous */
count++;
@@ -458,7 +452,7 @@ grub_memalign (grub_size_t align, grub_size_t size)
/* fallthrough */
- case 2:
+ case 1:
/* Request additional pages, anything at all */
count++;
@@ -474,6 +468,12 @@ grub_memalign (grub_size_t align, grub_size_t size)
/* fallthrough */
+ case 2:
+ /* Invalidate disk caches. */
+ grub_disk_cache_invalidate_all ();
+ count++;
+ goto again;
+
default:
break;
}

View File

@ -277,32 +277,47 @@ Patch0276: 0276-nx-set-page-permissions-for-loaded-modules.patch
Patch0277: 0277-nx-set-attrs-in-our-kernel-loaders.patch
Patch0278: 0278-nx-set-the-nx-compatible-flag-in-EFI-grub-images.patch
Patch0279: 0279-Make-debug-file-show-which-file-filters-get-run.patch
Patch0280: 0280-efi-make-the-default-arena-most-of-ram.patch
Patch0281: 0281-efi-use-enumerated-array-positions-for-our-allocatio.patch
Patch0282: 0282-efi-split-allocation-policy-for-kernel-vs-initrd-mem.patch
Patch0283: 0283-efi-allocate-the-initrd-within-the-bounds-expressed-.patch
Patch0284: 0284-efi-use-EFI_LOADER_-CODE-DATA-for-kernel-and-initrd-.patch
Patch0285: 0285-BLS-create-etc-kernel-cmdline-during-mkconfig.patch
Patch0286: 0286-ieee1275-implement-vec5-for-cas-negotiation.patch
Patch0287: 0287-squish-don-t-dup-rhgb-quiet-check-mtimes.patch
Patch0288: 0288-squish-give-up-on-rhgb-quiet.patch
Patch0289: 0289-squish-BLS-only-write-etc-kernel-cmdline-if-writable.patch
Patch0290: 0290-x86-efi-Fix-an-incorrect-array-size-in-kernel-alloca.patch
Patch0291: 0291-commands-efi-tpm-Refine-the-status-of-log-event.patch
Patch0292: 0292-commands-efi-tpm-Use-grub_strcpy-instead-of-grub_mem.patch
Patch0293: 0293-efi-tpm-Add-EFI_CC_MEASUREMENT_PROTOCOL-support.patch
Patch0294: 0294-font-Reject-glyphs-exceeds-font-max_glyph_width-or-f.patch
Patch0295: 0295-font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch
Patch0296: 0296-font-Fix-several-integer-overflows-in-grub_font_cons.patch
Patch0297: 0297-font-Remove-grub_font_dup_glyph.patch
Patch0298: 0298-font-Fix-integer-overflow-in-ensure_comb_space.patch
Patch0299: 0299-font-Fix-integer-overflow-in-BMP-index.patch
Patch0300: 0300-font-Fix-integer-underflow-in-binary-search-of-char-.patch
Patch0301: 0301-kern-efi-sb-Enforce-verification-of-font-files.patch
Patch0302: 0302-fbutil-Fix-integer-overflow.patch
Patch0303: 0303-font-Fix-an-integer-underflow-in-blit_comb.patch
Patch0304: 0304-font-Harden-grub_font_blit_glyph-and-grub_font_blit_.patch
Patch0305: 0305-font-Assign-null_font-to-glyphs-in-ascii_font_glyph.patch
Patch0306: 0306-normal-charset-Fix-an-integer-overflow-in-grub_unico.patch
Patch0307: 0307-Correction-in-vector-5-values.patch
Patch0308: 0308-ppc64le-signed-boot-media-changes.patch
Patch0280: 0280-efi-use-enumerated-array-positions-for-our-allocatio.patch
Patch0281: 0281-efi-split-allocation-policy-for-kernel-vs-initrd-mem.patch
Patch0282: 0282-efi-allocate-the-initrd-within-the-bounds-expressed-.patch
Patch0283: 0283-efi-use-EFI_LOADER_-CODE-DATA-for-kernel-and-initrd-.patch
Patch0284: 0284-BLS-create-etc-kernel-cmdline-during-mkconfig.patch
Patch0285: 0285-ieee1275-implement-vec5-for-cas-negotiation.patch
Patch0286: 0286-squish-don-t-dup-rhgb-quiet-check-mtimes.patch
Patch0287: 0287-squish-give-up-on-rhgb-quiet.patch
Patch0288: 0288-squish-BLS-only-write-etc-kernel-cmdline-if-writable.patch
Patch0289: 0289-x86-efi-Fix-an-incorrect-array-size-in-kernel-alloca.patch
Patch0290: 0290-commands-efi-tpm-Refine-the-status-of-log-event.patch
Patch0291: 0291-commands-efi-tpm-Use-grub_strcpy-instead-of-grub_mem.patch
Patch0292: 0292-efi-tpm-Add-EFI_CC_MEASUREMENT_PROTOCOL-support.patch
Patch0293: 0293-font-Reject-glyphs-exceeds-font-max_glyph_width-or-f.patch
Patch0294: 0294-font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch
Patch0295: 0295-font-Fix-several-integer-overflows-in-grub_font_cons.patch
Patch0296: 0296-font-Remove-grub_font_dup_glyph.patch
Patch0297: 0297-font-Fix-integer-overflow-in-ensure_comb_space.patch
Patch0298: 0298-font-Fix-integer-overflow-in-BMP-index.patch
Patch0299: 0299-font-Fix-integer-underflow-in-binary-search-of-char-.patch
Patch0300: 0300-kern-efi-sb-Enforce-verification-of-font-files.patch
Patch0301: 0301-fbutil-Fix-integer-overflow.patch
Patch0302: 0302-font-Fix-an-integer-underflow-in-blit_comb.patch
Patch0303: 0303-font-Harden-grub_font_blit_glyph-and-grub_font_blit_.patch
Patch0304: 0304-font-Assign-null_font-to-glyphs-in-ascii_font_glyph.patch
Patch0305: 0305-normal-charset-Fix-an-integer-overflow-in-grub_unico.patch
Patch0306: 0306-Correction-in-vector-5-values.patch
Patch0307: 0307-ppc64le-signed-boot-media-changes.patch
Patch0308: 0308-mm-Clarify-grub_real_malloc.patch
Patch0309: 0309-mm-grub_real_malloc-Make-small-allocs-comment-match-.patch
Patch0310: 0310-mm-Document-grub_free.patch
Patch0311: 0311-mm-Document-grub_mm_init_region.patch
Patch0312: 0312-mm-Document-GRUB-internal-memory-management-structur.patch
Patch0313: 0313-mm-Assert-that-we-preserve-header-vs-region-alignmen.patch
Patch0314: 0314-mm-When-adding-a-region-merge-with-region-after-as-w.patch
Patch0315: 0315-mm-Debug-support-for-region-operations.patch
Patch0316: 0316-mm-Drop-unused-unloading-of-modules-on-OOM.patch
Patch0317: 0317-mm-Allow-dynamically-requesting-additional-memory-re.patch
Patch0318: 0318-kern-efi-mm-Always-request-a-fixed-number-of-pages-o.patch
Patch0319: 0319-kern-efi-mm-Extract-function-to-add-memory-regions.patch
Patch0320: 0320-kern-efi-mm-Pass-up-errors-from-add_memory_regions.patch
Patch0321: 0321-kern-efi-mm-Implement-runtime-addition-of-pages.patch
Patch0322: 0322-efi-Increase-default-memory-allocation-to-32-MiB.patch
Patch0323: 0323-mm-Try-invalidate-disk-caches-last-when-out-of-memor.patch

View File

@ -14,7 +14,7 @@
Name: grub2
Epoch: 1
Version: 2.06
Release: 56%{?dist}
Release: 57%{?dist}
Summary: Bootloader with support for Linux, Multiboot and more
License: GPLv3+
URL: http://www.gnu.org/software/grub/
@ -532,6 +532,10 @@ mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg
%endif
%changelog
* Wed Feb 01 2023 Robbie Harwood <rharwood@redhat.com> - 2.06-57
- Pull in allocator fixes from upstream
- Resolves: #2156419
* Tue Jan 31 2023 Robbie Harwood <rharwood@redhat.com> - 2.06-56
- ppc64le: disable mdraid < 1.1
- Resolves: #2143420