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.

umalloc.c 5.6KB

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