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 
24 FILE_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  */
99 static 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  */
118 static 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 */
143  initrd_reverse ( data, len );
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  */
203 static 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  */
229 void initrd_reshuffle ( void ) {
231  physaddr_t end;
232 
233  /* Calculate limits of reshuffle region */
234  start = uheap_limit;
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  */
257 static 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  */
317 size_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  */
354 int initrd_region ( size_t len, struct memmap_region *region ) {
355  physaddr_t min;
356  size_t available;
357 
358  /* Calculate limits of available space for initrds */
359  min = uheap_limit;
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  */
379 static 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 */
396 struct startup_fn startup_initrd __startup_fn ( STARTUP_LATE ) = {
397  .name = "initrd",
398  .startup = initrd_startup,
399 };
static void initrd_reverse(void *data, size_t len)
Reverse aligned memory region.
Definition: initrd.c:99
unsigned int flags
Flags.
Definition: image.h:39
uint32_t low
Low 16 bits of address.
Definition: myson.h:19
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
A CPIO archive header.
Definition: cpio.h:20
uint64_t max
Maximum address in region.
Definition: memmap.h:52
Error codes.
const void * data
Read-only data.
Definition: image.h:50
uint64_t address
Base address.
Definition: ena.h:24
static size_t initrd_load(struct image *initrd, void *address)
Load initrd.
Definition: initrd.c:257
#define DBGC(...)
Definition: compiler.h:505
#define min(x, y)
Definition: ath.h:35
physaddr_t uheap_start
Start of external heap.
Definition: uheap.c:57
int initrd_region(size_t len, struct memmap_region *region)
Calculate post-reshuffle initrd load region.
Definition: initrd.c:354
An executable image.
Definition: image.h:23
static physaddr_t initrd_end
End of reshuffle region.
Definition: initrd.c:42
#define DBGC2_MD5A(...)
Definition: compiler.h:525
static size_t initrd_align(size_t len)
Align initrd length.
Definition: initrd.h:29
static size_t cpio_pad_len(size_t len)
Get CPIO header zero-padding length.
Definition: cpio.h:81
const char * name
Definition: init.h:43
struct startup_fn startup_initrd __startup_fn(STARTUP_LATE)
initrd startup function
void initrd_reshuffle(void)
Reshuffle initrds into desired order at top of memory.
Definition: initrd.c:229
CPIO archives.
#define STARTUP_LATE
Late startup.
Definition: init.h:65
#define INITRD_ALIGN
Initial ramdisk chunk alignment.
Definition: initrd.h:16
uint32_t start
Starting offset.
Definition: netvsc.h:12
unsigned long tmp
Definition: linux_pci.h:64
A startup/shutdown function.
Definition: init.h:42
void * memcpy(void *dest, const void *src, size_t len) __nonnull
size_t cpio_header(struct image *image, unsigned int index, struct cpio_header *cpio)
Construct CPIO header for image, if applicable.
Definition: cpio.c:155
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
Access to external ("user") memory.
Executable images.
static void initrd_dump(void)
Dump initrd locations (for debug)
Definition: initrd.c:203
size_t initrd_load_all(void *address)
Load all initrds.
Definition: initrd.c:317
ring len
Length.
Definition: dwmac.h:231
uint32_t high
High 32 bits of address.
Definition: myson.h:20
#define for_each_image(image)
Iterate over all registered images.
Definition: image.h:190
static void initrd_squash_high(physaddr_t start, physaddr_t end)
Squash initrds as high as possible in memory.
Definition: initrd.c:50
unsigned int flags
Region flags.
Definition: memmap.h:54
size_t len
Length of raw file image.
Definition: image.h:55
struct list_head images
List of registered images.
Definition: image.c:58
#define IMAGE_HIDDEN
Image will be hidden from enumeration.
Definition: image.h:85
uint32_t addr
Buffer address.
Definition: dwmac.h:20
static void initrd_startup(void)
initrd startup function
Definition: initrd.c:379
const char * name
Region name (for debug messages)
Definition: memmap.h:56
static void initrd_swap(struct image *low, struct image *high)
Swap position of two adjacent initrds.
Definition: initrd.c:118
void * memmove(void *dest, const void *src, size_t len) __nonnull
unsigned long physaddr_t
Definition: stdint.h:20
long pad_len
Definition: bigint.h:30
Initial ramdisk (initrd) reshuffling.
#define ENOSPC
No space left on device.
Definition: errno.h:549
External ("user") heap.
physaddr_t uheap_limit
Minimum possible start of external heap.
Definition: uheap.c:54
#define MEMMAP_FL_MEMORY
Contains memory.
Definition: memmap.h:59
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" return dest
Definition: string.h:150
uint32_t end
Ending offset.
Definition: netvsc.h:18
uint8_t data[48]
Additional event data.
Definition: ena.h:22
uint64_t min
Minimum address in region.
Definition: memmap.h:50
A memory region descriptor.
Definition: memmap.h:48
uint16_t offset
Offset to command line.
Definition: bzimage.h:8
#define DBG_LOG
Definition: compiler.h:317
char * name
Name.
Definition: image.h:37
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
String functions.
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
physaddr_t uheap_end
End of external heap.
Definition: uheap.c:60
void * memset(void *dest, int character, size_t len) __nonnull
static const char * cpio_name(struct image *image)
Get CPIO image name.
Definition: cpio.h:70