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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. /** Alignment of external allocated memory */
  38. #define EM_ALIGN ( 4 * 1024 )
  39. /** Equivalent of NOWHERE for user pointers */
  40. #define UNOWHERE ( ~UNULL )
  41. /** An external memory block */
  42. struct external_memory {
  43. /** Size of this memory block (excluding this header) */
  44. size_t size;
  45. /** Block is currently in use */
  46. int used;
  47. };
  48. /** Top of heap */
  49. static userptr_t top = UNULL;
  50. /** Bottom of heap (current lowest allocated block) */
  51. static userptr_t bottom = UNULL;
  52. /** Remaining space on heap */
  53. static size_t heap_size;
  54. /**
  55. * Initialise external heap
  56. *
  57. */
  58. static void init_eheap ( void ) {
  59. userptr_t base;
  60. heap_size = largest_memblock ( &base );
  61. bottom = top = userptr_add ( base, heap_size );
  62. DBG ( "External heap grows downwards from %lx (size %zx)\n",
  63. user_to_phys ( top, 0 ), heap_size );
  64. }
  65. /**
  66. * Collect free blocks
  67. *
  68. */
  69. static void ecollect_free ( void ) {
  70. struct external_memory extmem;
  71. size_t len;
  72. /* Walk the free list and collect empty blocks */
  73. while ( bottom != top ) {
  74. copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
  75. sizeof ( extmem ) );
  76. if ( extmem.used )
  77. break;
  78. DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
  79. user_to_phys ( bottom, extmem.size ) );
  80. len = ( extmem.size + sizeof ( extmem ) );
  81. bottom = userptr_add ( bottom, len );
  82. heap_size += len;
  83. }
  84. }
  85. /**
  86. * Reallocate external memory
  87. *
  88. * @v old_ptr Memory previously allocated by umalloc(), or UNULL
  89. * @v new_size Requested size
  90. * @ret new_ptr Allocated memory, or UNULL
  91. *
  92. * Calling realloc() with a new size of zero is a valid way to free a
  93. * memory block.
  94. */
  95. static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) {
  96. struct external_memory extmem;
  97. userptr_t new = ptr;
  98. size_t align;
  99. /* (Re)initialise external memory allocator if necessary */
  100. if ( bottom == top )
  101. init_eheap();
  102. /* Get block properties into extmem */
  103. if ( ptr && ( ptr != UNOWHERE ) ) {
  104. /* Determine old size */
  105. copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
  106. sizeof ( extmem ) );
  107. } else {
  108. /* Create a zero-length block */
  109. if ( heap_size < sizeof ( extmem ) ) {
  110. DBG ( "EXTMEM out of space\n" );
  111. return UNULL;
  112. }
  113. ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
  114. heap_size -= sizeof ( extmem );
  115. DBG ( "EXTMEM allocating [%lx,%lx)\n",
  116. user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
  117. extmem.size = 0;
  118. }
  119. extmem.used = ( new_size > 0 );
  120. /* Expand/shrink block if possible */
  121. if ( ptr == bottom ) {
  122. /* Update block */
  123. if ( new_size > ( heap_size - extmem.size ) ) {
  124. DBG ( "EXTMEM out of space\n" );
  125. return UNULL;
  126. }
  127. new = userptr_add ( ptr, - ( new_size - extmem.size ) );
  128. align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
  129. new_size += align;
  130. new = userptr_add ( new, -align );
  131. DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
  132. user_to_phys ( ptr, 0 ),
  133. user_to_phys ( ptr, extmem.size ),
  134. user_to_phys ( new, 0 ),
  135. user_to_phys ( new, new_size ));
  136. memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
  137. extmem.size : new_size ) );
  138. bottom = new;
  139. heap_size -= ( new_size - extmem.size );
  140. extmem.size = new_size;
  141. } else {
  142. /* Cannot expand; can only pretend to shrink */
  143. if ( new_size > extmem.size ) {
  144. /* Refuse to expand */
  145. DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
  146. user_to_phys ( ptr, 0 ),
  147. user_to_phys ( ptr, extmem.size ) );
  148. return UNULL;
  149. }
  150. }
  151. /* Write back block properties */
  152. copy_to_user ( new, -sizeof ( extmem ), &extmem,
  153. sizeof ( extmem ) );
  154. /* Collect any free blocks and update hidden memory region */
  155. ecollect_free();
  156. hide_umalloc ( user_to_phys ( bottom, ( ( bottom == top ) ?
  157. 0 : -sizeof ( extmem ) ) ),
  158. user_to_phys ( top, 0 ) );
  159. return ( new_size ? new : UNOWHERE );
  160. }
  161. PROVIDE_UMALLOC ( memtop, urealloc, memtop_urealloc );