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