From 8b6fc3a877d8169091d034ea6ac6d15593cc69a0 Mon Sep 17 00:00:00 2001
From: Jonathon Hall <jonathon.hall@puri.sm>
Date: Mon, 15 Jul 2024 15:01:52 -0400
Subject: [PATCH] src/lib/malloc.c: If allocation fails, leave the heap
 unchanged

If an allocation fails because it is too large for the rest of the heap,
don't consume the rest of the heap needlessly.

This started occurring with the Heads bootsplash image in 24.02.01,
following the switch to the Wuffs JPEG decoder.  The work area needed
was too large for the heap.  The bootsplash failed to show, but worse,
the boot failed entirely because we were then out of heap space, even
though we did not actually use the large allocation that failed.

With this change, that failure no longer prevents boot.

The error message is improved slightly also:
* missing line break is added
* "Tried to round up" now shows the beginning of the allocation before
  and after rounding instead of the unrounded beginning and rounded end
  (misleading, looked like it was trying to align by 1 MB when it
  was actually allocating 1 MB)

Change-Id: Ie72814027d9daa517c0794f3ea7abec2b9a9d596
Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
---
 src/lib/malloc.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/src/lib/malloc.c b/src/lib/malloc.c
index 30298064d9..281792c3d5 100644
--- a/src/lib/malloc.c
+++ b/src/lib/malloc.c
@@ -26,30 +26,29 @@ void *memalign(size_t boundary, size_t size)
 	MALLOCDBG("%s Enter, boundary %zu, size %zu, free_mem_ptr %p\n",
 		__func__, boundary, size, free_mem_ptr);
 
-	free_mem_ptr = (void *)ALIGN_UP((unsigned long)free_mem_ptr, boundary);
+	p = (void *)ALIGN_UP((unsigned long)free_mem_ptr, boundary);
 
-	p = free_mem_ptr;
-	free_mem_ptr += size;
-	/*
-	 * Store last allocation pointer after ALIGN, as malloc() will
-	 * return it. This may cause n bytes of gap between allocations
-	 * where n < boundary.
-	 */
-	free_last_alloc_ptr = p;
-
-	if (free_mem_ptr >= free_mem_end_ptr) {
+	if (p + size >= free_mem_end_ptr) {
 		printk(BIOS_ERR, "%s(boundary=%zu, size=%zu): failed: ",
 				__func__, boundary, size);
 		printk(BIOS_ERR, "Tried to round up free_mem_ptr %p to %p\n",
-				p, free_mem_ptr);
+				free_mem_ptr, p);
 		printk(BIOS_ERR, "but free_mem_end_ptr is %p\n",
 				free_mem_end_ptr);
 		printk(BIOS_ERR, "Error! %s: Out of memory "
-				"(free_mem_ptr >= free_mem_end_ptr)",
+				"(free_mem_ptr >= free_mem_end_ptr)\n",
 				__func__);
 		return NULL;
 	}
 
+	free_mem_ptr = p + size;
+	/*
+	 * Store last allocation pointer after ALIGN, as malloc() will
+	 * return it. This may cause n bytes of gap between allocations
+	 * where n < boundary.
+	 */
+	free_last_alloc_ptr = p;
+
 	MALLOCDBG("%s %p\n", __func__, p);
 
 	return p;
-- 
2.39.2

