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., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * External memory allocation
26 #include <gpxe/uaccess.h>
27 #include <gpxe/hidemem.h>
28 #include <gpxe/umalloc.h>
30 /** Alignment of external allocated memory */
31 #define EM_ALIGN ( 4 * 1024 )
33 /** Equivalent of NOWHERE for user pointers */
34 #define UNOWHERE ( ~UNULL )
36 /** Start of Etherboot text, as defined by the linker */
39 /** Top of allocatable memory */
40 #define TOP ( virt_to_user ( _text ) )
42 /** An external memory block */
43 struct external_memory {
44 /** Size of this memory block (excluding this header) */
46 /** Block is currently in use */
50 /** Current lowest allocated block
52 * A value of UNULL indicates that no blocks are currently allocated.
54 userptr_t bottom = UNULL;
60 static void ecollect_free ( void ) {
61 struct external_memory extmem;
63 /* Walk the free list and collect empty blocks */
64 while ( bottom != TOP ) {
65 copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
69 DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
70 user_to_phys ( bottom, extmem.size ) );
71 bottom = userptr_add ( bottom,
72 ( extmem.size + sizeof ( extmem ) ) );
77 * Reallocate external memory
79 * @v old_ptr Memory previously allocated by umalloc(), or UNULL
80 * @v new_size Requested size
81 * @ret new_ptr Allocated memory, or UNULL
83 * Calling realloc() with a new size of zero is a valid way to free a
86 userptr_t urealloc ( userptr_t ptr, size_t new_size ) {
87 struct external_memory extmem;
91 /* Initialise external memory allocator if necessary */
95 /* Get block properties into extmem */
96 if ( ptr && ( ptr != UNOWHERE ) ) {
97 /* Determine old size */
98 copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
101 /* Create a zero-length block */
102 ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
103 DBG ( "EXTMEM allocating [%lx,%lx)\n",
104 user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
107 extmem.used = ( new_size > 0 );
109 /* Expand/shrink block if possible */
110 if ( ptr == bottom ) {
112 new = userptr_add ( ptr, - ( new_size - extmem.size ) );
113 align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
115 new = userptr_add ( new, -align );
116 DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
117 user_to_phys ( ptr, 0 ),
118 user_to_phys ( ptr, extmem.size ),
119 user_to_phys ( new, 0 ),
120 user_to_phys ( new, new_size ));
121 memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
122 extmem.size : new_size ) );
123 extmem.size = new_size;
126 /* Cannot expand; can only pretend to shrink */
127 if ( new_size > extmem.size ) {
128 /* Refuse to expand */
129 DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
130 user_to_phys ( ptr, 0 ),
131 user_to_phys ( ptr, extmem.size ) );
136 /* Write back block properties */
137 copy_to_user ( new, -sizeof ( extmem ), &extmem,
140 /* Collect any free blocks and update hidden memory region */
142 hide_region ( EXTMEM, user_to_phys ( bottom, -sizeof ( extmem ) ),
143 user_to_phys ( TOP, 0 ) );
145 return ( new_size ? new : UNOWHERE );
149 * Allocate external memory
151 * @v size Requested size
152 * @ret ptr Memory, or UNULL
154 * Memory is guaranteed to be aligned to a page boundary.
156 userptr_t umalloc ( size_t size ) {
157 return urealloc ( UNULL, size );
161 * Free external memory
163 * @v ptr Memory allocated by umalloc(), or UNULL
165 * If @c ptr is UNULL, no action is taken.
167 void ufree ( userptr_t ptr ) {