summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/include/stdlib.h3
-rw-r--r--src/lib/malloc.c29
2 files changed, 30 insertions, 2 deletions
diff --git a/src/include/stdlib.h b/src/include/stdlib.h
index a8297f82a5..f6369bf0f4 100644
--- a/src/include/stdlib.h
+++ b/src/include/stdlib.h
@@ -5,7 +5,6 @@
void *memalign(size_t boundary, size_t size);
void *malloc(size_t size);
-/* We never free memory */
-static inline void free(void *ptr) {}
+void free(void *ptr);
#endif /* STDLIB_H */
diff --git a/src/lib/malloc.c b/src/lib/malloc.c
index aa266b44dc..ddc7ea5739 100644
--- a/src/lib/malloc.c
+++ b/src/lib/malloc.c
@@ -11,6 +11,8 @@
extern unsigned char _heap, _eheap;
static void *free_mem_ptr = &_heap; /* Start of heap */
static void *free_mem_end_ptr = &_eheap; /* End of heap */
+static void *free_last_alloc_ptr = &_heap; /* End of heap before
+ last allocation */
/* We don't restrict the boundary. This is firmware,
* you are supposed to know what you are doing.
@@ -26,6 +28,12 @@ void *memalign(size_t boundary, size_t size)
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) {
printk(BIOS_ERR, "memalign(boundary=%zu, size=%zu): failed: ",
@@ -46,3 +54,24 @@ void *malloc(size_t size)
{
return memalign(sizeof(u64), size);
}
+
+void free(void *ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ if (ptr < (void *)&_heap || ptr >= free_mem_end_ptr) {
+ printk(BIOS_WARNING, "Warning - Pointer passed to %s is not "
+ "pointing to the heap\n", __func__);
+ return;
+ }
+
+ /*
+ * Rewind the heap pointer to the end of heap
+ * before the last successful malloc().
+ */
+ if (ptr == free_last_alloc_ptr) {
+ free_mem_ptr = free_last_alloc_ptr;
+ free_last_alloc_ptr = NULL;
+ }
+}