U-Boot Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] efi: Start tidying up memory management
@ 2024-07-25 13:56 Simon Glass
  2024-07-25 13:56 ` [PATCH 1/6] doc: Show more information in efi index Simon Glass
                   ` (6 more replies)
  0 siblings, 7 replies; 45+ messages in thread
From: Simon Glass @ 2024-07-25 13:56 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Sughosh Ganu, Tom Rini,
	Simon Glass, AKASHI Takahiro, Bin Meng, Joao Marcos Costa,
	Masahisa Kojima, Raymond Mao, Vincent Stehlé

We have been discussing the state of EFI memory management for some
years so I thought it might be best to send a short series showing some
of the issues we have talked about.

This one just deals with memory allocation. It provides a way to detect
EFI 'page allocations' when U-Boot' malloc() should be used. It should
help us to clean up this area of U-Boot.

It also updates EFI to use U-Boot memory allocation for the pool. Most
functions use that anyway and it is much more efficient. It also avoids
allocating things 'in space' in conflict with the loaded images.

This series also includes a little patch to show more information in
the index for the EFI pages.

Note that this series is independent from Sugosh's lmb series[1],
although I believe it will point the way to simplifying some of the
later patches in that series.

Overall, some issues which should be resolved are:
- allocation inconsistency, e.g. efi_add_protocol() uses malloc() but
  efi_dp_dup() does not (this series makes a start)
- change efi_bl_init() to register events later, when the devices are
  actually used
- rather than efi_set_bootdev(), provide a bootstd way to take note of
  the device images came from and allow EFI to query that when needed
- EFI_LOADER_BOUNCE_BUFFER - can use U-Boot bounce buffers?

Minor questions on my mind:
- is unaligned access useful? Is there a performance penalty?
- API confusion about whether something is an address or a pointer

[1] https://patchwork.ozlabs.org/project/uboot/list/?series=416441


Simon Glass (6):
  doc: Show more information in efi index
  efi: Convert device_path allocation to use malloc()
  efi: Allow monitoring of page allocations
  efi: Avoid pool allocation in efi_get_memory_map_alloc()
  efi: Use malloc() for the EFI pool
  efi: Show the location of the bounce buffer

 doc/develop/uefi/index.rst               |   2 +-
 include/efi_loader.h                     |  24 ++++
 lib/efi_loader/efi_bootbin.c             |  10 ++
 lib/efi_loader/efi_device_path.c         |  43 ++++---
 lib/efi_loader/efi_device_path_to_text.c |   2 +-
 lib/efi_loader/efi_memory.c              | 148 +++++++++++------------
 lib/efi_loader/efi_setup.c               |   7 ++
 7 files changed, 133 insertions(+), 103 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 1/6] doc: Show more information in efi index
  2024-07-25 13:56 [PATCH 0/6] efi: Start tidying up memory management Simon Glass
@ 2024-07-25 13:56 ` Simon Glass
  2024-07-25 16:02   ` Heinrich Schuchardt
  2024-07-25 13:56 ` [PATCH 2/6] efi: Convert device_path allocation to use malloc() Simon Glass
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-25 13:56 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Sughosh Ganu, Tom Rini,
	Simon Glass

The index is much more helpful if it shows the next level of headings.
Update tt.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 doc/develop/uefi/index.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/develop/uefi/index.rst b/doc/develop/uefi/index.rst
index e26b1fbe05c..44540e8a537 100644
--- a/doc/develop/uefi/index.rst
+++ b/doc/develop/uefi/index.rst
@@ -8,7 +8,7 @@ compliant software like Linux, GRUB, and iPXE. Furthermore U-Boot itself
 can be run an UEFI payload.
 
 .. toctree::
-   :maxdepth: 2
+   :maxdepth: 3
 
    uefi.rst
    u-boot_on_efi.rst
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 2/6] efi: Convert device_path allocation to use malloc()
  2024-07-25 13:56 [PATCH 0/6] efi: Start tidying up memory management Simon Glass
  2024-07-25 13:56 ` [PATCH 1/6] doc: Show more information in efi index Simon Glass
@ 2024-07-25 13:56 ` Simon Glass
  2024-07-25 16:13   ` Heinrich Schuchardt
  2024-07-25 13:56 ` [PATCH 3/6] efi: Allow monitoring of page allocations Simon Glass
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-25 13:56 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Sughosh Ganu, Tom Rini,
	Simon Glass, AKASHI Takahiro

Currently this calls efi_alloc() which reserves a page for each
allocation and this can overwrite memory that will be used for reading
images.

Switch the code to use malloc(), as with other parts of EFI, such as
efi_add_protocol().

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 lib/efi_loader/efi_device_path.c         | 43 ++++++++++++------------
 lib/efi_loader/efi_device_path_to_text.c |  2 +-
 2 files changed, 22 insertions(+), 23 deletions(-)

diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index 0f684590f22..47038e8e585 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -262,7 +262,7 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
 	if (!dp)
 		return NULL;
 
-	ndp = efi_alloc(sz);
+	ndp = malloc(sz);
 	if (!ndp)
 		return NULL;
 	memcpy(ndp, dp, sz);
@@ -315,7 +315,7 @@ efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
 			end_size = 2 * sizeof(END);
 		else
 			end_size = sizeof(END);
-		p = efi_alloc(sz1 + sz2 + end_size);
+		p = malloc(sz1 + sz2 + end_size);
 		if (!p)
 			return NULL;
 		ret = p;
@@ -347,7 +347,7 @@ struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
 		ret = efi_dp_dup(dp);
 	} else if (!dp) {
 		size_t sz = node->length;
-		void *p = efi_alloc(sz + sizeof(END));
+		void *p = malloc(sz + sizeof(END));
 		if (!p)
 			return NULL;
 		memcpy(p, node, sz);
@@ -356,7 +356,7 @@ struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
 	} else {
 		/* both dp and node are non-null */
 		size_t sz = efi_dp_size(dp);
-		void *p = efi_alloc(sz + node->length + sizeof(END));
+		void *p = malloc(sz + node->length + sizeof(END));
 		if (!p)
 			return NULL;
 		memcpy(p, dp, sz);
@@ -377,7 +377,7 @@ struct efi_device_path *efi_dp_create_device_node(const u8 type,
 	if (length < sizeof(struct efi_device_path))
 		return NULL;
 
-	ret = efi_alloc(length);
+	ret = malloc(length);
 	if (!ret)
 		return ret;
 	ret->type = type;
@@ -399,7 +399,7 @@ struct efi_device_path *efi_dp_append_instance(
 		return efi_dp_dup(dpi);
 	sz = efi_dp_size(dp);
 	szi = efi_dp_instance_size(dpi);
-	p = efi_alloc(sz + szi + 2 * sizeof(END));
+	p = malloc(sz + szi + 2 * sizeof(END));
 	if (!p)
 		return NULL;
 	ret = p;
@@ -424,7 +424,7 @@ struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp,
 	if (!dp || !*dp)
 		return NULL;
 	sz = efi_dp_instance_size(*dp);
-	p = efi_alloc(sz + sizeof(END));
+	p = malloc(sz + sizeof(END));
 	if (!p)
 		return NULL;
 	memcpy(p, *dp, sz + sizeof(END));
@@ -825,11 +825,11 @@ struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
 {
 	void *buf, *start;
 
-	start = buf = efi_alloc(dp_part_size(desc, part) + sizeof(END));
-	if (!buf)
+	start = malloc(dp_part_size(desc, part) + sizeof(END));
+	if (!start)
 		return NULL;
 
-	buf = dp_part_fill(buf, desc, part);
+	buf = dp_part_fill(start, desc, part);
 
 	*((struct efi_device_path *)buf) = END;
 
@@ -852,7 +852,7 @@ struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part)
 		dpsize = sizeof(struct efi_device_path_cdrom_path);
 	else
 		dpsize = sizeof(struct efi_device_path_hard_drive_path);
-	buf = efi_alloc(dpsize);
+	buf = malloc(dpsize);
 
 	if (buf)
 		dp_part_node(buf, desc, part);
@@ -912,7 +912,7 @@ struct efi_device_path *efi_dp_from_file(const struct efi_device_path *dp,
 	if (fpsize > U16_MAX)
 		return NULL;
 
-	buf = efi_alloc(dpsize + fpsize + sizeof(END));
+	buf = malloc(dpsize + fpsize + sizeof(END));
 	if (!buf)
 		return NULL;
 
@@ -940,7 +940,7 @@ struct efi_device_path *efi_dp_from_uart(void)
 	struct efi_device_path_uart *uart;
 	size_t dpsize = dp_size(dm_root()) + sizeof(*uart) + sizeof(END);
 
-	buf = efi_alloc(dpsize);
+	buf = malloc(dpsize);
 	if (!buf)
 		return NULL;
 	pos = dp_fill(buf, dm_root());
@@ -963,11 +963,11 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void)
 
 	dpsize += dp_size(eth_get_dev());
 
-	start = buf = efi_alloc(dpsize + sizeof(END));
-	if (!buf)
+	start = malloc(dpsize + sizeof(END));
+	if (!start)
 		return NULL;
 
-	buf = dp_fill(buf, eth_get_dev());
+	buf = dp_fill(start, eth_get_dev());
 
 	*((struct efi_device_path *)buf) = END;
 
@@ -980,22 +980,21 @@ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type,
 					uint64_t end_address)
 {
 	struct efi_device_path_memory *mdp;
-	void *buf, *start;
+	void *start;
 
-	start = buf = efi_alloc(sizeof(*mdp) + sizeof(END));
-	if (!buf)
+	start = malloc(sizeof(*mdp) + sizeof(END));
+	if (!start)
 		return NULL;
 
-	mdp = buf;
+	mdp = start;
 	mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
 	mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY;
 	mdp->dp.length = sizeof(*mdp);
 	mdp->memory_type = memory_type;
 	mdp->start_address = start_address;
 	mdp->end_address = end_address;
-	buf = &mdp[1];
 
-	*((struct efi_device_path *)buf) = END;
+	*((struct efi_device_path *)&mdp[1]) = END;
 
 	return start;
 }
diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c
index 0c7b30a26e7..4192581ac45 100644
--- a/lib/efi_loader/efi_device_path_to_text.c
+++ b/lib/efi_loader/efi_device_path_to_text.c
@@ -33,7 +33,7 @@ static u16 *efi_str_to_u16(char *str)
 	u16 *out, *dst;
 
 	len = sizeof(u16) * (utf8_utf16_strlen(str) + 1);
-	out = efi_alloc(len);
+	out = malloc(len);
 	if (!out)
 		return NULL;
 	dst = out;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 3/6] efi: Allow monitoring of page allocations
  2024-07-25 13:56 [PATCH 0/6] efi: Start tidying up memory management Simon Glass
  2024-07-25 13:56 ` [PATCH 1/6] doc: Show more information in efi index Simon Glass
  2024-07-25 13:56 ` [PATCH 2/6] efi: Convert device_path allocation to use malloc() Simon Glass
@ 2024-07-25 13:56 ` Simon Glass
  2024-07-25 16:20   ` Heinrich Schuchardt
  2024-07-25 13:56 ` [PATCH 4/6] efi: Avoid pool allocation in efi_get_memory_map_alloc() Simon Glass
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-25 13:56 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Sughosh Ganu, Tom Rini,
	Simon Glass, AKASHI Takahiro, Bin Meng, Joao Marcos Costa,
	Masahisa Kojima, Raymond Mao, Vincent Stehlé

Some confusion has set in over which memory-allocation method to use in
EFI-related code, particularly for the pool allocator. Most of the time,
malloc() is used, which is correct.

However in some cases the page allocator is used. This means that some
EFI information is sitting 'in space', outside of the malloc() and not
allocated by lmb. This can cause problems if an image happens to be
loaded at the same address.

From what I can tell, there is currently no checking of this, but it
seems to be a real bug.

Add a way to control whether allocations are permitted, to help debug
these sorts of issues.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 include/efi_loader.h         | 24 +++++++++++++++++++
 lib/efi_loader/efi_bootbin.c |  2 ++
 lib/efi_loader/efi_memory.c  | 46 ++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_setup.c   |  7 ++++++
 4 files changed, 79 insertions(+)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index f2e5063a970..187078adf55 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -797,6 +797,30 @@ int efi_disk_probe(void *ctx, struct event *event);
 int efi_disk_remove(void *ctx, struct event *event);
 /* Called by board init to initialize the EFI memory map */
 int efi_memory_init(void);
+
+/**
+ * enum efi_alloc_action - action to take when EFI does a page allocation
+ *
+ * @EFIAA_FAIL: Fail the allocation and print an error
+ * @EFIAA_WARN: Allow the allocation but print a warning
+ * @EFIAA_ALLOW: Allow the allocation with no message
+ */
+enum efi_alloc_action {
+	EFIAA_FAIL,
+	EFIAA_WARN,
+	EFIAA_ALLOW,
+};
+
+/**
+ * efi_set_alloc() - Set behaviour on page allocation
+ *
+ * This is useful for debugging use of the page allocator when malloc() should
+ * be used instead
+ *
+ * @allow: true to allow EFI to allocate pages, false to fail the allocation
+ */
+void efi_set_alloc(enum efi_alloc_action action);
+
 /* Adds new or overrides configuration table entry to the system table */
 efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table);
 /* Sets up a loaded image */
diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c
index a87006b3c0e..07c8fca68cc 100644
--- a/lib/efi_loader/efi_bootbin.c
+++ b/lib/efi_loader/efi_bootbin.c
@@ -201,6 +201,8 @@ efi_status_t efi_binary_run(void *image, size_t size, void *fdt)
 {
 	efi_status_t ret;
 
+	efi_set_alloc(EFIAA_ALLOW);
+
 	/* Initialize EFI drivers */
 	ret = efi_init_obj_list();
 	if (ret != EFI_SUCCESS) {
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
index 12cf23fa3fa..087f4c88cdf 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -24,6 +24,43 @@ DECLARE_GLOBAL_DATA_PTR;
 /* Magic number identifying memory allocated from pool */
 #define EFI_ALLOC_POOL_MAGIC 0x1fe67ddf6491caa2
 
+/*
+ * This is true if EFI is permitted to allocate pages in memory. When false,
+ * such allocations will fail. This is useful for debugging page allocations
+ * which should in fact use malloc().
+ *
+ * This defaults to EFIAA_FAIL until set.
+ */
+static enum efi_alloc_action alloc_action;
+
+void efi_set_alloc(enum efi_alloc_action action)
+{
+	alloc_action = action;
+}
+
+/**
+ * efi_bad_alloc() - Indicate that an allocation is not allowed
+ *
+ * Set a breakpoint on this function to locate the bad allocation in the call
+ * stack
+ */
+static void efi_bad_alloc(void)
+{
+	log_err("EFI: alloc not allowed\n");
+}
+
+static bool check_allowed(void)
+{
+	if (alloc_action == EFIAA_FAIL) {
+		efi_bad_alloc();
+		return false;
+	} else if (alloc_action == EFIAA_WARN) {
+		log_warning("EFI: suspicious alloc detected\n");
+	}
+
+	return true;
+}
+
 efi_uintn_t efi_memory_map_key;
 
 struct efi_mem_list {
@@ -501,6 +538,9 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type,
 	efi_status_t ret;
 	uint64_t addr;
 
+	if (!check_allowed())
+		return EFI_UNSUPPORTED;
+
 	/* Check import parameters */
 	if (memory_type >= EFI_PERSISTENT_MEMORY_TYPE &&
 	    memory_type <= 0x6FFFFFFF)
@@ -649,6 +689,9 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
 	u64 num_pages = efi_size_in_pages(size +
 					  sizeof(struct efi_pool_allocation));
 
+	if (!check_allowed())
+		return EFI_UNSUPPORTED;
+
 	if (!buffer)
 		return EFI_INVALID_PARAMETER;
 
@@ -952,6 +995,9 @@ int efi_memory_init(void)
 	/* Request a 32bit 64MB bounce buffer region */
 	uint64_t efi_bounce_buffer_addr = 0xffffffff;
 
+	/* this is the earliest page allocation, so allow it */
+	efi_set_alloc(EFIAA_ALLOW);
+
 	if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, EFI_BOOT_SERVICES_DATA,
 			       (64 * 1024 * 1024) >> EFI_PAGE_SHIFT,
 			       &efi_bounce_buffer_addr) != EFI_SUCCESS)
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index a610e032d2f..27e24c2f223 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -193,6 +193,13 @@ int efi_init_early(void)
 	/* Allow unaligned memory access */
 	allow_unaligned();
 
+	/*
+	 * For now, allow EFI to allocate memory outside the malloc() region.
+	 * Once these bugs are fixed, this can be changed to EFIAA_FAIL, with
+	 * the allocations being allowed only when the EFI system is booting.
+	 */
+	efi_set_alloc(EFIAA_ALLOW);
+
 	/* Initialize root node */
 	ret = efi_root_node_register();
 	if (ret != EFI_SUCCESS)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 4/6] efi: Avoid pool allocation in efi_get_memory_map_alloc()
  2024-07-25 13:56 [PATCH 0/6] efi: Start tidying up memory management Simon Glass
                   ` (2 preceding siblings ...)
  2024-07-25 13:56 ` [PATCH 3/6] efi: Allow monitoring of page allocations Simon Glass
@ 2024-07-25 13:56 ` Simon Glass
  2024-07-25 16:38   ` Heinrich Schuchardt
  2024-07-25 13:56 ` [PATCH 5/6] efi: Use malloc() for the EFI pool Simon Glass
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-25 13:56 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Sughosh Ganu, Tom Rini,
	Simon Glass

This function returns the memory map, allocating memory for it. But it
can just use malloc() directly, rather than calling the pool allocator.
Update it.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 lib/efi_loader/efi_memory.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
index 087f4c88cdf..2945f5648c7 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -850,14 +850,13 @@ efi_status_t efi_get_memory_map_alloc(efi_uintn_t *map_size,
 	ret = efi_get_memory_map(map_size, *memory_map, NULL, NULL, NULL);
 	if (ret == EFI_BUFFER_TOO_SMALL) {
 		*map_size += sizeof(struct efi_mem_desc); /* for the map */
-		ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, *map_size,
-					(void **)memory_map);
-		if (ret != EFI_SUCCESS)
-			return ret;
+		*memory_map = malloc(*map_size);
+		if (!*memory_map)
+			return EFI_OUT_OF_RESOURCES;
 		ret = efi_get_memory_map(map_size, *memory_map,
 					 NULL, NULL, NULL);
 		if (ret != EFI_SUCCESS) {
-			efi_free_pool(*memory_map);
+			free(*memory_map);
 			*memory_map = NULL;
 		}
 	}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-25 13:56 [PATCH 0/6] efi: Start tidying up memory management Simon Glass
                   ` (3 preceding siblings ...)
  2024-07-25 13:56 ` [PATCH 4/6] efi: Avoid pool allocation in efi_get_memory_map_alloc() Simon Glass
@ 2024-07-25 13:56 ` Simon Glass
  2024-07-25 15:49   ` Heinrich Schuchardt
  2024-07-25 13:56 ` [PATCH 6/6] efi: Show the location of the bounce buffer Simon Glass
  2024-07-25 14:25 ` [PATCH 0/6] efi: Start tidying up memory management Ilias Apalodimas
  6 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-25 13:56 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Sughosh Ganu, Tom Rini,
	Simon Glass

This API call is intended for allocating small amounts of memory,
similar to malloc(). The current implementation rounds up to whole pages
which can waste large amounts of memory. It also implements its own
malloc()-style header on each block.

Use U-Boot's built-in malloc() instead, to avoid these problems:

- it should normally be large enough for pool allocations
- if it isn't we can enforce a minimum size for boards which use
  EFI_LOADER
- the existing mechanism may create an unwatned entry in the memory map
- it is used for most EFI allocations already

One side effect is that this seems to be showing up some bugs in the
EFI code, since the malloc() pool becomes corrupted with some tests.
This has likely crept in due to the very large gaps between allocations
(around 4KB), which provides a lot of leeway when the allocation size is
too small. Work around this by increasing the size for now, until these
(presumed) bugs are located.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
 1 file changed, 19 insertions(+), 74 deletions(-)

diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
index 2945f5648c7..fabe9e3a87a 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
 void *efi_bounce_buffer;
 #endif
 
-/**
- * struct efi_pool_allocation - memory block allocated from pool
- *
- * @num_pages:	number of pages allocated
- * @checksum:	checksum
- * @data:	allocated pool memory
- *
- * U-Boot services each UEFI AllocatePool() request as a separate
- * (multiple) page allocation. We have to track the number of pages
- * to be able to free the correct amount later.
- *
- * The checksum calculated in function checksum() is used in FreePool() to avoid
- * freeing memory not allocated by AllocatePool() and duplicate freeing.
- *
- * EFI requires 8 byte alignment for pool allocations, so we can
- * prepend each allocation with these header fields.
- */
-struct efi_pool_allocation {
-	u64 num_pages;
-	u64 checksum;
-	char data[] __aligned(ARCH_DMA_MINALIGN);
-};
-
-/**
- * checksum() - calculate checksum for memory allocated from pool
- *
- * @alloc:	allocation header
- * Return:	checksum, always non-zero
- */
-static u64 checksum(struct efi_pool_allocation *alloc)
-{
-	u64 addr = (uintptr_t)alloc;
-	u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
-		  EFI_ALLOC_POOL_MAGIC;
-	if (!ret)
-		++ret;
-	return ret;
-}
-
 /**
  * efi_mem_cmp() - comparator function for sorting memory map
  *
@@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
  * @buffer:	allocated memory
  * Return:	status code
  */
-efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
+efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
+			       void **buffer)
 {
-	efi_status_t r;
-	u64 addr;
-	struct efi_pool_allocation *alloc;
-	u64 num_pages = efi_size_in_pages(size +
-					  sizeof(struct efi_pool_allocation));
+	void *ptr;
 
 	if (!check_allowed())
 		return EFI_UNSUPPORTED;
@@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
 		return EFI_SUCCESS;
 	}
 
-	r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
-			       &addr);
-	if (r == EFI_SUCCESS) {
-		alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
-		alloc->num_pages = num_pages;
-		alloc->checksum = checksum(alloc);
-		*buffer = alloc->data;
-	}
+	/*
+	 * Some tests crash on qemu_arm etc. if the correct size is allocated.
+	 * Adding 0x10 seems to fix test_efi_selftest_device_tree
+	 * Increasing it to 0x20 seems to fix test_efi_selftest_base except
+	 * for riscv64 (in CI only). But 0x100 fixes CI too.
+	 *
+	 * This workaround can be dropped once these problems are resolved
+	 */
+	ptr = memalign(8, size + 0x100);
+	if (!ptr)
+		return EFI_OUT_OF_RESOURCES;
+
+	*buffer = ptr;
 
-	return r;
+	return EFI_SUCCESS;
 }
 
 /**
@@ -742,30 +705,12 @@ void *efi_alloc(size_t size)
  */
 efi_status_t efi_free_pool(void *buffer)
 {
-	efi_status_t ret;
-	struct efi_pool_allocation *alloc;
-
 	if (!buffer)
 		return EFI_INVALID_PARAMETER;
 
-	ret = efi_check_allocated((uintptr_t)buffer, true);
-	if (ret != EFI_SUCCESS)
-		return ret;
-
-	alloc = container_of(buffer, struct efi_pool_allocation, data);
-
-	/* Check that this memory was allocated by efi_allocate_pool() */
-	if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
-	    alloc->checksum != checksum(alloc)) {
-		printf("%s: illegal free 0x%p\n", __func__, buffer);
-		return EFI_INVALID_PARAMETER;
-	}
-	/* Avoid double free */
-	alloc->checksum = 0;
-
-	ret = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
+	free(buffer);
 
-	return ret;
+	return EFI_SUCCESS;
 }
 
 /**
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 6/6] efi: Show the location of the bounce buffer
  2024-07-25 13:56 [PATCH 0/6] efi: Start tidying up memory management Simon Glass
                   ` (4 preceding siblings ...)
  2024-07-25 13:56 ` [PATCH 5/6] efi: Use malloc() for the EFI pool Simon Glass
@ 2024-07-25 13:56 ` Simon Glass
  2024-07-25 16:31   ` Heinrich Schuchardt
  2024-07-25 14:25 ` [PATCH 0/6] efi: Start tidying up memory management Ilias Apalodimas
  6 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-25 13:56 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Sughosh Ganu, Tom Rini,
	Simon Glass, AKASHI Takahiro

The EFI_LOADER_BOUNCE_BUFFER feature was added many years ago. It is not
clear whether it is still needed, but 24 boards (lx2160ardb_tfa_stmm,
lx2162aqds_tfa_SECURE_BOOT and the like) use it.

This feature uses EFI page allocation to create a 64MB buffer 'in space'
without any knowledge of where boards intend to load their images. This
may result in image corruption or other problems.

For example, if the feature is enabled on qemu_arm64 it puts the EFI
bounce buffer at 1045MB, with the kernel at 1028MB and the ramdisk at
1088MB. The kernel is probably smaller than 27MB but the buffer does
overlap the ramdisk.

The solution is probably to use BOUNCE_BUFFER instead, with the EFI
version being dropped. For now, show the address of the EFI bounce
buffer so people have a better chance to detect the problem.

Note: I avoided converting this #ifdef to use IS_ENABLED() since I hope
that the feature may be removed.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 lib/efi_loader/efi_bootbin.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c
index 07c8fca68cc..53e5e429d2e 100644
--- a/lib/efi_loader/efi_bootbin.c
+++ b/lib/efi_loader/efi_bootbin.c
@@ -211,6 +211,14 @@ efi_status_t efi_binary_run(void *image, size_t size, void *fdt)
 		return -1;
 	}
 
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+	/*
+	 * Add a warning about this buffer, since it may conflict with other
+	 * things
+	 */
+	printf("EFI bounce buffer at %p\n", efi_bounce_buffer);
+#endif
+
 	ret = efi_install_fdt(fdt);
 	if (ret != EFI_SUCCESS)
 		return ret;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* Re: [PATCH 0/6] efi: Start tidying up memory management
  2024-07-25 13:56 [PATCH 0/6] efi: Start tidying up memory management Simon Glass
                   ` (5 preceding siblings ...)
  2024-07-25 13:56 ` [PATCH 6/6] efi: Show the location of the bounce buffer Simon Glass
@ 2024-07-25 14:25 ` Ilias Apalodimas
  2024-07-25 15:58   ` Simon Glass
  6 siblings, 1 reply; 45+ messages in thread
From: Ilias Apalodimas @ 2024-07-25 14:25 UTC (permalink / raw)
  To: Simon Glass
  Cc: U-Boot Mailing List, Heinrich Schuchardt, Sughosh Ganu, Tom Rini,
	AKASHI Takahiro, Bin Meng, Joao Marcos Costa, Masahisa Kojima,
	Raymond Mao, Vincent Stehlé

Hi Simon

On Thu, 25 Jul 2024 at 16:58, Simon Glass <sjg@chromium.org> wrote:
>
> We have been discussing the state of EFI memory management for some
> years so I thought it might be best to send a short series showing some
> of the issues we have talked about.
>
> This one just deals with memory allocation. It provides a way to detect
> EFI 'page allocations' when U-Boot' malloc() should be used. It should
> help us to clean up this area of U-Boot.
>
> It also updates EFI to use U-Boot memory allocation for the pool. Most
> functions use that anyway and it is much more efficient. It also avoids
> allocating things 'in space' in conflict with the loaded images.
>
> This series also includes a little patch to show more information in
> the index for the EFI pages.
>
> Note that this series is independent from Sugosh's lmb series[1],
> although I believe it will point the way to simplifying some of the
> later patches in that series.
>
> Overall, some issues which should be resolved are:
> - allocation inconsistency, e.g. efi_add_protocol() uses malloc() but
>   efi_dp_dup() does not (this series makes a start)

The EFI memory allocations and mappings are described in the EFI spec.
Look at chapter 7.2 Memory Allocation Services. This patchset breaks
various aspects of the spec, since the metadata needed for the EFI
memory map is not kept from malloc. The reason efi_add_protocol() uses
calloc, is that this is memory used from our internal implementation
of the protocol linked list -- IOW how to search and identify
protocols that we add in a list and not UEFI boot or runtime services.

Thanks
/Ilias


> - change efi_bl_init() to register events later, when the devices are
>   actually used
> - rather than efi_set_bootdev(), provide a bootstd way to take note of
>   the device images came from and allow EFI to query that when needed
> - EFI_LOADER_BOUNCE_BUFFER - can use U-Boot bounce buffers?
>
> Minor questions on my mind:
> - is unaligned access useful? Is there a performance penalty?
> - API confusion about whether something is an address or a pointer
>
> [1] https://patchwork.ozlabs.org/project/uboot/list/?series=416441
>
>
> Simon Glass (6):
>   doc: Show more information in efi index
>   efi: Convert device_path allocation to use malloc()
>   efi: Allow monitoring of page allocations
>   efi: Avoid pool allocation in efi_get_memory_map_alloc()
>   efi: Use malloc() for the EFI pool
>   efi: Show the location of the bounce buffer
>
>  doc/develop/uefi/index.rst               |   2 +-
>  include/efi_loader.h                     |  24 ++++
>  lib/efi_loader/efi_bootbin.c             |  10 ++
>  lib/efi_loader/efi_device_path.c         |  43 ++++---
>  lib/efi_loader/efi_device_path_to_text.c |   2 +-
>  lib/efi_loader/efi_memory.c              | 148 +++++++++++------------
>  lib/efi_loader/efi_setup.c               |   7 ++
>  7 files changed, 133 insertions(+), 103 deletions(-)
>
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-25 13:56 ` [PATCH 5/6] efi: Use malloc() for the EFI pool Simon Glass
@ 2024-07-25 15:49   ` Heinrich Schuchardt
  2024-07-25 23:32     ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2024-07-25 15:49 UTC (permalink / raw)
  To: Simon Glass; +Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, U-Boot Mailing List

