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