You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

memtop_umalloc.c 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. /**
  25. * @file
  26. *
  27. * External memory allocation
  28. *
  29. */
  30. #include <limits.h>
  31. #include <errno.h>
  32. #include <ipxe/uaccess.h>
  33. #include <ipxe/hidemem.h>
  34. #include <ipxe/io.h>
  35. #include <ipxe/memblock.h>
  36. #include <ipxe/umalloc.h>
  37. /** Maximum usable address for external allocated memory */
  38. #define EM_MAX_ADDRESS 0xffffffffUL
  39. /** Alignment of external allocated memory */
  40. #define EM_ALIGN ( 4 * 1024 )
  41. /** Equivalent of NOWHERE for user pointers */
  42. #define UNOWHERE ( ~UNULL )
  43. /** An external memory block */
  44. struct external_memory {
  45. /** Size of this memory block (excluding this header) */
  46. size_t size;
  47. /** Block is currently in use */
  48. int used;
  49. };
  50. /** Top of heap */
  51. static userptr_t top = UNULL;
  52. /** Bottom of heap (current lowest allocated block) */
  53. static userptr_t bottom = UNULL;
  54. /** Remaining space on heap */
  55. static size_t heap_size;
  56. /**
  57. * Find largest usable memory region
  58. *
  59. * @ret start Start of region
  60. * @ret len Length of region
  61. */
  62. size_t largest_memblock ( userptr_t *start ) {
  63. struct memory_map memmap;
  64. struct memory_region *region;
  65. physaddr_t max = EM_MAX_ADDRESS;
  66. physaddr_t region_start;
  67. physaddr_t region_end;
  68. size_t region_len;
  69. unsigned int i;
  70. size_t len = 0;
  71. /* Avoid returning uninitialised data on error */
  72. *start = UNULL;
  73. /* Scan through all memory regions */
  74. get_memmap ( &memmap );
  75. for ( i = 0 ; i < memmap.count ; i++ ) {
  76. region = &memmap.regions[i];
  77. DBG ( "Considering [%llx,%llx)\n", region->start, region->end );
  78. /* Truncate block to maximum physical address */
  79. if ( region->start > max ) {
  80. DBG ( "...starts after maximum address %lx\n", max );
  81. continue;
  82. }
  83. region_start = region->start;
  84. if ( region->end > max ) {
  85. DBG ( "...end truncated to maximum address %lx\n", max);
  86. region_end = 0; /* =max, given the wraparound */
  87. } else {
  88. region_end = region->end;
  89. }
  90. region_len = ( region_end - region_start );
  91. /* Use largest block */
  92. if ( region_len > len ) {
  93. DBG ( "...new best block found\n" );
  94. *start = phys_to_user ( region_start );
  95. len = region_len;
  96. }
  97. }
  98. return len;
  99. }
  100. /**
  101. * Initialise external heap
  102. *
  103. */
  104. static void init_eheap ( void ) {
  105. userptr_t base;
  106. heap_size = largest_memblock ( &base );
  107. bottom = top = userptr_add ( base, heap_size );
  108. DBG ( "External heap grows downwards from %lx (size %zx)\n",
  109. user_to_phys ( top, 0 ), heap_size );
  110. }
  111. /**
  112. * Collect free blocks
  113. *
  114. */
  115. static void ecollect_free ( void ) {
  116. struct external_memory extmem;
  117. size_t len;
  118. /* Walk the free list and collect empty blocks */
  119. while ( bottom != top ) {
  120. copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
  121. sizeof ( extmem ) );
  122. if ( extmem.used )
  123. break;
  124. DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
  125. user_to_phys ( bottom, extmem.size ) );
  126. len = ( extmem.size + sizeof ( extmem ) );
  127. bottom = userptr_add ( bottom, len );
  128. heap_size += len;
  129. }
  130. }
  131. /**
  132. * Reallocate external memory
  133. *
  134. * @v old_ptr Memory previously allocated by umalloc(), or UNULL
  135. * @v new_size Requested size
  136. * @ret new_ptr Allocated memory, or UNULL
  137. *
  138. * Calling realloc() with a new size of zero is a valid way to free a
  139. * memory block.
  140. */
  141. static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) {
  142. struct external_memory extmem;
  143. userptr_t new = ptr;
  144. size_t align;
  145. /* (Re)initialise external memory allocator if necessary */
  146. if ( bottom == top )
  147. init_eheap();
  148. /* Get block properties into extmem */
  149. if ( ptr && ( ptr != UNOWHERE ) ) {
  150. /* Determine old size */
  151. copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
  152. sizeof ( extmem ) );
  153. } else {
  154. /* Create a zero-length block */
  155. if ( heap_size < sizeof ( extmem ) ) {
  156. DBG ( "EXTMEM out of space\n" );
  157. return UNULL;
  158. }
  159. ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
  160. heap_size -= sizeof ( extmem );
  161. DBG ( "EXTMEM allocating [%lx,%lx)\n",
  162. user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
  163. extmem.size = 0;
  164. }
  165. extmem.used = ( new_size > 0 );
  166. /* Expand/shrink block if possible */
  167. if ( ptr == bottom ) {
  168. /* Update block */
  169. if ( new_size > ( heap_size - extmem.size ) ) {
  170. DBG ( "EXTMEM out of space\n" );
  171. return UNULL;
  172. }
  173. new = userptr_add ( ptr, - ( new_size - extmem.size ) );
  174. align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
  175. new_size += align;
  176. new = userptr_add ( new, -align );
  177. DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
  178. user_to_phys ( ptr, 0 ),
  179. user_to_phys ( ptr, extmem.size ),
  180. user_to_phys ( new, 0 ),
  181. user_to_phys ( new, new_size ));
  182. memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
  183. extmem.size : new_size ) );
  184. bottom = new;
  185. heap_size -= ( new_size - extmem.size );
  186. extmem.size = new_size;
  187. } else {
  188. /* Cannot expand; can only pretend to shrink */
  189. if ( new_size > extmem.size ) {
  190. /* Refuse to expand */
  191. DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
  192. user_to_phys ( ptr, 0 ),
  193. user_to_phys ( ptr, extmem.size ) );
  194. return UNULL;
  195. }
  196. }
  197. /* Write back block properties */
  198. copy_to_user ( new, -sizeof ( extmem ), &extmem,
  199. sizeof ( extmem ) );
  200. /* Collect any free blocks and update hidden memory region */
  201. ecollect_free();
  202. hide_umalloc ( user_to_phys ( bottom, ( ( bottom == top ) ?
  203. 0 : -sizeof ( extmem ) ) ),
  204. user_to_phys ( top, 0 ) );
  205. return ( new_size ? new : UNOWHERE );
  206. }
  207. PROVIDE_UMALLOC ( memtop, urealloc, memtop_urealloc );