On 25.07.24 15:56, Simon Glass wrote:
> This API call is intended for allocating small amounts of memory,
> similar to malloc(). The current implementation rounds up to whole pages
> which can waste large amounts of memory. It also implements its own
> malloc()-style header on each block.
>
> Use U-Boot's built-in malloc() instead, to avoid these problems:
>
> - it should normally be large enough for pool allocations
> - if it isn't we can enforce a minimum size for boards which use
>    EFI_LOADER
> - the existing mechanism may create an unwatned entry in the memory map
> - it is used for most EFI allocations already
>
> One side effect is that this seems to be showing up some bugs in the
> EFI code, since the malloc() pool becomes corrupted with some tests.
> This has likely crept in due to the very large gaps between allocations
> (around 4KB), which provides a lot of leeway when the allocation size is
> too small. Work around this by increasing the size for now, until these
> (presumed) bugs are located.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
>   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
>   1 file changed, 19 insertions(+), 74 deletions(-)
>
> diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> index 2945f5648c7..fabe9e3a87a 100644
> --- a/lib/efi_loader/efi_memory.c
> +++ b/lib/efi_loader/efi_memory.c
> @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
>   void *efi_bounce_buffer;
>   #endif
>
> -/**
> - * struct efi_pool_allocation - memory block allocated from pool
> - *
> - * @num_pages:	number of pages allocated
> - * @checksum:	checksum
> - * @data:	allocated pool memory
> - *
> - * U-Boot services each UEFI AllocatePool() request as a separate
> - * (multiple) page allocation. We have to track the number of pages
> - * to be able to free the correct amount later.
> - *
> - * The checksum calculated in function checksum() is used in FreePool() to avoid
> - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> - *
> - * EFI requires 8 byte alignment for pool allocations, so we can
> - * prepend each allocation with these header fields.
> - */
> -struct efi_pool_allocation {
> -	u64 num_pages;
> -	u64 checksum;
> -	char data[] __aligned(ARCH_DMA_MINALIGN);
> -};
> -
> -/**
> - * checksum() - calculate checksum for memory allocated from pool
> - *
> - * @alloc:	allocation header
> - * Return:	checksum, always non-zero
> - */
> -static u64 checksum(struct efi_pool_allocation *alloc)
> -{
> -	u64 addr = (uintptr_t)alloc;
> -	u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> -		  EFI_ALLOC_POOL_MAGIC;
> -	if (!ret)
> -		++ret;
> -	return ret;
> -}
> -
>   /**
>    * efi_mem_cmp() - comparator function for sorting memory map
>    *
> @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
>    * @buffer:	allocated memory
>    * Return:	status code
>    */
> -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> +			       void **buffer)
>   {
> -	efi_status_t r;
> -	u64 addr;
> -	struct efi_pool_allocation *alloc;
> -	u64 num_pages = efi_size_in_pages(size +
> -					  sizeof(struct efi_pool_allocation));
> +	void *ptr;
>
>   	if (!check_allowed())
>   		return EFI_UNSUPPORTED;
> @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
>   		return EFI_SUCCESS;
>   	}

Unfortunately this patch does not match the UEFI specification:

Two different memory types cannot reside in the same memory page. You
have to keep track of the memory type of each allocated memory page and
report it in GetMemoryMap().

The specification is available https://uefi.org/specifications.

>
> -	r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
> -			       &addr);
> -	if (r == EFI_SUCCESS) {
> -		alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
> -		alloc->num_pages = num_pages;
> -		alloc->checksum = checksum(alloc);
> -		*buffer = alloc->data;
> -	}
> +	/*
> +	 * Some tests crash on qemu_arm etc. if the correct size is allocated.
> +	 * Adding 0x10 seems to fix test_efi_selftest_device_tree
> +	 * Increasing it to 0x20 seems to fix test_efi_selftest_base except
> +	 * for riscv64 (in CI only). But 0x100 fixes CI too.
> +	 *
> +	 * This workaround can be dropped once these problems are resolved
> +	 */
> +	ptr = memalign(8, size + 0x100);

We were using ARCH_DMA_MINALIGN before this patch.

If we are overwriting allocated memory, we must fix that instead of
increasing size. Do you have a git tag showing the problem?

Best regards

Heinrich

> +	if (!ptr)
> +		return EFI_OUT_OF_RESOURCES;
> +
> +	*buffer = ptr;
>
> -	return r;
> +	return EFI_SUCCESS;
>   }
>
>   /**
> @@ -742,30 +705,12 @@ void *efi_alloc(size_t size)
>    */
>   efi_status_t efi_free_pool(void *buffer)
>   {
> -	efi_status_t ret;
> -	struct efi_pool_allocation *alloc;
> -
>   	if (!buffer)
>   		return EFI_INVALID_PARAMETER;
>
> -	ret = efi_check_allocated((uintptr_t)buffer, true);
> -	if (ret != EFI_SUCCESS)
> -		return ret;
> -
> -	alloc = container_of(buffer, struct efi_pool_allocation, data);
> -
> -	/* Check that this memory was allocated by efi_allocate_pool() */
> -	if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
> -	    alloc->checksum != checksum(alloc)) {
> -		printf("%s: illegal free 0x%p\n", __func__, buffer);
> -		return EFI_INVALID_PARAMETER;
> -	}
> -	/* Avoid double free */
> -	alloc->checksum = 0;
> -
> -	ret = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
> +	free(buffer);
>
> -	return ret;
> +	return EFI_SUCCESS;
>   }
>
>   /**


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 0/6] efi: Start tidying up memory management
  2024-07-25 14:25 ` [PATCH 0/6] efi: Start tidying up memory management Ilias Apalodimas
@ 2024-07-25 15:58   ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2024-07-25 15:58 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: U-Boot Mailing List, Heinrich Schuchardt, Sughosh Ganu, Tom Rini,
	AKASHI Takahiro, Bin Meng, Joao Marcos Costa, Masahisa Kojima,
	Raymond Mao, Vincent Stehlé

Hi Ilias,

On Thu, Jul 25, 2024, 08:26 Ilias Apalodimas <ilias.apalodimas@linaro.org>
wrote:

> Hi Simon
>
> On Thu, 25 Jul 2024 at 16:58, Simon Glass <sjg@chromium.org> wrote:
> >
> > We have been discussing the state of EFI memory management for some
> > years so I thought it might be best to send a short series showing some
> > of the issues we have talked about.
> >
> > This one just deals with memory allocation. It provides a way to detect
> > EFI 'page allocations' when U-Boot' malloc() should be used. It should
> > help us to clean up this area of U-Boot.
> >
> > It also updates EFI to use U-Boot memory allocation for the pool. Most
> > functions use that anyway and it is much more efficient. It also avoids
> > allocating things 'in space' in conflict with the loaded images.
> >
> > This series also includes a little patch to show more information in
> > the index for the EFI pages.
> >
> > Note that this series is independent from Sugosh's lmb series[1],
> > although I believe it will point the way to simplifying some of the
> > later patches in that series.
> >
> > Overall, some issues which should be resolved are:
> > - allocation inconsistency, e.g. efi_add_protocol() uses malloc() but
> >   efi_dp_dup() does not (this series makes a start)
>
> The EFI memory allocations and mappings are described in the EFI spec.
> Look at chapter 7.2 Memory Allocation Services. This patchset breaks
> various aspects of the spec, since the metadata needed for the EFI
> memory map is not kept from malloc. The reason efi_add_protocol() uses
> calloc, is that this is memory used from our internal implementation
> of the protocol linked list -- IOW how to search and identify
> protocols that we add in a list and not UEFI boot or runtime services.


Er, yes, I am aware of that chapter:-)

Can you please be more specific about which various aspects this will break?

Also please let me know if you have comments on any of the patches.

Regards,
Simon


> Thanks
> /Ilias
>
>
> > - change efi_bl_init() to register events later, when the devices are
> >   actually used
> > - rather than efi_set_bootdev(), provide a bootstd way to take note of
> >   the device images came from and allow EFI to query that when needed
> > - EFI_LOADER_BOUNCE_BUFFER - can use U-Boot bounce buffers?
> >
> > Minor questions on my mind:
> > - is unaligned access useful? Is there a performance penalty?
> > - API confusion about whether something is an address or a pointer
> >
> > [1] https://patchwork.ozlabs.org/project/uboot/list/?series=416441
> >
> >
> > Simon Glass (6):
> >   doc: Show more information in efi index
> >   efi: Convert device_path allocation to use malloc()
> >   efi: Allow monitoring of page allocations
> >   efi: Avoid pool allocation in efi_get_memory_map_alloc()
> >   efi: Use malloc() for the EFI pool
> >   efi: Show the location of the bounce buffer
> >
> >  doc/develop/uefi/index.rst               |   2 +-
> >  include/efi_loader.h                     |  24 ++++
> >  lib/efi_loader/efi_bootbin.c             |  10 ++
> >  lib/efi_loader/efi_device_path.c         |  43 ++++---
> >  lib/efi_loader/efi_device_path_to_text.c |   2 +-
> >  lib/efi_loader/efi_memory.c              | 148 +++++++++++------------
> >  lib/efi_loader/efi_setup.c               |   7 ++
> >  7 files changed, 133 insertions(+), 103 deletions(-)
> >
> > --
> > 2.34.1
> >
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 1/6] doc: Show more information in efi index
  2024-07-25 13:56 ` [PATCH 1/6] doc: Show more information in efi index Simon Glass
@ 2024-07-25 16:02   ` Heinrich Schuchardt
  0 siblings, 0 replies; 45+ messages in thread
From: Heinrich Schuchardt @ 2024-07-25 16:02 UTC (permalink / raw)
  To: Simon Glass; +Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, U-Boot Mailing List

On 25.07.24 15:56, Simon Glass wrote:
> The index is much more helpful if it shows the next level of headings.
> Update tt.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
>   doc/develop/uefi/index.rst | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/doc/develop/uefi/index.rst b/doc/develop/uefi/index.rst
> index e26b1fbe05c..44540e8a537 100644
> --- a/doc/develop/uefi/index.rst
> +++ b/doc/develop/uefi/index.rst
> @@ -8,7 +8,7 @@ compliant software like Linux, GRUB, and iPXE. Furthermore U-Boot itself
>   can be run an UEFI payload.
>
>   .. toctree::
> -   :maxdepth: 2
> +   :maxdepth: 3
>
>      uefi.rst
>      u-boot_on_efi.rst

What we really need to do is moving usage documentation like

* Executing a UEFI binary
* Launching a UEFI binary from a FIT image
* Run on VirtualBox (x86_64)

to pages in /usage instead of having these as sections in uefi.rst and
u-boot_on_efi.rst.

Best regards

Heinrich

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 2/6] efi: Convert device_path allocation to use malloc()
  2024-07-25 13:56 ` [PATCH 2/6] efi: Convert device_path allocation to use malloc() Simon Glass
@ 2024-07-25 16:13   ` Heinrich Schuchardt
  2024-07-25 23:32     ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2024-07-25 16:13 UTC (permalink / raw)
  To: Simon Glass
  Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, AKASHI Takahiro,
	U-Boot Mailing List

On 25.07.24 15:56, Simon Glass wrote:
> Currently this calls efi_alloc() which reserves a page for each
> allocation and this can overwrite memory that will be used for reading
> images.

We already agreed to integrate LMB and UEFI memory management to avoid
this sort of problem.

Please, have a look at chapter 10.5, "Device Path Utilities Protocol" of
the UEFI specification.

EFI_DEVICE_PATH_UTILITIES_PROTOCOL.DuplicateDevicePath() must return
memory that an EFI application can release by calling
EFI_BOOT_SERVICES.FreePool().

Your patch does not comply to the specification.

Best regards

Heinrich

>
> Switch the code to use malloc(), as with other parts of EFI, such as
> efi_add_protocol().
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
>   lib/efi_loader/efi_device_path.c         | 43 ++++++++++++------------
>   lib/efi_loader/efi_device_path_to_text.c |  2 +-
>   2 files changed, 22 insertions(+), 23 deletions(-)
>
> diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
> index 0f684590f22..47038e8e585 100644
> --- a/lib/efi_loader/efi_device_path.c
> +++ b/lib/efi_loader/efi_device_path.c
> @@ -262,7 +262,7 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
>   	if (!dp)
>   		return NULL;
>
> -	ndp = efi_alloc(sz);
> +	ndp = malloc(sz);
>   	if (!ndp)
>   		return NULL;
>   	memcpy(ndp, dp, sz);
> @@ -315,7 +315,7 @@ efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
>   			end_size = 2 * sizeof(END);
>   		else
>   			end_size = sizeof(END);
> -		p = efi_alloc(sz1 + sz2 + end_size);
> +		p = malloc(sz1 + sz2 + end_size);
>   		if (!p)
>   			return NULL;
>   		ret = p;
> @@ -347,7 +347,7 @@ struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
>   		ret = efi_dp_dup(dp);
>   	} else if (!dp) {
>   		size_t sz = node->length;
> -		void *p = efi_alloc(sz + sizeof(END));
> +		void *p = malloc(sz + sizeof(END));
>   		if (!p)
>   			return NULL;
>   		memcpy(p, node, sz);
> @@ -356,7 +356,7 @@ struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
>   	} else {
>   		/* both dp and node are non-null */
>   		size_t sz = efi_dp_size(dp);
> -		void *p = efi_alloc(sz + node->length + sizeof(END));
> +		void *p = malloc(sz + node->length + sizeof(END));
>   		if (!p)
>   			return NULL;
>   		memcpy(p, dp, sz);
> @@ -377,7 +377,7 @@ struct efi_device_path *efi_dp_create_device_node(const u8 type,
>   	if (length < sizeof(struct efi_device_path))
>   		return NULL;
>
> -	ret = efi_alloc(length);
> +	ret = malloc(length);
>   	if (!ret)
>   		return ret;
>   	ret->type = type;
> @@ -399,7 +399,7 @@ struct efi_device_path *efi_dp_append_instance(
>   		return efi_dp_dup(dpi);
>   	sz = efi_dp_size(dp);
>   	szi = efi_dp_instance_size(dpi);
> -	p = efi_alloc(sz + szi + 2 * sizeof(END));
> +	p = malloc(sz + szi + 2 * sizeof(END));
>   	if (!p)
>   		return NULL;
>   	ret = p;
> @@ -424,7 +424,7 @@ struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp,
>   	if (!dp || !*dp)
>   		return NULL;
>   	sz = efi_dp_instance_size(*dp);
> -	p = efi_alloc(sz + sizeof(END));
> +	p = malloc(sz + sizeof(END));
>   	if (!p)
>   		return NULL;
>   	memcpy(p, *dp, sz + sizeof(END));
> @@ -825,11 +825,11 @@ struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
>   {
>   	void *buf, *start;
>
> -	start = buf = efi_alloc(dp_part_size(desc, part) + sizeof(END));
> -	if (!buf)
> +	start = malloc(dp_part_size(desc, part) + sizeof(END));
> +	if (!start)
>   		return NULL;
>
> -	buf = dp_part_fill(buf, desc, part);
> +	buf = dp_part_fill(start, desc, part);
>
>   	*((struct efi_device_path *)buf) = END;
>
> @@ -852,7 +852,7 @@ struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part)
>   		dpsize = sizeof(struct efi_device_path_cdrom_path);
>   	else
>   		dpsize = sizeof(struct efi_device_path_hard_drive_path);
> -	buf = efi_alloc(dpsize);
> +	buf = malloc(dpsize);
>
>   	if (buf)
>   		dp_part_node(buf, desc, part);
> @@ -912,7 +912,7 @@ struct efi_device_path *efi_dp_from_file(const struct efi_device_path *dp,
>   	if (fpsize > U16_MAX)
>   		return NULL;
>
> -	buf = efi_alloc(dpsize + fpsize + sizeof(END));
> +	buf = malloc(dpsize + fpsize + sizeof(END));
>   	if (!buf)
>   		return NULL;
>
> @@ -940,7 +940,7 @@ struct efi_device_path *efi_dp_from_uart(void)
>   	struct efi_device_path_uart *uart;
>   	size_t dpsize = dp_size(dm_root()) + sizeof(*uart) + sizeof(END);
>
> -	buf = efi_alloc(dpsize);
> +	buf = malloc(dpsize);
>   	if (!buf)
>   		return NULL;
>   	pos = dp_fill(buf, dm_root());
> @@ -963,11 +963,11 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void)
>
>   	dpsize += dp_size(eth_get_dev());
>
> -	start = buf = efi_alloc(dpsize + sizeof(END));
> -	if (!buf)
> +	start = malloc(dpsize + sizeof(END));
> +	if (!start)
>   		return NULL;
>
> -	buf = dp_fill(buf, eth_get_dev());
> +	buf = dp_fill(start, eth_get_dev());
>
>   	*((struct efi_device_path *)buf) = END;
>
> @@ -980,22 +980,21 @@ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type,
>   					uint64_t end_address)
>   {
>   	struct efi_device_path_memory *mdp;
> -	void *buf, *start;
> +	void *start;
>
> -	start = buf = efi_alloc(sizeof(*mdp) + sizeof(END));
> -	if (!buf)
> +	start = malloc(sizeof(*mdp) + sizeof(END));
> +	if (!start)
>   		return NULL;
>
> -	mdp = buf;
> +	mdp = start;
>   	mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
>   	mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY;
>   	mdp->dp.length = sizeof(*mdp);
>   	mdp->memory_type = memory_type;
>   	mdp->start_address = start_address;
>   	mdp->end_address = end_address;
> -	buf = &mdp[1];
>
> -	*((struct efi_device_path *)buf) = END;
> +	*((struct efi_device_path *)&mdp[1]) = END;
>
>   	return start;
>   }
> diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c
> index 0c7b30a26e7..4192581ac45 100644
> --- a/lib/efi_loader/efi_device_path_to_text.c
> +++ b/lib/efi_loader/efi_device_path_to_text.c
> @@ -33,7 +33,7 @@ static u16 *efi_str_to_u16(char *str)
>   	u16 *out, *dst;
>
>   	len = sizeof(u16) * (utf8_utf16_strlen(str) + 1);
> -	out = efi_alloc(len);
> +	out = malloc(len);
>   	if (!out)
>   		return NULL;
>   	dst = out;


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 3/6] efi: Allow monitoring of page allocations
  2024-07-25 13:56 ` [PATCH 3/6] efi: Allow monitoring of page allocations Simon Glass
@ 2024-07-25 16:20   ` Heinrich Schuchardt
  2024-07-25 23:33     ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2024-07-25 16:20 UTC (permalink / raw)
  To: Simon Glass
  Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, AKASHI Takahiro,
	Bin Meng, Joao Marcos Costa, Masahisa Kojima, Raymond Mao,
	Vincent Stehlé, U-Boot Mailing List

On 25.07.24 15:56, Simon Glass wrote:
> Some confusion has set in over which memory-allocation method to use in
> EFI-related code, particularly for the pool allocator. Most of the time,
> malloc() is used, which is correct.
>
> However in some cases the page allocator is used. This means that some
> EFI information is sitting 'in space', outside of the malloc() and not
> allocated by lmb. This can cause problems if an image happens to be
> loaded at the same address.

We already agreed to resolve this problem by integrating LMB and UEFI
memory allocation.

Please, contribute to reviewing
[RFC PATCH v2 00/48] Make U-Boot memory reservations coherent
https://lists.denx.de/pipermail/u-boot/2024-July/557962.html

>
>  From what I can tell, there is currently no checking of this, but it
> seems to be a real bug.
>
> Add a way to control whether allocations are permitted, to help debug
> these sorts of issues.

Calling AllocatePages() and AllocatedPool() cannot be forbidden as we
want to comply to the UEFI specification.

Best regards

Heinrich

>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
>   include/efi_loader.h         | 24 +++++++++++++++++++
>   lib/efi_loader/efi_bootbin.c |  2 ++
>   lib/efi_loader/efi_memory.c  | 46 ++++++++++++++++++++++++++++++++++++
>   lib/efi_loader/efi_setup.c   |  7 ++++++
>   4 files changed, 79 insertions(+)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index f2e5063a970..187078adf55 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -797,6 +797,30 @@ int efi_disk_probe(void *ctx, struct event *event);
>   int efi_disk_remove(void *ctx, struct event *event);
>   /* Called by board init to initialize the EFI memory map */
>   int efi_memory_init(void);
> +
> +/**
> + * enum efi_alloc_action - action to take when EFI does a page allocation
> + *
> + * @EFIAA_FAIL: Fail the allocation and print an error
> + * @EFIAA_WARN: Allow the allocation but print a warning
> + * @EFIAA_ALLOW: Allow the allocation with no message
> + */
> +enum efi_alloc_action {
> +	EFIAA_FAIL,
> +	EFIAA_WARN,
> +	EFIAA_ALLOW,
> +};
> +
> +/**
> + * efi_set_alloc() - Set behaviour on page allocation
> + *
> + * This is useful for debugging use of the page allocator when malloc() should
> + * be used instead
> + *
> + * @allow: true to allow EFI to allocate pages, false to fail the allocation
> + */
> +void efi_set_alloc(enum efi_alloc_action action);
> +
>   /* Adds new or overrides configuration table entry to the system table */
>   efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table);
>   /* Sets up a loaded image */
> diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c
> index a87006b3c0e..07c8fca68cc 100644
> --- a/lib/efi_loader/efi_bootbin.c
> +++ b/lib/efi_loader/efi_bootbin.c
> @@ -201,6 +201,8 @@ efi_status_t efi_binary_run(void *image, size_t size, void *fdt)
>   {
>   	efi_status_t ret;
>
> +	efi_set_alloc(EFIAA_ALLOW);
> +
>   	/* Initialize EFI drivers */
>   	ret = efi_init_obj_list();
>   	if (ret != EFI_SUCCESS) {
> diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> index 12cf23fa3fa..087f4c88cdf 100644
> --- a/lib/efi_loader/efi_memory.c
> +++ b/lib/efi_loader/efi_memory.c
> @@ -24,6 +24,43 @@ DECLARE_GLOBAL_DATA_PTR;
>   /* Magic number identifying memory allocated from pool */
>   #define EFI_ALLOC_POOL_MAGIC 0x1fe67ddf6491caa2
>
> +/*
> + * This is true if EFI is permitted to allocate pages in memory. When false,
> + * such allocations will fail. This is useful for debugging page allocations
> + * which should in fact use malloc().
> + *
> + * This defaults to EFIAA_FAIL until set.
> + */
> +static enum efi_alloc_action alloc_action;
> +
> +void efi_set_alloc(enum efi_alloc_action action)
> +{
> +	alloc_action = action;
> +}
> +
> +/**
> + * efi_bad_alloc() - Indicate that an allocation is not allowed
> + *
> + * Set a breakpoint on this function to locate the bad allocation in the call
> + * stack
> + */
> +static void efi_bad_alloc(void)
> +{
> +	log_err("EFI: alloc not allowed\n");
> +}
> +
> +static bool check_allowed(void)
> +{
> +	if (alloc_action == EFIAA_FAIL) {
> +		efi_bad_alloc();
> +		return false;
> +	} else if (alloc_action == EFIAA_WARN) {
> +		log_warning("EFI: suspicious alloc detected\n");
> +	}
> +
> +	return true;
> +}
> +
>   efi_uintn_t efi_memory_map_key;
>
>   struct efi_mem_list {
> @@ -501,6 +538,9 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type,
>   	efi_status_t ret;
>   	uint64_t addr;
>
> +	if (!check_allowed())
> +		return EFI_UNSUPPORTED;
> +
>   	/* Check import parameters */
>   	if (memory_type >= EFI_PERSISTENT_MEMORY_TYPE &&
>   	    memory_type <= 0x6FFFFFFF)
> @@ -649,6 +689,9 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
>   	u64 num_pages = efi_size_in_pages(size +
>   					  sizeof(struct efi_pool_allocation));
>
> +	if (!check_allowed())
> +		return EFI_UNSUPPORTED;
> +
>   	if (!buffer)
>   		return EFI_INVALID_PARAMETER;
>
> @@ -952,6 +995,9 @@ int efi_memory_init(void)
>   	/* Request a 32bit 64MB bounce buffer region */
>   	uint64_t efi_bounce_buffer_addr = 0xffffffff;
>
> +	/* this is the earliest page allocation, so allow it */
> +	efi_set_alloc(EFIAA_ALLOW);
> +
>   	if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, EFI_BOOT_SERVICES_DATA,
>   			       (64 * 1024 * 1024) >> EFI_PAGE_SHIFT,
>   			       &efi_bounce_buffer_addr) != EFI_SUCCESS)
> diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
> index a610e032d2f..27e24c2f223 100644
> --- a/lib/efi_loader/efi_setup.c
> +++ b/lib/efi_loader/efi_setup.c
> @@ -193,6 +193,13 @@ int efi_init_early(void)
>   	/* Allow unaligned memory access */
>   	allow_unaligned();
>
> +	/*
> +	 * For now, allow EFI to allocate memory outside the malloc() region.
> +	 * Once these bugs are fixed, this can be changed to EFIAA_FAIL, with
> +	 * the allocations being allowed only when the EFI system is booting.
> +	 */
> +	efi_set_alloc(EFIAA_ALLOW);
> +
>   	/* Initialize root node */
>   	ret = efi_root_node_register();
>   	if (ret != EFI_SUCCESS)


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 6/6] efi: Show the location of the bounce buffer
  2024-07-25 13:56 ` [PATCH 6/6] efi: Show the location of the bounce buffer Simon Glass
@ 2024-07-25 16:31   ` Heinrich Schuchardt
  2024-07-31 14:39     ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2024-07-25 16:31 UTC (permalink / raw)
  To: Simon Glass
  Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, AKASHI Takahiro,
	U-Boot Mailing List

On 25.07.24 15:56, Simon Glass wrote:
> The EFI_LOADER_BOUNCE_BUFFER feature was added many years ago. It is not
> clear whether it is still needed, but 24 boards (lx2160ardb_tfa_stmm,
> lx2162aqds_tfa_SECURE_BOOT and the like) use it.
>
> This feature uses EFI page allocation to create a 64MB buffer 'in space'
> without any knowledge of where boards intend to load their images. This
> may result in image corruption or other problems.

This is what Sughosh's LMB series in addressing.

With CONFIG_SYS_MALLOC_LEN=0x202000 for lx2160ardb_tfa_stmm we cannot
move the buffer to malloc(). I don't understand why
CONFIG_SYS_MALLOC_LEN is so small.

>
> For example, if the feature is enabled on qemu_arm64 it puts the EFI
> bounce buffer at 1045MB, with the kernel at 1028MB and the ramdisk at
> 1088MB. The kernel is probably smaller than 27MB but the buffer does
> overlap the ramdisk.
>
> The solution is probably to use BOUNCE_BUFFER instead, with the EFI
> version being dropped. For now, show the address of the EFI bounce
> buffer so people have a better chance to detect the problem.
>
> Note: I avoided converting this #ifdef to use IS_ENABLED() since I hope
> that the feature may be removed.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
>   lib/efi_loader/efi_bootbin.c | 8 ++++++++
>   1 file changed, 8 insertions(+)
>
> diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c
> index 07c8fca68cc..53e5e429d2e 100644
> --- a/lib/efi_loader/efi_bootbin.c
> +++ b/lib/efi_loader/efi_bootbin.c
> @@ -211,6 +211,14 @@ efi_status_t efi_binary_run(void *image, size_t size, void *fdt)
>   		return -1;
>   	}
>
> +#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
> +	/*
> +	 * Add a warning about this buffer, since it may conflict with other
> +	 * things
> +	 */
> +	printf("EFI bounce buffer at %p\n", efi_bounce_buffer);
> +#endif

Does this sound like a warning to you?

There is nothing that a user can reasonably do when seeing this message
while the board is booting via one of the boot methods. So we should not
write it.

Best regards

Heinrich


> +
>   	ret = efi_install_fdt(fdt);
>   	if (ret != EFI_SUCCESS)
>   		return ret;


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 4/6] efi: Avoid pool allocation in efi_get_memory_map_alloc()
  2024-07-25 13:56 ` [PATCH 4/6] efi: Avoid pool allocation in efi_get_memory_map_alloc() Simon Glass
@ 2024-07-25 16:38   ` Heinrich Schuchardt
  2024-07-31 14:39     ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2024-07-25 16:38 UTC (permalink / raw)
  To: Simon Glass; +Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, U-Boot Mailing List

