Browse Source

[initrd] Add ability to reshuffle initrds into image list order

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 12 years ago
parent
commit
4ca98693b9
3 changed files with 330 additions and 0 deletions
  1. 300
    0
      src/arch/i386/image/initrd.c
  2. 29
    0
      src/arch/i386/include/initrd.h
  3. 1
    0
      src/arch/x86/include/bits/errfile.h

+ 300
- 0
src/arch/i386/image/initrd.c View File

@@ -0,0 +1,300 @@
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
+};

+ 29
- 0
src/arch/i386/include/initrd.h View File

@@ -0,0 +1,29 @@
1
+#ifndef _INITRD_H
2
+#define _INITRD_H
3
+
4
+/** @file
5
+ *
6
+ * Initial ramdisk (initrd) reshuffling
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <ipxe/uaccess.h>
13
+
14
+/** Minimum alignment for initrds
15
+ *
16
+ * Chosen to maximise memcpy() speeds
17
+ */
18
+#define INITRD_ALIGN 4
19
+
20
+/** Minimum free space required to reshuffle initrds
21
+ *
22
+ * Chosen to avoid absurdly long reshuffling times
23
+ */
24
+#define INITRD_MIN_FREE_LEN ( 512 * 1024 )
25
+
26
+extern void initrd_reshuffle ( userptr_t bottom );
27
+extern int initrd_reshuffle_check ( size_t len, userptr_t bottom );
28
+
29
+#endif /* _INITRD_H */

+ 1
- 0
src/arch/x86/include/bits/errfile.h View File

@@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
32 32
 #define ERRFILE_comboot_resolv ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00090000 )
33 33
 #define ERRFILE_comboot_call   ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000a0000 )
34 34
 #define ERRFILE_sdi	       ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000b0000 )
35
+#define ERRFILE_initrd	       ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000c0000 )
35 36
 
36 37
 #define ERRFILE_undi		 ( ERRFILE_ARCH | ERRFILE_NET | 0x00000000 )
37 38
 #define ERRFILE_undiload	 ( ERRFILE_ARCH | ERRFILE_NET | 0x00010000 )

Loading…
Cancel
Save