2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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
20 FILE_LICENCE ( GPL2_OR_LATER );
25 * External memory allocation
31 #include <ipxe/uaccess.h>
32 #include <ipxe/hidemem.h>
34 #include <ipxe/memblock.h>
35 #include <ipxe/umalloc.h>
37 /** Alignment of external allocated memory */
38 #define EM_ALIGN ( 4 * 1024 )
40 /** Equivalent of NOWHERE for user pointers */
41 #define UNOWHERE ( ~UNULL )
43 /** An external memory block */
44 struct external_memory {
45 /** Size of this memory block (excluding this header) */
47 /** Block is currently in use */
52 static userptr_t top = UNULL;
54 /** Bottom of heap (current lowest allocated block) */
55 static userptr_t bottom = UNULL;
57 /** Remaining space on heap */
58 static size_t heap_size;
61 * Initialise external heap
64 static void init_eheap ( void ) {
67 heap_size = largest_memblock ( &base );
68 bottom = top = userptr_add ( base, heap_size );
69 DBG ( "External heap grows downwards from %lx (size %zx)\n",
70 user_to_phys ( top, 0 ), heap_size );
77 static void ecollect_free ( void ) {
78 struct external_memory extmem;
81 /* Walk the free list and collect empty blocks */
82 while ( bottom != top ) {
83 copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
87 DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
88 user_to_phys ( bottom, extmem.size ) );
89 len = ( extmem.size + sizeof ( extmem ) );
90 bottom = userptr_add ( bottom, len );
96 * Reallocate external memory
98 * @v old_ptr Memory previously allocated by umalloc(), or UNULL
99 * @v new_size Requested size
100 * @ret new_ptr Allocated memory, or UNULL
102 * Calling realloc() with a new size of zero is a valid way to free a
105 static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) {
106 struct external_memory extmem;
110 /* (Re)initialise external memory allocator if necessary */
114 /* Get block properties into extmem */
115 if ( ptr && ( ptr != UNOWHERE ) ) {
116 /* Determine old size */
117 copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
120 /* Create a zero-length block */
121 if ( heap_size < sizeof ( extmem ) ) {
122 DBG ( "EXTMEM out of space\n" );
125 ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
126 heap_size -= sizeof ( extmem );
127 DBG ( "EXTMEM allocating [%lx,%lx)\n",
128 user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
131 extmem.used = ( new_size > 0 );
133 /* Expand/shrink block if possible */
134 if ( ptr == bottom ) {
136 if ( new_size > ( heap_size - extmem.size ) ) {
137 DBG ( "EXTMEM out of space\n" );
140 new = userptr_add ( ptr, - ( new_size - extmem.size ) );
141 align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
143 new = userptr_add ( new, -align );
144 DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
145 user_to_phys ( ptr, 0 ),
146 user_to_phys ( ptr, extmem.size ),
147 user_to_phys ( new, 0 ),
148 user_to_phys ( new, new_size ));
149 memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
150 extmem.size : new_size ) );
152 heap_size -= ( new_size - extmem.size );
153 extmem.size = new_size;
155 /* Cannot expand; can only pretend to shrink */
156 if ( new_size > extmem.size ) {
157 /* Refuse to expand */
158 DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
159 user_to_phys ( ptr, 0 ),
160 user_to_phys ( ptr, extmem.size ) );
165 /* Write back block properties */
166 copy_to_user ( new, -sizeof ( extmem ), &extmem,
169 /* Collect any free blocks and update hidden memory region */
171 hide_umalloc ( user_to_phys ( bottom, ( ( bottom == top ) ?
172 0 : -sizeof ( extmem ) ) ),
173 user_to_phys ( top, 0 ) );
175 return ( new_size ? new : UNOWHERE );
178 PROVIDE_UMALLOC ( memtop, urealloc, memtop_urealloc );