On 25.07.24 15:56, Simon Glass wrote:
> This function returns the memory map, allocating memory for it. But it
> can just use malloc() directly, rather than calling the pool allocator.
> Update it.

The size of the memory map may exceed CONFIG_SYS_MALLOC_LEN which tends
to be astonishingly small: 1 MiB or less on many boards.

Best regards

Heinrich

>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
>   lib/efi_loader/efi_memory.c | 9 ++++-----
>   1 file changed, 4 insertions(+), 5 deletions(-)
>
> diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> index 087f4c88cdf..2945f5648c7 100644
> --- a/lib/efi_loader/efi_memory.c
> +++ b/lib/efi_loader/efi_memory.c
> @@ -850,14 +850,13 @@ efi_status_t efi_get_memory_map_alloc(efi_uintn_t *map_size,
>   	ret = efi_get_memory_map(map_size, *memory_map, NULL, NULL, NULL);
>   	if (ret == EFI_BUFFER_TOO_SMALL) {
>   		*map_size += sizeof(struct efi_mem_desc); /* for the map */
> -		ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, *map_size,
> -					(void **)memory_map);
> -		if (ret != EFI_SUCCESS)
> -			return ret;
> +		*memory_map = malloc(*map_size);
> +		if (!*memory_map)
> +			return EFI_OUT_OF_RESOURCES;
>   		ret = efi_get_memory_map(map_size, *memory_map,
>   					 NULL, NULL, NULL);
>   		if (ret != EFI_SUCCESS) {
> -			efi_free_pool(*memory_map);
> +			free(*memory_map);
>   			*memory_map = NULL;
>   		}
>   	}


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 2/6] efi: Convert device_path allocation to use malloc()
  2024-07-25 16:13   ` Heinrich Schuchardt
@ 2024-07-25 23:32     ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2024-07-25 23:32 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, AKASHI Takahiro,
	U-Boot Mailing List

Hi Heinrich,

On Thu, 25 Jul 2024 at 10:18, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 25.07.24 15:56, Simon Glass wrote:
> > Currently this calls efi_alloc() which reserves a page for each
> > allocation and this can overwrite memory that will be used for reading
> > images.
>
> We already agreed to integrate LMB and UEFI memory management to avoid
> this sort of problem.

Yes that series will be good in general, but I don't believe that will
fix either problem:

- Using LMB for tiny allocations doesn't really make any more sense
than allocating a 4KB page for each one.
- LMB cannot stop overwriting memory used by kern_addr_r, for example,
as mentioned in the other patch

>
> Please, have a look at chapter 10.5, "Device Path Utilities Protocol" of
> the UEFI specification.
>
> EFI_DEVICE_PATH_UTILITIES_PROTOCOL.DuplicateDevicePath() must return
> memory that an EFI application can release by calling
> EFI_BOOT_SERVICES.FreePool().
>
> Your patch does not comply to the specification.

Oh yes, I mistakenly changed the ordering of the patches before
sending...this one should go after the pool one.

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-25 15:49   ` Heinrich Schuchardt
@ 2024-07-25 23:32     ` Simon Glass
  2024-07-26  8:31       ` Ilias Apalodimas
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-25 23:32 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, U-Boot Mailing List

Hi Heinrich,

On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 25.07.24 15:56, Simon Glass wrote:
> > This API call is intended for allocating small amounts of memory,
> > similar to malloc(). The current implementation rounds up to whole pages
> > which can waste large amounts of memory. It also implements its own
> > malloc()-style header on each block.
> >
> > Use U-Boot's built-in malloc() instead, to avoid these problems:
> >
> > - it should normally be large enough for pool allocations
> > - if it isn't we can enforce a minimum size for boards which use
> >    EFI_LOADER
> > - the existing mechanism may create an unwatned entry in the memory map
> > - it is used for most EFI allocations already
> >
> > One side effect is that this seems to be showing up some bugs in the
> > EFI code, since the malloc() pool becomes corrupted with some tests.
> > This has likely crept in due to the very large gaps between allocations
> > (around 4KB), which provides a lot of leeway when the allocation size is
> > too small. Work around this by increasing the size for now, until these
> > (presumed) bugs are located.
> >
> > Signed-off-by: Simon Glass <sjg@chromium.org>
> > ---
> >
> >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> >   1 file changed, 19 insertions(+), 74 deletions(-)
> >
> > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > index 2945f5648c7..fabe9e3a87a 100644
> > --- a/lib/efi_loader/efi_memory.c
> > +++ b/lib/efi_loader/efi_memory.c
> > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> >   void *efi_bounce_buffer;
> >   #endif
> >
> > -/**
> > - * struct efi_pool_allocation - memory block allocated from pool
> > - *
> > - * @num_pages:       number of pages allocated
> > - * @checksum:        checksum
> > - * @data:    allocated pool memory
> > - *
> > - * U-Boot services each UEFI AllocatePool() request as a separate
> > - * (multiple) page allocation. We have to track the number of pages
> > - * to be able to free the correct amount later.
> > - *
> > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > - *
> > - * EFI requires 8 byte alignment for pool allocations, so we can
> > - * prepend each allocation with these header fields.
> > - */
> > -struct efi_pool_allocation {
> > -     u64 num_pages;
> > -     u64 checksum;
> > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > -};
> > -
> > -/**
> > - * checksum() - calculate checksum for memory allocated from pool
> > - *
> > - * @alloc:   allocation header
> > - * Return:   checksum, always non-zero
> > - */
> > -static u64 checksum(struct efi_pool_allocation *alloc)
> > -{
> > -     u64 addr = (uintptr_t)alloc;
> > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > -               EFI_ALLOC_POOL_MAGIC;
> > -     if (!ret)
> > -             ++ret;
> > -     return ret;
> > -}
> > -
> >   /**
> >    * efi_mem_cmp() - comparator function for sorting memory map
> >    *
> > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> >    * @buffer: allocated memory
> >    * Return:  status code
> >    */
> > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > +                            void **buffer)
> >   {
> > -     efi_status_t r;
> > -     u64 addr;
> > -     struct efi_pool_allocation *alloc;
> > -     u64 num_pages = efi_size_in_pages(size +
> > -                                       sizeof(struct efi_pool_allocation));
> > +     void *ptr;
> >
> >       if (!check_allowed())
> >               return EFI_UNSUPPORTED;
> > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> >               return EFI_SUCCESS;
> >       }
>
> Unfortunately this patch does not match the UEFI specification:
>
> Two different memory types cannot reside in the same memory page. You
> have to keep track of the memory type of each allocated memory page and
> report it in GetMemoryMap().

I actually hadn't thought about that. Is it implied in the spec or
actually stated somewhere?

Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.

So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
and stick with whole pages otherwise? That will mostly fix the problem
I am seeing.

>
> The specification is available https://uefi.org/specifications.

The problem is that it is thousands of pages so it is hard to pick up
on some of these issues.

>
> >
> > -     r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
> > -                            &addr);
> > -     if (r == EFI_SUCCESS) {
> > -             alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
> > -             alloc->num_pages = num_pages;
> > -             alloc->checksum = checksum(alloc);
> > -             *buffer = alloc->data;
> > -     }
> > +     /*
> > +      * Some tests crash on qemu_arm etc. if the correct size is allocated.
> > +      * Adding 0x10 seems to fix test_efi_selftest_device_tree
> > +      * Increasing it to 0x20 seems to fix test_efi_selftest_base except
> > +      * for riscv64 (in CI only). But 0x100 fixes CI too.
> > +      *
> > +      * This workaround can be dropped once these problems are resolved
> > +      */
> > +     ptr = memalign(8, size + 0x100);
>
> We were using ARCH_DMA_MINALIGN before this patch.

Where is that in the code? All I see is EFI_PAGE_SHIFT.

>
> If we are overwriting allocated memory, we must fix that instead of
> increasing size. Do you have a git tag showing the problem?

Not easy to do, because the 4KB per allocation thing has been there
from the start. It might be possible to apply my patch to earlier and
earlier U-Boots to try to bisect it.

Regards,
Simon



>
> Best regards
>
> Heinrich
>
> > +     if (!ptr)
> > +             return EFI_OUT_OF_RESOURCES;
> > +
> > +     *buffer = ptr;
> >
> > -     return r;
> > +     return EFI_SUCCESS;
> >   }
> >
> >   /**
> > @@ -742,30 +705,12 @@ void *efi_alloc(size_t size)
> >    */
> >   efi_status_t efi_free_pool(void *buffer)
> >   {
> > -     efi_status_t ret;
> > -     struct efi_pool_allocation *alloc;
> > -
> >       if (!buffer)
> >               return EFI_INVALID_PARAMETER;
> >
> > -     ret = efi_check_allocated((uintptr_t)buffer, true);
> > -     if (ret != EFI_SUCCESS)
> > -             return ret;
> > -
> > -     alloc = container_of(buffer, struct efi_pool_allocation, data);
> > -
> > -     /* Check that this memory was allocated by efi_allocate_pool() */
> > -     if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
> > -         alloc->checksum != checksum(alloc)) {
> > -             printf("%s: illegal free 0x%p\n", __func__, buffer);
> > -             return EFI_INVALID_PARAMETER;
> > -     }
> > -     /* Avoid double free */
> > -     alloc->checksum = 0;
> > -
> > -     ret = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
> > +     free(buffer);
> >
> > -     return ret;
> > +     return EFI_SUCCESS;
> >   }
> >
> >   /**
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 3/6] efi: Allow monitoring of page allocations
  2024-07-25 16:20   ` Heinrich Schuchardt
@ 2024-07-25 23:33     ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2024-07-25 23:33 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, AKASHI Takahiro,
	Bin Meng, Joao Marcos Costa, Masahisa Kojima, Raymond Mao,
	Vincent Stehlé, U-Boot Mailing List

Hi Heinrich,

On Thu, 25 Jul 2024 at 10:26, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 25.07.24 15:56, Simon Glass wrote:
> > Some confusion has set in over which memory-allocation method to use in
> > EFI-related code, particularly for the pool allocator. Most of the time,
> > malloc() is used, which is correct.
> >
> > However in some cases the page allocator is used. This means that some
> > EFI information is sitting 'in space', outside of the malloc() and not
> > allocated by lmb. This can cause problems if an image happens to be
> > loaded at the same address.
>
> We already agreed to resolve this problem by integrating LMB and UEFI
> memory allocation.
>
> Please, contribute to reviewing
> [RFC PATCH v2 00/48] Make U-Boot memory reservations coherent
> https://lists.denx.de/pipermail/u-boot/2024-July/557962.html

Yes, I reviewed the RFC some time ago and actually referenced that
series in the cover letter. This series is aimed at something
different, though. Please see the cover letter.

>
> >
> >  From what I can tell, there is currently no checking of this, but it
> > seems to be a real bug.
> >
> > Add a way to control whether allocations are permitted, to help debug
> > these sorts of issues.
>
> Calling AllocatePages() and AllocatedPool() cannot be forbidden as we
> want to comply to the UEFI specification.

Right. My point is this for debugging only, to allow allocations to be
forbidden when they should not be done, thus allowing us to sort out
the problems. Once there are no more problems, this patch can be
reverted, but that might take some years.
[..]

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-25 23:32     ` Simon Glass
@ 2024-07-26  8:31       ` Ilias Apalodimas
  2024-07-26 14:54         ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Ilias Apalodimas @ 2024-07-26  8:31 UTC (permalink / raw)
  To: Simon Glass, Heinrich Schuchardt
  Cc: Sughosh Ganu, Tom Rini, U-Boot Mailing List

Hi Simon,


On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Heinrich,
>
> On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> >
> > On 25.07.24 15:56, Simon Glass wrote:
> > > This API call is intended for allocating small amounts of memory,
> > > similar to malloc(). The current implementation rounds up to whole pages
> > > which can waste large amounts of memory. It also implements its own
> > > malloc()-style header on each block.
> > >
> > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > >
> > > - it should normally be large enough for pool allocations
> > > - if it isn't we can enforce a minimum size for boards which use
> > >    EFI_LOADER
> > > - the existing mechanism may create an unwatned entry in the memory map
> > > - it is used for most EFI allocations already
> > >
> > > One side effect is that this seems to be showing up some bugs in the
> > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > This has likely crept in due to the very large gaps between allocations
> > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > too small. Work around this by increasing the size for now, until these
> > > (presumed) bugs are located.
> > >
> > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > ---
> > >
> > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > >
> > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > index 2945f5648c7..fabe9e3a87a 100644
> > > --- a/lib/efi_loader/efi_memory.c
> > > +++ b/lib/efi_loader/efi_memory.c
> > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > >   void *efi_bounce_buffer;
> > >   #endif
> > >
> > > -/**
> > > - * struct efi_pool_allocation - memory block allocated from pool
> > > - *
> > > - * @num_pages:       number of pages allocated
> > > - * @checksum:        checksum
> > > - * @data:    allocated pool memory
> > > - *
> > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > - * (multiple) page allocation. We have to track the number of pages
> > > - * to be able to free the correct amount later.
> > > - *
> > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > - *
> > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > - * prepend each allocation with these header fields.
> > > - */
> > > -struct efi_pool_allocation {
> > > -     u64 num_pages;
> > > -     u64 checksum;
> > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > -};
> > > -
> > > -/**
> > > - * checksum() - calculate checksum for memory allocated from pool
> > > - *
> > > - * @alloc:   allocation header
> > > - * Return:   checksum, always non-zero
> > > - */
> > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > -{
> > > -     u64 addr = (uintptr_t)alloc;
> > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > -               EFI_ALLOC_POOL_MAGIC;
> > > -     if (!ret)
> > > -             ++ret;
> > > -     return ret;
> > > -}
> > > -
> > >   /**
> > >    * efi_mem_cmp() - comparator function for sorting memory map
> > >    *
> > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > >    * @buffer: allocated memory
> > >    * Return:  status code
> > >    */
> > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > +                            void **buffer)
> > >   {
> > > -     efi_status_t r;
> > > -     u64 addr;
> > > -     struct efi_pool_allocation *alloc;
> > > -     u64 num_pages = efi_size_in_pages(size +
> > > -                                       sizeof(struct efi_pool_allocation));
> > > +     void *ptr;
> > >
> > >       if (!check_allowed())
> > >               return EFI_UNSUPPORTED;
> > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > >               return EFI_SUCCESS;
> > >       }
> >
> > Unfortunately this patch does not match the UEFI specification:
> >
> > Two different memory types cannot reside in the same memory page. You
> > have to keep track of the memory type of each allocated memory page and
> > report it in GetMemoryMap().
>
> I actually hadn't thought about that. Is it implied in the spec or
> actually stated somewhere?

I don't remember if it's explicitly stated, but it *really* doesn't
have to be. Although u-boot doesn't currently support this on modern
secure systems you can't have code and data mixed on the same page.
This would prevent you to map pages with proper permissions and take
advantage of CPU security features  e.g RW^X.

In any case, the AllocatePool description reads "This function
allocates pages from EfiConventionalMemory as needed to grow the
requested pool type. All allocations are eight-byte aligned". I don't
think using malloc in AllocatePool is appropriate. If we ever want to
do that and allocate from the malloc space, we need to teach malloc
some EFI semantics, but that's a really bad idea.

>
> Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.

That's not entirely correct, we use a lot more than these 2.
EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
quick grep

/Ilias

>
> So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> and stick with whole pages otherwise? That will mostly fix the problem
> I am seeing.
>
> >
> > The specification is available https://uefi.org/specifications.
>
> The problem is that it is thousands of pages so it is hard to pick up
> on some of these issues.
>
> >
> > >
> > > -     r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
> > > -                            &addr);
> > > -     if (r == EFI_SUCCESS) {
> > > -             alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
> > > -             alloc->num_pages = num_pages;
> > > -             alloc->checksum = checksum(alloc);
> > > -             *buffer = alloc->data;
> > > -     }
> > > +     /*
> > > +      * Some tests crash on qemu_arm etc. if the correct size is allocated.
> > > +      * Adding 0x10 seems to fix test_efi_selftest_device_tree
> > > +      * Increasing it to 0x20 seems to fix test_efi_selftest_base except
> > > +      * for riscv64 (in CI only). But 0x100 fixes CI too.
> > > +      *
> > > +      * This workaround can be dropped once these problems are resolved
> > > +      */
> > > +     ptr = memalign(8, size + 0x100);
> >
> > We were using ARCH_DMA_MINALIGN before this patch.
>
> Where is that in the code? All I see is EFI_PAGE_SHIFT.
>
> >
> > If we are overwriting allocated memory, we must fix that instead of
> > increasing size. Do you have a git tag showing the problem?
>
> Not easy to do, because the 4KB per allocation thing has been there
> from the start. It might be possible to apply my patch to earlier and
> earlier U-Boots to try to bisect it.
>
> Regards,
> Simon
>
>
>
> >
> > Best regards
> >
> > Heinrich
> >
> > > +     if (!ptr)
> > > +             return EFI_OUT_OF_RESOURCES;
> > > +
> > > +     *buffer = ptr;
> > >
> > > -     return r;
> > > +     return EFI_SUCCESS;
> > >   }
> > >
> > >   /**
> > > @@ -742,30 +705,12 @@ void *efi_alloc(size_t size)
> > >    */
> > >   efi_status_t efi_free_pool(void *buffer)
> > >   {
> > > -     efi_status_t ret;
> > > -     struct efi_pool_allocation *alloc;
> > > -
> > >       if (!buffer)
> > >               return EFI_INVALID_PARAMETER;
> > >
> > > -     ret = efi_check_allocated((uintptr_t)buffer, true);
> > > -     if (ret != EFI_SUCCESS)
> > > -             return ret;
> > > -
> > > -     alloc = container_of(buffer, struct efi_pool_allocation, data);
> > > -
> > > -     /* Check that this memory was allocated by efi_allocate_pool() */
> > > -     if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
> > > -         alloc->checksum != checksum(alloc)) {
> > > -             printf("%s: illegal free 0x%p\n", __func__, buffer);
> > > -             return EFI_INVALID_PARAMETER;
> > > -     }
> > > -     /* Avoid double free */
> > > -     alloc->checksum = 0;
> > > -
> > > -     ret = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
> > > +     free(buffer);
> > >
> > > -     return ret;
> > > +     return EFI_SUCCESS;
> > >   }
> > >
> > >   /**
> >

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-26  8:31       ` Ilias Apalodimas
@ 2024-07-26 14:54         ` Simon Glass
  2024-07-29 10:01           ` Ilias Apalodimas
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-26 14:54 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Heinrich Schuchardt, Sughosh Ganu, Tom Rini, U-Boot Mailing List

Hi Ilias,

On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> Hi Simon,
>
>
> On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Heinrich,
> >
> > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > >
> > > On 25.07.24 15:56, Simon Glass wrote:
> > > > This API call is intended for allocating small amounts of memory,
> > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > which can waste large amounts of memory. It also implements its own
> > > > malloc()-style header on each block.
> > > >
> > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > >
> > > > - it should normally be large enough for pool allocations
> > > > - if it isn't we can enforce a minimum size for boards which use
> > > >    EFI_LOADER
> > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > - it is used for most EFI allocations already
> > > >
> > > > One side effect is that this seems to be showing up some bugs in the
> > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > This has likely crept in due to the very large gaps between allocations
> > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > too small. Work around this by increasing the size for now, until these
> > > > (presumed) bugs are located.
> > > >
> > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > ---
> > > >
> > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > >
> > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > --- a/lib/efi_loader/efi_memory.c
> > > > +++ b/lib/efi_loader/efi_memory.c
> > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > >   void *efi_bounce_buffer;
> > > >   #endif
> > > >
> > > > -/**
> > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > - *
> > > > - * @num_pages:       number of pages allocated
> > > > - * @checksum:        checksum
> > > > - * @data:    allocated pool memory
> > > > - *
> > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > - * (multiple) page allocation. We have to track the number of pages
> > > > - * to be able to free the correct amount later.
> > > > - *
> > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > - *
> > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > - * prepend each allocation with these header fields.
> > > > - */
> > > > -struct efi_pool_allocation {
> > > > -     u64 num_pages;
> > > > -     u64 checksum;
> > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > -};
> > > > -
> > > > -/**
> > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > - *
> > > > - * @alloc:   allocation header
> > > > - * Return:   checksum, always non-zero
> > > > - */
> > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > -{
> > > > -     u64 addr = (uintptr_t)alloc;
> > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > -     if (!ret)
> > > > -             ++ret;
> > > > -     return ret;
> > > > -}
> > > > -
> > > >   /**
> > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > >    *
> > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > >    * @buffer: allocated memory
> > > >    * Return:  status code
> > > >    */
> > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > +                            void **buffer)
> > > >   {
> > > > -     efi_status_t r;
> > > > -     u64 addr;
> > > > -     struct efi_pool_allocation *alloc;
> > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > -                                       sizeof(struct efi_pool_allocation));
> > > > +     void *ptr;
> > > >
> > > >       if (!check_allowed())
> > > >               return EFI_UNSUPPORTED;
> > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > >               return EFI_SUCCESS;
> > > >       }
> > >
> > > Unfortunately this patch does not match the UEFI specification:
> > >
> > > Two different memory types cannot reside in the same memory page. You
> > > have to keep track of the memory type of each allocated memory page and
> > > report it in GetMemoryMap().
> >
> > I actually hadn't thought about that. Is it implied in the spec or
> > actually stated somewhere?
>
> I don't remember if it's explicitly stated, but it *really* doesn't
> have to be. Although u-boot doesn't currently support this on modern
> secure systems you can't have code and data mixed on the same page.
> This would prevent you to map pages with proper permissions and take
> advantage of CPU security features  e.g RW^X.

As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
EFI_BOOT_SERVICES_DATA isn't that important?

But if we did want to fix that, we could make the malloc region
EFI_BOOT_SERVICES_DATA.

>
> In any case, the AllocatePool description reads "This function
> allocates pages from EfiConventionalMemory as needed to grow the
> requested pool type. All allocations are eight-byte aligned". I don't
> think using malloc in AllocatePool is appropriate. If we ever want to
> do that and allocate from the malloc space, we need to teach malloc
> some EFI semantics, but that's a really bad idea.

Here I don't see the difference between EFI's malloc(n) and
memalign(8, n). I do see a lot of comments like 'use the spec', 'read
the spec'. It seems very clear to me that the bootloader can use
whatever algorithm it likes to provide the allocated memory for the
pool.

What sort of EFI semantics are you referring to?

>
> >
> > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
>
> That's not entirely correct, we use a lot more than these 2.
> EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> quick grep

Yes I did a quick grep too, but then checked each site. The first two
are used in an EFI app, not U-Boot itself. The ACPI one is not an
allocation.

> >
> > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > and stick with whole pages otherwise? That will mostly fix the problem
> > I am seeing.

Any comments on this?

> >
> > >
> > > The specification is available https://uefi.org/specifications.
> >
> > The problem is that it is thousands of pages so it is hard to pick up
> > on some of these issues.
> >
> > >
> > > >
> > > > -     r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
> > > > -                            &addr);
> > > > -     if (r == EFI_SUCCESS) {
> > > > -             alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
> > > > -             alloc->num_pages = num_pages;
> > > > -             alloc->checksum = checksum(alloc);
> > > > -             *buffer = alloc->data;
> > > > -     }
> > > > +     /*
> > > > +      * Some tests crash on qemu_arm etc. if the correct size is allocated.
> > > > +      * Adding 0x10 seems to fix test_efi_selftest_device_tree
> > > > +      * Increasing it to 0x20 seems to fix test_efi_selftest_base except
> > > > +      * for riscv64 (in CI only). But 0x100 fixes CI too.
> > > > +      *
> > > > +      * This workaround can be dropped once these problems are resolved
> > > > +      */
> > > > +     ptr = memalign(8, size + 0x100);
> > >
> > > We were using ARCH_DMA_MINALIGN before this patch.
> >
> > Where is that in the code? All I see is EFI_PAGE_SHIFT.
> >
> > >
> > > If we are overwriting allocated memory, we must fix that instead of
> > > increasing size. Do you have a git tag showing the problem?
> >
> > Not easy to do, because the 4KB per allocation thing has been there
> > from the start. It might be possible to apply my patch to earlier and
> > earlier U-Boots to try to bisect it.

Would anyone like to take a look at these presumed bugs?

It might be worth moving to a newer dlmalloc() [1,2] as it has a checker.

> >
[..]

Regards,
Simon

[1] https://gee.cs.oswego.edu/dl/html/malloc.html
[2] https://gee.cs.oswego.edu/pub/misc/malloc.c

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-26 14:54         ` Simon Glass
@ 2024-07-29 10:01           ` Ilias Apalodimas
  2024-07-29 15:28             ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Ilias Apalodimas @ 2024-07-29 10:01 UTC (permalink / raw)
  To: Simon Glass
  Cc: Heinrich Schuchardt, Sughosh Ganu, Tom Rini, U-Boot Mailing List

Hi Simon

On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Ilias,
>
> On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > Hi Simon,
> >
> >
> > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Heinrich,
> > >
> > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > >
> > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > This API call is intended for allocating small amounts of memory,
> > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > which can waste large amounts of memory. It also implements its own
> > > > > malloc()-style header on each block.
> > > > >
> > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > >
> > > > > - it should normally be large enough for pool allocations
> > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > >    EFI_LOADER
> > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > - it is used for most EFI allocations already
> > > > >
> > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > This has likely crept in due to the very large gaps between allocations
> > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > too small. Work around this by increasing the size for now, until these
> > > > > (presumed) bugs are located.
> > > > >
> > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > ---
> > > > >
> > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > >
> > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > >   void *efi_bounce_buffer;
> > > > >   #endif
> > > > >
> > > > > -/**
> > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > - *
> > > > > - * @num_pages:       number of pages allocated
> > > > > - * @checksum:        checksum
> > > > > - * @data:    allocated pool memory
> > > > > - *
> > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > - * to be able to free the correct amount later.
> > > > > - *
> > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > - *
> > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > - * prepend each allocation with these header fields.
> > > > > - */
> > > > > -struct efi_pool_allocation {
> > > > > -     u64 num_pages;
> > > > > -     u64 checksum;
> > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > -};
> > > > > -
> > > > > -/**
> > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > - *
> > > > > - * @alloc:   allocation header
> > > > > - * Return:   checksum, always non-zero
> > > > > - */
> > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > -{
> > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > -     if (!ret)
> > > > > -             ++ret;
> > > > > -     return ret;
> > > > > -}
> > > > > -
> > > > >   /**
> > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > >    *
> > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > >    * @buffer: allocated memory
> > > > >    * Return:  status code
> > > > >    */
> > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > +                            void **buffer)
> > > > >   {
> > > > > -     efi_status_t r;
> > > > > -     u64 addr;
> > > > > -     struct efi_pool_allocation *alloc;
> > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > +     void *ptr;
> > > > >
> > > > >       if (!check_allowed())
> > > > >               return EFI_UNSUPPORTED;
> > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > >               return EFI_SUCCESS;
> > > > >       }
> > > >
> > > > Unfortunately this patch does not match the UEFI specification:
> > > >
> > > > Two different memory types cannot reside in the same memory page. You
> > > > have to keep track of the memory type of each allocated memory page and
> > > > report it in GetMemoryMap().
> > >
> > > I actually hadn't thought about that. Is it implied in the spec or
> > > actually stated somewhere?
> >
> > I don't remember if it's explicitly stated, but it *really* doesn't
> > have to be. Although u-boot doesn't currently support this on modern
> > secure systems you can't have code and data mixed on the same page.
> > This would prevent you to map pages with proper permissions and take
> > advantage of CPU security features  e.g RW^X.
>
> As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> EFI_BOOT_SERVICES_DATA isn't that important?
>
> But if we did want to fix that, we could make the malloc region
> EFI_BOOT_SERVICES_DATA.
>
> >
> > In any case, the AllocatePool description reads "This function
> > allocates pages from EfiConventionalMemory as needed to grow the
> > requested pool type. All allocations are eight-byte aligned". I don't
> > think using malloc in AllocatePool is appropriate. If we ever want to
> > do that and allocate from the malloc space, we need to teach malloc
> > some EFI semantics, but that's a really bad idea.
>
> Here I don't see the difference between EFI's malloc(n) and
> memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> the spec'. It seems very clear to me that the bootloader can use
> whatever algorithm it likes to provide the allocated memory for the
> pool.
>
> What sort of EFI semantics are you referring to?

the memory type. Your patch removes the call to efi_allocate_pages
which tracks it.

>
> >
> > >
> > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> >
> > That's not entirely correct, we use a lot more than these 2.
> > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > quick grep
>
> Yes I did a quick grep too, but then checked each site. The first two
> are used in an EFI app, not U-Boot itself. The ACPI one is not an
> allocation.
>
> > >
> > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > and stick with whole pages otherwise? That will mostly fix the problem
> > > I am seeing.
>
> Any comments on this?

This violates the spec and probably breaks a few tests and the EFI
boot services API, as you are supposed to be able to define the memory
type. We might be able to control it from U-Boot, but EFI apps are
going to end up being buggy and hard to debug -- e.g an app calls
allocatePool to allocate memory that needs to be preserved at runtime.

