iPXE
initrd.c File Reference

Initial ramdisk (initrd) reshuffling. More...

#include <string.h>
#include <errno.h>
#include <ipxe/image.h>
#include <ipxe/uaccess.h>
#include <ipxe/init.h>
#include <ipxe/cpio.h>
#include <ipxe/uheap.h>
#include <ipxe/initrd.h>

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static void initrd_squash_high (physaddr_t start, physaddr_t end)
 Squash initrds as high as possible in memory.
static void initrd_reverse (void *data, size_t len)
 Reverse aligned memory region.
static void initrd_swap (struct image *low, struct image *high)
 Swap position of two adjacent initrds.
static int initrd_swap_any (physaddr_t start, physaddr_t end)
 Swap position of any two adjacent initrds not currently in the correct order.
static void initrd_dump (void)
 Dump initrd locations (for debug)
void initrd_reshuffle (void)
 Reshuffle initrds into desired order at top of memory.
static size_t initrd_load (struct image *initrd, void *address)
 Load initrd.
size_t initrd_load_all (void *address)
 Load all initrds.
int initrd_region (size_t len, struct memmap_region *region)
 Calculate post-reshuffle initrd load region.
static void initrd_startup (void)
 initrd startup function
struct startup_fn startup_initrd __startup_fn (STARTUP_LATE)
 initrd startup function

Variables

static physaddr_t initrd_end
 End of reshuffle region.

Detailed Description

Initial ramdisk (initrd) reshuffling.

Definition in file initrd.c.

Function Documentation

◆ FILE_LICENCE()

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )

◆ initrd_squash_high()

void initrd_squash_high ( physaddr_t start,
physaddr_t end )
static

Squash initrds as high as possible in memory.

Parameters
startStart of reshuffle region
endEnd of reshuffle region

Definition at line 50 of file initrd.c.

50 {
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}
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
unsigned long physaddr_t
Definition stdint.h:20
uint8_t data[48]
Additional event data.
Definition ena.h:11
#define DBGC(...)
Definition compiler.h:505
uint32_t start
Starting offset.
Definition netvsc.h:1
struct list_head images
List of registered images.
Definition image.c:59
#define for_each_image(image)
Iterate over all registered images.
Definition image.h:191
void * memmove(void *dest, const void *src, size_t len) __nonnull
static size_t initrd_align(size_t len)
Align initrd length.
Definition initrd.h:30
uint32_t end
Ending offset.
Definition netvsc.h:7
An executable image.
Definition image.h:24
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

References data, image::data, DBGC, end, for_each_image, images, initrd_align(), image::len, memmove(), image::name, NULL, and start.

Referenced by initrd_reshuffle().

◆ initrd_reverse()

void initrd_reverse ( void * data,
size_t len )
static

Reverse aligned memory region.

Parameters
dataMemory region
lenLength of region

Definition at line 99 of file initrd.c.

99 {
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}
ring len
Length.
Definition dwmac.h:226
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

References data, high, len, low, and tmp.

Referenced by initrd_swap().

◆ initrd_swap()

void initrd_swap ( struct image * low,
struct image * high )
static

Swap position of two adjacent initrds.

Parameters
lowLower initrd
highHigher initrd

Definition at line 118 of file initrd.c.

118 {
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}
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
static void initrd_reverse(void *data, size_t len)
Reverse aligned memory region.
Definition initrd.c:99

References assert, data, DBGC, high, images, initrd_align(), initrd_reverse(), len, and low.

Referenced by initrd_swap_any().

◆ initrd_swap_any()

int initrd_swap_any ( physaddr_t start,
physaddr_t end )
static

Swap position of any two adjacent initrds not currently in the correct order.

Parameters
startStart of reshuffle region
endEnd of reshuffle region
Return values
swappedA pair of initrds was swapped

Definition at line 155 of file initrd.c.

155 {
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}
uint32_t addr
Buffer address.
Definition dwmac.h:9
static void initrd_swap(struct image *low, struct image *high)
Swap position of two adjacent initrds.
Definition initrd.c:118

References addr, end, for_each_image, high, initrd_align(), initrd_swap(), low, and start.

Referenced by initrd_reshuffle().

◆ initrd_dump()

void initrd_dump ( void )
static

Dump initrd locations (for debug)

Definition at line 203 of file initrd.c.

203 {
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}
#define DBGC2_MD5A(...)
Definition compiler.h:525
#define DBG_LOG
Definition compiler.h:317

References image::data, DBG_LOG, DBGC, DBGC2_MD5A, for_each_image, images, image::len, and image::name.

Referenced by initrd_reshuffle().

◆ initrd_reshuffle()

void initrd_reshuffle ( void )

