iPXE
uheap.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26#include <ipxe/io.h>
27#include <ipxe/memmap.h>
28#include <ipxe/malloc.h>
29#include <ipxe/umalloc.h>
30
31/** @file
32 *
33 * External ("user") heap
34 *
35 * This file implements an external heap (for umalloc()) that grows
36 * downwards from the top of the largest contiguous accessible block
37 * in the system memory map.
38 */
39
40/**
41 * Alignment for external heap allocations
42 *
43 * Historically, umalloc() has produced page-aligned allocations, and
44 * the hidden region in the system memory map has been aligned to a
45 * page boundary. Preserve this behaviour, to avoid needing to
46 * inspect and update large amounts of driver code, and also because
47 * it keeps the resulting memory maps easy to read.
48 */
49#define UHEAP_ALIGN PAGE_SIZE
50
51static struct heap uheap;
52
53/** Minimum possible start of external heap */
55
56/** Start of external heap */
58
59/** End of external heap */
61
62/** In-use memory region */
63struct used_region uheap_used __used_region = {
64 .name = "uheap",
65};
66
67/**
68 * Adjust size of external heap in-use memory region
69 *
70 * @v delta Size change
71 */
72static void uheap_resize ( ssize_t delta ) {
73
74 /* Update in-use memory region */
75 assert ( ( delta & ( UHEAP_ALIGN - 1 ) ) == 0 );
76 uheap_start -= delta;
79 assert ( ( uheap_limit & ( UHEAP_ALIGN - 1 ) ) == 0 );
80 assert ( ( uheap_start & ( UHEAP_ALIGN - 1 ) ) == 0 );
81 assert ( ( uheap_end & ( UHEAP_ALIGN - 1 ) ) == 0 );
82 memmap_use ( &uheap_used, uheap_start, ( uheap_end - uheap_start ) );
83 DBGC ( &uheap, "UHEAP now at (%#08lx)...[%#08lx,%#08lx)\n",
85 memmap_dump_all ( 1 );
86}
87
88/**
89 * Find an external heap region
90 *
91 */
92static void uheap_find ( void ) {
95 size_t before;
96 size_t after;
97 size_t strip;
98 size_t size;
99
100 /* Sanity checks */
103 assert ( uheap_used.size == 0 );
104
105 /* Find the largest region within the system memory map */
107 end = ( start + size );
108 DBGC ( &uheap, "UHEAP largest region is [%#08lx,%#08lx)\n",
109 start, end );
110
111 /* Align start and end addresses, and prevent overflow to zero */
112 after = ( end ? ( end & ( UHEAP_ALIGN - 1 ) ) : UHEAP_ALIGN );
113 before = ( ( -start ) & ( UHEAP_ALIGN - 1 ) );
114 strip = ( before + after );
115 if ( strip > size )
116 return;
117 start += before;
118 end -= after;
119 size -= strip;
120 assert ( ( end - start ) == size );
121
122 /* Record region */
125 uheap_end = end;
126 uheap_resize ( 0 );
127}
128
129/**
130 * Attempt to grow external heap
131 *
132 * @v size Failed allocation size
133 * @ret grown Heap has grown: retry allocations
134 */
135static unsigned int uheap_grow ( size_t size ) {
136 void *new;
137
138 /* Initialise heap, if it does not yet exist */
139 if ( uheap_limit == uheap_end )
140 uheap_find();
141
142 /* Fail if insufficient space remains */
143 if ( size > ( uheap_start - uheap_limit ) )
144 return 0;
145
146 /* Grow heap */
147 new = ( phys_to_virt ( uheap_start ) - size );
148 heap_populate ( &uheap, new, size );
149 uheap_resize ( size );
150
151 return 1;
152}
153
154/**
155 * Allow external heap to shrink
156 *
157 * @v ptr Start of free block
158 * @v size Size of free block
159 * @ret shrunk Heap has shrunk: discard block
160 */
161static unsigned int uheap_shrink ( void *ptr, size_t size ) {
162
163 /* Do nothing unless this is the lowest block in the heap */
164 if ( virt_to_phys ( ptr ) != uheap_start )
165 return 0;
166
167 /* Shrink heap */
168 uheap_resize ( -size );
169
170 return 1;
171}
172
173/** The external heap */
174static struct heap uheap = {
175 .blocks = LIST_HEAD_INIT ( uheap.blocks ),
176 .align = UHEAP_ALIGN,
177 .ptr_align = UHEAP_ALIGN,
178 .grow = uheap_grow,
179 .shrink = uheap_shrink,
180};
181
182/**
183 * Reallocate external memory
184 *
185 * @v old_ptr Memory previously allocated by umalloc(), or NULL
186 * @v new_size Requested size
187 * @ret new_ptr Allocated memory, or NULL
188 *
189 * Calling urealloc() with a new size of zero is a valid way to free a
190 * memory block.
191 */
192static void * uheap_realloc ( void *old_ptr, size_t new_size ) {
193
194 return heap_realloc ( &uheap, old_ptr, new_size );
195}
196
unsigned long physaddr_t
Definition stdint.h:20
signed long ssize_t
Definition stdint.h:7
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
#define DBGC(...)
Definition compiler.h:505
uint32_t start
Starting offset.
Definition netvsc.h:1
uint16_t size
Buffer size.
Definition dwmac.h:3
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
iPXE I/O API
System memory map.
static void memmap_use(struct used_region *used, physaddr_t start, size_t size)
Update an in-use memory region.
Definition memmap.h:154
static void memmap_dump_all(int hide)
Dump system memory map (for debugging)
Definition memmap.h:216
#define __used_region
Declare an in-use memory region.
Definition memmap.h:119
User memory allocation.
void * urealloc(void *ptr, size_t new_size)
Reallocate external memory.
#define PROVIDE_UMALLOC(_subsys, _api_func, _func)
Provide a user memory allocation API implementation.
Definition umalloc.h:26
#define LIST_HEAD_INIT(list)
Initialise a static list head.
Definition list.h:31
void * heap_realloc(struct heap *heap, void *old_ptr, size_t new_size)
Reallocate memory.
Definition malloc.c:537
void heap_populate(struct heap *heap, void *start, size_t len)
Add memory to allocation pool.
Definition malloc.c:739
Dynamic memory allocation.
size_t memmap_largest(physaddr_t *start)
Find largest usable memory region.
Definition memmap.c:120
uint32_t end
Ending offset.
Definition netvsc.h:7
A heap.
Definition malloc.h:45
An in-use memory region.
Definition memmap.h:106
int32_t after
Final microcode version.
Definition ucode.h:7
int32_t before
Initial microcode version.
Definition ucode.h:5
static void * uheap_realloc(void *old_ptr, size_t new_size)
Reallocate external memory.
Definition uheap.c:192
physaddr_t uheap_end
End of external heap.
Definition uheap.c:60
static void uheap_find(void)
Find an external heap region.
Definition uheap.c:92
physaddr_t uheap_start
Start of external heap.
Definition uheap.c:57
static void uheap_resize(ssize_t delta)
Adjust size of external heap in-use memory region.
Definition uheap.c:72
physaddr_t uheap_limit
Minimum possible start of external heap.
Definition uheap.c:54
static unsigned int uheap_grow(size_t size)
Attempt to grow external heap.
Definition uheap.c:135
#define UHEAP_ALIGN
Alignment for external heap allocations.
Definition uheap.c:49
static struct heap uheap
The external heap.
Definition uheap.c:51
static unsigned int uheap_shrink(void *ptr, size_t size)
Allow external heap to shrink.
Definition uheap.c:161