You can switch some callsite of efi_allocate_pool to
efi_allocate_pages() internally. Will that fix the behavior?

Regards
/Ilias
>
> > >
> > > >
> > > > The specification is available https://uefi.org/specifications.
> > >
> > > The problem is that it is thousands of pages so it is hard to pick up
> > > on some of these issues.
> > >
> > > >
> > > > >
> > > > > -     r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
> > > > > -                            &addr);
> > > > > -     if (r == EFI_SUCCESS) {
> > > > > -             alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
> > > > > -             alloc->num_pages = num_pages;
> > > > > -             alloc->checksum = checksum(alloc);
> > > > > -             *buffer = alloc->data;
> > > > > -     }
> > > > > +     /*
> > > > > +      * Some tests crash on qemu_arm etc. if the correct size is allocated.
> > > > > +      * Adding 0x10 seems to fix test_efi_selftest_device_tree
> > > > > +      * Increasing it to 0x20 seems to fix test_efi_selftest_base except
> > > > > +      * for riscv64 (in CI only). But 0x100 fixes CI too.
> > > > > +      *
> > > > > +      * This workaround can be dropped once these problems are resolved
> > > > > +      */
> > > > > +     ptr = memalign(8, size + 0x100);
> > > >
> > > > We were using ARCH_DMA_MINALIGN before this patch.
> > >
> > > Where is that in the code? All I see is EFI_PAGE_SHIFT.
> > >
> > > >
> > > > If we are overwriting allocated memory, we must fix that instead of
> > > > increasing size. Do you have a git tag showing the problem?
> > >
> > > Not easy to do, because the 4KB per allocation thing has been there
> > > from the start. It might be possible to apply my patch to earlier and
> > > earlier U-Boots to try to bisect it.
>
> Would anyone like to take a look at these presumed bugs?
>
> It might be worth moving to a newer dlmalloc() [1,2] as it has a checker.
>
> > >
> [..]
>
> Regards,
> Simon
>
> [1] https://gee.cs.oswego.edu/dl/html/malloc.html
> [2] https://gee.cs.oswego.edu/pub/misc/malloc.c

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-29 10:01           ` Ilias Apalodimas
@ 2024-07-29 15:28             ` Simon Glass
  2024-07-30  8:19               ` Ilias Apalodimas
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-29 15:28 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Heinrich Schuchardt, Sughosh Ganu, Tom Rini, U-Boot Mailing List

Hi Ilias,

On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> Hi Simon
>
> On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Ilias,
> >
> > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > <ilias.apalodimas@linaro.org> wrote:
> > >
> > > Hi Simon,
> > >
> > >
> > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Heinrich,
> > > >
> > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > >
> > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > malloc()-style header on each block.
> > > > > >
> > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > >
> > > > > > - it should normally be large enough for pool allocations
> > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > >    EFI_LOADER
> > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > - it is used for most EFI allocations already
> > > > > >
> > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > (presumed) bugs are located.
> > > > > >
> > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > ---
> > > > > >
> > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > >
> > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > >   void *efi_bounce_buffer;
> > > > > >   #endif
> > > > > >
> > > > > > -/**
> > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > - *
> > > > > > - * @num_pages:       number of pages allocated
> > > > > > - * @checksum:        checksum
> > > > > > - * @data:    allocated pool memory
> > > > > > - *
> > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > - * to be able to free the correct amount later.
> > > > > > - *
> > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > - *
> > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > - * prepend each allocation with these header fields.
> > > > > > - */
> > > > > > -struct efi_pool_allocation {
> > > > > > -     u64 num_pages;
> > > > > > -     u64 checksum;
> > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > -};
> > > > > > -
> > > > > > -/**
> > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > - *
> > > > > > - * @alloc:   allocation header
> > > > > > - * Return:   checksum, always non-zero
> > > > > > - */
> > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > -{
> > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > -     if (!ret)
> > > > > > -             ++ret;
> > > > > > -     return ret;
> > > > > > -}
> > > > > > -
> > > > > >   /**
> > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > >    *
> > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > >    * @buffer: allocated memory
> > > > > >    * Return:  status code
> > > > > >    */
> > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > +                            void **buffer)
> > > > > >   {
> > > > > > -     efi_status_t r;
> > > > > > -     u64 addr;
> > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > +     void *ptr;
> > > > > >
> > > > > >       if (!check_allowed())
> > > > > >               return EFI_UNSUPPORTED;
> > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > >               return EFI_SUCCESS;
> > > > > >       }
> > > > >
> > > > > Unfortunately this patch does not match the UEFI specification:
> > > > >
> > > > > Two different memory types cannot reside in the same memory page. You
> > > > > have to keep track of the memory type of each allocated memory page and
> > > > > report it in GetMemoryMap().
> > > >
> > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > actually stated somewhere?
> > >
> > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > have to be. Although u-boot doesn't currently support this on modern
> > > secure systems you can't have code and data mixed on the same page.
> > > This would prevent you to map pages with proper permissions and take
> > > advantage of CPU security features  e.g RW^X.
> >
> > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > EFI_BOOT_SERVICES_DATA isn't that important?
> >
> > But if we did want to fix that, we could make the malloc region
> > EFI_BOOT_SERVICES_DATA.
> >
> > >
> > > In any case, the AllocatePool description reads "This function
> > > allocates pages from EfiConventionalMemory as needed to grow the
> > > requested pool type. All allocations are eight-byte aligned". I don't
> > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > do that and allocate from the malloc space, we need to teach malloc
> > > some EFI semantics, but that's a really bad idea.
> >
> > Here I don't see the difference between EFI's malloc(n) and
> > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > the spec'. It seems very clear to me that the bootloader can use
> > whatever algorithm it likes to provide the allocated memory for the
> > pool.
> >
> > What sort of EFI semantics are you referring to?
>
> the memory type. Your patch removes the call to efi_allocate_pages
> which tracks it.

I can put that code back, depending on what we decide below.

>
> >
> > >
> > > >
> > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > >
> > > That's not entirely correct, we use a lot more than these 2.
> > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > quick grep
> >
> > Yes I did a quick grep too, but then checked each site. The first two
> > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > allocation.
> >
> > > >
> > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > I am seeing.
> >
> > Any comments on this?
>
> This violates the spec and probably breaks a few tests and the EFI
> boot services API, as you are supposed to be able to define the memory
> type. We might be able to control it from U-Boot, but EFI apps are
> going to end up being buggy and hard to debug -- e.g an app calls
> allocatePool to allocate memory that needs to be preserved at runtime.

To be more specific, I am suggesting:
- use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
type since it is always that). This should cover all the U-Boot
allocations and make sure they are safely within the malloc() pool
- use efi_allocate_pages() for other memory types (keeping the memory
type in metadata)

>
> You can switch some callsite of efi_allocate_pool to
> efi_allocate_pages() internally. Will that fix the behavior?

It still allocates memory 'in space' and uses 4KB for each allocation.

Regards,
Simon



>
> Regards
> /Ilias
> >
> > > >
> > > > >
> > > > > The specification is available https://uefi.org/specifications.
> > > >
> > > > The problem is that it is thousands of pages so it is hard to pick up
> > > > on some of these issues.
> > > >
> > > > >
> > > > > >
> > > > > > -     r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
> > > > > > -                            &addr);
> > > > > > -     if (r == EFI_SUCCESS) {
> > > > > > -             alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
> > > > > > -             alloc->num_pages = num_pages;
> > > > > > -             alloc->checksum = checksum(alloc);
> > > > > > -             *buffer = alloc->data;
> > > > > > -     }
> > > > > > +     /*
> > > > > > +      * Some tests crash on qemu_arm etc. if the correct size is allocated.
> > > > > > +      * Adding 0x10 seems to fix test_efi_selftest_device_tree
> > > > > > +      * Increasing it to 0x20 seems to fix test_efi_selftest_base except
> > > > > > +      * for riscv64 (in CI only). But 0x100 fixes CI too.
> > > > > > +      *
> > > > > > +      * This workaround can be dropped once these problems are resolved
> > > > > > +      */
> > > > > > +     ptr = memalign(8, size + 0x100);
> > > > >
> > > > > We were using ARCH_DMA_MINALIGN before this patch.
> > > >
> > > > Where is that in the code? All I see is EFI_PAGE_SHIFT.
> > > >
> > > > >
> > > > > If we are overwriting allocated memory, we must fix that instead of
> > > > > increasing size. Do you have a git tag showing the problem?
> > > >
> > > > Not easy to do, because the 4KB per allocation thing has been there
> > > > from the start. It might be possible to apply my patch to earlier and
> > > > earlier U-Boots to try to bisect it.
> >
> > Would anyone like to take a look at these presumed bugs?
> >
> > It might be worth moving to a newer dlmalloc() [1,2] as it has a checker.
> >
> > > >
> > [..]
> >
> > Regards,
> > Simon
> >
> > [1] https://gee.cs.oswego.edu/dl/html/malloc.html
> > [2] https://gee.cs.oswego.edu/pub/misc/malloc.c

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-29 15:28             ` Simon Glass
@ 2024-07-30  8:19               ` Ilias Apalodimas
  2024-07-30 14:38                 ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Ilias Apalodimas @ 2024-07-30  8:19 UTC (permalink / raw)
  To: Simon Glass
  Cc: Heinrich Schuchardt, Sughosh Ganu, Tom Rini, U-Boot Mailing List

On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Ilias,
>
> On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > Hi Simon
> >
> > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Ilias,
> > >
> > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > <ilias.apalodimas@linaro.org> wrote:
> > > >
> > > > Hi Simon,
> > > >
> > > >
> > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > >
> > > > > Hi Heinrich,
> > > > >
> > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > >
> > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > malloc()-style header on each block.
> > > > > > >
> > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > >
> > > > > > > - it should normally be large enough for pool allocations
> > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > >    EFI_LOADER
> > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > - it is used for most EFI allocations already
> > > > > > >
> > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > (presumed) bugs are located.
> > > > > > >
> > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > ---
> > > > > > >
> > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > >
> > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > >   void *efi_bounce_buffer;
> > > > > > >   #endif
> > > > > > >
> > > > > > > -/**
> > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > - *
> > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > - * @checksum:        checksum
> > > > > > > - * @data:    allocated pool memory
> > > > > > > - *
> > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > - * to be able to free the correct amount later.
> > > > > > > - *
> > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > - *
> > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > - * prepend each allocation with these header fields.
> > > > > > > - */
> > > > > > > -struct efi_pool_allocation {
> > > > > > > -     u64 num_pages;
> > > > > > > -     u64 checksum;
> > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > -};
> > > > > > > -
> > > > > > > -/**
> > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > - *
> > > > > > > - * @alloc:   allocation header
> > > > > > > - * Return:   checksum, always non-zero
> > > > > > > - */
> > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > -{
> > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > -     if (!ret)
> > > > > > > -             ++ret;
> > > > > > > -     return ret;
> > > > > > > -}
> > > > > > > -
> > > > > > >   /**
> > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > >    *
> > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > >    * @buffer: allocated memory
> > > > > > >    * Return:  status code
> > > > > > >    */
> > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > +                            void **buffer)
> > > > > > >   {
> > > > > > > -     efi_status_t r;
> > > > > > > -     u64 addr;
> > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > +     void *ptr;
> > > > > > >
> > > > > > >       if (!check_allowed())
> > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > >               return EFI_SUCCESS;
> > > > > > >       }
> > > > > >
> > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > >
> > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > report it in GetMemoryMap().
> > > > >
> > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > actually stated somewhere?
> > > >
> > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > have to be. Although u-boot doesn't currently support this on modern
> > > > secure systems you can't have code and data mixed on the same page.
> > > > This would prevent you to map pages with proper permissions and take
> > > > advantage of CPU security features  e.g RW^X.
> > >
> > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > EFI_BOOT_SERVICES_DATA isn't that important?
> > >
> > > But if we did want to fix that, we could make the malloc region
> > > EFI_BOOT_SERVICES_DATA.
> > >
> > > >
> > > > In any case, the AllocatePool description reads "This function
> > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > do that and allocate from the malloc space, we need to teach malloc
> > > > some EFI semantics, but that's a really bad idea.
> > >
> > > Here I don't see the difference between EFI's malloc(n) and
> > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > the spec'. It seems very clear to me that the bootloader can use
> > > whatever algorithm it likes to provide the allocated memory for the
> > > pool.
> > >
> > > What sort of EFI semantics are you referring to?
> >
> > the memory type. Your patch removes the call to efi_allocate_pages
> > which tracks it.
>
> I can put that code back, depending on what we decide below.
>
> >
> > >
> > > >
> > > > >
> > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > >
> > > > That's not entirely correct, we use a lot more than these 2.
> > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > quick grep
> > >
> > > Yes I did a quick grep too, but then checked each site. The first two
> > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > allocation.
> > >
> > > > >
> > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > I am seeing.
> > >
> > > Any comments on this?
> >
> > This violates the spec and probably breaks a few tests and the EFI
> > boot services API, as you are supposed to be able to define the memory
> > type. We might be able to control it from U-Boot, but EFI apps are
> > going to end up being buggy and hard to debug -- e.g an app calls
> > allocatePool to allocate memory that needs to be preserved at runtime.
>
> To be more specific, I am suggesting:
> - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> type since it is always that). This should cover all the U-Boot
> allocations and make sure they are safely within the malloc() pool
> - use efi_allocate_pages() for other memory types (keeping the memory
> type in metadata)
>

Personally, I don't see the point and we deviate from the spec as
well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
you need to allocate *pages* to grow the pool. What's missing from our
efi_allocate_pool() is the ability to merge allocations of the same
memory type to an existing pool assuming there's space, rather than
requesting a new pool of 4kb.

Regards
/Ilias


> >
> > You can switch some callsite of efi_allocate_pool to
> > efi_allocate_pages() internally. Will that fix the behavior?
>
> It still allocates memory 'in space' and uses 4KB for each allocation.
>
> Regards,
> Simon
>
>
>
> >
> > Regards
> > /Ilias
> > >
> > > > >
> > > > > >
> > > > > > The specification is available https://uefi.org/specifications.
> > > > >
> > > > > The problem is that it is thousands of pages so it is hard to pick up
> > > > > on some of these issues.
> > > > >
> > > > > >
> > > > > > >
> > > > > > > -     r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
> > > > > > > -                            &addr);
> > > > > > > -     if (r == EFI_SUCCESS) {
> > > > > > > -             alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
> > > > > > > -             alloc->num_pages = num_pages;
> > > > > > > -             alloc->checksum = checksum(alloc);
> > > > > > > -             *buffer = alloc->data;
> > > > > > > -     }
> > > > > > > +     /*
> > > > > > > +      * Some tests crash on qemu_arm etc. if the correct size is allocated.
> > > > > > > +      * Adding 0x10 seems to fix test_efi_selftest_device_tree
> > > > > > > +      * Increasing it to 0x20 seems to fix test_efi_selftest_base except
> > > > > > > +      * for riscv64 (in CI only). But 0x100 fixes CI too.
> > > > > > > +      *
> > > > > > > +      * This workaround can be dropped once these problems are resolved
> > > > > > > +      */
> > > > > > > +     ptr = memalign(8, size + 0x100);
> > > > > >
> > > > > > We were using ARCH_DMA_MINALIGN before this patch.
> > > > >
> > > > > Where is that in the code? All I see is EFI_PAGE_SHIFT.
> > > > >
> > > > > >
> > > > > > If we are overwriting allocated memory, we must fix that instead of
> > > > > > increasing size. Do you have a git tag showing the problem?
> > > > >
> > > > > Not easy to do, because the 4KB per allocation thing has been there
> > > > > from the start. It might be possible to apply my patch to earlier and
> > > > > earlier U-Boots to try to bisect it.
> > >
> > > Would anyone like to take a look at these presumed bugs?
> > >
> > > It might be worth moving to a newer dlmalloc() [1,2] as it has a checker.
> > >
> > > > >
> > > [..]
> > >
> > > Regards,
> > > Simon
> > >
> > > [1] https://gee.cs.oswego.edu/dl/html/malloc.html
> > > [2] https://gee.cs.oswego.edu/pub/misc/malloc.c

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30  8:19               ` Ilias Apalodimas
@ 2024-07-30 14:38                 ` Simon Glass
  2024-07-30 14:53                   ` Mark Kettenis
  2024-07-30 14:53                   ` Ilias Apalodimas
  0 siblings, 2 replies; 45+ messages in thread
From: Simon Glass @ 2024-07-30 14:38 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Heinrich Schuchardt, Sughosh Ganu, Tom Rini, U-Boot Mailing List

Hi Ilias,

On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Ilias,
> >
> > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > <ilias.apalodimas@linaro.org> wrote:
> > >
> > > Hi Simon
> > >
> > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Ilias,
> > > >
> > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > <ilias.apalodimas@linaro.org> wrote:
> > > > >
> > > > > Hi Simon,
> > > > >
> > > > >
> > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > >
> > > > > > Hi Heinrich,
> > > > > >
> > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > >
> > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > malloc()-style header on each block.
> > > > > > > >
> > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > >
> > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > >    EFI_LOADER
> > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > - it is used for most EFI allocations already
> > > > > > > >
> > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > (presumed) bugs are located.
> > > > > > > >
> > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > ---
> > > > > > > >
> > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > >
> > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > >   void *efi_bounce_buffer;
> > > > > > > >   #endif
> > > > > > > >
> > > > > > > > -/**
> > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > - *
> > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > - * @checksum:        checksum
> > > > > > > > - * @data:    allocated pool memory
> > > > > > > > - *
> > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > - *
> > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > - *
> > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > - */
> > > > > > > > -struct efi_pool_allocation {
> > > > > > > > -     u64 num_pages;
> > > > > > > > -     u64 checksum;
> > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > -};
> > > > > > > > -
> > > > > > > > -/**
> > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > - *
> > > > > > > > - * @alloc:   allocation header
> > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > - */
> > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > -{
> > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > -     if (!ret)
> > > > > > > > -             ++ret;
> > > > > > > > -     return ret;
> > > > > > > > -}
> > > > > > > > -
> > > > > > > >   /**
> > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > >    *
> > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > >    * @buffer: allocated memory
> > > > > > > >    * Return:  status code
> > > > > > > >    */
> > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > +                            void **buffer)
> > > > > > > >   {
> > > > > > > > -     efi_status_t r;
> > > > > > > > -     u64 addr;
> > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > +     void *ptr;
> > > > > > > >
> > > > > > > >       if (!check_allowed())
> > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > >               return EFI_SUCCESS;
> > > > > > > >       }
> > > > > > >
> > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > >
> > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > report it in GetMemoryMap().
> > > > > >
> > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > actually stated somewhere?
> > > > >
> > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > secure systems you can't have code and data mixed on the same page.
> > > > > This would prevent you to map pages with proper permissions and take
> > > > > advantage of CPU security features  e.g RW^X.
> > > >
> > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > >
> > > > But if we did want to fix that, we could make the malloc region
> > > > EFI_BOOT_SERVICES_DATA.
> > > >
> > > > >
> > > > > In any case, the AllocatePool description reads "This function
> > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > some EFI semantics, but that's a really bad idea.
> > > >
> > > > Here I don't see the difference between EFI's malloc(n) and
> > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > the spec'. It seems very clear to me that the bootloader can use
> > > > whatever algorithm it likes to provide the allocated memory for the
> > > > pool.
> > > >
> > > > What sort of EFI semantics are you referring to?
> > >
> > > the memory type. Your patch removes the call to efi_allocate_pages
> > > which tracks it.
> >
> > I can put that code back, depending on what we decide below.
> >
> > >
> > > >
> > > > >
> > > > > >
> > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > >
> > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > quick grep
> > > >
> > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > allocation.
> > > >
> > > > > >
> > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > I am seeing.
> > > >
> > > > Any comments on this?
> > >
> > > This violates the spec and probably breaks a few tests and the EFI
> > > boot services API, as you are supposed to be able to define the memory
> > > type. We might be able to control it from U-Boot, but EFI apps are
> > > going to end up being buggy and hard to debug -- e.g an app calls
> > > allocatePool to allocate memory that needs to be preserved at runtime.
> >
> > To be more specific, I am suggesting:
> > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > type since it is always that). This should cover all the U-Boot
> > allocations and make sure they are safely within the malloc() pool
> > - use efi_allocate_pages() for other memory types (keeping the memory
> > type in metadata)
> >
>
> Personally, I don't see the point and we deviate from the spec as
> well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> you need to allocate *pages* to grow the pool. What's missing from our
> efi_allocate_pool() is the ability to merge allocations of the same
> memory type to an existing pool assuming there's space, rather than
> requesting a new pool of 4kb.

"This function allocates pages from EfiConventionalMemory as needed to
grow the requested pool type". So far as EFI is concerned, the
malloc() region is in EfiConventionalMemory, so I don't see any
deviation.

Please also see below.

> > >
> > > You can switch some callsite of efi_allocate_pool to
> > > efi_allocate_pages() internally. Will that fix the behavior?
> >
> > It still allocates memory 'in space' and uses 4KB for each allocation.
> >

This is the key point that doesn't seem to be coming across. The
current allocator is allocating memory wherever it likes, potentially
interfering with the kernel_addr_r addresses, etc. as on qemu_arm.

> > > > > > >
> > > > > > > The specification is available https://uefi.org/specifications.
> > > > > >
> > > > > > The problem is that it is thousands of pages so it is hard to pick up
> > > > > > on some of these issues.
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > -     r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
> > > > > > > > -                            &addr);
> > > > > > > > -     if (r == EFI_SUCCESS) {
> > > > > > > > -             alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
> > > > > > > > -             alloc->num_pages = num_pages;
> > > > > > > > -             alloc->checksum = checksum(alloc);
> > > > > > > > -             *buffer = alloc->data;
> > > > > > > > -     }
> > > > > > > > +     /*
> > > > > > > > +      * Some tests crash on qemu_arm etc. if the correct size is allocated.
> > > > > > > > +      * Adding 0x10 seems to fix test_efi_selftest_device_tree
> > > > > > > > +      * Increasing it to 0x20 seems to fix test_efi_selftest_base except
> > > > > > > > +      * for riscv64 (in CI only). But 0x100 fixes CI too.
> > > > > > > > +      *
> > > > > > > > +      * This workaround can be dropped once these problems are resolved
> > > > > > > > +      */
> > > > > > > > +     ptr = memalign(8, size + 0x100);
> > > > > > >
> > > > > > > We were using ARCH_DMA_MINALIGN before this patch.
> > > > > >
> > > > > > Where is that in the code? All I see is EFI_PAGE_SHIFT.
> > > > > >
> > > > > > >
> > > > > > > If we are overwriting allocated memory, we must fix that instead of
> > > > > > > increasing size. Do you have a git tag showing the problem?
> > > > > >
> > > > > > Not easy to do, because the 4KB per allocation thing has been there
> > > > > > from the start. It might be possible to apply my patch to earlier and
> > > > > > earlier U-Boots to try to bisect it.
> > > >
> > > > Would anyone like to take a look at these presumed bugs?
> > > >
> > > > It might be worth moving to a newer dlmalloc() [1,2] as it has a checker.
> > > >
> > > > > >
> > > > [..]
> > > >
> > > >
> > > > [1] https://gee.cs.oswego.edu/dl/html/malloc.html
> > > > [2] https://gee.cs.oswego.edu/pub/misc/malloc.c

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 14:38                 ` Simon Glass
@ 2024-07-30 14:53                   ` Mark Kettenis
  2024-07-30 15:18                     ` Simon Glass
  2024-07-30 14:53                   ` Ilias Apalodimas
  1 sibling, 1 reply; 45+ messages in thread
From: Mark Kettenis @ 2024-07-30 14:53 UTC (permalink / raw)
  To: Simon Glass; +Cc: ilias.apalodimas, xypron.glpk, sughosh.ganu, trini, u-boot

> From: Simon Glass <sjg@chromium.org>
> Date: Tue, 30 Jul 2024 08:38:03 -0600
> 
> Hi Ilias,
> 
> On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Ilias,
> > >
> > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > <ilias.apalodimas@linaro.org> wrote:
> > > >
> > > > Hi Simon
> > > >
> > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > >
> > > > > Hi Ilias,
> > > > >
> > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > >
> > > > > > Hi Simon,
> > > > > >
> > > > > >
> > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > >
> > > > > > > Hi Heinrich,
> > > > > > >
> > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > >
> > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > malloc()-style header on each block.
> > > > > > > > >
> > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > >
> > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > >    EFI_LOADER
> > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > >
> > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > (presumed) bugs are located.
> > > > > > > > >
> > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > ---
> > > > > > > > >
> > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > >
> > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > >   #endif
> > > > > > > > >
> > > > > > > > > -/**
> > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > - *
> > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > - * @checksum:        checksum
> > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > - *
> > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > - *
> > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > - *
> > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > - */
> > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > -     u64 num_pages;
> > > > > > > > > -     u64 checksum;
> > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > -};
> > > > > > > > > -
> > > > > > > > > -/**
> > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > - *
> > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > - */
> > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > -{
> > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > -     if (!ret)
> > > > > > > > > -             ++ret;
> > > > > > > > > -     return ret;
> > > > > > > > > -}
> > > > > > > > > -
> > > > > > > > >   /**
> > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > >    *
> > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > >    * @buffer: allocated memory
> > > > > > > > >    * Return:  status code
> > > > > > > > >    */
> > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > +                            void **buffer)
> > > > > > > > >   {
> > > > > > > > > -     efi_status_t r;
> > > > > > > > > -     u64 addr;
> > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > +     void *ptr;
> > > > > > > > >
> > > > > > > > >       if (!check_allowed())
> > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > >       }
> > > > > > > >
> > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > >
> > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > report it in GetMemoryMap().
> > > > > > >
> > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > actually stated somewhere?
> > > > > >
> > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > advantage of CPU security features  e.g RW^X.
> > > > >
> > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > >
> > > > > But if we did want to fix that, we could make the malloc region
> > > > > EFI_BOOT_SERVICES_DATA.
> > > > >
> > > > > >
> > > > > > In any case, the AllocatePool description reads "This function
> > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > some EFI semantics, but that's a really bad idea.
> > > > >
> > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > pool.
> > > > >
> > > > > What sort of EFI semantics are you referring to?
> > > >
> > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > which tracks it.
> > >
> > > I can put that code back, depending on what we decide below.
> > >
> > > >
> > > > >
> > > > > >
> > > > > > >
> > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > >
> > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > quick grep
> > > > >
> > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > allocation.
> > > > >
> > > > > > >
> > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > I am seeing.
> > > > >
> > > > > Any comments on this?
> > > >
> > > > This violates the spec and probably breaks a few tests and the EFI
> > > > boot services API, as you are supposed to be able to define the memory
> > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > >
> > > To be more specific, I am suggesting:
> > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > type since it is always that). This should cover all the U-Boot
> > > allocations and make sure they are safely within the malloc() pool
> > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > type in metadata)
> > >
> >
> > Personally, I don't see the point and we deviate from the spec as
> > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > you need to allocate *pages* to grow the pool. What's missing from our
> > efi_allocate_pool() is the ability to merge allocations of the same
> > memory type to an existing pool assuming there's space, rather than
> > requesting a new pool of 4kb.
> 
> "This function allocates pages from EfiConventionalMemory as needed to
> grow the requested pool type". So far as EFI is concerned, the
> malloc() region is in EfiConventionalMemory, so I don't see any
> deviation.
> 
> Please also see below.
> 
> > > >
> > > > You can switch some callsite of efi_allocate_pool to
> > > > efi_allocate_pages() internally. Will that fix the behavior?
> > >
> > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > >
> 
> This is the key point that doesn't seem to be coming across. The
> current allocator is allocating memory wherever it likes, potentially
> interfering with the kernel_addr_r addresses, etc. as on qemu_arm.

And even if we'd go ahead with your idea, there would still be other
EFI allocations that would interefere.

The real solution here is to start thinking about these addresses as
reservations, and actually record those reservations properly.  That
is kind-of what I did for the apple machines: the various addresses
are determined by allocating memory blocks using LMB.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 14:38                 ` Simon Glass
  2024-07-30 14:53                   ` Mark Kettenis
@ 2024-07-30 14:53                   ` Ilias Apalodimas
  2024-07-30 15:18                     ` Simon Glass
  1 sibling, 1 reply; 45+ messages in thread
From: Ilias Apalodimas @ 2024-07-30 14:53 UTC (permalink / raw)
  To: Simon Glass
  Cc: Heinrich Schuchardt, Sughosh Ganu, Tom Rini, U-Boot Mailing List

On Tue, 30 Jul 2024 at 17:38, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Ilias,
>
> On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Ilias,
> > >
> > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > <ilias.apalodimas@linaro.org> wrote:
> > > >
> > > > Hi Simon
> > > >
> > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > >
> > > > > Hi Ilias,
> > > > >
> > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > >
> > > > > > Hi Simon,
> > > > > >
> > > > > >
> > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > >
> > > > > > > Hi Heinrich,
> > > > > > >
> > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > >
> > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > malloc()-style header on each block.
> > > > > > > > >
> > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > >
> > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > >    EFI_LOADER
> > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > >
> > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > (presumed) bugs are located.
> > > > > > > > >
> > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > ---
> > > > > > > > >
> > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > >
> > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > >   #endif
> > > > > > > > >
> > > > > > > > > -/**
> > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > - *
> > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > - * @checksum:        checksum
> > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > - *
> > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > - *
> > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > - *
> > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > - */
> > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > -     u64 num_pages;
> > > > > > > > > -     u64 checksum;
> > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > -};
> > > > > > > > > -
> > > > > > > > > -/**
> > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > - *
> > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > - */
> > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > -{
> > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > -     if (!ret)
> > > > > > > > > -             ++ret;
> > > > > > > > > -     return ret;
> > > > > > > > > -}
> > > > > > > > > -
> > > > > > > > >   /**
> > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > >    *
> > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > >    * @buffer: allocated memory
> > > > > > > > >    * Return:  status code
> > > > > > > > >    */
> > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > +                            void **buffer)
> > > > > > > > >   {
> > > > > > > > > -     efi_status_t r;
> > > > > > > > > -     u64 addr;
> > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > +     void *ptr;
> > > > > > > > >
> > > > > > > > >       if (!check_allowed())
> > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > >       }
> > > > > > > >
> > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > >
> > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > report it in GetMemoryMap().
> > > > > > >
> > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > actually stated somewhere?
> > > > > >
> > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > advantage of CPU security features  e.g RW^X.
> > > > >
> > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > >
> > > > > But if we did want to fix that, we could make the malloc region
> > > > > EFI_BOOT_SERVICES_DATA.
> > > > >
> > > > > >
> > > > > > In any case, the AllocatePool description reads "This function
> > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > some EFI semantics, but that's a really bad idea.
> > > > >
> > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > pool.
> > > > >
> > > > > What sort of EFI semantics are you referring to?
> > > >
> > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > which tracks it.
> > >
> > > I can put that code back, depending on what we decide below.
> > >
> > > >
> > > > >
> > > > > >
> > > > > > >
> > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > >
> > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > quick grep
> > > > >
> > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > allocation.
> > > > >
> > > > > > >
> > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > I am seeing.
> > > > >
> > > > > Any comments on this?
> > > >
> > > > This violates the spec and probably breaks a few tests and the EFI
> > > > boot services API, as you are supposed to be able to define the memory
> > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > >
> > > To be more specific, I am suggesting:
> > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > type since it is always that). This should cover all the U-Boot
> > > allocations and make sure they are safely within the malloc() pool
> > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > type in metadata)
> > >
> >
> > Personally, I don't see the point and we deviate from the spec as
> > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > you need to allocate *pages* to grow the pool. What's missing from our
> > efi_allocate_pool() is the ability to merge allocations of the same
> > memory type to an existing pool assuming there's space, rather than
> > requesting a new pool of 4kb.
>
> "This function allocates pages from EfiConventionalMemory as needed to
> grow the requested pool type". So far as EFI is concerned, the
> malloc() region is in EfiConventionalMemory, so I don't see any
> deviation.
>
> Please also see below.
>
> > > >
> > > > You can switch some callsite of efi_allocate_pool to
> > > > efi_allocate_pages() internally. Will that fix the behavior?
> > >
> > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > >
>
> This is the key point that doesn't seem to be coming across. The
> current allocator is allocating memory wherever it likes, potentially
> interfering with the kernel_addr_r addresses, etc. as on qemu_arm.

It is, but I don't think using malloc is solving it. It's papering
over the problem, because if someone in the future launches an EFI app
or allocates EFI memory with a different type you are back on the same
problem.

Regards
/Ilias



>
> > > > > > > >
> > > > > > > > The specification is available https://uefi.org/specifications.
> > > > > > >
> > > > > > > The problem is that it is thousands of pages so it is hard to pick up
> > > > > > > on some of these issues.
> > > > > > >
> > > > > > > >
> > > > > > > > >
> > > > > > > > > -     r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
> > > > > > > > > -                            &addr);
> > > > > > > > > -     if (r == EFI_SUCCESS) {
> > > > > > > > > -             alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
> > > > > > > > > -             alloc->num_pages = num_pages;
> > > > > > > > > -             alloc->checksum = checksum(alloc);
> > > > > > > > > -             *buffer = alloc->data;
> > > > > > > > > -     }
> > > > > > > > > +     /*
> > > > > > > > > +      * Some tests crash on qemu_arm etc. if the correct size is allocated.
> > > > > > > > > +      * Adding 0x10 seems to fix test_efi_selftest_device_tree
> > > > > > > > > +      * Increasing it to 0x20 seems to fix test_efi_selftest_base except
> > > > > > > > > +      * for riscv64 (in CI only). But 0x100 fixes CI too.
> > > > > > > > > +      *
> > > > > > > > > +      * This workaround can be dropped once these problems are resolved
> > > > > > > > > +      */
> > > > > > > > > +     ptr = memalign(8, size + 0x100);
> > > > > > > >
> > > > > > > > We were using ARCH_DMA_MINALIGN before this patch.
> > > > > > >
> > > > > > > Where is that in the code? All I see is EFI_PAGE_SHIFT.
> > > > > > >
> > > > > > > >
> > > > > > > > If we are overwriting allocated memory, we must fix that instead of
> > > > > > > > increasing size. Do you have a git tag showing the problem?
> > > > > > >
> > > > > > > Not easy to do, because the 4KB per allocation thing has been there
> > > > > > > from the start. It might be possible to apply my patch to earlier and
> > > > > > > earlier U-Boots to try to bisect it.
> > > > >
> > > > > Would anyone like to take a look at these presumed bugs?
> > > > >
> > > > > It might be worth moving to a newer dlmalloc() [1,2] as it has a checker.
> > > > >
> > > > > > >
> > > > > [..]
> > > > >
> > > > >
> > > > > [1] https://gee.cs.oswego.edu/dl/html/malloc.html
> > > > > [2] https://gee.cs.oswego.edu/pub/misc/malloc.c
>
> Regards,
> Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 14:53                   ` Ilias Apalodimas
@ 2024-07-30 15:18                     ` Simon Glass
  2024-07-30 15:24                       ` Tom Rini
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-30 15:18 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Heinrich Schuchardt, Sughosh Ganu, Tom Rini, U-Boot Mailing List

