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