iPXE
initrd.c
Go to the documentation of this file.
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
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26#include <string.h>
27#include <errno.h>
28#include <ipxe/image.h>
29#include <ipxe/uaccess.h>
30#include <ipxe/init.h>
31#include <ipxe/cpio.h>
32#include <ipxe/uheap.h>
33#include <ipxe/initrd.h>
34
35/** @file
36 *
37 * Initial ramdisk (initrd) reshuffling
38 *
39 */
40
41/** End of reshuffle region */
43
44/**
45 * Squash initrds as high as possible in memory
46 *
47 * @v start Start of reshuffle region
48 * @v end End of reshuffle region
49 */
51 physaddr_t current = end;
52 struct image *initrd;
53 struct image *highest;
54 void *data;
55
56 /* Squash up any initrds already within the region */
57 while ( 1 ) {
58
59 /* Find the highest image not yet in its final position */
60 highest = NULL;
61 for_each_image ( initrd ) {
62 if ( ( virt_to_phys ( initrd->data ) >= start ) &&
63 ( virt_to_phys ( initrd->data ) < current ) &&
64 ( ( highest == NULL ) ||
65 ( virt_to_phys ( initrd->data ) >
66 virt_to_phys ( highest->data ) ) ) ) {
67 highest = initrd;
68 }
69 }
70 if ( ! highest )
71 break;
72
73 /* Calculate final position */
74 current -= initrd_align ( highest->len );
75 if ( current <= virt_to_phys ( highest->data ) ) {
76 /* Already at (or crossing) end of region */
77 current = virt_to_phys ( highest->data );
78 continue;
79 }
80
81 /* Move this image to its final position */
82 DBGC ( &images, "INITRD squashing %s [%#08lx,%#08lx)->"
83 "[%#08lx,%#08lx)\n", highest->name,
84 virt_to_phys ( highest->data ),
85 ( virt_to_phys ( highest->data ) + highest->len ),
86 current, ( current + highest->len ) );
87 data = phys_to_virt ( current );
88 memmove ( data, highest->data, highest->len );
89 highest->data = data;
90 }
91}
92
93/**
94 * Reverse aligned memory region
95 *
96 * @v data Memory region
97 * @v len Length of region
98 */
99static void initrd_reverse ( void *data, size_t len ) {
100 unsigned long *low = data;
101 unsigned long *high = ( data + len );
102 unsigned long tmp;
103
104 /* Reverse region */
105 for ( high-- ; low < high ; low++, high-- ) {
106 tmp = *low;
107 *low = *high;
108 *high = tmp;
109 }
110}
111
112/**
113 * Swap position of two adjacent initrds
114 *
115 * @v low Lower initrd
116 * @v high Higher initrd
117 */
118static void initrd_swap ( struct image *low, struct image *high ) {
119 size_t low_len;
120 size_t high_len;
121 size_t len;
122 void *data;
123
124 DBGC ( &images, "INITRD swapping %s [%#08lx,%#08lx)<->[%#08lx,%#08lx) "
125 "%s\n", low->name, virt_to_phys ( low->data ),
126 ( virt_to_phys ( low->data ) + low->len ),
127 virt_to_phys ( high->data ),
128 ( virt_to_phys ( high->data ) + high->len ), high->name );
129
130 /* Calculate padded lengths */
131 low_len = initrd_align ( low->len );
132 high_len = initrd_align ( high->len );
133 len = ( low_len + high_len );
134 data = low->rwdata;
135 assert ( high->data == ( data + low_len ) );
136
137 /* Adjust data pointers */
138 high->data -= low_len;
139 low->data += high_len;
140 assert ( high->data == data );
141
142 /* Swap content via triple reversal */
144 initrd_reverse ( low->rwdata, low_len );
145 initrd_reverse ( high->rwdata, high_len );
146}
147
148/**
149 * Swap position of any two adjacent initrds not currently in the correct order
150 *
151 * @v start Start of reshuffle region
152 * @v end End of reshuffle region
153 * @ret swapped A pair of initrds was swapped
154 */
156 struct image *low;
157 struct image *high;
158 const void *adjacent;
160
161 /* Find any pair of initrds that can be swapped */
162 for_each_image ( low ) {
163
164 /* Ignore images wholly outside the reshuffle region */
165 addr = virt_to_phys ( low->data );
166 if ( ( addr < start ) || ( addr >= end ) )
167 continue;
168
169 /* Calculate location of adjacent image (if any) */
170 adjacent = ( low->data + initrd_align ( low->len ) );
171
172 /* Search for adjacent image */
173 for_each_image ( high ) {
174
175 /* Ignore images wholly outside the reshuffle region */
176 addr = virt_to_phys ( high->data );
177 if ( ( addr < start ) || ( addr >= end ) )
178 continue;
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 /* If we have found the adjacent image, swap and exit */
188 if ( high->data == adjacent ) {
189 initrd_swap ( low, high );
190 return 1;
191 }
192 }
193 }
194
195 /* Nothing swapped */
196 return 0;
197}
198
199/**
200 * Dump initrd locations (for debug)
201 *
202 */
203static void initrd_dump ( void ) {
204 struct image *initrd;
205
206 /* Do nothing unless debugging is enabled */
207 if ( ! DBG_LOG )
208 return;
209
210 /* Dump initrd locations */
211 for_each_image ( initrd ) {
212 DBGC ( &images, "INITRD %s at [%#08lx,%#08lx)\n",
213 initrd->name, virt_to_phys ( initrd->data ),
214 ( virt_to_phys ( initrd->data ) + initrd->len ) );
215 DBGC2_MD5A ( &images, virt_to_phys ( initrd->data ),
216 initrd->data, initrd->len );
217 }
218}
219
220/**
221 * Reshuffle initrds into desired order at top of memory
222 *
223 * After this function returns, the initrds have been rearranged in
224 * memory and the external heap structures will have been corrupted.
225 * Reshuffling must therefore take place immediately prior to jumping
226 * to the loaded OS kernel; no further execution within iPXE is
227 * permitted.
228 */
229void initrd_reshuffle ( void ) {
232
233 /* Calculate limits of reshuffle region */
236
237 /* Debug */
238 initrd_dump();
239
240 /* Squash initrds as high as possible in memory */
242
243 /* Bubble-sort initrds into desired order */
244 while ( initrd_swap_any ( start, end ) ) {}
245
246 /* Debug */
247 initrd_dump();
248}
249
250/**
251 * Load initrd
252 *
253 * @v initrd initrd image
254 * @v address Address at which to load, or NULL
255 * @ret len Length of loaded image, excluding zero-padding
256 */
257static size_t initrd_load ( struct image *initrd, void *address ) {
258 const char *filename = cpio_name ( initrd );
259 struct cpio_header cpio;
260 size_t offset;
261 size_t cpio_len;
262 size_t len;
263 unsigned int i;
264
265 /* Sanity check */
266 assert ( ( address == NULL ) ||
267 ( ( virt_to_phys ( address ) & ( INITRD_ALIGN - 1 ) ) == 0 ));
268
269 /* Skip hidden images */
270 if ( initrd->flags & IMAGE_HIDDEN )
271 return 0;
272
273 /* Determine length of cpio headers for non-prebuilt images */
274 len = 0;
275 for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ; i++ )
276 len += ( cpio_len + cpio_pad_len ( cpio_len ) );
277
278 /* Copy in initrd image body and construct any cpio headers */
279 if ( address ) {
280 memmove ( ( address + len ), initrd->data, initrd->len );
281 memset ( address, 0, len );
282 offset = 0;
283 for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ;
284 i++ ) {
285 memcpy ( ( address + offset ), &cpio,
286 sizeof ( cpio ) );
287 memcpy ( ( address + offset + sizeof ( cpio ) ),
288 filename, ( cpio_len - sizeof ( cpio ) ) );
289 offset += ( cpio_len + cpio_pad_len ( cpio_len ) );
290 }
291 assert ( offset == len );
292 DBGC ( &images, "INITRD %s [%#08lx,%#08lx,%#08lx)%s%s\n",
293 initrd->name, virt_to_phys ( address ),
294 ( virt_to_phys ( address ) + offset ),
295 ( virt_to_phys ( address ) + offset + initrd->len ),
296 ( filename ? " " : "" ), ( filename ? filename : "" ) );
297 DBGC2_MD5A ( &images, ( virt_to_phys ( address ) + offset ),
298 ( address + offset ), initrd->len );
299 }
300 len += initrd->len;
301
302 return len;
303}
304
305/**
306 * Load all initrds
307 *
308 * @v address Load address, or NULL
309 * @ret len Length
310 *
311 * This function is called after the point of no return, when the
312 * external heap has been corrupted by reshuffling and there is no way
313 * to resume normal execution. The caller must have previously
314 * ensured that there is no way for installation to this address to
315 * fail.
316 */
317size_t initrd_load_all ( void *address ) {
318 struct image *initrd;
319 size_t len = 0;
320 size_t pad_len;
321 void *dest;
322
323 /* Load all initrds */
324 for_each_image ( initrd ) {
325
326 /* Zero-pad to next INITRD_ALIGN boundary */
327 pad_len = ( ( -len ) & ( INITRD_ALIGN - 1 ) );
328 if ( address )
329 memset ( ( address + len ), 0, pad_len );
330 len += pad_len;
331 assert ( len == initrd_align ( len ) );
332
333 /* Load initrd */
334 dest = ( address ? ( address + len ) : NULL );
335 len += initrd_load ( initrd, dest );
336 }
337
338 return len;
339}
340
341/**
342 * Calculate post-reshuffle initrd load region
343 *
344 * @v len Length of initrds (from initrd_len())
345 * @v region Region descriptor to fill in
346 * @ret rc Return status code
347 *
348 * If successful, then any suitably aligned range within the region
349 * may be used as the load address after reshuffling. The caller does
350 * not need to call prep_segment() for a range in this region.
351 * (Calling prep_segment() would probably fail, since prior to
352 * reshuffling the region is still in use by the external heap.)
353 */
354int initrd_region ( size_t len, struct memmap_region *region ) {
356 size_t available;
357
358 /* Calculate limits of available space for initrds */
360 available = ( ( initrd_end ? initrd_end : uheap_end ) - min );
361 if ( available < len )
362 return -ENOSPC;
363 DBGC ( &images, "INITRD post-reshuffle region is [%#08lx,%#08lx)\n",
364 min, ( min + available ) );
365
366 /* Populate region descriptor */
367 region->min = min;
368 region->max = ( min + available - 1 );
369 region->flags = MEMMAP_FL_MEMORY;
370 region->name = "initrd";
371
372 return 0;
373}
374
375/**
376 * initrd startup function
377 *
378 */
379static void initrd_startup ( void ) {
380
381 /* Record address above which reshuffling cannot take place.
382 * If any external heap allocations have been made during
383 * driver startup (e.g. large host memory blocks for
384 * Infiniband devices, which may still be in use at the time
385 * of rearranging if a SAN device is hooked), then we must not
386 * overwrite these allocations during reshuffling.
387 */
389 if ( initrd_end ) {
390 DBGC ( &images, "INITRD limiting reshuffling to below "
391 "%#08lx\n", initrd_end );
392 }
393}
394
395/** initrd startup function */
396struct startup_fn startup_initrd __startup_fn ( STARTUP_LATE ) = {
397 .name = "initrd",
398 .startup = initrd_startup,
399};
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
unsigned long physaddr_t
Definition stdint.h:20
long pad_len
Definition bigint.h:31
if(len >=6 *4) __asm__ __volatile__("movsl" if(len >=5 *4) __asm__ __volatile__("movsl" if(len >=4 *4) __asm__ __volatile__("movsl" if(len >=3 *4) __asm__ __volatile__("movsl" if(len >=2 *4) __asm__ __volatile__("movsl" if(len >=1 *4) __asm__ __volatile__("movsl" if((len % 4) >=2) __asm__ __volatile__("movsw" if((len % 2) >=1) __asm__ __volatile__("movsb" retur dest)
Definition string.h:151
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
#define min(x, y)
Definition ath.h:36
uint16_t offset
Offset to command line.
Definition bzimage.h:3
size_t cpio_header(struct image *image, unsigned int index, struct cpio_header *cpio)
Construct CPIO header for image, if applicable.
Definition cpio.c:156
CPIO archives.
static size_t cpio_pad_len(size_t len)
Get CPIO header zero-padding length.
Definition cpio.h:82
static const char * cpio_name(struct image *image)
Get CPIO image name.
Definition cpio.h:71
ring len
Length.
Definition dwmac.h:226
uint32_t addr
Buffer address.
Definition dwmac.h:9
uint8_t data[48]
Additional event data.
Definition ena.h:11
uint64_t address
Base address.
Definition ena.h:13
Error codes.
#define DBGC2_MD5A(...)
Definition compiler.h:525
#define DBGC(...)
Definition compiler.h:505
#define DBG_LOG
Definition compiler.h:317
uint32_t start
Starting offset.
Definition netvsc.h:1
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define ENOSPC
No space left on device.
Definition errno.h:550
#define STARTUP_LATE
Late startup.
Definition init.h:66
struct list_head images
List of registered images.
Definition image.c:59
Executable images.
#define for_each_image(image)
Iterate over all registered images.
Definition image.h:191
#define IMAGE_HIDDEN
Image will be hidden from enumeration.
Definition image.h:86
#define MEMMAP_FL_MEMORY
Contains memory.
Definition memmap.h:60
String functions.
void * memcpy(void *dest, const void *src, size_t len) __nonnull
void * memset(void *dest, int character, size_t len) __nonnull
void * memmove(void *dest, const void *src, size_t len) __nonnull
#define __startup_fn(startup_order)
Declare a startup/shutdown function.
Definition init.h:53
static void initrd_swap(struct image *low, struct image *high)
Swap position of two adjacent initrds.
Definition initrd.c:118
int initrd_region(size_t len, struct memmap_region *region)
Calculate post-reshuffle initrd load region.
Definition initrd.c:354
static int initrd_swap_any(physaddr_t start, physaddr_t end)
Swap position of any two adjacent initrds not currently in the correct order.
Definition initrd.c:155
void initrd_reshuffle(void)
Reshuffle initrds into desired order at top of memory.
Definition initrd.c:229
static physaddr_t initrd_end
End of reshuffle region.
Definition initrd.c:42
static void initrd_startup(void)
initrd startup function
Definition initrd.c:379
static size_t initrd_load(struct image *initrd, void *address)
Load initrd.
Definition initrd.c:257
static void initrd_squash_high(physaddr_t start, physaddr_t end)
Squash initrds as high as possible in memory.
Definition initrd.c:50
size_t initrd_load_all(void *address)
Load all initrds.
Definition initrd.c:317
static void initrd_dump(void)
Dump initrd locations (for debug)
Definition initrd.c:203
static void initrd_reverse(void *data, size_t len)
Reverse aligned memory region.
Definition initrd.c:99
Initial ramdisk (initrd) reshuffling.
#define INITRD_ALIGN
Initial ramdisk chunk alignment.
Definition initrd.h:17
static size_t initrd_align(size_t len)
Align initrd length.
Definition initrd.h:30
Access to external ("user") memory.
unsigned long tmp
Definition linux_pci.h:65
uint32_t high
High 32 bits of address.
Definition myson.h:1
uint32_t low
Low 16 bits of address.
Definition myson.h:0
uint32_t end
Ending offset.
Definition netvsc.h:7
A CPIO archive header.
Definition cpio.h:21
An executable image.
Definition image.h:24
unsigned int flags
Flags.
Definition image.h:40
const void * data
Read-only data.
Definition image.h:51
char * name
Name.
Definition image.h:38
size_t len
Length of raw file image.
Definition image.h:56
A memory region descriptor.
Definition memmap.h:49
const char * name
Region name (for debug messages)
Definition memmap.h:57
uint64_t min
Minimum address in region.
Definition memmap.h:51
unsigned int flags
Region flags.
Definition memmap.h:55
uint64_t max
Maximum address in region.
Definition memmap.h:53
A startup/shutdown function.
Definition init.h:43
physaddr_t uheap_end
End of external heap.
Definition uheap.c:60
physaddr_t uheap_start
Start of external heap.
Definition uheap.c:57
physaddr_t uheap_limit
Minimum possible start of external heap.
Definition uheap.c:54
External ("user") heap.