Hi Ilias,

On Tue, 30 Jul 2024 at 08:54, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> On Tue, 30 Jul 2024 at 17:38, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Ilias,
> >
> > On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> > <ilias.apalodimas@linaro.org> wrote:
> > >
> > > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Ilias,
> > > >
> > > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > > <ilias.apalodimas@linaro.org> wrote:
> > > > >
> > > > > Hi Simon
> > > > >
> > > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > > >
> > > > > > Hi Ilias,
> > > > > >
> > > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > >
> > > > > > > Hi Simon,
> > > > > > >
> > > > > > >
> > > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > >
> > > > > > > > Hi Heinrich,
> > > > > > > >
> > > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > > >
> > > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > > malloc()-style header on each block.
> > > > > > > > > >
> > > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > > >
> > > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > > >    EFI_LOADER
> > > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > > >
> > > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > > (presumed) bugs are located.
> > > > > > > > > >
> > > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > > ---
> > > > > > > > > >
> > > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > > >
> > > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > > >   #endif
> > > > > > > > > >
> > > > > > > > > > -/**
> > > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > > - *
> > > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > > - * @checksum:        checksum
> > > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > > - *
> > > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > > - *
> > > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > > - *
> > > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > > - */
> > > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > > -     u64 num_pages;
> > > > > > > > > > -     u64 checksum;
> > > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > > -};
> > > > > > > > > > -
> > > > > > > > > > -/**
> > > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > > - *
> > > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > > - */
> > > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > > -{
> > > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > > -     if (!ret)
> > > > > > > > > > -             ++ret;
> > > > > > > > > > -     return ret;
> > > > > > > > > > -}
> > > > > > > > > > -
> > > > > > > > > >   /**
> > > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > > >    *
> > > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > > >    * @buffer: allocated memory
> > > > > > > > > >    * Return:  status code
> > > > > > > > > >    */
> > > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > +                            void **buffer)
> > > > > > > > > >   {
> > > > > > > > > > -     efi_status_t r;
> > > > > > > > > > -     u64 addr;
> > > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > > +     void *ptr;
> > > > > > > > > >
> > > > > > > > > >       if (!check_allowed())
> > > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > > >       }
> > > > > > > > >
> > > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > > >
> > > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > > report it in GetMemoryMap().
> > > > > > > >
> > > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > > actually stated somewhere?
> > > > > > >
> > > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > > advantage of CPU security features  e.g RW^X.
> > > > > >
> > > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > > >
> > > > > > But if we did want to fix that, we could make the malloc region
> > > > > > EFI_BOOT_SERVICES_DATA.
> > > > > >
> > > > > > >
> > > > > > > In any case, the AllocatePool description reads "This function
> > > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > > some EFI semantics, but that's a really bad idea.
> > > > > >
> > > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > > pool.
> > > > > >
> > > > > > What sort of EFI semantics are you referring to?
> > > > >
> > > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > > which tracks it.
> > > >
> > > > I can put that code back, depending on what we decide below.
> > > >
> > > > >
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > > >
> > > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > > quick grep
> > > > > >
> > > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > > allocation.
> > > > > >
> > > > > > > >
> > > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > > I am seeing.
> > > > > >
> > > > > > Any comments on this?
> > > > >
> > > > > This violates the spec and probably breaks a few tests and the EFI
> > > > > boot services API, as you are supposed to be able to define the memory
> > > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > > >
> > > > To be more specific, I am suggesting:
> > > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > > type since it is always that). This should cover all the U-Boot
> > > > allocations and make sure they are safely within the malloc() pool
> > > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > > type in metadata)
> > > >
> > >
> > > Personally, I don't see the point and we deviate from the spec as
> > > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > > you need to allocate *pages* to grow the pool. What's missing from our
> > > efi_allocate_pool() is the ability to merge allocations of the same
> > > memory type to an existing pool assuming there's space, rather than
> > > requesting a new pool of 4kb.
> >
> > "This function allocates pages from EfiConventionalMemory as needed to
> > grow the requested pool type". So far as EFI is concerned, the
> > malloc() region is in EfiConventionalMemory, so I don't see any
> > deviation.
> >
> > Please also see below.
> >
> > > > >
> > > > > You can switch some callsite of efi_allocate_pool to
> > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > >
> > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > >
> >
> > This is the key point that doesn't seem to be coming across. The
> > current allocator is allocating memory wherever it likes, potentially
> > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
>
> It is, but I don't think using malloc is solving it. It's papering
> over the problem, because if someone in the future launches an EFI app
> or allocates EFI memory with a different type you are back on the same
> problem.

It is certainly solving this problem.

Once the app is launched it is OK to overwrite memory...after all it
has been loaded and is running. The issue is that these little
allocations can end up anywhere in memory. Did you see the qemu_arm
note?

Regards,
Simon

> >
> > > > > > > > >
> > > > > > > > > The specification is available https://uefi.org/specifications.
> > > > > > > >
> > > > > > > > The problem is that it is thousands of pages so it is hard to pick up
> > > > > > > > on some of these issues.
> > > > > > > >
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > -     r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
> > > > > > > > > > -                            &addr);
> > > > > > > > > > -     if (r == EFI_SUCCESS) {
> > > > > > > > > > -             alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
> > > > > > > > > > -             alloc->num_pages = num_pages;
> > > > > > > > > > -             alloc->checksum = checksum(alloc);
> > > > > > > > > > -             *buffer = alloc->data;
> > > > > > > > > > -     }
> > > > > > > > > > +     /*
> > > > > > > > > > +      * Some tests crash on qemu_arm etc. if the correct size is allocated.
> > > > > > > > > > +      * Adding 0x10 seems to fix test_efi_selftest_device_tree
> > > > > > > > > > +      * Increasing it to 0x20 seems to fix test_efi_selftest_base except
> > > > > > > > > > +      * for riscv64 (in CI only). But 0x100 fixes CI too.
> > > > > > > > > > +      *
> > > > > > > > > > +      * This workaround can be dropped once these problems are resolved
> > > > > > > > > > +      */
> > > > > > > > > > +     ptr = memalign(8, size + 0x100);
> > > > > > > > >
> > > > > > > > > We were using ARCH_DMA_MINALIGN before this patch.
> > > > > > > >
> > > > > > > > Where is that in the code? All I see is EFI_PAGE_SHIFT.
> > > > > > > >
> > > > > > > > >
> > > > > > > > > If we are overwriting allocated memory, we must fix that instead of
> > > > > > > > > increasing size. Do you have a git tag showing the problem?
> > > > > > > >
> > > > > > > > Not easy to do, because the 4KB per allocation thing has been there
> > > > > > > > from the start. It might be possible to apply my patch to earlier and
> > > > > > > > earlier U-Boots to try to bisect it.
> > > > > >
> > > > > > Would anyone like to take a look at these presumed bugs?
> > > > > >
> > > > > > It might be worth moving to a newer dlmalloc() [1,2] as it has a checker.
> > > > > >
> > > > > > > >
> > > > > > [..]
> > > > > >
> > > > > >
> > > > > > [1] https://gee.cs.oswego.edu/dl/html/malloc.html
> > > > > > [2] https://gee.cs.oswego.edu/pub/misc/malloc.c

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 14:53                   ` Mark Kettenis
@ 2024-07-30 15:18                     ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2024-07-30 15:18 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: ilias.apalodimas, xypron.glpk, sughosh.ganu, trini, u-boot

Hi Mark,

On Tue, 30 Jul 2024 at 08:53, Mark Kettenis <mark.kettenis@xs4all.nl> wrote:
>
> > From: Simon Glass <sjg@chromium.org>
> > Date: Tue, 30 Jul 2024 08:38:03 -0600
> >
> > Hi Ilias,
> >
> > On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> > <ilias.apalodimas@linaro.org> wrote:
> > >
> > > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Ilias,
> > > >
> > > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > > <ilias.apalodimas@linaro.org> wrote:
> > > > >
> > > > > Hi Simon
> > > > >
> > > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > > >
> > > > > > Hi Ilias,
> > > > > >
> > > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > >
> > > > > > > Hi Simon,
> > > > > > >
> > > > > > >
> > > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > >
> > > > > > > > Hi Heinrich,
> > > > > > > >
> > > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > > >
> > > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > > malloc()-style header on each block.
> > > > > > > > > >
> > > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > > >
> > > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > > >    EFI_LOADER
> > > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > > >
> > > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > > (presumed) bugs are located.
> > > > > > > > > >
> > > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > > ---
> > > > > > > > > >
> > > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > > >
> > > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > > >   #endif
> > > > > > > > > >
> > > > > > > > > > -/**
> > > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > > - *
> > > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > > - * @checksum:        checksum
> > > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > > - *
> > > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > > - *
> > > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > > - *
> > > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > > - */
> > > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > > -     u64 num_pages;
> > > > > > > > > > -     u64 checksum;
> > > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > > -};
> > > > > > > > > > -
> > > > > > > > > > -/**
> > > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > > - *
> > > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > > - */
> > > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > > -{
> > > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > > -     if (!ret)
> > > > > > > > > > -             ++ret;
> > > > > > > > > > -     return ret;
> > > > > > > > > > -}
> > > > > > > > > > -
> > > > > > > > > >   /**
> > > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > > >    *
> > > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > > >    * @buffer: allocated memory
> > > > > > > > > >    * Return:  status code
> > > > > > > > > >    */
> > > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > +                            void **buffer)
> > > > > > > > > >   {
> > > > > > > > > > -     efi_status_t r;
> > > > > > > > > > -     u64 addr;
> > > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > > +     void *ptr;
> > > > > > > > > >
> > > > > > > > > >       if (!check_allowed())
> > > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > > >       }
> > > > > > > > >
> > > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > > >
> > > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > > report it in GetMemoryMap().
> > > > > > > >
> > > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > > actually stated somewhere?
> > > > > > >
> > > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > > advantage of CPU security features  e.g RW^X.
> > > > > >
> > > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > > >
> > > > > > But if we did want to fix that, we could make the malloc region
> > > > > > EFI_BOOT_SERVICES_DATA.
> > > > > >
> > > > > > >
> > > > > > > In any case, the AllocatePool description reads "This function
> > > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > > some EFI semantics, but that's a really bad idea.
> > > > > >
> > > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > > pool.
> > > > > >
> > > > > > What sort of EFI semantics are you referring to?
> > > > >
> > > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > > which tracks it.
> > > >
> > > > I can put that code back, depending on what we decide below.
> > > >
> > > > >
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > > >
> > > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > > quick grep
> > > > > >
> > > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > > allocation.
> > > > > >
> > > > > > > >
> > > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > > I am seeing.
> > > > > >
> > > > > > Any comments on this?
> > > > >
> > > > > This violates the spec and probably breaks a few tests and the EFI
> > > > > boot services API, as you are supposed to be able to define the memory
> > > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > > >
> > > > To be more specific, I am suggesting:
> > > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > > type since it is always that). This should cover all the U-Boot
> > > > allocations and make sure they are safely within the malloc() pool
> > > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > > type in metadata)
> > > >
> > >
> > > Personally, I don't see the point and we deviate from the spec as
> > > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > > you need to allocate *pages* to grow the pool. What's missing from our
> > > efi_allocate_pool() is the ability to merge allocations of the same
> > > memory type to an existing pool assuming there's space, rather than
> > > requesting a new pool of 4kb.
> >
> > "This function allocates pages from EfiConventionalMemory as needed to
> > grow the requested pool type". So far as EFI is concerned, the
> > malloc() region is in EfiConventionalMemory, so I don't see any
> > deviation.
> >
> > Please also see below.
> >
> > > > >
> > > > > You can switch some callsite of efi_allocate_pool to
> > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > >
> > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > >
> >
> > This is the key point that doesn't seem to be coming across. The
> > current allocator is allocating memory wherever it likes, potentially
> > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
>
> And even if we'd go ahead with your idea, there would still be other
> EFI allocations that would interefere.

Could you be a bit more specific? I believe I covered that already.

>
> The real solution here is to start thinking about these addresses as
> reservations, and actually record those reservations properly.  That
> is kind-of what I did for the apple machines: the various addresses
> are determined by allocating memory blocks using LMB.

That's fine, but we are a way off from that.

Of course, at the moment that lmb is local, so EFI can still overwrite
your addresses. All you have done with the Apple code is to select the
addresses at runtime, instead of using env vars. But the vast majority
of platforms use the latter, with no lmb reservations.

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 15:18                     ` Simon Glass
@ 2024-07-30 15:24                       ` Tom Rini
  2024-07-30 19:42                         ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Tom Rini @ 2024-07-30 15:24 UTC (permalink / raw)
  To: Simon Glass
  Cc: Ilias Apalodimas, Heinrich Schuchardt, Sughosh Ganu,
	U-Boot Mailing List

[-- Attachment #1: Type: text/plain, Size: 13121 bytes --]

On Tue, Jul 30, 2024 at 09:18:02AM -0600, Simon Glass wrote:
> Hi Ilias,
> 
> On Tue, 30 Jul 2024 at 08:54, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > On Tue, 30 Jul 2024 at 17:38, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Ilias,
> > >
> > > On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> > > <ilias.apalodimas@linaro.org> wrote:
> > > >
> > > > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > > > >
> > > > > Hi Ilias,
> > > > >
> > > > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > >
> > > > > > Hi Simon
> > > > > >
> > > > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > > > >
> > > > > > > Hi Ilias,
> > > > > > >
> > > > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > >
> > > > > > > > Hi Simon,
> > > > > > > >
> > > > > > > >
> > > > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi Heinrich,
> > > > > > > > >
> > > > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > > > >
> > > > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > > > malloc()-style header on each block.
> > > > > > > > > > >
> > > > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > > > >
> > > > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > > > >    EFI_LOADER
> > > > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > > > >
> > > > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > > > (presumed) bugs are located.
> > > > > > > > > > >
> > > > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > > > ---
> > > > > > > > > > >
> > > > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > > > >
> > > > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > > > >   #endif
> > > > > > > > > > >
> > > > > > > > > > > -/**
> > > > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > > > - *
> > > > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > > > - * @checksum:        checksum
> > > > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > > > - *
> > > > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > > > - *
> > > > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > > > - *
> > > > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > > > - */
> > > > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > > > -     u64 num_pages;
> > > > > > > > > > > -     u64 checksum;
> > > > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > > > -};
> > > > > > > > > > > -
> > > > > > > > > > > -/**
> > > > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > > > - *
> > > > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > > > - */
> > > > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > > > -{
> > > > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > > > -     if (!ret)
> > > > > > > > > > > -             ++ret;
> > > > > > > > > > > -     return ret;
> > > > > > > > > > > -}
> > > > > > > > > > > -
> > > > > > > > > > >   /**
> > > > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > > > >    *
> > > > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > > > >    * @buffer: allocated memory
> > > > > > > > > > >    * Return:  status code
> > > > > > > > > > >    */
> > > > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > +                            void **buffer)
> > > > > > > > > > >   {
> > > > > > > > > > > -     efi_status_t r;
> > > > > > > > > > > -     u64 addr;
> > > > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > > > +     void *ptr;
> > > > > > > > > > >
> > > > > > > > > > >       if (!check_allowed())
> > > > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > > > >       }
> > > > > > > > > >
> > > > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > > > >
> > > > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > > > report it in GetMemoryMap().
> > > > > > > > >
> > > > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > > > actually stated somewhere?
> > > > > > > >
> > > > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > > > advantage of CPU security features  e.g RW^X.
> > > > > > >
> > > > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > > > >
> > > > > > > But if we did want to fix that, we could make the malloc region
> > > > > > > EFI_BOOT_SERVICES_DATA.
> > > > > > >
> > > > > > > >
> > > > > > > > In any case, the AllocatePool description reads "This function
> > > > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > > > some EFI semantics, but that's a really bad idea.
> > > > > > >
> > > > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > > > pool.
> > > > > > >
> > > > > > > What sort of EFI semantics are you referring to?
> > > > > >
> > > > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > > > which tracks it.
> > > > >
> > > > > I can put that code back, depending on what we decide below.
> > > > >
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > >
> > > > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > > > >
> > > > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > > > quick grep
> > > > > > >
> > > > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > > > allocation.
> > > > > > >
> > > > > > > > >
> > > > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > > > I am seeing.
> > > > > > >
> > > > > > > Any comments on this?
> > > > > >
> > > > > > This violates the spec and probably breaks a few tests and the EFI
> > > > > > boot services API, as you are supposed to be able to define the memory
> > > > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > > > >
> > > > > To be more specific, I am suggesting:
> > > > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > > > type since it is always that). This should cover all the U-Boot
> > > > > allocations and make sure they are safely within the malloc() pool
> > > > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > > > type in metadata)
> > > > >
> > > >
> > > > Personally, I don't see the point and we deviate from the spec as
> > > > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > > > you need to allocate *pages* to grow the pool. What's missing from our
> > > > efi_allocate_pool() is the ability to merge allocations of the same
> > > > memory type to an existing pool assuming there's space, rather than
> > > > requesting a new pool of 4kb.
> > >
> > > "This function allocates pages from EfiConventionalMemory as needed to
> > > grow the requested pool type". So far as EFI is concerned, the
> > > malloc() region is in EfiConventionalMemory, so I don't see any
> > > deviation.
> > >
> > > Please also see below.
> > >
> > > > > >
> > > > > > You can switch some callsite of efi_allocate_pool to
> > > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > > >
> > > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > > >
> > >
> > > This is the key point that doesn't seem to be coming across. The
> > > current allocator is allocating memory wherever it likes, potentially
> > > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
> >
> > It is, but I don't think using malloc is solving it. It's papering
> > over the problem, because if someone in the future launches an EFI app
> > or allocates EFI memory with a different type you are back on the same
> > problem.
> 
> It is certainly solving this problem.
> 
> Once the app is launched it is OK to overwrite memory...after all it
> has been loaded and is running. The issue is that these little
> allocations can end up anywhere in memory. Did you see the qemu_arm
> note?

Isn't this another part of why we need the LMB rework? So that
kernel_addr_r, et al, can be marked as reserved.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 15:24                       ` Tom Rini
@ 2024-07-30 19:42                         ` Simon Glass
  2024-07-30 19:49                           ` Tom Rini
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-30 19:42 UTC (permalink / raw)
  To: Tom Rini
  Cc: Ilias Apalodimas, Heinrich Schuchardt, Sughosh Ganu,
	U-Boot Mailing List

Hi Tom,

On Tue, 30 Jul 2024 at 09:24, Tom Rini <trini@konsulko.com> wrote:
>
> On Tue, Jul 30, 2024 at 09:18:02AM -0600, Simon Glass wrote:
> > Hi Ilias,
> >
> > On Tue, 30 Jul 2024 at 08:54, Ilias Apalodimas
> > <ilias.apalodimas@linaro.org> wrote:
> > >
> > > On Tue, 30 Jul 2024 at 17:38, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Ilias,
> > > >
> > > > On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> > > > <ilias.apalodimas@linaro.org> wrote:
> > > > >
> > > > > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > > > > >
> > > > > > Hi Ilias,
> > > > > >
> > > > > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > >
> > > > > > > Hi Simon
> > > > > > >
> > > > > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > >
> > > > > > > > Hi Ilias,
> > > > > > > >
> > > > > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi Simon,
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Hi Heinrich,
> > > > > > > > > >
> > > > > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > > > > >
> > > > > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > > > > malloc()-style header on each block.
> > > > > > > > > > > >
> > > > > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > > > > >
> > > > > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > > > > >    EFI_LOADER
> > > > > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > > > > >
> > > > > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > > > > (presumed) bugs are located.
> > > > > > > > > > > >
> > > > > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > > > > ---
> > > > > > > > > > > >
> > > > > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > > > > >
> > > > > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > > > > >   #endif
> > > > > > > > > > > >
> > > > > > > > > > > > -/**
> > > > > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > > > > - *
> > > > > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > > > > - * @checksum:        checksum
> > > > > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > > > > - *
> > > > > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > > > > - *
> > > > > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > > > > - *
> > > > > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > > > > - */
> > > > > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > > > > -     u64 num_pages;
> > > > > > > > > > > > -     u64 checksum;
> > > > > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > > > > -};
> > > > > > > > > > > > -
> > > > > > > > > > > > -/**
> > > > > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > > > > - *
> > > > > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > > > > - */
> > > > > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > > > > -{
> > > > > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > > > > -     if (!ret)
> > > > > > > > > > > > -             ++ret;
> > > > > > > > > > > > -     return ret;
> > > > > > > > > > > > -}
> > > > > > > > > > > > -
> > > > > > > > > > > >   /**
> > > > > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > > > > >    *
> > > > > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > > > > >    * @buffer: allocated memory
> > > > > > > > > > > >    * Return:  status code
> > > > > > > > > > > >    */
> > > > > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > +                            void **buffer)
> > > > > > > > > > > >   {
> > > > > > > > > > > > -     efi_status_t r;
> > > > > > > > > > > > -     u64 addr;
> > > > > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > > > > +     void *ptr;
> > > > > > > > > > > >
> > > > > > > > > > > >       if (!check_allowed())
> > > > > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > > > > >       }
> > > > > > > > > > >
> > > > > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > > > > >
> > > > > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > > > > report it in GetMemoryMap().
> > > > > > > > > >
> > > > > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > > > > actually stated somewhere?
> > > > > > > > >
> > > > > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > > > > advantage of CPU security features  e.g RW^X.
> > > > > > > >
> > > > > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > > > > >
> > > > > > > > But if we did want to fix that, we could make the malloc region
> > > > > > > > EFI_BOOT_SERVICES_DATA.
> > > > > > > >
> > > > > > > > >
> > > > > > > > > In any case, the AllocatePool description reads "This function
> > > > > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > > > > some EFI semantics, but that's a really bad idea.
> > > > > > > >
> > > > > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > > > > pool.
> > > > > > > >
> > > > > > > > What sort of EFI semantics are you referring to?
> > > > > > >
> > > > > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > > > > which tracks it.
> > > > > >
> > > > > > I can put that code back, depending on what we decide below.
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > > > > >
> > > > > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > > > > quick grep
> > > > > > > >
> > > > > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > > > > allocation.
> > > > > > > >
> > > > > > > > > >
> > > > > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > > > > I am seeing.
> > > > > > > >
> > > > > > > > Any comments on this?
> > > > > > >
> > > > > > > This violates the spec and probably breaks a few tests and the EFI
> > > > > > > boot services API, as you are supposed to be able to define the memory
> > > > > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > > > > >
> > > > > > To be more specific, I am suggesting:
> > > > > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > > > > type since it is always that). This should cover all the U-Boot
> > > > > > allocations and make sure they are safely within the malloc() pool
> > > > > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > > > > type in metadata)
> > > > > >
> > > > >
> > > > > Personally, I don't see the point and we deviate from the spec as
> > > > > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > > > > you need to allocate *pages* to grow the pool. What's missing from our
> > > > > efi_allocate_pool() is the ability to merge allocations of the same
> > > > > memory type to an existing pool assuming there's space, rather than
> > > > > requesting a new pool of 4kb.
> > > >
> > > > "This function allocates pages from EfiConventionalMemory as needed to
> > > > grow the requested pool type". So far as EFI is concerned, the
> > > > malloc() region is in EfiConventionalMemory, so I don't see any
> > > > deviation.
> > > >
> > > > Please also see below.
> > > >
> > > > > > >
> > > > > > > You can switch some callsite of efi_allocate_pool to
> > > > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > > > >
> > > > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > > > >
> > > >
> > > > This is the key point that doesn't seem to be coming across. The
> > > > current allocator is allocating memory wherever it likes, potentially
> > > > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
> > >
> > > It is, but I don't think using malloc is solving it. It's papering
> > > over the problem, because if someone in the future launches an EFI app
> > > or allocates EFI memory with a different type you are back on the same
> > > problem.
> >
> > It is certainly solving this problem.
> >
> > Once the app is launched it is OK to overwrite memory...after all it
> > has been loaded and is running. The issue is that these little
> > allocations can end up anywhere in memory. Did you see the qemu_arm
> > note?
>
> Isn't this another part of why we need the LMB rework? So that
> kernel_addr_r, et al, can be marked as reserved.

