iPXE
memtop_umalloc.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007 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 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 /**
27  * @file
28  *
29  * External memory allocation
30  *
31  */
32 
33 #include <limits.h>
34 #include <errno.h>
35 #include <ipxe/uaccess.h>
36 #include <ipxe/hidemem.h>
37 #include <ipxe/io.h>
38 #include <ipxe/memblock.h>
39 #include <ipxe/umalloc.h>
40 
41 /** Maximum usable address for external allocated memory */
42 #define EM_MAX_ADDRESS 0xffffffffUL
43 
44 /** Alignment of external allocated memory */
45 #define EM_ALIGN ( 4 * 1024 )
46 
47 /** Equivalent of NOWHERE for user pointers */
48 #define UNOWHERE ( ~UNULL )
49 
50 /** An external memory block */
52  /** Size of this memory block (excluding this header) */
53  size_t size;
54  /** Block is currently in use */
55  int used;
56 };
57 
58 /** Top of heap */
59 static userptr_t top = UNULL;
60 
61 /** Bottom of heap (current lowest allocated block) */
63 
64 /** Remaining space on heap */
65 static size_t heap_size;
66 
67 /**
68  * Find largest usable memory region
69  *
70  * @ret start Start of region
71  * @ret len Length of region
72  */
74  struct memory_map memmap;
75  struct memory_region *region;
77  physaddr_t region_start;
78  physaddr_t region_end;
79  size_t region_len;
80  unsigned int i;
81  size_t len = 0;
82 
83  /* Avoid returning uninitialised data on error */
84  *start = UNULL;
85 
86  /* Scan through all memory regions */
87  get_memmap ( &memmap );
88  for ( i = 0 ; i < memmap.count ; i++ ) {
89  region = &memmap.regions[i];
90  DBG ( "Considering [%llx,%llx)\n", region->start, region->end );
91 
92  /* Truncate block to maximum physical address */
93  if ( region->start > max ) {
94  DBG ( "...starts after maximum address %lx\n", max );
95  continue;
96  }
97  region_start = region->start;
98  if ( region->end > max ) {
99  DBG ( "...end truncated to maximum address %lx\n", max);
100  region_end = 0; /* =max, given the wraparound */
101  } else {
102  region_end = region->end;
103  }
104  region_len = ( region_end - region_start );
105 
106  /* Use largest block */
107  if ( region_len > len ) {
108  DBG ( "...new best block found\n" );
109  *start = phys_to_user ( region_start );
110  len = region_len;
111  }
112  }
113 
114  return len;
115 }
116 
117 /**
118  * Initialise external heap
119  *
120  */
121 static void init_eheap ( void ) {
122  userptr_t base;
123 
126  DBG ( "External heap grows downwards from %lx (size %zx)\n",
127  user_to_phys ( top, 0 ), heap_size );
128 }
129 
130 /**
131  * Collect free blocks
132  *
133  */
134 static void ecollect_free ( void ) {
135  struct external_memory extmem;
136  size_t len;
137 
138  /* Walk the free list and collect empty blocks */
139  while ( bottom != top ) {
140  copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
141  sizeof ( extmem ) );
142  if ( extmem.used )
143  break;
144  DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
145  user_to_phys ( bottom, extmem.size ) );
146  len = ( extmem.size + sizeof ( extmem ) );
147  bottom = userptr_add ( bottom, len );
148  heap_size += len;
149  }
150 }
151 
152 /**
153  * Reallocate external memory
154  *
155  * @v old_ptr Memory previously allocated by umalloc(), or UNULL
156  * @v new_size Requested size
157  * @ret new_ptr Allocated memory, or UNULL
158  *
159  * Calling realloc() with a new size of zero is a valid way to free a
160  * memory block.
161  */
162 static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) {
163  struct external_memory extmem;
164  userptr_t new = ptr;
165  size_t align;
166 
167  /* (Re)initialise external memory allocator if necessary */
168  if ( bottom == top )
169  init_eheap();
170 
171  /* Get block properties into extmem */
172  if ( ptr && ( ptr != UNOWHERE ) ) {
173  /* Determine old size */
174  copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
175  sizeof ( extmem ) );
176  } else {
177  /* Create a zero-length block */
178  if ( heap_size < sizeof ( extmem ) ) {
179  DBG ( "EXTMEM out of space\n" );
180  return UNULL;
181  }
182  ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
183  heap_size -= sizeof ( extmem );
184  DBG ( "EXTMEM allocating [%lx,%lx)\n",
185  user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
186  extmem.size = 0;
187  }
188  extmem.used = ( new_size > 0 );
189 
190  /* Expand/shrink block if possible */
191  if ( ptr == bottom ) {
192  /* Update block */
193  if ( new_size > ( heap_size - extmem.size ) ) {
194  DBG ( "EXTMEM out of space\n" );
195  return UNULL;
196  }
197  new = userptr_add ( ptr, - ( new_size - extmem.size ) );
198  align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
199  new_size += align;
200  new = userptr_add ( new, -align );
201  DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
202  user_to_phys ( ptr, 0 ),
203  user_to_phys ( ptr, extmem.size ),
204  user_to_phys ( new, 0 ),
205  user_to_phys ( new, new_size ));
206  memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
207  extmem.size : new_size ) );
208  bottom = new;
209  heap_size -= ( new_size - extmem.size );
210  extmem.size = new_size;
211  } else {
212  /* Cannot expand; can only pretend to shrink */
213  if ( new_size > extmem.size ) {
214  /* Refuse to expand */
215  DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
216  user_to_phys ( ptr, 0 ),
217  user_to_phys ( ptr, extmem.size ) );
218  return UNULL;
219  }
220  }
221 
222  /* Write back block properties */
223  copy_to_user ( new, -sizeof ( extmem ), &extmem,
224  sizeof ( extmem ) );
225 
226  /* Collect any free blocks and update hidden memory region */
227  ecollect_free();
228  hide_umalloc ( user_to_phys ( bottom, ( ( bottom == top ) ?
229  0 : -sizeof ( extmem ) ) ),
230  user_to_phys ( top, 0 ) );
231 
232  return ( new_size ? new : UNOWHERE );
233 }
234 
static void init_eheap(void)
Initialise external heap.
iPXE I/O API
void get_memmap(struct memory_map *memmap)
Get memory map.
#define max(x, y)
Definition: ath.h:39
unsigned int count
Number of used regions.
Definition: io.h:503
Error codes.
void hide_umalloc(physaddr_t start, physaddr_t end)
Hide umalloc() region.
Definition: hidemem.c:119
size_t size
Size of this memory block (excluding this header)
static __always_inline void copy_from_user(void *dest, userptr_t src, off_t src_off, size_t len)
Copy data from user buffer.
Definition: uaccess.h:337
unsigned long user_to_phys(userptr_t userptr, off_t offset)
Convert user pointer to physical address.
PROVIDE_UMALLOC(memtop, urealloc, memtop_urealloc)
userptr_t phys_to_user(unsigned long phys_addr)
Convert physical address to user pointer.
static size_t heap_size
Remaining space on heap.
static userptr_t bottom
Bottom of heap (current lowest allocated block)
A memory map.
Definition: io.h:499
Access to external ("user") memory.
Largest memory block.
uint32_t start
Starting offset.
Definition: netvsc.h:12
struct memory_region regions[MAX_MEMORY_REGIONS]
Memory regions.
Definition: io.h:501
userptr_t userptr_add(userptr_t userptr, off_t offset)
Add offset to user pointer.
An external memory block.
static userptr_t memtop_urealloc(userptr_t ptr, size_t new_size)
Reallocate external memory.
userptr_t urealloc(userptr_t userptr, size_t new_size)
Reallocate external memory.
#define EM_MAX_ADDRESS
Maximum usable address for external allocated memory.
static userptr_t top
Top of heap.
static __always_inline void copy_to_user(userptr_t dest, off_t dest_off, const void *src, size_t len)
Copy data to user buffer.
Definition: uaccess.h:324
User memory allocation.
#define EM_ALIGN
Alignment of external allocated memory.
A usable memory region.
Definition: io.h:488
uint16_t base
Base address.
Definition: edd.h:14
#define UNOWHERE
Equivalent of NOWHERE for user pointers.
Hidden memory regions.
unsigned long physaddr_t
Definition: stdint.h:20
#define UNULL
Equivalent of NULL for user pointers.
Definition: uaccess.h:36
uint32_t len
Length.
Definition: ena.h:14
uint64_t start
Physical start address.
Definition: io.h:490
int used
Block is currently in use.
size_t largest_memblock(userptr_t *start)
Find largest usable memory region.
void memmove_user(userptr_t dest, off_t dest_off, userptr_t src, off_t src_off, size_t len)
Copy data between user buffers, allowing for overlap.
static void ecollect_free(void)
Collect free blocks.
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
#define DBG(...)
Print a debugging message.
Definition: compiler.h:498
uint64_t end
Physical end address.
Definition: io.h:492
unsigned long userptr_t
A pointer to a user buffer.
Definition: uaccess.h:33