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.3KB

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