aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2022-04-21 15:24:18 +1000
committerDaniel Kiper <daniel.kiper@oracle.com>2022-07-04 14:25:41 +0200
commit887f98f0db43e33fba4ec1f85e42fae1185700bc (patch)
tree89fdfcea7073dd1bf596c5abfbc248ab0ac71c62
parent139fd9b134a01e0b5fe0ebefafa7f48d1ffb6d60 (diff)
downloadgrub-887f98f0db43e33fba4ec1f85e42fae1185700bc.tar.gz
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>
-rw-r--r--grub-core/kern/mm.c30
-rw-r--r--include/grub/mm.h18
2 files changed, 48 insertions, 0 deletions
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index e859772ab..75f6eacbe 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 44fde7cb9..f3bf87fa0 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 <config.h>
@@ -28,6 +29,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);