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.

initrd.c 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. * Copyright (C) 2012 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 (at your option) 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. #include <errno.h>
  25. #include <initrd.h>
  26. #include <ipxe/image.h>
  27. #include <ipxe/uaccess.h>
  28. #include <ipxe/init.h>
  29. #include <ipxe/memblock.h>
  30. /** @file
  31. *
  32. * Initial ramdisk (initrd) reshuffling
  33. *
  34. */
  35. /** Maximum address available for initrd */
  36. userptr_t initrd_top;
  37. /** Minimum address available for initrd */
  38. userptr_t initrd_bottom;
  39. /**
  40. * Squash initrds as high as possible in memory
  41. *
  42. * @v top Highest possible address
  43. * @ret used Lowest address used by initrds
  44. */
  45. static userptr_t initrd_squash_high ( userptr_t top ) {
  46. userptr_t current = top;
  47. struct image *initrd;
  48. struct image *highest;
  49. size_t len;
  50. /* Squash up any initrds already within or below the region */
  51. while ( 1 ) {
  52. /* Find the highest image not yet in its final position */
  53. highest = NULL;
  54. for_each_image ( initrd ) {
  55. if ( ( userptr_sub ( initrd->data, current ) < 0 ) &&
  56. ( ( highest == NULL ) ||
  57. ( userptr_sub ( initrd->data,
  58. highest->data ) > 0 ) ) ) {
  59. highest = initrd;
  60. }
  61. }
  62. if ( ! highest )
  63. break;
  64. /* Move this image to its final position */
  65. len = ( ( highest->len + INITRD_ALIGN - 1 ) &
  66. ~( INITRD_ALIGN - 1 ) );
  67. current = userptr_sub ( current, len );
  68. DBGC ( &images, "INITRD squashing %s [%#08lx,%#08lx)->"
  69. "[%#08lx,%#08lx)\n", highest->name,
  70. user_to_phys ( highest->data, 0 ),
  71. user_to_phys ( highest->data, highest->len ),
  72. user_to_phys ( current, 0 ),
  73. user_to_phys ( current, highest->len ) );
  74. memmove_user ( current, 0, highest->data, 0, highest->len );
  75. highest->data = current;
  76. }
  77. /* Copy any remaining initrds (e.g. embedded images) to the region */
  78. for_each_image ( initrd ) {
  79. if ( userptr_sub ( initrd->data, top ) >= 0 ) {
  80. len = ( ( initrd->len + INITRD_ALIGN - 1 ) &
  81. ~( INITRD_ALIGN - 1 ) );
  82. current = userptr_sub ( current, len );
  83. DBGC ( &images, "INITRD copying %s [%#08lx,%#08lx)->"
  84. "[%#08lx,%#08lx)\n", initrd->name,
  85. user_to_phys ( initrd->data, 0 ),
  86. user_to_phys ( initrd->data, initrd->len ),
  87. user_to_phys ( current, 0 ),
  88. user_to_phys ( current, initrd->len ) );
  89. memcpy_user ( current, 0, initrd->data, 0,
  90. initrd->len );
  91. initrd->data = current;
  92. }
  93. }
  94. return current;
  95. }
  96. /**
  97. * Swap position of two adjacent initrds
  98. *
  99. * @v low Lower initrd
  100. * @v high Higher initrd
  101. * @v free Free space
  102. * @v free_len Length of free space
  103. */
  104. static void initrd_swap ( struct image *low, struct image *high,
  105. userptr_t free, size_t free_len ) {
  106. size_t len = 0;
  107. size_t frag_len;
  108. size_t new_len;
  109. DBGC ( &images, "INITRD swapping %s [%#08lx,%#08lx)<->[%#08lx,%#08lx) "
  110. "%s\n", low->name, user_to_phys ( low->data, 0 ),
  111. user_to_phys ( low->data, low->len ),
  112. user_to_phys ( high->data, 0 ),
  113. user_to_phys ( high->data, high->len ), high->name );
  114. /* Round down length of free space */
  115. free_len &= ~( INITRD_ALIGN - 1 );
  116. assert ( free_len > 0 );
  117. /* Swap image data */
  118. while ( len < high->len ) {
  119. /* Calculate maximum fragment length */
  120. frag_len = ( high->len - len );
  121. if ( frag_len > free_len )
  122. frag_len = free_len;
  123. new_len = ( ( len + frag_len + INITRD_ALIGN - 1 ) &
  124. ~( INITRD_ALIGN - 1 ) );
  125. /* Swap fragments */
  126. memcpy_user ( free, 0, high->data, len, frag_len );
  127. memmove_user ( low->data, new_len, low->data, len, low->len );
  128. memcpy_user ( low->data, len, free, 0, frag_len );
  129. len = new_len;
  130. }
  131. /* Adjust data pointers */
  132. high->data = low->data;
  133. low->data = userptr_add ( low->data, len );
  134. }
  135. /**
  136. * Swap position of any two adjacent initrds not currently in the correct order
  137. *
  138. * @v free Free space
  139. * @v free_len Length of free space
  140. * @ret swapped A pair of initrds was swapped
  141. */
  142. static int initrd_swap_any ( userptr_t free, size_t free_len ) {
  143. struct image *low;
  144. struct image *high;
  145. size_t padded_len;
  146. userptr_t adjacent;
  147. /* Find any pair of initrds that can be swapped */
  148. for_each_image ( low ) {
  149. /* Calculate location of adjacent image (if any) */
  150. padded_len = ( ( low->len + INITRD_ALIGN - 1 ) &
  151. ~( INITRD_ALIGN - 1 ) );
  152. adjacent = userptr_add ( low->data, padded_len );
  153. /* Search for adjacent image */
  154. for_each_image ( high ) {
  155. /* If we have found the adjacent image, swap and exit */
  156. if ( high->data == adjacent ) {
  157. initrd_swap ( low, high, free, free_len );
  158. return 1;
  159. }
  160. /* Stop search if all remaining potential
  161. * adjacent images are already in the correct
  162. * order.
  163. */
  164. if ( high == low )
  165. break;
  166. }
  167. }
  168. /* Nothing swapped */
  169. return 0;
  170. }
  171. /**
  172. * Dump initrd locations (for debug)
  173. *
  174. */
  175. static void initrd_dump ( void ) {
  176. struct image *initrd;
  177. /* Do nothing unless debugging is enabled */
  178. if ( ! DBG_LOG )
  179. return;
  180. /* Dump initrd locations */
  181. for_each_image ( initrd ) {
  182. DBGC ( &images, "INITRD %s at [%#08lx,%#08lx)\n",
  183. initrd->name, user_to_phys ( initrd->data, 0 ),
  184. user_to_phys ( initrd->data, initrd->len ) );
  185. DBGC2_MD5A ( &images, user_to_phys ( initrd->data, 0 ),
  186. user_to_virt ( initrd->data, 0 ), initrd->len );
  187. }
  188. }
  189. /**
  190. * Reshuffle initrds into desired order at top of memory
  191. *
  192. * @v bottom Lowest address available for initrds
  193. *
  194. * After this function returns, the initrds have been rearranged in
  195. * memory and the external heap structures will have been corrupted.
  196. * Reshuffling must therefore take place immediately prior to jumping
  197. * to the loaded OS kernel; no further execution within iPXE is
  198. * permitted.
  199. */
  200. void initrd_reshuffle ( userptr_t bottom ) {
  201. userptr_t top;
  202. userptr_t used;
  203. userptr_t free;
  204. size_t free_len;
  205. /* Calculate limits of available space for initrds */
  206. top = initrd_top;
  207. if ( userptr_sub ( initrd_bottom, bottom ) > 0 )
  208. bottom = initrd_bottom;
  209. /* Debug */
  210. DBGC ( &images, "INITRD region [%#08lx,%#08lx)\n",
  211. user_to_phys ( bottom, 0 ), user_to_phys ( top, 0 ) );
  212. initrd_dump();
  213. /* Squash initrds as high as possible in memory */
  214. used = initrd_squash_high ( top );
  215. /* Calculate available free space */
  216. free = bottom;
  217. free_len = userptr_sub ( used, free );
  218. /* Bubble-sort initrds into desired order */
  219. while ( initrd_swap_any ( free, free_len ) ) {}
  220. /* Debug */
  221. initrd_dump();
  222. }
  223. /**
  224. * Check that there is enough space to reshuffle initrds
  225. *
  226. * @v len Total length of initrds (including padding)
  227. * @v bottom Lowest address available for initrds
  228. * @ret rc Return status code
  229. */
  230. int initrd_reshuffle_check ( size_t len, userptr_t bottom ) {
  231. userptr_t top;
  232. size_t available;
  233. /* Calculate limits of available space for initrds */
  234. top = initrd_top;
  235. if ( userptr_sub ( initrd_bottom, bottom ) > 0 )
  236. bottom = initrd_bottom;
  237. available = userptr_sub ( top, bottom );
  238. /* Allow for a sensible minimum amount of free space */
  239. len += INITRD_MIN_FREE_LEN;
  240. /* Check for available space */
  241. return ( ( len < available ) ? 0 : -ENOBUFS );
  242. }
  243. /**
  244. * initrd startup function
  245. *
  246. */
  247. static void initrd_startup ( void ) {
  248. size_t len;
  249. /* Record largest memory block available. Do this after any
  250. * allocations made during driver startup (e.g. large host
  251. * memory blocks for Infiniband devices, which may still be in
  252. * use at the time of rearranging if a SAN device is hooked)
  253. * but before any allocations for downloaded images (which we
  254. * can safely reuse when rearranging).
  255. */
  256. len = largest_memblock ( &initrd_bottom );
  257. initrd_top = userptr_add ( initrd_bottom, len );
  258. }
  259. /** initrd startup function */
  260. struct startup_fn startup_initrd __startup_fn ( STARTUP_LATE ) = {
  261. .name = "initrd",
  262. .startup = initrd_startup,
  263. };