Reshuffle initrds into desired order at top of memory.

After this function returns, the initrds have been rearranged in memory and the external heap structures will have been corrupted. Reshuffling must therefore take place immediately prior to jumping to the loaded OS kernel; no further execution within iPXE is permitted.

Definition at line 229 of file initrd.c.

229 {
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}
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
static physaddr_t initrd_end
End of reshuffle region.
Definition initrd.c:42
static void initrd_squash_high(physaddr_t start, physaddr_t end)
Squash initrds as high as possible in memory.
Definition initrd.c:50
static void initrd_dump(void)
Dump initrd locations (for debug)
Definition initrd.c:203
physaddr_t uheap_end
End of external heap.
Definition uheap.c:60
physaddr_t uheap_limit
Minimum possible start of external heap.
Definition uheap.c:54

References end, initrd_dump(), initrd_end, initrd_squash_high(), initrd_swap_any(), start, uheap_end, and uheap_limit.

Referenced by bzimage_load_initrds(), and lkrn_exec().

◆ initrd_load()

size_t initrd_load ( struct image * initrd,
void * address )
static

Load initrd.

Parameters
initrdinitrd image
addressAddress at which to load, or NULL
Return values
lenLength of loaded image, excluding zero-padding

Definition at line 257 of file initrd.c.

257 {
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}
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
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
uint64_t address
Base address.
Definition ena.h:13
#define IMAGE_HIDDEN
Image will be hidden from enumeration.
Definition image.h:86
void * memcpy(void *dest, const void *src, size_t len) __nonnull
void * memset(void *dest, int character, size_t len) __nonnull
#define INITRD_ALIGN
Initial ramdisk chunk alignment.
Definition initrd.h:17
A CPIO archive header.
Definition cpio.h:21
unsigned int flags
Flags.
Definition image.h:40

References address, assert, cpio_header(), cpio_name(), cpio_pad_len(), image::data, DBGC, DBGC2_MD5A, image::flags, IMAGE_HIDDEN, images, INITRD_ALIGN, image::len, len, memcpy(), memmove(), memset(), image::name, NULL, and offset.

Referenced by initrd_load_all().

◆ initrd_load_all()

size_t initrd_load_all ( void * address)

Load all initrds.

Parameters
addressLoad address, or NULL
Return values
lenLength

This function is called after the point of no return, when the external heap has been corrupted by reshuffling and there is no way to resume normal execution. The caller must have previously ensured that there is no way for installation to this address to fail.

Definition at line 317 of file initrd.c.

317 {
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}
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
static size_t initrd_load(struct image *initrd, void *address)
Load initrd.
Definition initrd.c:257

References address, assert, dest, for_each_image, INITRD_ALIGN, initrd_align(), initrd_load(), len, memset(), NULL, and pad_len.

Referenced by bzimage_load_initrds(), initrd_len(), and lkrn_exec().

◆ initrd_region()

int initrd_region ( size_t len,
struct memmap_region * region )

Calculate post-reshuffle initrd load region.

Parameters
lenLength of initrds (from initrd_len())
regionRegion descriptor to fill in
Return values
rcReturn status code

If successful, then any suitably aligned range within the region may be used as the load address after reshuffling. The caller does not need to call prep_segment() for a range in this region. (Calling prep_segment() would probably fail, since prior to reshuffling the region is still in use by the external heap.)

Definition at line 354 of file initrd.c.

354 {
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}
#define min(x, y)
Definition ath.h:36
#define ENOSPC
No space left on device.
Definition errno.h:550
#define MEMMAP_FL_MEMORY
Contains memory.
Definition memmap.h:60
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

References DBGC, ENOSPC, memmap_region::flags, images, initrd_end, len, memmap_region::max, MEMMAP_FL_MEMORY, memmap_region::min, min, memmap_region::name, uheap_end, and uheap_limit.

Referenced by bzimage_check_initrds(), and lkrn_exec().

◆ initrd_startup()

void initrd_startup ( void )
static

initrd startup function

Definition at line 379 of file initrd.c.

379 {
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}
physaddr_t uheap_start
Start of external heap.
Definition uheap.c:57

References DBGC, images, initrd_end, and uheap_start.

Referenced by __startup_fn().

◆ __startup_fn()

struct startup_fn startup_initrd __startup_fn ( STARTUP_LATE )

initrd startup function

References __startup_fn, initrd_startup(), and STARTUP_LATE.

Variable Documentation

◆ initrd_end

physaddr_t initrd_end
static

End of reshuffle region.

Definition at line 42 of file initrd.c.

Referenced by comboot_fetch_kernel(), initrd_region(), initrd_reshuffle(), and initrd_startup().