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 
24 FILE_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 
51 static 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 */
63 struct 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  */
72 static 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  */
92 static 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 */
106  size = memmap_largest ( &start );
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 */
123  uheap_limit = start;
124  uheap_start = end;
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  */
135 static 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  */
161 static 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 */
174 static struct heap uheap = {
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  */
192 static void * uheap_realloc ( void *old_ptr, size_t new_size ) {
193 
194  return heap_realloc ( &uheap, old_ptr, new_size );
195 }
196 
size_t memmap_largest(physaddr_t *start)
Find largest usable memory region.
Definition: memmap.c:120
static void memmap_use(struct used_region *used, physaddr_t start, size_t size)
Update an in-use memory region.
Definition: memmap.h:153
iPXE I/O API
PROVIDE_UMALLOC(uheap, urealloc, uheap_realloc)
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
uint16_t size
Buffer size.
Definition: dwmac.h:14
#define DBGC(...)
Definition: compiler.h:505
static void uheap_resize(ssize_t delta)
Adjust size of external heap in-use memory region.
Definition: uheap.c:72
physaddr_t uheap_start
Start of external heap.
Definition: uheap.c:57
int32_t before
Initial microcode version.
Definition: ucode.h:16
static void * uheap_realloc(void *old_ptr, size_t new_size)
Reallocate external memory.
Definition: uheap.c:192
Dynamic memory allocation.
uint32_t start
Starting offset.
Definition: netvsc.h:12
static void uheap_find(void)
Find an external heap region.
Definition: uheap.c:92
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
void heap_populate(struct heap *heap, void *start, size_t len)
Add memory to allocation pool.
Definition: malloc.c:738
static struct heap uheap
The external heap.
Definition: uheap.c:51
#define UHEAP_ALIGN
Alignment for external heap allocations.
Definition: uheap.c:49
static unsigned int uheap_grow(size_t size)
Attempt to grow external heap.
Definition: uheap.c:135
struct used_region uheap_used __used_region
In-use memory region.
Definition: uheap.c:63
An in-use memory region.
Definition: memmap.h:105
User memory allocation.
struct list_head blocks
List of free memory blocks.
Definition: malloc.h:46
unsigned long physaddr_t
Definition: stdint.h:20
static void memmap_dump_all(int hide)
Dump system memory map (for debugging)
Definition: memmap.h:215
A heap.
Definition: malloc.h:44
int32_t after
Final microcode version.
Definition: ucode.h:18
physaddr_t uheap_limit
Minimum possible start of external heap.
Definition: uheap.c:54
const char * name
Region name.
Definition: memmap.h:107
uint32_t end
Ending offset.
Definition: netvsc.h:18
signed long ssize_t
Definition: stdint.h:7
void * urealloc(void *ptr, size_t new_size)
Reallocate external memory.
static unsigned int uheap_shrink(void *ptr, size_t size)
Allow external heap to shrink.
Definition: uheap.c:161
#define LIST_HEAD_INIT(list)
Initialise a static list head.
Definition: list.h:30
System memory map.
physaddr_t uheap_end
End of external heap.
Definition: uheap.c:60
void * heap_realloc(struct heap *heap, void *old_ptr, size_t new_size)
Reallocate memory.
Definition: malloc.c:536