00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include <limits.h>
00029 #include <errno.h>
00030 #include <gpxe/uaccess.h>
00031 #include <gpxe/hidemem.h>
00032 #include <gpxe/memmap.h>
00033 #include <gpxe/umalloc.h>
00034
00035
00036 #define EM_ALIGN ( 4 * 1024 )
00037
00038
00039 #define UNOWHERE ( ~UNULL )
00040
00041
00042 struct external_memory {
00043
00044 size_t size;
00045
00046 int used;
00047 };
00048
00049
00050 static userptr_t top = UNULL;
00051
00052
00053 static userptr_t bottom = UNULL;
00054
00055
00056
00057
00058
00059
00060 static int init_eheap ( void ) {
00061 struct memory_map memmap;
00062 unsigned long heap_size = 0;
00063 unsigned int i;
00064
00065 DBG ( "Allocating external heap\n" );
00066
00067 get_memmap ( &memmap );
00068 for ( i = 0 ; i < memmap.count ; i++ ) {
00069 struct memory_region *region = &memmap.regions[i];
00070 unsigned long r_start, r_end;
00071 unsigned long r_size;
00072
00073 DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
00074
00075
00076 if ( region->start > UINT_MAX ) {
00077 DBG ( "...starts after 4GB\n" );
00078 continue;
00079 }
00080 r_start = region->start;
00081 if ( region->end > UINT_MAX ) {
00082 DBG ( "...end truncated to 4GB\n" );
00083 r_end = 0;
00084 } else {
00085 r_end = region->end;
00086 }
00087
00088
00089 r_size = ( r_end - r_start );
00090 if ( r_size > heap_size ) {
00091 DBG ( "...new best block found\n" );
00092 top = bottom = phys_to_user ( r_end );
00093 heap_size = r_size;
00094 }
00095 }
00096
00097 if ( ! heap_size ) {
00098 DBG ( "No external heap available\n" );
00099 return -ENOMEM;
00100 }
00101
00102 DBG ( "External heap grows downwards from %lx\n",
00103 user_to_phys ( top, 0 ) );
00104 return 0;
00105 }
00106
00107
00108
00109
00110
00111 static void ecollect_free ( void ) {
00112 struct external_memory extmem;
00113
00114
00115 while ( bottom != top ) {
00116 copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
00117 sizeof ( extmem ) );
00118 if ( extmem.used )
00119 break;
00120 DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
00121 user_to_phys ( bottom, extmem.size ) );
00122 bottom = userptr_add ( bottom,
00123 ( extmem.size + sizeof ( extmem ) ) );
00124 }
00125 }
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137 static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) {
00138 struct external_memory extmem;
00139 userptr_t new = ptr;
00140 size_t align;
00141 int rc;
00142
00143
00144 if ( bottom == top ) {
00145 if ( ( rc = init_eheap() ) != 0 )
00146 return UNULL;
00147 }
00148
00149
00150 if ( ptr && ( ptr != UNOWHERE ) ) {
00151
00152 copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
00153 sizeof ( extmem ) );
00154 } else {
00155
00156 ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
00157 DBG ( "EXTMEM allocating [%lx,%lx)\n",
00158 user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
00159 extmem.size = 0;
00160 }
00161 extmem.used = ( new_size > 0 );
00162
00163
00164 if ( ptr == bottom ) {
00165
00166 new = userptr_add ( ptr, - ( new_size - extmem.size ) );
00167 align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
00168 new_size += align;
00169 new = userptr_add ( new, -align );
00170 DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
00171 user_to_phys ( ptr, 0 ),
00172 user_to_phys ( ptr, extmem.size ),
00173 user_to_phys ( new, 0 ),
00174 user_to_phys ( new, new_size ));
00175 memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
00176 extmem.size : new_size ) );
00177 extmem.size = new_size;
00178 bottom = new;
00179 } else {
00180
00181 if ( new_size > extmem.size ) {
00182
00183 DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
00184 user_to_phys ( ptr, 0 ),
00185 user_to_phys ( ptr, extmem.size ) );
00186 return UNULL;
00187 }
00188 }
00189
00190
00191 copy_to_user ( new, -sizeof ( extmem ), &extmem,
00192 sizeof ( extmem ) );
00193
00194
00195 ecollect_free();
00196 hide_umalloc ( user_to_phys ( bottom, -sizeof ( extmem ) ),
00197 user_to_phys ( top, 0 ) );
00198
00199 return ( new_size ? new : UNOWHERE );
00200 }
00201
00202 PROVIDE_UMALLOC ( memtop, urealloc, memtop_urealloc );