If we mark them as reserved, we won't be able to load a file into that
region, so boot scripts will fail.

Please take a look at the whole series and let me know if there is
anything missing from the descriptions I have given. I have had this
problem in the back of my mind for some time...but just a few hours of
investigation was enough to determine that it really is broken.

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 19:42                         ` Simon Glass
@ 2024-07-30 19:49                           ` Tom Rini
  2024-07-30 20:05                             ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Tom Rini @ 2024-07-30 19:49 UTC (permalink / raw)
  To: Simon Glass
  Cc: Ilias Apalodimas, Heinrich Schuchardt, Sughosh Ganu,
	U-Boot Mailing List

[-- Attachment #1: Type: text/plain, Size: 15040 bytes --]

On Tue, Jul 30, 2024 at 01:42:17PM -0600, Simon Glass wrote:
> Hi Tom,
> 
> On Tue, 30 Jul 2024 at 09:24, Tom Rini <trini@konsulko.com> wrote:
> >
> > On Tue, Jul 30, 2024 at 09:18:02AM -0600, Simon Glass wrote:
> > > Hi Ilias,
> > >
> > > On Tue, 30 Jul 2024 at 08:54, Ilias Apalodimas
> > > <ilias.apalodimas@linaro.org> wrote:
> > > >
> > > > On Tue, 30 Jul 2024 at 17:38, Simon Glass <sjg@chromium.org> wrote:
> > > > >
> > > > > Hi Ilias,
> > > > >
> > > > > On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > >
> > > > > > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > > > > > >
> > > > > > > Hi Ilias,
> > > > > > >
> > > > > > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > >
> > > > > > > > Hi Simon
> > > > > > > >
> > > > > > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi Ilias,
> > > > > > > > >
> > > > > > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Hi Simon,
> > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > Hi Heinrich,
> > > > > > > > > > >
> > > > > > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > > > > > malloc()-style header on each block.
> > > > > > > > > > > > >
> > > > > > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > > > > > >
> > > > > > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > > > > > >    EFI_LOADER
> > > > > > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > > > > > >
> > > > > > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > > > > > (presumed) bugs are located.
> > > > > > > > > > > > >
> > > > > > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > > > > > ---
> > > > > > > > > > > > >
> > > > > > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > > > > > >
> > > > > > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > > > > > >   #endif
> > > > > > > > > > > > >
> > > > > > > > > > > > > -/**
> > > > > > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > > > > > - *
> > > > > > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > > > > > - * @checksum:        checksum
> > > > > > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > > > > > - *
> > > > > > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > > > > > - *
> > > > > > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > > > > > - *
> > > > > > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > > > > > - */
> > > > > > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > > > > > -     u64 num_pages;
> > > > > > > > > > > > > -     u64 checksum;
> > > > > > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > > > > > -};
> > > > > > > > > > > > > -
> > > > > > > > > > > > > -/**
> > > > > > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > > > > > - *
> > > > > > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > > > > > - */
> > > > > > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > > > > > -{
> > > > > > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > > > > > -     if (!ret)
> > > > > > > > > > > > > -             ++ret;
> > > > > > > > > > > > > -     return ret;
> > > > > > > > > > > > > -}
> > > > > > > > > > > > > -
> > > > > > > > > > > > >   /**
> > > > > > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > > > > > >    *
> > > > > > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > > > > > >    * @buffer: allocated memory
> > > > > > > > > > > > >    * Return:  status code
> > > > > > > > > > > > >    */
> > > > > > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > +                            void **buffer)
> > > > > > > > > > > > >   {
> > > > > > > > > > > > > -     efi_status_t r;
> > > > > > > > > > > > > -     u64 addr;
> > > > > > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > > > > > +     void *ptr;
> > > > > > > > > > > > >
> > > > > > > > > > > > >       if (!check_allowed())
> > > > > > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > > > > > >       }
> > > > > > > > > > > >
> > > > > > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > > > > > >
> > > > > > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > > > > > report it in GetMemoryMap().
> > > > > > > > > > >
> > > > > > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > > > > > actually stated somewhere?
> > > > > > > > > >
> > > > > > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > > > > > advantage of CPU security features  e.g RW^X.
> > > > > > > > >
> > > > > > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > > > > > >
> > > > > > > > > But if we did want to fix that, we could make the malloc region
> > > > > > > > > EFI_BOOT_SERVICES_DATA.
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > In any case, the AllocatePool description reads "This function
> > > > > > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > > > > > some EFI semantics, but that's a really bad idea.
> > > > > > > > >
> > > > > > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > > > > > pool.
> > > > > > > > >
> > > > > > > > > What sort of EFI semantics are you referring to?
> > > > > > > >
> > > > > > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > > > > > which tracks it.
> > > > > > >
> > > > > > > I can put that code back, depending on what we decide below.
> > > > > > >
> > > > > > > >
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > > > > > >
> > > > > > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > > > > > quick grep
> > > > > > > > >
> > > > > > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > > > > > allocation.
> > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > > > > > I am seeing.
> > > > > > > > >
> > > > > > > > > Any comments on this?
> > > > > > > >
> > > > > > > > This violates the spec and probably breaks a few tests and the EFI
> > > > > > > > boot services API, as you are supposed to be able to define the memory
> > > > > > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > > > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > > > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > > > > > >
> > > > > > > To be more specific, I am suggesting:
> > > > > > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > > > > > type since it is always that). This should cover all the U-Boot
> > > > > > > allocations and make sure they are safely within the malloc() pool
> > > > > > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > > > > > type in metadata)
> > > > > > >
> > > > > >
> > > > > > Personally, I don't see the point and we deviate from the spec as
> > > > > > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > > > > > you need to allocate *pages* to grow the pool. What's missing from our
> > > > > > efi_allocate_pool() is the ability to merge allocations of the same
> > > > > > memory type to an existing pool assuming there's space, rather than
> > > > > > requesting a new pool of 4kb.
> > > > >
> > > > > "This function allocates pages from EfiConventionalMemory as needed to
> > > > > grow the requested pool type". So far as EFI is concerned, the
> > > > > malloc() region is in EfiConventionalMemory, so I don't see any
> > > > > deviation.
> > > > >
> > > > > Please also see below.
> > > > >
> > > > > > > >
> > > > > > > > You can switch some callsite of efi_allocate_pool to
> > > > > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > > > > >
> > > > > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > > > > >
> > > > >
> > > > > This is the key point that doesn't seem to be coming across. The
> > > > > current allocator is allocating memory wherever it likes, potentially
> > > > > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
> > > >
> > > > It is, but I don't think using malloc is solving it. It's papering
> > > > over the problem, because if someone in the future launches an EFI app
> > > > or allocates EFI memory with a different type you are back on the same
> > > > problem.
> > >
> > > It is certainly solving this problem.
> > >
> > > Once the app is launched it is OK to overwrite memory...after all it
> > > has been loaded and is running. The issue is that these little
> > > allocations can end up anywhere in memory. Did you see the qemu_arm
> > > note?
> >
> > Isn't this another part of why we need the LMB rework? So that
> > kernel_addr_r, et al, can be marked as reserved.
> 
> If we mark them as reserved, we won't be able to load a file into that
> region, so boot scripts will fail.

No? We mark it as being overwriteable but allocated. This is part of the
LMB rework series.

> Please take a look at the whole series and let me know if there is
> anything missing from the descriptions I have given. I have had this
> problem in the back of my mind for some time...but just a few hours of
> investigation was enough to determine that it really is broken.

I'm missing something, sorry. Yes, it is known that EFI can make some
incorrect assumptions about what memory is/isn't available (as other
implementations give EFI the world to work with), hence the LMB rework
series to address some of these problems.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 19:49                           ` Tom Rini
@ 2024-07-30 20:05                             ` Simon Glass
  2024-07-30 20:11                               ` Tom Rini
  2024-07-31  7:39                               ` Ilias Apalodimas
  0 siblings, 2 replies; 45+ messages in thread
From: Simon Glass @ 2024-07-30 20:05 UTC (permalink / raw)
  To: Tom Rini
  Cc: Ilias Apalodimas, Heinrich Schuchardt, Sughosh Ganu,
	U-Boot Mailing List

Hi Tom,

On Tue, 30 Jul 2024 at 13:49, Tom Rini <trini@konsulko.com> wrote:
>
> On Tue, Jul 30, 2024 at 01:42:17PM -0600, Simon Glass wrote:
> > Hi Tom,
> >
> > On Tue, 30 Jul 2024 at 09:24, Tom Rini <trini@konsulko.com> wrote:
> > >
> > > On Tue, Jul 30, 2024 at 09:18:02AM -0600, Simon Glass wrote:
> > > > Hi Ilias,
> > > >
> > > > On Tue, 30 Jul 2024 at 08:54, Ilias Apalodimas
> > > > <ilias.apalodimas@linaro.org> wrote:
> > > > >
> > > > > On Tue, 30 Jul 2024 at 17:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > >
> > > > > > Hi Ilias,
> > > > > >
> > > > > > On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > >
> > > > > > > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > >
> > > > > > > > Hi Ilias,
> > > > > > > >
> > > > > > > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi Simon
> > > > > > > > >
> > > > > > > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Hi Ilias,
> > > > > > > > > >
> > > > > > > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > Hi Simon,
> > > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > Hi Heinrich,
> > > > > > > > > > > >
> > > > > > > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > > > > > > malloc()-style header on each block.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > > > > > > >    EFI_LOADER
> > > > > > > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > > > > > > (presumed) bugs are located.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > > > > > > ---
> > > > > > > > > > > > > >
> > > > > > > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > > > > > > >   #endif
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > -/**
> > > > > > > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > > > > > > - * @checksum:        checksum
> > > > > > > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > > > > > > - */
> > > > > > > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > > > > > > -     u64 num_pages;
> > > > > > > > > > > > > > -     u64 checksum;
> > > > > > > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > > > > > > -};
> > > > > > > > > > > > > > -
> > > > > > > > > > > > > > -/**
> > > > > > > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > > > > > > - */
> > > > > > > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > > > > > > -{
> > > > > > > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > > > > > > -     if (!ret)
> > > > > > > > > > > > > > -             ++ret;
> > > > > > > > > > > > > > -     return ret;
> > > > > > > > > > > > > > -}
> > > > > > > > > > > > > > -
> > > > > > > > > > > > > >   /**
> > > > > > > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > > > > > > >    *
> > > > > > > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > > > > > > >    * @buffer: allocated memory
> > > > > > > > > > > > > >    * Return:  status code
> > > > > > > > > > > > > >    */
> > > > > > > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > > +                            void **buffer)
> > > > > > > > > > > > > >   {
> > > > > > > > > > > > > > -     efi_status_t r;
> > > > > > > > > > > > > > -     u64 addr;
> > > > > > > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > > > > > > +     void *ptr;
> > > > > > > > > > > > > >
> > > > > > > > > > > > > >       if (!check_allowed())
> > > > > > > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > > > > > > >       }
> > > > > > > > > > > > >
> > > > > > > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > > > > > > >
> > > > > > > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > > > > > > report it in GetMemoryMap().
> > > > > > > > > > > >
> > > > > > > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > > > > > > actually stated somewhere?
> > > > > > > > > > >
> > > > > > > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > > > > > > advantage of CPU security features  e.g RW^X.
> > > > > > > > > >
> > > > > > > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > > > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > > > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > > > > > > >
> > > > > > > > > > But if we did want to fix that, we could make the malloc region
> > > > > > > > > > EFI_BOOT_SERVICES_DATA.
> > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > In any case, the AllocatePool description reads "This function
> > > > > > > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > > > > > > some EFI semantics, but that's a really bad idea.
> > > > > > > > > >
> > > > > > > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > > > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > > > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > > > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > > > > > > pool.
> > > > > > > > > >
> > > > > > > > > > What sort of EFI semantics are you referring to?
> > > > > > > > >
> > > > > > > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > > > > > > which tracks it.
> > > > > > > >
> > > > > > > > I can put that code back, depending on what we decide below.
> > > > > > > >
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > > > > > > >
> > > > > > > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > > > > > > quick grep
> > > > > > > > > >
> > > > > > > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > > > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > > > > > > allocation.
> > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > > > > > > I am seeing.
> > > > > > > > > >
> > > > > > > > > > Any comments on this?
> > > > > > > > >
> > > > > > > > > This violates the spec and probably breaks a few tests and the EFI
> > > > > > > > > boot services API, as you are supposed to be able to define the memory
> > > > > > > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > > > > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > > > > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > > > > > > >
> > > > > > > > To be more specific, I am suggesting:
> > > > > > > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > > > > > > type since it is always that). This should cover all the U-Boot
> > > > > > > > allocations and make sure they are safely within the malloc() pool
> > > > > > > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > > > > > > type in metadata)
> > > > > > > >
> > > > > > >
> > > > > > > Personally, I don't see the point and we deviate from the spec as
> > > > > > > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > > > > > > you need to allocate *pages* to grow the pool. What's missing from our
> > > > > > > efi_allocate_pool() is the ability to merge allocations of the same
> > > > > > > memory type to an existing pool assuming there's space, rather than
> > > > > > > requesting a new pool of 4kb.
> > > > > >
> > > > > > "This function allocates pages from EfiConventionalMemory as needed to
> > > > > > grow the requested pool type". So far as EFI is concerned, the
> > > > > > malloc() region is in EfiConventionalMemory, so I don't see any
> > > > > > deviation.
> > > > > >
> > > > > > Please also see below.
> > > > > >
> > > > > > > > >
> > > > > > > > > You can switch some callsite of efi_allocate_pool to
> > > > > > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > > > > > >
> > > > > > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > > > > > >
> > > > > >
> > > > > > This is the key point that doesn't seem to be coming across. The
> > > > > > current allocator is allocating memory wherever it likes, potentially
> > > > > > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
> > > > >
> > > > > It is, but I don't think using malloc is solving it. It's papering
> > > > > over the problem, because if someone in the future launches an EFI app
> > > > > or allocates EFI memory with a different type you are back on the same
> > > > > problem.
> > > >
> > > > It is certainly solving this problem.
> > > >
> > > > Once the app is launched it is OK to overwrite memory...after all it
> > > > has been loaded and is running. The issue is that these little
> > > > allocations can end up anywhere in memory. Did you see the qemu_arm
> > > > note?
> > >
> > > Isn't this another part of why we need the LMB rework? So that
> > > kernel_addr_r, et al, can be marked as reserved.
> >
> > If we mark them as reserved, we won't be able to load a file into that
> > region, so boot scripts will fail.
>
> No? We mark it as being overwriteable but allocated. This is part of the
> LMB rework series.

Yes, I see, that makes sense. I don't know any way to guess the
expected size of the kernel or ramdisk. I see that Apple uses 128MB
for the kernel (plenty) and 1GB for the ramdisk.

>
> > Please take a look at the whole series and let me know if there is
> > anything missing from the descriptions I have given. I have had this
> > problem in the back of my mind for some time...but just a few hours of
> > investigation was enough to determine that it really is broken.
>
> I'm missing something, sorry. Yes, it is known that EFI can make some
> incorrect assumptions about what memory is/isn't available (as other
> implementations give EFI the world to work with), hence the LMB rework
> series to address some of these problems.

My goal here is to tidy up EFI memory allocation so that it doesn't
result in allocating memory in strange places. Avoiding overlaps is
one thing, but the way this is heading, we will get overlap errors
randomly on platforms when someone tries to load something into RAM,
or we won't protect things that need to be protected. It is all a bit
mushy without a proper design.

Does that make sense?

Regards,
Simon

[1] https://lore.kernel.org/all/CAFLszThqH01GOqnTxucVE7s+wJU-dz7uqUtkb4iKxs6GgX6tqw@mail.gmail.com/T/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 20:05                             ` Simon Glass
@ 2024-07-30 20:11                               ` Tom Rini
  2024-07-30 20:52                                 ` Simon Glass
  2024-07-31  7:39                               ` Ilias Apalodimas
  1 sibling, 1 reply; 45+ messages in thread
From: Tom Rini @ 2024-07-30 20:11 UTC (permalink / raw)
  To: Simon Glass
  Cc: Ilias Apalodimas, Heinrich Schuchardt, Sughosh Ganu,
	U-Boot Mailing List

[-- Attachment #1: Type: text/plain, Size: 17451 bytes --]

On Tue, Jul 30, 2024 at 02:05:19PM -0600, Simon Glass wrote:
> Hi Tom,
> 
> On Tue, 30 Jul 2024 at 13:49, Tom Rini <trini@konsulko.com> wrote:
> >
> > On Tue, Jul 30, 2024 at 01:42:17PM -0600, Simon Glass wrote:
> > > Hi Tom,
> > >
> > > On Tue, 30 Jul 2024 at 09:24, Tom Rini <trini@konsulko.com> wrote:
> > > >
> > > > On Tue, Jul 30, 2024 at 09:18:02AM -0600, Simon Glass wrote:
> > > > > Hi Ilias,
> > > > >
> > > > > On Tue, 30 Jul 2024 at 08:54, Ilias Apalodimas
> > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > >
> > > > > > On Tue, 30 Jul 2024 at 17:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > > >
> > > > > > > Hi Ilias,
> > > > > > >
> > > > > > > On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > >
> > > > > > > > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi Ilias,
> > > > > > > > >
> > > > > > > > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Hi Simon
> > > > > > > > > >
> > > > > > > > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > Hi Ilias,
> > > > > > > > > > >
> > > > > > > > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > Hi Simon,
> > > > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > Hi Heinrich,
> > > > > > > > > > > > >
> > > > > > > > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > > > > > > > malloc()-style header on each block.
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > > > > > > > >    EFI_LOADER
> > > > > > > > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > > > > > > > (presumed) bugs are located.
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > > > > > > > ---
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > > > > > > > >   #endif
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > -/**
> > > > > > > > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > > > > > > > - * @checksum:        checksum
> > > > > > > > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > > > > > > > - */
> > > > > > > > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > > > > > > > -     u64 num_pages;
> > > > > > > > > > > > > > > -     u64 checksum;
> > > > > > > > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > > > > > > > -};
> > > > > > > > > > > > > > > -
> > > > > > > > > > > > > > > -/**
> > > > > > > > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > > > > > > > - */
> > > > > > > > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > > > > > > > -{
> > > > > > > > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > > > > > > > -     if (!ret)
> > > > > > > > > > > > > > > -             ++ret;
> > > > > > > > > > > > > > > -     return ret;
> > > > > > > > > > > > > > > -}
> > > > > > > > > > > > > > > -
> > > > > > > > > > > > > > >   /**
> > > > > > > > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > > > > > > > >    *
> > > > > > > > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > > > > > > > >    * @buffer: allocated memory
> > > > > > > > > > > > > > >    * Return:  status code
> > > > > > > > > > > > > > >    */
> > > > > > > > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > > > +                            void **buffer)
> > > > > > > > > > > > > > >   {
> > > > > > > > > > > > > > > -     efi_status_t r;
> > > > > > > > > > > > > > > -     u64 addr;
> > > > > > > > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > > > > > > > +     void *ptr;
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > >       if (!check_allowed())
> > > > > > > > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > > > > > > > >       }
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > > > > > > > report it in GetMemoryMap().
> > > > > > > > > > > > >
> > > > > > > > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > > > > > > > actually stated somewhere?
> > > > > > > > > > > >
> > > > > > > > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > > > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > > > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > > > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > > > > > > > advantage of CPU security features  e.g RW^X.
> > > > > > > > > > >
> > > > > > > > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > > > > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > > > > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > > > > > > > >
> > > > > > > > > > > But if we did want to fix that, we could make the malloc region
> > > > > > > > > > > EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > In any case, the AllocatePool description reads "This function
> > > > > > > > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > > > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > > > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > > > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > > > > > > > some EFI semantics, but that's a really bad idea.
> > > > > > > > > > >
> > > > > > > > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > > > > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > > > > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > > > > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > > > > > > > pool.
> > > > > > > > > > >
> > > > > > > > > > > What sort of EFI semantics are you referring to?
> > > > > > > > > >
> > > > > > > > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > > > > > > > which tracks it.
> > > > > > > > >
> > > > > > > > > I can put that code back, depending on what we decide below.
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > > > > > > > >
> > > > > > > > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > > > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > > > > > > > quick grep
> > > > > > > > > > >
> > > > > > > > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > > > > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > > > > > > > allocation.
> > > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > > > > > > > I am seeing.
> > > > > > > > > > >
> > > > > > > > > > > Any comments on this?
> > > > > > > > > >
> > > > > > > > > > This violates the spec and probably breaks a few tests and the EFI
> > > > > > > > > > boot services API, as you are supposed to be able to define the memory
> > > > > > > > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > > > > > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > > > > > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > > > > > > > >
> > > > > > > > > To be more specific, I am suggesting:
> > > > > > > > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > > > > > > > type since it is always that). This should cover all the U-Boot
> > > > > > > > > allocations and make sure they are safely within the malloc() pool
> > > > > > > > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > > > > > > > type in metadata)
> > > > > > > > >
> > > > > > > >
> > > > > > > > Personally, I don't see the point and we deviate from the spec as
> > > > > > > > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > > > > > > > you need to allocate *pages* to grow the pool. What's missing from our
> > > > > > > > efi_allocate_pool() is the ability to merge allocations of the same
> > > > > > > > memory type to an existing pool assuming there's space, rather than
> > > > > > > > requesting a new pool of 4kb.
> > > > > > >
> > > > > > > "This function allocates pages from EfiConventionalMemory as needed to
> > > > > > > grow the requested pool type". So far as EFI is concerned, the
> > > > > > > malloc() region is in EfiConventionalMemory, so I don't see any
> > > > > > > deviation.
> > > > > > >
> > > > > > > Please also see below.
> > > > > > >
> > > > > > > > > >
> > > > > > > > > > You can switch some callsite of efi_allocate_pool to
> > > > > > > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > > > > > > >
> > > > > > > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > > > > > > >
> > > > > > >
> > > > > > > This is the key point that doesn't seem to be coming across. The
> > > > > > > current allocator is allocating memory wherever it likes, potentially
> > > > > > > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
> > > > > >
> > > > > > It is, but I don't think using malloc is solving it. It's papering
> > > > > > over the problem, because if someone in the future launches an EFI app
> > > > > > or allocates EFI memory with a different type you are back on the same
> > > > > > problem.
> > > > >
> > > > > It is certainly solving this problem.
> > > > >
> > > > > Once the app is launched it is OK to overwrite memory...after all it
> > > > > has been loaded and is running. The issue is that these little
> > > > > allocations can end up anywhere in memory. Did you see the qemu_arm
> > > > > note?
> > > >
> > > > Isn't this another part of why we need the LMB rework? So that
> > > > kernel_addr_r, et al, can be marked as reserved.
> > >
> > > If we mark them as reserved, we won't be able to load a file into that
> > > region, so boot scripts will fail.
> >
> > No? We mark it as being overwriteable but allocated. This is part of the
> > LMB rework series.
> 
> Yes, I see, that makes sense. I don't know any way to guess the
> expected size of the kernel or ramdisk. I see that Apple uses 128MB
> for the kernel (plenty) and 1GB for the ramdisk.

Yes, 128MiB is a practical limit. Ramdisk is where it gets trickier.

> > > Please take a look at the whole series and let me know if there is
> > > anything missing from the descriptions I have given. I have had this
> > > problem in the back of my mind for some time...but just a few hours of
> > > investigation was enough to determine that it really is broken.
> >
> > I'm missing something, sorry. Yes, it is known that EFI can make some
> > incorrect assumptions about what memory is/isn't available (as other
> > implementations give EFI the world to work with), hence the LMB rework
> > series to address some of these problems.
> 
> My goal here is to tidy up EFI memory allocation so that it doesn't
> result in allocating memory in strange places. Avoiding overlaps is
> one thing, but the way this is heading, we will get overlap errors
> randomly on platforms when someone tries to load something into RAM,
> or we won't protect things that need to be protected. It is all a bit
> mushy without a proper design.
> 
> Does that make sense?

Well, to me step one is to get the lmb series done so that if something
needs a range of memory it can both check if it's available and mark it
as unavailable to others. As yes, we have something analogous to
cooperative multitasking when it comes to memory management today, and
that doesn't always work out.

Step two is getting the new lmb series and EFI_LOADER talking, so that
step three can be seeing what tweaks may be needed in where things
allocate memory.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 20:11                               ` Tom Rini
@ 2024-07-30 20:52                                 ` Simon Glass
  2024-07-30 21:20                                   ` Tom Rini
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-30 20:52 UTC (permalink / raw)
  To: Tom Rini
  Cc: Ilias Apalodimas, Heinrich Schuchardt, Sughosh Ganu,
	U-Boot Mailing List

Hi Tom,

