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 5.7KB

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