[PATCH 0/6] efi: Start tidying up memory management

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(-)

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

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

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;

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);
if (!p) return NULL; ret = p;p = malloc(sz1 + sz2 + end_size);
@@ -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));
if (!p) return NULL; memcpy(p, node, sz);void *p = malloc(sz + sizeof(END));
@@ -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));
if (!p) return NULL; memcpy(p, dp, sz);void *p = malloc(sz + node->length + sizeof(END));
@@ -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;

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

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)

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)

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

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; } }

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)
ret = efi_get_memory_map(map_size, *memory_map, NULL, NULL, NULL); if (ret != EFI_SUCCESS) {return EFI_OUT_OF_RESOURCES;
efi_free_pool(*memory_map);
} }free(*memory_map); *memory_map = NULL;

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; } }

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; }
/**

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; }
/**

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;
}
/**

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;
}
/**

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

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

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

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

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

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.

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

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

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

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.

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

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.

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-dz7uqUtkb4iKxs6GgX6...

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.

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

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.

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

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".

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

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

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

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.

[...]
> > 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-dz7uqUtkb4iKxs6GgX6...

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-dz7uqUtkb4iKxs6GgX6...
Regards, Simon

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;

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;

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

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

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
participants (5)
-
Heinrich Schuchardt
-
Ilias Apalodimas
-
Mark Kettenis
-
Simon Glass
-
Tom Rini