On Tue, 30 Jul 2024 at 14:11, Tom Rini <trini@konsulko.com> wrote:
>
> On Tue, Jul 30, 2024 at 02:05:19PM -0600, Simon Glass wrote:
> > Hi Tom,
> >
> > On Tue, 30 Jul 2024 at 13:49, Tom Rini <trini@konsulko.com> wrote:
> > >
> > > On Tue, Jul 30, 2024 at 01:42:17PM -0600, Simon Glass wrote:
> > > > Hi Tom,
> > > >
> > > > On Tue, 30 Jul 2024 at 09:24, Tom Rini <trini@konsulko.com> wrote:
> > > > >
> > > > > On Tue, Jul 30, 2024 at 09:18:02AM -0600, Simon Glass wrote:
> > > > > > Hi Ilias,
> > > > > >
> > > > > > On Tue, 30 Jul 2024 at 08:54, Ilias Apalodimas
> > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > >
> > > > > > > On Tue, 30 Jul 2024 at 17:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > >
> > > > > > > > Hi Ilias,
> > > > > > > >
> > > > > > > > On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > >
> > > > > > > > > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Hi Ilias,
> > > > > > > > > >
> > > > > > > > > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > Hi Simon
> > > > > > > > > > >
> > > > > > > > > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > Hi Ilias,
> > > > > > > > > > > >
> > > > > > > > > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > Hi Simon,
> > > > > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Hi Heinrich,
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > > > > > > > > malloc()-style header on each block.
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > > > > > > > > >    EFI_LOADER
> > > > > > > > > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > > > > > > > > (presumed) bugs are located.
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > > > > > > > > ---
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > > > > > > > > >   #endif
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > -/**
> > > > > > > > > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > > > > > > > > - * @checksum:        checksum
> > > > > > > > > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > > > > > > > > - */
> > > > > > > > > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > > > > > > > > -     u64 num_pages;
> > > > > > > > > > > > > > > > -     u64 checksum;
> > > > > > > > > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > > > > > > > > -};
> > > > > > > > > > > > > > > > -
> > > > > > > > > > > > > > > > -/**
> > > > > > > > > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > > > > > > > > - */
> > > > > > > > > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > > > > > > > > -{
> > > > > > > > > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > > > > > > > > -     if (!ret)
> > > > > > > > > > > > > > > > -             ++ret;
> > > > > > > > > > > > > > > > -     return ret;
> > > > > > > > > > > > > > > > -}
> > > > > > > > > > > > > > > > -
> > > > > > > > > > > > > > > >   /**
> > > > > > > > > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > > > > > > > > >    *
> > > > > > > > > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > > > > > > > > >    * @buffer: allocated memory
> > > > > > > > > > > > > > > >    * Return:  status code
> > > > > > > > > > > > > > > >    */
> > > > > > > > > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > > > > +                            void **buffer)
> > > > > > > > > > > > > > > >   {
> > > > > > > > > > > > > > > > -     efi_status_t r;
> > > > > > > > > > > > > > > > -     u64 addr;
> > > > > > > > > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > > > > > > > > +     void *ptr;
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > >       if (!check_allowed())
> > > > > > > > > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > > > > > > > > >       }
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > > > > > > > > report it in GetMemoryMap().
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > > > > > > > > actually stated somewhere?
> > > > > > > > > > > > >
> > > > > > > > > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > > > > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > > > > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > > > > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > > > > > > > > advantage of CPU security features  e.g RW^X.
> > > > > > > > > > > >
> > > > > > > > > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > > > > > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > > > > > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > > > > > > > > >
> > > > > > > > > > > > But if we did want to fix that, we could make the malloc region
> > > > > > > > > > > > EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > > > > > > In any case, the AllocatePool description reads "This function
> > > > > > > > > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > > > > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > > > > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > > > > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > > > > > > > > some EFI semantics, but that's a really bad idea.
> > > > > > > > > > > >
> > > > > > > > > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > > > > > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > > > > > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > > > > > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > > > > > > > > pool.
> > > > > > > > > > > >
> > > > > > > > > > > > What sort of EFI semantics are you referring to?
> > > > > > > > > > >
> > > > > > > > > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > > > > > > > > which tracks it.
> > > > > > > > > >
> > > > > > > > > > I can put that code back, depending on what we decide below.
> > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > > > > > > > > >
> > > > > > > > > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > > > > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > > > > > > > > quick grep
> > > > > > > > > > > >
> > > > > > > > > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > > > > > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > > > > > > > > allocation.
> > > > > > > > > > > >
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > > > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > > > > > > > > I am seeing.
> > > > > > > > > > > >
> > > > > > > > > > > > Any comments on this?
> > > > > > > > > > >
> > > > > > > > > > > This violates the spec and probably breaks a few tests and the EFI
> > > > > > > > > > > boot services API, as you are supposed to be able to define the memory
> > > > > > > > > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > > > > > > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > > > > > > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > > > > > > > > >
> > > > > > > > > > To be more specific, I am suggesting:
> > > > > > > > > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > > > > > > > > type since it is always that). This should cover all the U-Boot
> > > > > > > > > > allocations and make sure they are safely within the malloc() pool
> > > > > > > > > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > > > > > > > > type in metadata)
> > > > > > > > > >
> > > > > > > > >
> > > > > > > > > Personally, I don't see the point and we deviate from the spec as
> > > > > > > > > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > > > > > > > > you need to allocate *pages* to grow the pool. What's missing from our
> > > > > > > > > efi_allocate_pool() is the ability to merge allocations of the same
> > > > > > > > > memory type to an existing pool assuming there's space, rather than
> > > > > > > > > requesting a new pool of 4kb.
> > > > > > > >
> > > > > > > > "This function allocates pages from EfiConventionalMemory as needed to
> > > > > > > > grow the requested pool type". So far as EFI is concerned, the
> > > > > > > > malloc() region is in EfiConventionalMemory, so I don't see any
> > > > > > > > deviation.
> > > > > > > >
> > > > > > > > Please also see below.
> > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > You can switch some callsite of efi_allocate_pool to
> > > > > > > > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > > > > > > > >
> > > > > > > > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > > > > > > > >
> > > > > > > >
> > > > > > > > This is the key point that doesn't seem to be coming across. The
> > > > > > > > current allocator is allocating memory wherever it likes, potentially
> > > > > > > > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
> > > > > > >
> > > > > > > It is, but I don't think using malloc is solving it. It's papering
> > > > > > > over the problem, because if someone in the future launches an EFI app
> > > > > > > or allocates EFI memory with a different type you are back on the same
> > > > > > > problem.
> > > > > >
> > > > > > It is certainly solving this problem.
> > > > > >
> > > > > > Once the app is launched it is OK to overwrite memory...after all it
> > > > > > has been loaded and is running. The issue is that these little
> > > > > > allocations can end up anywhere in memory. Did you see the qemu_arm
> > > > > > note?
> > > > >
> > > > > Isn't this another part of why we need the LMB rework? So that
> > > > > kernel_addr_r, et al, can be marked as reserved.
> > > >
> > > > If we mark them as reserved, we won't be able to load a file into that
> > > > region, so boot scripts will fail.
> > >
> > > No? We mark it as being overwriteable but allocated. This is part of the
> > > LMB rework series.
> >
> > Yes, I see, that makes sense. I don't know any way to guess the
> > expected size of the kernel or ramdisk. I see that Apple uses 128MB
> > for the kernel (plenty) and 1GB for the ramdisk.
>
> Yes, 128MiB is a practical limit. Ramdisk is where it gets trickier.
>
> > > > Please take a look at the whole series and let me know if there is
> > > > anything missing from the descriptions I have given. I have had this
> > > > problem in the back of my mind for some time...but just a few hours of
> > > > investigation was enough to determine that it really is broken.
> > >
> > > I'm missing something, sorry. Yes, it is known that EFI can make some
> > > incorrect assumptions about what memory is/isn't available (as other
> > > implementations give EFI the world to work with), hence the LMB rework
> > > series to address some of these problems.
> >
> > My goal here is to tidy up EFI memory allocation so that it doesn't
> > result in allocating memory in strange places. Avoiding overlaps is
> > one thing, but the way this is heading, we will get overlap errors
> > randomly on platforms when someone tries to load something into RAM,
> > or we won't protect things that need to be protected. It is all a bit
> > mushy without a proper design.
> >
> > Does that make sense?
>
> Well, to me step one is to get the lmb series done so that if something
> needs a range of memory it can both check if it's available and mark it
> as unavailable to others. As yes, we have something analogous to
> cooperative multitasking when it comes to memory management today, and
> that doesn't always work out.

OK, and that is in progress.

>
> Step two is getting the new lmb series and EFI_LOADER talking,

What sort of talking? Bear in mind that EFI_LOADER currently does
allocations even if it isn't used.

> so that
> step three can be seeing what tweaks may be needed in where things
> allocate memory.

So my series is step 3?

Regards,
SImon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 20:52                                 ` Simon Glass
@ 2024-07-30 21:20                                   ` Tom Rini
  2024-07-31 14:39                                     ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Tom Rini @ 2024-07-30 21:20 UTC (permalink / raw)
  To: Simon Glass
  Cc: Ilias Apalodimas, Heinrich Schuchardt, Sughosh Ganu,
	U-Boot Mailing List

[-- Attachment #1: Type: text/plain, Size: 19233 bytes --]

On Tue, Jul 30, 2024 at 02:52:21PM -0600, Simon Glass wrote:
> Hi Tom,
> 
> On Tue, 30 Jul 2024 at 14:11, Tom Rini <trini@konsulko.com> wrote:
> >
> > On Tue, Jul 30, 2024 at 02:05:19PM -0600, Simon Glass wrote:
> > > Hi Tom,
> > >
> > > On Tue, 30 Jul 2024 at 13:49, Tom Rini <trini@konsulko.com> wrote:
> > > >
> > > > On Tue, Jul 30, 2024 at 01:42:17PM -0600, Simon Glass wrote:
> > > > > Hi Tom,
> > > > >
> > > > > On Tue, 30 Jul 2024 at 09:24, Tom Rini <trini@konsulko.com> wrote:
> > > > > >
> > > > > > On Tue, Jul 30, 2024 at 09:18:02AM -0600, Simon Glass wrote:
> > > > > > > Hi Ilias,
> > > > > > >
> > > > > > > On Tue, 30 Jul 2024 at 08:54, Ilias Apalodimas
> > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > >
> > > > > > > > On Tue, 30 Jul 2024 at 17:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi Ilias,
> > > > > > > > >
> > > > > > > > > On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > >
> > > > > > > > > > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > Hi Ilias,
> > > > > > > > > > >
> > > > > > > > > > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > Hi Simon
> > > > > > > > > > > >
> > > > > > > > > > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > Hi Ilias,
> > > > > > > > > > > > >
> > > > > > > > > > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Hi Simon,
> > > > > > > > > > > > > >
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Hi Heinrich,
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > > > > > > > > > malloc()-style header on each block.
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > > > > > > > > > >    EFI_LOADER
> > > > > > > > > > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > > > > > > > > > (presumed) bugs are located.
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > > > > > > > > > ---
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > > > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > > > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > > > > > > > > > >   #endif
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > -/**
> > > > > > > > > > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > > > > > > > > > - * @checksum:        checksum
> > > > > > > > > > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > > > > > > > > > - */
> > > > > > > > > > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > > > > > > > > > -     u64 num_pages;
> > > > > > > > > > > > > > > > > -     u64 checksum;
> > > > > > > > > > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > > > > > > > > > -};
> > > > > > > > > > > > > > > > > -
> > > > > > > > > > > > > > > > > -/**
> > > > > > > > > > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > > > > > > > > > - */
> > > > > > > > > > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > > > > > > > > > -{
> > > > > > > > > > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > > > > > > > > > -     if (!ret)
> > > > > > > > > > > > > > > > > -             ++ret;
> > > > > > > > > > > > > > > > > -     return ret;
> > > > > > > > > > > > > > > > > -}
> > > > > > > > > > > > > > > > > -
> > > > > > > > > > > > > > > > >   /**
> > > > > > > > > > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > > > > > > > > > >    *
> > > > > > > > > > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > > > > > > > > > >    * @buffer: allocated memory
> > > > > > > > > > > > > > > > >    * Return:  status code
> > > > > > > > > > > > > > > > >    */
> > > > > > > > > > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > > > > > +                            void **buffer)
> > > > > > > > > > > > > > > > >   {
> > > > > > > > > > > > > > > > > -     efi_status_t r;
> > > > > > > > > > > > > > > > > -     u64 addr;
> > > > > > > > > > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > > > > > > > > > +     void *ptr;
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > >       if (!check_allowed())
> > > > > > > > > > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > > > > > > > > > >       }
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > > > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > > > > > > > > > report it in GetMemoryMap().
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > > > > > > > > > actually stated somewhere?
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > > > > > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > > > > > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > > > > > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > > > > > > > > > advantage of CPU security features  e.g RW^X.
> > > > > > > > > > > > >
> > > > > > > > > > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > > > > > > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > > > > > > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > > > > > > > > > >
> > > > > > > > > > > > > But if we did want to fix that, we could make the malloc region
> > > > > > > > > > > > > EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > > > >
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > In any case, the AllocatePool description reads "This function
> > > > > > > > > > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > > > > > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > > > > > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > > > > > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > > > > > > > > > some EFI semantics, but that's a really bad idea.
> > > > > > > > > > > > >
> > > > > > > > > > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > > > > > > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > > > > > > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > > > > > > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > > > > > > > > > pool.
> > > > > > > > > > > > >
> > > > > > > > > > > > > What sort of EFI semantics are you referring to?
> > > > > > > > > > > >
> > > > > > > > > > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > > > > > > > > > which tracks it.
> > > > > > > > > > >
> > > > > > > > > > > I can put that code back, depending on what we decide below.
> > > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > > > > > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > > > > > > > > > quick grep
> > > > > > > > > > > > >
> > > > > > > > > > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > > > > > > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > > > > > > > > > allocation.
> > > > > > > > > > > > >
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > > > > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > > > > > > > > > I am seeing.
> > > > > > > > > > > > >
> > > > > > > > > > > > > Any comments on this?
> > > > > > > > > > > >
> > > > > > > > > > > > This violates the spec and probably breaks a few tests and the EFI
> > > > > > > > > > > > boot services API, as you are supposed to be able to define the memory
> > > > > > > > > > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > > > > > > > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > > > > > > > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > > > > > > > > > >
> > > > > > > > > > > To be more specific, I am suggesting:
> > > > > > > > > > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > > > > > > > > > type since it is always that). This should cover all the U-Boot
> > > > > > > > > > > allocations and make sure they are safely within the malloc() pool
> > > > > > > > > > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > > > > > > > > > type in metadata)
> > > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > Personally, I don't see the point and we deviate from the spec as
> > > > > > > > > > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > > > > > > > > > you need to allocate *pages* to grow the pool. What's missing from our
> > > > > > > > > > efi_allocate_pool() is the ability to merge allocations of the same
> > > > > > > > > > memory type to an existing pool assuming there's space, rather than
> > > > > > > > > > requesting a new pool of 4kb.
> > > > > > > > >
> > > > > > > > > "This function allocates pages from EfiConventionalMemory as needed to
> > > > > > > > > grow the requested pool type". So far as EFI is concerned, the
> > > > > > > > > malloc() region is in EfiConventionalMemory, so I don't see any
> > > > > > > > > deviation.
> > > > > > > > >
> > > > > > > > > Please also see below.
> > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > You can switch some callsite of efi_allocate_pool to
> > > > > > > > > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > > > > > > > > >
> > > > > > > > > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > > > > > > > > >
> > > > > > > > >
> > > > > > > > > This is the key point that doesn't seem to be coming across. The
> > > > > > > > > current allocator is allocating memory wherever it likes, potentially
> > > > > > > > > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
> > > > > > > >
> > > > > > > > It is, but I don't think using malloc is solving it. It's papering
> > > > > > > > over the problem, because if someone in the future launches an EFI app
> > > > > > > > or allocates EFI memory with a different type you are back on the same
> > > > > > > > problem.
> > > > > > >
> > > > > > > It is certainly solving this problem.
> > > > > > >
> > > > > > > Once the app is launched it is OK to overwrite memory...after all it
> > > > > > > has been loaded and is running. The issue is that these little
> > > > > > > allocations can end up anywhere in memory. Did you see the qemu_arm
> > > > > > > note?
> > > > > >
> > > > > > Isn't this another part of why we need the LMB rework? So that
> > > > > > kernel_addr_r, et al, can be marked as reserved.
> > > > >
> > > > > If we mark them as reserved, we won't be able to load a file into that
> > > > > region, so boot scripts will fail.
> > > >
> > > > No? We mark it as being overwriteable but allocated. This is part of the
> > > > LMB rework series.
> > >
> > > Yes, I see, that makes sense. I don't know any way to guess the
> > > expected size of the kernel or ramdisk. I see that Apple uses 128MB
> > > for the kernel (plenty) and 1GB for the ramdisk.
> >
> > Yes, 128MiB is a practical limit. Ramdisk is where it gets trickier.
> >
> > > > > Please take a look at the whole series and let me know if there is
> > > > > anything missing from the descriptions I have given. I have had this
> > > > > problem in the back of my mind for some time...but just a few hours of
> > > > > investigation was enough to determine that it really is broken.
> > > >
> > > > I'm missing something, sorry. Yes, it is known that EFI can make some
> > > > incorrect assumptions about what memory is/isn't available (as other
> > > > implementations give EFI the world to work with), hence the LMB rework
> > > > series to address some of these problems.
> > >
> > > My goal here is to tidy up EFI memory allocation so that it doesn't
> > > result in allocating memory in strange places. Avoiding overlaps is
> > > one thing, but the way this is heading, we will get overlap errors
> > > randomly on platforms when someone tries to load something into RAM,
> > > or we won't protect things that need to be protected. It is all a bit
> > > mushy without a proper design.
> > >
> > > Does that make sense?
> >
> > Well, to me step one is to get the lmb series done so that if something
> > needs a range of memory it can both check if it's available and mark it
> > as unavailable to others. As yes, we have something analogous to
> > cooperative multitasking when it comes to memory management today, and
> > that doesn't always work out.
> 
> OK, and that is in progress.
> 
> >
> > Step two is getting the new lmb series and EFI_LOADER talking,
> 
> What sort of talking? Bear in mind that EFI_LOADER currently does
> allocations even if it isn't used.

Well, Sughosh is (well, has, it wasn't in v3) splitting the latter half
of his series out since that's where some of the objections you had were
coming from.

> > so that
> > step three can be seeing what tweaks may be needed in where things
> > allocate memory.
> 
> So my series is step 3?

Or at least understanding what the problems may still be, yes.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 20:05                             ` Simon Glass
  2024-07-30 20:11                               ` Tom Rini
@ 2024-07-31  7:39                               ` Ilias Apalodimas
  2024-07-31 14:39                                 ` Simon Glass
  1 sibling, 1 reply; 45+ messages in thread
From: Ilias Apalodimas @ 2024-07-31  7:39 UTC (permalink / raw)
  To: Simon Glass
  Cc: Tom Rini, Heinrich Schuchardt, Sughosh Ganu, U-Boot Mailing List

[...]

> > > > > > >
> > > > > > > Please also see below.
> > > > > > >
> > > > > > > > > >
> > > > > > > > > > You can switch some callsite of efi_allocate_pool to
> > > > > > > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > > > > > > >
> > > > > > > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > > > > > > >
> > > > > > >
> > > > > > > This is the key point that doesn't seem to be coming across. The
> > > > > > > current allocator is allocating memory wherever it likes, potentially
> > > > > > > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
> > > > > >
> > > > > > It is, but I don't think using malloc is solving it. It's papering
> > > > > > over the problem, because if someone in the future launches an EFI app
> > > > > > or allocates EFI memory with a different type you are back on the same
> > > > > > problem.
> > > > >
> > > > > It is certainly solving this problem.
> > > > >
> > > > > Once the app is launched it is OK to overwrite memory...

Why is it ok to overwrite memory? Do you assume that all EFI apps do
is load something and boot it ?

> after all it
> > > > > has been loaded and is running. The issue is that these little
> > > > > allocations can end up anywhere in memory. Did you see the qemu_arm
> > > > > note?
> > > >
> > > > Isn't this another part of why we need the LMB rework? So that
> > > > kernel_addr_r, et al, can be marked as reserved.
> > >
> > > If we mark them as reserved, we won't be able to load a file into that
> > > region, so boot scripts will fail.
> >
> > No? We mark it as being overwriteable but allocated. This is part of the
> > LMB rework series.
>
> Yes, I see, that makes sense. I don't know any way to guess the
> expected size of the kernel or ramdisk. I see that Apple uses 128MB
> for the kernel (plenty) and 1GB for the ramdisk.

FWIW we dont preload the initrd in EFI. The kernel during the efi stub
hadoff allocates pages, and loads it on the fly.

/Ilias
>
> >
> > > Please take a look at the whole series and let me know if there is
> > > anything missing from the descriptions I have given. I have had this
> > > problem in the back of my mind for some time...but just a few hours of
> > > investigation was enough to determine that it really is broken.
> >
> > I'm missing something, sorry. Yes, it is known that EFI can make some
> > incorrect assumptions about what memory is/isn't available (as other
> > implementations give EFI the world to work with), hence the LMB rework
> > series to address some of these problems.
>
> My goal here is to tidy up EFI memory allocation so that it doesn't
> result in allocating memory in strange places. Avoiding overlaps is
> one thing, but the way this is heading, we will get overlap errors
> randomly on platforms when someone tries to load something into RAM,
> or we won't protect things that need to be protected. It is all a bit
> mushy without a proper design.
>
> Does that make sense?
>
> Regards,
> Simon
>
> [1] https://lore.kernel.org/all/CAFLszThqH01GOqnTxucVE7s+wJU-dz7uqUtkb4iKxs6GgX6tqw@mail.gmail.com/T/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-31  7:39                               ` Ilias Apalodimas
@ 2024-07-31 14:39                                 ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2024-07-31 14:39 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Tom Rini, Heinrich Schuchardt, Sughosh Ganu, U-Boot Mailing List

Hi Ilias,

On Wed, 31 Jul 2024 at 01:40, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> [...]
>
> > > > > > > >
> > > > > > > > Please also see below.
> > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > You can switch some callsite of efi_allocate_pool to
> > > > > > > > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > > > > > > > >
> > > > > > > > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > > > > > > > >
> > > > > > > >
> > > > > > > > This is the key point that doesn't seem to be coming across. The
> > > > > > > > current allocator is allocating memory wherever it likes, potentially
> > > > > > > > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
> > > > > > >
> > > > > > > It is, but I don't think using malloc is solving it. It's papering
> > > > > > > over the problem, because if someone in the future launches an EFI app
> > > > > > > or allocates EFI memory with a different type you are back on the same
> > > > > > > problem.
> > > > > >
> > > > > > It is certainly solving this problem.
> > > > > >
> > > > > > Once the app is launched it is OK to overwrite memory...
>
> Why is it ok to overwrite memory? Do you assume that all EFI apps do
> is load something and boot it ?

I mean that by that point U-Boot has finished loading images etc., so
it doesn't matter if EFI starts putting things where it likes in the
address space.

>
> > after all it
> > > > > > has been loaded and is running. The issue is that these little
> > > > > > allocations can end up anywhere in memory. Did you see the qemu_arm
> > > > > > note?
> > > > >
> > > > > Isn't this another part of why we need the LMB rework? So that
> > > > > kernel_addr_r, et al, can be marked as reserved.
> > > >
> > > > If we mark them as reserved, we won't be able to load a file into that
> > > > region, so boot scripts will fail.
> > >
> > > No? We mark it as being overwriteable but allocated. This is part of the
> > > LMB rework series.
> >
> > Yes, I see, that makes sense. I don't know any way to guess the
> > expected size of the kernel or ramdisk. I see that Apple uses 128MB
> > for the kernel (plenty) and 1GB for the ramdisk.
>
> FWIW we dont preload the initrd in EFI. The kernel during the efi stub
> hadoff allocates pages, and loads it on the fly.

OK thanks.

>
> /Ilias
> >
> > >
> > > > Please take a look at the whole series and let me know if there is
> > > > anything missing from the descriptions I have given. I have had this
> > > > problem in the back of my mind for some time...but just a few hours of
> > > > investigation was enough to determine that it really is broken.
> > >
> > > I'm missing something, sorry. Yes, it is known that EFI can make some
> > > incorrect assumptions about what memory is/isn't available (as other
> > > implementations give EFI the world to work with), hence the LMB rework
> > > series to address some of these problems.
> >
> > My goal here is to tidy up EFI memory allocation so that it doesn't
> > result in allocating memory in strange places. Avoiding overlaps is
> > one thing, but the way this is heading, we will get overlap errors
> > randomly on platforms when someone tries to load something into RAM,
> > or we won't protect things that need to be protected. It is all a bit
> > mushy without a proper design.
> >
> > Does that make sense?
> >
> > Regards,
> > Simon
> >
> > [1] https://lore.kernel.org/all/CAFLszThqH01GOqnTxucVE7s+wJU-dz7uqUtkb4iKxs6GgX6tqw@mail.gmail.com/T/

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-30 21:20                                   ` Tom Rini
@ 2024-07-31 14:39                                     ` Simon Glass
  2024-07-31 17:17                                       ` Tom Rini
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-07-31 14:39 UTC (permalink / raw)
  To: Tom Rini
  Cc: Ilias Apalodimas, Heinrich Schuchardt, Sughosh Ganu,
	U-Boot Mailing List

Hi Tom,

On Tue, 30 Jul 2024 at 15:20, Tom Rini <trini@konsulko.com> wrote:
>
> On Tue, Jul 30, 2024 at 02:52:21PM -0600, Simon Glass wrote:
> > Hi Tom,
> >
> > On Tue, 30 Jul 2024 at 14:11, Tom Rini <trini@konsulko.com> wrote:
> > >
> > > On Tue, Jul 30, 2024 at 02:05:19PM -0600, Simon Glass wrote:
> > > > Hi Tom,
> > > >
> > > > On Tue, 30 Jul 2024 at 13:49, Tom Rini <trini@konsulko.com> wrote:
> > > > >
> > > > > On Tue, Jul 30, 2024 at 01:42:17PM -0600, Simon Glass wrote:
> > > > > > Hi Tom,
> > > > > >
> > > > > > On Tue, 30 Jul 2024 at 09:24, Tom Rini <trini@konsulko.com> wrote:
> > > > > > >
> > > > > > > On Tue, Jul 30, 2024 at 09:18:02AM -0600, Simon Glass wrote:
> > > > > > > > Hi Ilias,
> > > > > > > >
> > > > > > > > On Tue, 30 Jul 2024 at 08:54, Ilias Apalodimas
> > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > >
> > > > > > > > > On Tue, 30 Jul 2024 at 17:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Hi Ilias,
> > > > > > > > > >
> > > > > > > > > > On Tue, 30 Jul 2024 at 02:20, Ilias Apalodimas
> > > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > On Mon, 29 Jul 2024 at 18:28, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > Hi Ilias,
> > > > > > > > > > > >
> > > > > > > > > > > > On Mon, 29 Jul 2024 at 04:02, Ilias Apalodimas
> > > > > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > Hi Simon
> > > > > > > > > > > > >
> > > > > > > > > > > > > On Fri, 26 Jul 2024 at 17:54, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Hi Ilias,
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > On Fri, 26 Jul 2024 at 02:31, Ilias Apalodimas
> > > > > > > > > > > > > > <ilias.apalodimas@linaro.org> wrote:
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Hi Simon,
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > On Fri, 26 Jul 2024 at 02:33, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > Hi Heinrich,
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > On Thu, 25 Jul 2024 at 09:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > On 25.07.24 15:56, Simon Glass wrote:
> > > > > > > > > > > > > > > > > > This API call is intended for allocating small amounts of memory,
> > > > > > > > > > > > > > > > > > similar to malloc(). The current implementation rounds up to whole pages
> > > > > > > > > > > > > > > > > > which can waste large amounts of memory. It also implements its own
> > > > > > > > > > > > > > > > > > malloc()-style header on each block.
> > > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > > Use U-Boot's built-in malloc() instead, to avoid these problems:
> > > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > > - it should normally be large enough for pool allocations
> > > > > > > > > > > > > > > > > > - if it isn't we can enforce a minimum size for boards which use
> > > > > > > > > > > > > > > > > >    EFI_LOADER
> > > > > > > > > > > > > > > > > > - the existing mechanism may create an unwatned entry in the memory map
> > > > > > > > > > > > > > > > > > - it is used for most EFI allocations already
> > > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > > One side effect is that this seems to be showing up some bugs in the
> > > > > > > > > > > > > > > > > > EFI code, since the malloc() pool becomes corrupted with some tests.
> > > > > > > > > > > > > > > > > > This has likely crept in due to the very large gaps between allocations
> > > > > > > > > > > > > > > > > > (around 4KB), which provides a lot of leeway when the allocation size is
> > > > > > > > > > > > > > > > > > too small. Work around this by increasing the size for now, until these
> > > > > > > > > > > > > > > > > > (presumed) bugs are located.
> > > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > > > > > > > > > > > > > > > ---
> > > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > >   lib/efi_loader/efi_memory.c | 93 ++++++++-----------------------------
> > > > > > > > > > > > > > > > > >   1 file changed, 19 insertions(+), 74 deletions(-)
> > > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > > > > index 2945f5648c7..fabe9e3a87a 100644
> > > > > > > > > > > > > > > > > > --- a/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > > > > +++ b/lib/efi_loader/efi_memory.c
> > > > > > > > > > > > > > > > > > @@ -80,45 +80,6 @@ static LIST_HEAD(efi_mem);
> > > > > > > > > > > > > > > > > >   void *efi_bounce_buffer;
> > > > > > > > > > > > > > > > > >   #endif
> > > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > > -/**
> > > > > > > > > > > > > > > > > > - * struct efi_pool_allocation - memory block allocated from pool
> > > > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > > > - * @num_pages:       number of pages allocated
> > > > > > > > > > > > > > > > > > - * @checksum:        checksum
> > > > > > > > > > > > > > > > > > - * @data:    allocated pool memory
> > > > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > > > - * U-Boot services each UEFI AllocatePool() request as a separate
> > > > > > > > > > > > > > > > > > - * (multiple) page allocation. We have to track the number of pages
> > > > > > > > > > > > > > > > > > - * to be able to free the correct amount later.
> > > > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > > > - * The checksum calculated in function checksum() is used in FreePool() to avoid
> > > > > > > > > > > > > > > > > > - * freeing memory not allocated by AllocatePool() and duplicate freeing.
> > > > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > > > - * EFI requires 8 byte alignment for pool allocations, so we can
> > > > > > > > > > > > > > > > > > - * prepend each allocation with these header fields.
> > > > > > > > > > > > > > > > > > - */
> > > > > > > > > > > > > > > > > > -struct efi_pool_allocation {
> > > > > > > > > > > > > > > > > > -     u64 num_pages;
> > > > > > > > > > > > > > > > > > -     u64 checksum;
> > > > > > > > > > > > > > > > > > -     char data[] __aligned(ARCH_DMA_MINALIGN);
> > > > > > > > > > > > > > > > > > -};
> > > > > > > > > > > > > > > > > > -
> > > > > > > > > > > > > > > > > > -/**
> > > > > > > > > > > > > > > > > > - * checksum() - calculate checksum for memory allocated from pool
> > > > > > > > > > > > > > > > > > - *
> > > > > > > > > > > > > > > > > > - * @alloc:   allocation header
> > > > > > > > > > > > > > > > > > - * Return:   checksum, always non-zero
> > > > > > > > > > > > > > > > > > - */
> > > > > > > > > > > > > > > > > > -static u64 checksum(struct efi_pool_allocation *alloc)
> > > > > > > > > > > > > > > > > > -{
> > > > > > > > > > > > > > > > > > -     u64 addr = (uintptr_t)alloc;
> > > > > > > > > > > > > > > > > > -     u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
> > > > > > > > > > > > > > > > > > -               EFI_ALLOC_POOL_MAGIC;
> > > > > > > > > > > > > > > > > > -     if (!ret)
> > > > > > > > > > > > > > > > > > -             ++ret;
> > > > > > > > > > > > > > > > > > -     return ret;
> > > > > > > > > > > > > > > > > > -}
> > > > > > > > > > > > > > > > > > -
> > > > > > > > > > > > > > > > > >   /**
> > > > > > > > > > > > > > > > > >    * efi_mem_cmp() - comparator function for sorting memory map
> > > > > > > > > > > > > > > > > >    *
> > > > > > > > > > > > > > > > > > @@ -681,13 +642,10 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align)
> > > > > > > > > > > > > > > > > >    * @buffer: allocated memory
> > > > > > > > > > > > > > > > > >    * Return:  status code
> > > > > > > > > > > > > > > > > >    */
> > > > > > > > > > > > > > > > > > -efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size, void **buffer)
> > > > > > > > > > > > > > > > > > +efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > > > > > > +                            void **buffer)
> > > > > > > > > > > > > > > > > >   {
> > > > > > > > > > > > > > > > > > -     efi_status_t r;
> > > > > > > > > > > > > > > > > > -     u64 addr;
> > > > > > > > > > > > > > > > > > -     struct efi_pool_allocation *alloc;
> > > > > > > > > > > > > > > > > > -     u64 num_pages = efi_size_in_pages(size +
> > > > > > > > > > > > > > > > > > -                                       sizeof(struct efi_pool_allocation));
> > > > > > > > > > > > > > > > > > +     void *ptr;
> > > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > >       if (!check_allowed())
> > > > > > > > > > > > > > > > > >               return EFI_UNSUPPORTED;
> > > > > > > > > > > > > > > > > > @@ -700,16 +658,21 @@ efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
> > > > > > > > > > > > > > > > > >               return EFI_SUCCESS;
> > > > > > > > > > > > > > > > > >       }
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > Unfortunately this patch does not match the UEFI specification:
> > > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > > Two different memory types cannot reside in the same memory page. You
> > > > > > > > > > > > > > > > > have to keep track of the memory type of each allocated memory page and
> > > > > > > > > > > > > > > > > report it in GetMemoryMap().
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > I actually hadn't thought about that. Is it implied in the spec or
> > > > > > > > > > > > > > > > actually stated somewhere?
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > I don't remember if it's explicitly stated, but it *really* doesn't
> > > > > > > > > > > > > > > have to be. Although u-boot doesn't currently support this on modern
> > > > > > > > > > > > > > > secure systems you can't have code and data mixed on the same page.
> > > > > > > > > > > > > > > This would prevent you to map pages with proper permissions and take
> > > > > > > > > > > > > > > advantage of CPU security features  e.g RW^X.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > As it stands, U-Boot's data is reported as EFI_BOOT_SERVICES_CODE
> > > > > > > > > > > > > > through EFI. Perhaps the difference between EFI_BOOT_SERVICES_CODE and
> > > > > > > > > > > > > > EFI_BOOT_SERVICES_DATA isn't that important?
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > But if we did want to fix that, we could make the malloc region
> > > > > > > > > > > > > > EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > In any case, the AllocatePool description reads "This function
> > > > > > > > > > > > > > > allocates pages from EfiConventionalMemory as needed to grow the
> > > > > > > > > > > > > > > requested pool type. All allocations are eight-byte aligned". I don't
> > > > > > > > > > > > > > > think using malloc in AllocatePool is appropriate. If we ever want to
> > > > > > > > > > > > > > > do that and allocate from the malloc space, we need to teach malloc
> > > > > > > > > > > > > > > some EFI semantics, but that's a really bad idea.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Here I don't see the difference between EFI's malloc(n) and
> > > > > > > > > > > > > > memalign(8, n). I do see a lot of comments like 'use the spec', 'read
> > > > > > > > > > > > > > the spec'. It seems very clear to me that the bootloader can use
> > > > > > > > > > > > > > whatever algorithm it likes to provide the allocated memory for the
> > > > > > > > > > > > > > pool.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > What sort of EFI semantics are you referring to?
> > > > > > > > > > > > >
> > > > > > > > > > > > > the memory type. Your patch removes the call to efi_allocate_pages
> > > > > > > > > > > > > which tracks it.
> > > > > > > > > > > >
> > > > > > > > > > > > I can put that code back, depending on what we decide below.
> > > > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > Anyway, from what I can tell, we mostly use EFI_BOOT_SERVICES_DATA.
> > > > > > > > > > > > > > > > The only exception is EFI_RUNTIME_SERVICES_DATA for the runtime stuff.
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > That's not entirely correct, we use a lot more than these 2.
> > > > > > > > > > > > > > > EFI_LOADER_DATA, EFI_LOADER_CODE and EFI_ACPI_RECLAIM_MEMORY from a
> > > > > > > > > > > > > > > quick grep
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Yes I did a quick grep too, but then checked each site. The first two
> > > > > > > > > > > > > > are used in an EFI app, not U-Boot itself. The ACPI one is not an
> > > > > > > > > > > > > > allocation.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > So perhaps we can use malloc() for EFI_BOOT_SERVICES_DATA allocations
> > > > > > > > > > > > > > > > and stick with whole pages otherwise? That will mostly fix the problem
> > > > > > > > > > > > > > > > I am seeing.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Any comments on this?
> > > > > > > > > > > > >
> > > > > > > > > > > > > This violates the spec and probably breaks a few tests and the EFI
> > > > > > > > > > > > > boot services API, as you are supposed to be able to define the memory
> > > > > > > > > > > > > type. We might be able to control it from U-Boot, but EFI apps are
> > > > > > > > > > > > > going to end up being buggy and hard to debug -- e.g an app calls
> > > > > > > > > > > > > allocatePool to allocate memory that needs to be preserved at runtime.
> > > > > > > > > > > >
> > > > > > > > > > > > To be more specific, I am suggesting:
> > > > > > > > > > > > - use malloc() for EFI_BOOT_SERVICES_DATA allocations (with no memory
> > > > > > > > > > > > type since it is always that). This should cover all the U-Boot
> > > > > > > > > > > > allocations and make sure they are safely within the malloc() pool
> > > > > > > > > > > > - use efi_allocate_pages() for other memory types (keeping the memory
> > > > > > > > > > > > type in metadata)
> > > > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > Personally, I don't see the point and we deviate from the spec as
> > > > > > > > > > > well. Perhaps Heinrich thinks otherwise, but the EFI spec still says
> > > > > > > > > > > you need to allocate *pages* to grow the pool. What's missing from our
> > > > > > > > > > > efi_allocate_pool() is the ability to merge allocations of the same
> > > > > > > > > > > memory type to an existing pool assuming there's space, rather than
> > > > > > > > > > > requesting a new pool of 4kb.
> > > > > > > > > >
> > > > > > > > > > "This function allocates pages from EfiConventionalMemory as needed to
> > > > > > > > > > grow the requested pool type". So far as EFI is concerned, the
> > > > > > > > > > malloc() region is in EfiConventionalMemory, so I don't see any
> > > > > > > > > > deviation.
> > > > > > > > > >
> > > > > > > > > > Please also see below.
> > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > > > > > > You can switch some callsite of efi_allocate_pool to
> > > > > > > > > > > > > efi_allocate_pages() internally. Will that fix the behavior?
> > > > > > > > > > > >
> > > > > > > > > > > > It still allocates memory 'in space' and uses 4KB for each allocation.
> > > > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > This is the key point that doesn't seem to be coming across. The
> > > > > > > > > > current allocator is allocating memory wherever it likes, potentially
> > > > > > > > > > interfering with the kernel_addr_r addresses, etc. as on qemu_arm.
> > > > > > > > >
> > > > > > > > > It is, but I don't think using malloc is solving it. It's papering
> > > > > > > > > over the problem, because if someone in the future launches an EFI app
> > > > > > > > > or allocates EFI memory with a different type you are back on the same
> > > > > > > > > problem.
> > > > > > > >
> > > > > > > > It is certainly solving this problem.
> > > > > > > >
> > > > > > > > Once the app is launched it is OK to overwrite memory...after all it
> > > > > > > > has been loaded and is running. The issue is that these little
> > > > > > > > allocations can end up anywhere in memory. Did you see the qemu_arm
> > > > > > > > note?
> > > > > > >
> > > > > > > Isn't this another part of why we need the LMB rework? So that
> > > > > > > kernel_addr_r, et al, can be marked as reserved.
> > > > > >
> > > > > > If we mark them as reserved, we won't be able to load a file into that
> > > > > > region, so boot scripts will fail.
> > > > >
> > > > > No? We mark it as being overwriteable but allocated. This is part of the
> > > > > LMB rework series.
> > > >
> > > > Yes, I see, that makes sense. I don't know any way to guess the
> > > > expected size of the kernel or ramdisk. I see that Apple uses 128MB
> > > > for the kernel (plenty) and 1GB for the ramdisk.
> > >
> > > Yes, 128MiB is a practical limit. Ramdisk is where it gets trickier.
> > >
> > > > > > Please take a look at the whole series and let me know if there is
> > > > > > anything missing from the descriptions I have given. I have had this
> > > > > > problem in the back of my mind for some time...but just a few hours of
> > > > > > investigation was enough to determine that it really is broken.
> > > > >
> > > > > I'm missing something, sorry. Yes, it is known that EFI can make some
> > > > > incorrect assumptions about what memory is/isn't available (as other
> > > > > implementations give EFI the world to work with), hence the LMB rework
> > > > > series to address some of these problems.
> > > >
> > > > My goal here is to tidy up EFI memory allocation so that it doesn't
> > > > result in allocating memory in strange places. Avoiding overlaps is
> > > > one thing, but the way this is heading, we will get overlap errors
> > > > randomly on platforms when someone tries to load something into RAM,
> > > > or we won't protect things that need to be protected. It is all a bit
> > > > mushy without a proper design.
> > > >
> > > > Does that make sense?
> > >
> > > Well, to me step one is to get the lmb series done so that if something
> > > needs a range of memory it can both check if it's available and mark it
> > > as unavailable to others. As yes, we have something analogous to
> > > cooperative multitasking when it comes to memory management today, and
> > > that doesn't always work out.
> >
> > OK, and that is in progress.
> >
> > >
> > > Step two is getting the new lmb series and EFI_LOADER talking,
> >
> > What sort of talking? Bear in mind that EFI_LOADER currently does
> > allocations even if it isn't used.
>
> Well, Sughosh is (well, has, it wasn't in v3) splitting the latter half
> of his series out since that's where some of the objections you had were
> coming from.

Oh I see.

>
> > > so that
> > > step three can be seeing what tweaks may be needed in where things
> > > allocate memory.
> >
> > So my series is step 3?
>
> Or at least understanding what the problems may still be, yes.

In that case I would like to clean up EFI's memory management before
doing step 2, since I believe many of the problems will just go away
if we can get that right.

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 6/6] efi: Show the location of the bounce buffer
  2024-07-25 16:31   ` Heinrich Schuchardt
@ 2024-07-31 14:39     ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2024-07-31 14:39 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, AKASHI Takahiro,
	U-Boot Mailing List

Hi Heinrich,

On Thu, 25 Jul 2024 at 10:36, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 25.07.24 15:56, Simon Glass wrote:
> > The EFI_LOADER_BOUNCE_BUFFER feature was added many years ago. It is not
> > clear whether it is still needed, but 24 boards (lx2160ardb_tfa_stmm,
> > lx2162aqds_tfa_SECURE_BOOT and the like) use it.
> >
> > This feature uses EFI page allocation to create a 64MB buffer 'in space'
> > without any knowledge of where boards intend to load their images. This
> > may result in image corruption or other problems.
>
> This is what Sughosh's LMB series in addressing.

Not really...it will still be 'in space' so will be annoying for some
boards. This series is separate from Sughosh's.

>
> With CONFIG_SYS_MALLOC_LEN=0x202000 for lx2160ardb_tfa_stmm we cannot
> move the buffer to malloc(). I don't understand why
> CONFIG_SYS_MALLOC_LEN is so small.

Because it is designed for small allocations, e.g. the equivalent of
EFI's pool. We have never used malloc() to load images, not for
allocating bounce buffers.

>
> >
> > For example, if the feature is enabled on qemu_arm64 it puts the EFI
> > bounce buffer at 1045MB, with the kernel at 1028MB and the ramdisk at
> > 1088MB. The kernel is probably smaller than 27MB but the buffer does
> > overlap the ramdisk.
> >
> > The solution is probably to use BOUNCE_BUFFER instead, with the EFI
> > version being dropped. For now, show the address of the EFI bounce
> > buffer so people have a better chance to detect the problem.
> >
> > Note: I avoided converting this #ifdef to use IS_ENABLED() since I hope
> > that the feature may be removed.
> >
> > Signed-off-by: Simon Glass <sjg@chromium.org>
> > ---
> >
> >   lib/efi_loader/efi_bootbin.c | 8 ++++++++
> >   1 file changed, 8 insertions(+)
> >
> > diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c
> > index 07c8fca68cc..53e5e429d2e 100644
> > --- a/lib/efi_loader/efi_bootbin.c
> > +++ b/lib/efi_loader/efi_bootbin.c
> > @@ -211,6 +211,14 @@ efi_status_t efi_binary_run(void *image, size_t size, void *fdt)
> >               return -1;
> >       }
> >
> > +#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
> > +     /*
> > +      * Add a warning about this buffer, since it may conflict with other
> > +      * things
> > +      */
> > +     printf("EFI bounce buffer at %p\n", efi_bounce_buffer);
> > +#endif
>
> Does this sound like a warning to you?
>
> There is nothing that a user can reasonably do when seeing this message
> while the board is booting via one of the boot methods. So we should not
> write it.

We could add a 'warning:' prefix, perhaps? At the moment there is no
clue that the bounce buffer may overwrite an image. What do you
suggest?

> > +
> >       ret = efi_install_fdt(fdt);
> >       if (ret != EFI_SUCCESS)
> >               return ret;
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 4/6] efi: Avoid pool allocation in efi_get_memory_map_alloc()
  2024-07-25 16:38   ` Heinrich Schuchardt
@ 2024-07-31 14:39     ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2024-07-31 14:39 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Ilias Apalodimas, Sughosh Ganu, Tom Rini, U-Boot Mailing List

Hi Heinrich,

On Thu, 25 Jul 2024 at 10:38, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 25.07.24 15:56, Simon Glass wrote:
> > This function returns the memory map, allocating memory for it. But it
> > can just use malloc() directly, rather than calling the pool allocator.
> > Update it.
>
> The size of the memory map may exceed CONFIG_SYS_MALLOC_LEN which tends
> to be astonishingly small: 1 MiB or less on many boards.

The memory map is typically <20 entries, isn't it? I can't see that
using too much space. If EFI needs malloc() to be larger than 1MB, we
could enforce that with Kconfig.

Regards,
Simon


>
> Best regards
>
> Heinrich
>
> >
> > Signed-off-by: Simon Glass <sjg@chromium.org>
> > ---
> >
> >   lib/efi_loader/efi_memory.c | 9 ++++-----
> >   1 file changed, 4 insertions(+), 5 deletions(-)
> >
> > diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
> > index 087f4c88cdf..2945f5648c7 100644
> > --- a/lib/efi_loader/efi_memory.c
> > +++ b/lib/efi_loader/efi_memory.c
> > @@ -850,14 +850,13 @@ efi_status_t efi_get_memory_map_alloc(efi_uintn_t *map_size,
> >       ret = efi_get_memory_map(map_size, *memory_map, NULL, NULL, NULL);
> >       if (ret == EFI_BUFFER_TOO_SMALL) {
> >               *map_size += sizeof(struct efi_mem_desc); /* for the map */
> > -             ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, *map_size,
> > -                                     (void **)memory_map);
> > -             if (ret != EFI_SUCCESS)
> > -                     return ret;
> > +             *memory_map = malloc(*map_size);
> > +             if (!*memory_map)
> > +                     return EFI_OUT_OF_RESOURCES;
> >               ret = efi_get_memory_map(map_size, *memory_map,
> >                                        NULL, NULL, NULL);
> >               if (ret != EFI_SUCCESS) {
> > -                     efi_free_pool(*memory_map);
> > +                     free(*memory_map);
> >                       *memory_map = NULL;
> >               }
> >       }
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-31 14:39                                     ` Simon Glass
@ 2024-07-31 17:17                                       ` Tom Rini
  2024-08-01 10:14                                         ` Ilias Apalodimas
  0 siblings, 1 reply; 45+ messages in thread
From: Tom Rini @ 2024-07-31 17:17 UTC (permalink / raw)
  To: Simon Glass
  Cc: Ilias Apalodimas, Heinrich Schuchardt, Sughosh Ganu,
	U-Boot Mailing List

[-- Attachment #1: Type: text/plain, Size: 751 bytes --]

On Wed, Jul 31, 2024 at 08:39:23AM -0600, Simon Glass wrote:

[snip]
> > > > so that
> > > > step three can be seeing what tweaks may be needed in where things
> > > > allocate memory.
> > >
> > > So my series is step 3?
> >
> > Or at least understanding what the problems may still be, yes.
> 
> In that case I would like to clean up EFI's memory management before
> doing step 2, since I believe many of the problems will just go away
> if we can get that right.

Well, I think that's where some of the big points of contention are,
yes. You say "fix" and Ilias and Heinrich say "but the spec".

Perhaps once step one is done it will be easier to find a way to address
your concerns without also breaking "the spec".

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-07-31 17:17                                       ` Tom Rini
@ 2024-08-01 10:14                                         ` Ilias Apalodimas
  2024-08-01 16:14                                           ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Ilias Apalodimas @ 2024-08-01 10:14 UTC (permalink / raw)
  To: Tom Rini, Simon Glass
  Cc: Heinrich Schuchardt, Sughosh Ganu, U-Boot Mailing List

Hi Tom

On Wed, 31 Jul 2024 at 20:17, Tom Rini <trini@konsulko.com> wrote:
>
> On Wed, Jul 31, 2024 at 08:39:23AM -0600, Simon Glass wrote:
>
> [snip]
> > > > > so that
> > > > > step three can be seeing what tweaks may be needed in where things
> > > > > allocate memory.
> > > >
> > > > So my series is step 3?
> > >
> > > Or at least understanding what the problems may still be, yes.
> >
> > In that case I would like to clean up EFI's memory management before
> > doing step 2, since I believe many of the problems will just go away
> > if we can get that right.
>
> Well, I think that's where some of the big points of contention are,
> yes. You say "fix" and Ilias and Heinrich say "but the spec".

Using malloc on efi_allocate_pool() would be ok spec-wise. Simon's
current patch is ignoring the efi memory type metadata we have to
preserve. On top of that using malloc for a *single* memory type, just
kicks the can down the road until an EFI app chooses a different type
and we are back to the same problem.

It's fine to teach efi_allocate_pool to use malloc with 2 conditions
- memory types are preserved for all allocations
- malloc area is big enough

Cheers
/Ilias

>
> Perhaps once step one is done it will be easier to find a way to address
> your concerns without also breaking "the spec".
>
> --
> Tom

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-08-01 10:14                                         ` Ilias Apalodimas
@ 2024-08-01 16:14                                           ` Simon Glass
  2024-08-01 16:22                                             ` Ilias Apalodimas
  0 siblings, 1 reply; 45+ messages in thread
From: Simon Glass @ 2024-08-01 16:14 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Tom Rini, Heinrich Schuchardt, Sughosh Ganu, U-Boot Mailing List

Hi Ilias,

On Thu, 1 Aug 2024 at 04:14, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> Hi Tom
>
> On Wed, 31 Jul 2024 at 20:17, Tom Rini <trini@konsulko.com> wrote:
> >
> > On Wed, Jul 31, 2024 at 08:39:23AM -0600, Simon Glass wrote:
> >
> > [snip]
> > > > > > so that
> > > > > > step three can be seeing what tweaks may be needed in where things
> > > > > > allocate memory.
> > > > >
> > > > > So my series is step 3?
> > > >
> > > > Or at least understanding what the problems may still be, yes.
> > >
> > > In that case I would like to clean up EFI's memory management before
> > > doing step 2, since I believe many of the problems will just go away
> > > if we can get that right.
> >
> > Well, I think that's where some of the big points of contention are,
> > yes. You say "fix" and Ilias and Heinrich say "but the spec".
>
> Using malloc on efi_allocate_pool() would be ok spec-wise. Simon's
> current patch is ignoring the efi memory type metadata we have to
> preserve. On top of that using malloc for a *single* memory type, just
> kicks the can down the road until an EFI app chooses a different type
> and we are back to the same problem.
>
> It's fine to teach efi_allocate_pool to use malloc with 2 conditions
> - memory types are preserved for all allocations
> - malloc area is big enough

OK thanks I will take a look.

I see the EFI code eventually dealing with memory in two distinct phases:
- before the app runs: we try to keep memory usage to within areas people expect
- once the app runs: t doesn't matter anymore

>
> Cheers
> /Ilias
>
> >
> > Perhaps once step one is done it will be easier to find a way to address
> > your concerns without also breaking "the spec".

Regards,
Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-08-01 16:14                                           ` Simon Glass
@ 2024-08-01 16:22                                             ` Ilias Apalodimas
  2024-08-01 17:43                                               ` Simon Glass
  0 siblings, 1 reply; 45+ messages in thread
From: Ilias Apalodimas @ 2024-08-01 16:22 UTC (permalink / raw)
  To: Simon Glass
  Cc: Tom Rini, Heinrich Schuchardt, Sughosh Ganu, U-Boot Mailing List

hi Simon,

On Thu, 1 Aug 2024 at 19:14, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Ilias,
>
> On Thu, 1 Aug 2024 at 04:14, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > Hi Tom
> >
> > On Wed, 31 Jul 2024 at 20:17, Tom Rini <trini@konsulko.com> wrote:
> > >
> > > On Wed, Jul 31, 2024 at 08:39:23AM -0600, Simon Glass wrote:
> > >
> > > [snip]
> > > > > > > so that
> > > > > > > step three can be seeing what tweaks may be needed in where things
> > > > > > > allocate memory.
> > > > > >
> > > > > > So my series is step 3?
> > > > >
> > > > > Or at least understanding what the problems may still be, yes.
> > > >
> > > > In that case I would like to clean up EFI's memory management before
> > > > doing step 2, since I believe many of the problems will just go away
> > > > if we can get that right.
> > >
> > > Well, I think that's where some of the big points of contention are,
> > > yes. You say "fix" and Ilias and Heinrich say "but the spec".
> >
> > Using malloc on efi_allocate_pool() would be ok spec-wise. Simon's
> > current patch is ignoring the efi memory type metadata we have to
> > preserve. On top of that using malloc for a *single* memory type, just
> > kicks the can down the road until an EFI app chooses a different type
> > and we are back to the same problem.
> >
> > It's fine to teach efi_allocate_pool to use malloc with 2 conditions
> > - memory types are preserved for all allocations
> > - malloc area is big enough
>
> OK thanks I will take a look.
>
> I see the EFI code eventually dealing with memory in two distinct phases:
> - before the app runs: we try to keep memory usage to within areas people expect
> - once the app runs: t doesn't matter anymore

You need to define 'the app' a bit better.
An app for example may load, use efi_allocate_pool to inject some
runtime memory data which the OS expects and exit. The you would load
you OS (which is really another EFI app). In general I wouldn't make
any assumptions on what EFI apps will do wrt to memory

/Ilias
>
> >
> > Cheers
> > /Ilias
> >
> > >
> > > Perhaps once step one is done it will be easier to find a way to address
> > > your concerns without also breaking "the spec".
>
> Regards,
> Simon

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 5/6] efi: Use malloc() for the EFI pool
  2024-08-01 16:22                                             ` Ilias Apalodimas
@ 2024-08-01 17:43                                               ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2024-08-01 17:43 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Tom Rini, Heinrich Schuchardt, Sughosh Ganu, U-Boot Mailing List

Hi Ilias,

On Thu, 1 Aug 2024 at 10:22, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> hi Simon,
>
> On Thu, 1 Aug 2024 at 19:14, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Ilias,
> >
> > On Thu, 1 Aug 2024 at 04:14, Ilias Apalodimas
> > <ilias.apalodimas@linaro.org> wrote:
> > >
> > > Hi Tom
> > >
> > > On Wed, 31 Jul 2024 at 20:17, Tom Rini <trini@konsulko.com> wrote:
> > > >
> > > > On Wed, Jul 31, 2024 at 08:39:23AM -0600, Simon Glass wrote:
> > > >
> > > > [snip]
> > > > > > > > so that
> > > > > > > > step three can be seeing what tweaks may be needed in where things
> > > > > > > > allocate memory.
> > > > > > >
> > > > > > > So my series is step 3?
> > > > > >
> > > > > > Or at least understanding what the problems may still be, yes.
> > > > >
> > > > > In that case I would like to clean up EFI's memory management before
> > > > > doing step 2, since I believe many of the problems will just go away
> > > > > if we can get that right.
> > > >
> > > > Well, I think that's where some of the big points of contention are,
> > > > yes. You say "fix" and Ilias and Heinrich say "but the spec".
> > >
> > > Using malloc on efi_allocate_pool() would be ok spec-wise. Simon's
> > > current patch is ignoring the efi memory type metadata we have to
> > > preserve. On top of that using malloc for a *single* memory type, just
> > > kicks the can down the road until an EFI app chooses a different type
> > > and we are back to the same problem.
> > >
> > > It's fine to teach efi_allocate_pool to use malloc with 2 conditions
> > > - memory types are preserved for all allocations
> > > - malloc area is big enough
> >
> > OK thanks I will take a look.
> >
> > I see the EFI code eventually dealing with memory in two distinct phases:
> > - before the app runs: we try to keep memory usage to within areas people expect
> > - once the app runs: t doesn't matter anymore
>
> You need to define 'the app' a bit better.
> An app for example may load, use efi_allocate_pool to inject some
> runtime memory data which the OS expects and exit. The you would load
> you OS (which is really another EFI app). In general I wouldn't make
> any assumptions on what EFI apps will do wrt to memory

My intention is not to make assumptions about what EFI apps will do*.

But I do see value in separating the setup done by U-Boot from the
operation of the app. My point is that once the app starts, we don't
care what memory the app uses, so long as it allocates things
properly. If the app exits back to U-Boot and then U-Boot runs another
app, I would say we are in 'EFI land' as soon as the first app runs,
so further EFI apps don't really change that. EFI apps are allowed to
allocate memory as they wish...they can even request a particular
memory address!

Regards,
Simon

> > > > Perhaps once step one is done it will be easier to find a way to address
> > > > your concerns without also breaking "the spec".


* After all, that is my primary criticism of EFI as a whole.

^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2024-08-01 17:44 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-25 13:56 [PATCH 0/6] efi: Start tidying up memory management Simon Glass
2024-07-25 13:56 ` [PATCH 1/6] doc: Show more information in efi index Simon Glass
2024-07-25 16:02   ` Heinrich Schuchardt
2024-07-25 13:56 ` [PATCH 2/6] efi: Convert device_path allocation to use malloc() Simon Glass
2024-07-25 16:13   ` Heinrich Schuchardt
2024-07-25 23:32     ` Simon Glass
2024-07-25 13:56 ` [PATCH 3/6] efi: Allow monitoring of page allocations Simon Glass
2024-07-25 16:20   ` Heinrich Schuchardt
2024-07-25 23:33     ` Simon Glass
2024-07-25 13:56 ` [PATCH 4/6] efi: Avoid pool allocation in efi_get_memory_map_alloc() Simon Glass
2024-07-25 16:38   ` Heinrich Schuchardt
2024-07-31 14:39     ` Simon Glass
2024-07-25 13:56 ` [PATCH 5/6] efi: Use malloc() for the EFI pool Simon Glass
2024-07-25 15:49   ` Heinrich Schuchardt
2024-07-25 23:32     ` Simon Glass
2024-07-26  8:31       ` Ilias Apalodimas
2024-07-26 14:54         ` Simon Glass
2024-07-29 10:01           ` Ilias Apalodimas
2024-07-29 15:28             ` Simon Glass
2024-07-30  8:19               ` Ilias Apalodimas
2024-07-30 14:38                 ` Simon Glass
2024-07-30 14:53                   ` Mark Kettenis
2024-07-30 15:18                     ` Simon Glass
2024-07-30 14:53                   ` Ilias Apalodimas
2024-07-30 15:18                     ` Simon Glass
2024-07-30 15:24                       ` Tom Rini
2024-07-30 19:42                         ` Simon Glass
2024-07-30 19:49                           ` Tom Rini
2024-07-30 20:05                             ` Simon Glass
2024-07-30 20:11                               ` Tom Rini
2024-07-30 20:52                                 ` Simon Glass
2024-07-30 21:20                                   ` Tom Rini
2024-07-31 14:39                                     ` Simon Glass
2024-07-31 17:17                                       ` Tom Rini
2024-08-01 10:14                                         ` Ilias Apalodimas
2024-08-01 16:14                                           ` Simon Glass
2024-08-01 16:22                                             ` Ilias Apalodimas
2024-08-01 17:43                                               ` Simon Glass
2024-07-31  7:39                               ` Ilias Apalodimas
2024-07-31 14:39                                 ` Simon Glass
2024-07-25 13:56 ` [PATCH 6/6] efi: Show the location of the bounce buffer Simon Glass
2024-07-25 16:31   ` Heinrich Schuchardt
2024-07-31 14:39     ` Simon Glass
2024-07-25 14:25 ` [PATCH 0/6] efi: Start tidying up memory management Ilias Apalodimas
2024-07-25 15:58   ` Simon Glass

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox