diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2006-12-06 20:14:04 -0500 |
---|---|---|
committer | Andi Kleen <andi@basil.nowhere.org> | 2006-12-06 20:14:04 -0500 |
commit | 968de4f02621db35b8ae5239c8cfc6664fb872d8 (patch) | |
tree | 9388da7f18f9511e1bbfeaf934cba8dbc696e9f4 /arch/i386/boot/compressed/misc.c | |
parent | fd593d12770d4a0d1ff095d44b96436c18479ee8 (diff) |
[PATCH] i386: Relocatable kernel support
This patch modifies the i386 kernel so that if CONFIG_RELOCATABLE is
selected it will be able to be loaded at any 4K aligned address below
1G. The technique used is to compile the decompressor with -fPIC and
modify it so the decompressor is fully relocatable. For the main
kernel relocations are generated. Resulting in a kernel that is relocatable
with no runtime overhead and no need to modify the source code.
A reserved 32bit word in the parameters has been assigned
to serve as a stack so we figure out where are running.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Vivek Goyal <vgoyal@in.ibm.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Diffstat (limited to 'arch/i386/boot/compressed/misc.c')
-rw-r--r-- | arch/i386/boot/compressed/misc.c | 261 |
1 files changed, 133 insertions, 128 deletions
diff --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c index 20970ff44119..4eac24e95a10 100644 --- a/arch/i386/boot/compressed/misc.c +++ b/arch/i386/boot/compressed/misc.c | |||
@@ -13,6 +13,88 @@ | |||
13 | #include <linux/vmalloc.h> | 13 | #include <linux/vmalloc.h> |
14 | #include <linux/screen_info.h> | 14 | #include <linux/screen_info.h> |
15 | #include <asm/io.h> | 15 | #include <asm/io.h> |
16 | #include <asm/page.h> | ||
17 | |||
18 | /* WARNING!! | ||
19 | * This code is compiled with -fPIC and it is relocated dynamically | ||
20 | * at run time, but no relocation processing is performed. | ||
21 | * This means that it is not safe to place pointers in static structures. | ||
22 | */ | ||
23 | |||
24 | /* | ||
25 | * Getting to provable safe in place decompression is hard. | ||
26 | * Worst case behaviours need to be analized. | ||
27 | * Background information: | ||
28 | * | ||
29 | * The file layout is: | ||
30 | * magic[2] | ||
31 | * method[1] | ||
32 | * flags[1] | ||
33 | * timestamp[4] | ||
34 | * extraflags[1] | ||
35 | * os[1] | ||
36 | * compressed data blocks[N] | ||
37 | * crc[4] orig_len[4] | ||
38 | * | ||
39 | * resulting in 18 bytes of non compressed data overhead. | ||
40 | * | ||
41 | * Files divided into blocks | ||
42 | * 1 bit (last block flag) | ||
43 | * 2 bits (block type) | ||
44 | * | ||
45 | * 1 block occurs every 32K -1 bytes or when there 50% compression has been achieved. | ||
46 | * The smallest block type encoding is always used. | ||
47 | * | ||
48 | * stored: | ||
49 | * 32 bits length in bytes. | ||
50 | * | ||
51 | * fixed: | ||
52 | * magic fixed tree. | ||
53 | * symbols. | ||
54 | * | ||
55 | * dynamic: | ||
56 | * dynamic tree encoding. | ||
57 | * symbols. | ||
58 | * | ||
59 | * | ||
60 | * The buffer for decompression in place is the length of the | ||
61 | * uncompressed data, plus a small amount extra to keep the algorithm safe. | ||
62 | * The compressed data is placed at the end of the buffer. The output | ||
63 | * pointer is placed at the start of the buffer and the input pointer | ||
64 | * is placed where the compressed data starts. Problems will occur | ||
65 | * when the output pointer overruns the input pointer. | ||
66 | * | ||
67 | * The output pointer can only overrun the input pointer if the input | ||
68 | * pointer is moving faster than the output pointer. A condition only | ||
69 | * triggered by data whose compressed form is larger than the uncompressed | ||
70 | * form. | ||
71 | * | ||
72 | * The worst case at the block level is a growth of the compressed data | ||
73 | * of 5 bytes per 32767 bytes. | ||
74 | * | ||
75 | * The worst case internal to a compressed block is very hard to figure. | ||
76 | * The worst case can at least be boundined by having one bit that represents | ||
77 | * 32764 bytes and then all of the rest of the bytes representing the very | ||
78 | * very last byte. | ||
79 | * | ||
80 | * All of which is enough to compute an amount of extra data that is required | ||
81 | * to be safe. To avoid problems at the block level allocating 5 extra bytes | ||
82 | * per 32767 bytes of data is sufficient. To avoind problems internal to a block | ||
83 | * adding an extra 32767 bytes (the worst case uncompressed block size) is | ||
84 | * sufficient, to ensure that in the worst case the decompressed data for | ||
85 | * block will stop the byte before the compressed data for a block begins. | ||
86 | * To avoid problems with the compressed data's meta information an extra 18 | ||
87 | * bytes are needed. Leading to the formula: | ||
88 | * | ||
89 | * extra_bytes = (uncompressed_size >> 12) + 32768 + 18 + decompressor_size. | ||
90 | * | ||
91 | * Adding 8 bytes per 32K is a bit excessive but much easier to calculate. | ||
92 | * Adding 32768 instead of 32767 just makes for round numbers. | ||
93 | * Adding the decompressor_size is necessary as it musht live after all | ||
94 | * of the data as well. Last I measured the decompressor is about 14K. | ||
95 | * 10K of actuall data and 4K of bss. | ||
96 | * | ||
97 | */ | ||
16 | 98 | ||
17 | /* | 99 | /* |
18 | * gzip declarations | 100 | * gzip declarations |
@@ -29,15 +111,20 @@ typedef unsigned char uch; | |||
29 | typedef unsigned short ush; | 111 | typedef unsigned short ush; |
30 | typedef unsigned long ulg; | 112 | typedef unsigned long ulg; |
31 | 113 | ||
32 | #define WSIZE 0x8000 /* Window size must be at least 32k, */ | 114 | #define WSIZE 0x80000000 /* Window size must be at least 32k, |
33 | /* and a power of two */ | 115 | * and a power of two |
116 | * We don't actually have a window just | ||
117 | * a huge output buffer so I report | ||
118 | * a 2G windows size, as that should | ||
119 | * always be larger than our output buffer. | ||
120 | */ | ||
34 | 121 | ||
35 | static uch *inbuf; /* input buffer */ | 122 | static uch *inbuf; /* input buffer */ |
36 | static uch window[WSIZE]; /* Sliding window buffer */ | 123 | static uch *window; /* Sliding window buffer, (and final output buffer) */ |
37 | 124 | ||
38 | static unsigned insize = 0; /* valid bytes in inbuf */ | 125 | static unsigned insize; /* valid bytes in inbuf */ |
39 | static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ | 126 | static unsigned inptr; /* index of next byte to be processed in inbuf */ |
40 | static unsigned outcnt = 0; /* bytes in output buffer */ | 127 | static unsigned outcnt; /* bytes in output buffer */ |
41 | 128 | ||
42 | /* gzip flag byte */ | 129 | /* gzip flag byte */ |
43 | #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ | 130 | #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ |
@@ -88,8 +175,6 @@ extern unsigned char input_data[]; | |||
88 | extern int input_len; | 175 | extern int input_len; |
89 | 176 | ||
90 | static long bytes_out = 0; | 177 | static long bytes_out = 0; |
91 | static uch *output_data; | ||
92 | static unsigned long output_ptr = 0; | ||
93 | 178 | ||
94 | static void *malloc(int size); | 179 | static void *malloc(int size); |
95 | static void free(void *where); | 180 | static void free(void *where); |
@@ -99,17 +184,10 @@ static void *memcpy(void *dest, const void *src, unsigned n); | |||
99 | 184 | ||
100 | static void putstr(const char *); | 185 | static void putstr(const char *); |
101 | 186 | ||
102 | extern int end; | 187 | static unsigned long free_mem_ptr; |
103 | static long free_mem_ptr = (long)&end; | 188 | static unsigned long free_mem_end_ptr; |
104 | static long free_mem_end_ptr; | ||
105 | 189 | ||
106 | #define INPLACE_MOVE_ROUTINE 0x1000 | ||
107 | #define LOW_BUFFER_START 0x2000 | ||
108 | #define LOW_BUFFER_MAX 0x90000 | ||
109 | #define HEAP_SIZE 0x3000 | 190 | #define HEAP_SIZE 0x3000 |
110 | static unsigned int low_buffer_end, low_buffer_size; | ||
111 | static int high_loaded =0; | ||
112 | static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; | ||
113 | 191 | ||
114 | static char *vidmem = (char *)0xb8000; | 192 | static char *vidmem = (char *)0xb8000; |
115 | static int vidport; | 193 | static int vidport; |
@@ -150,7 +228,7 @@ static void gzip_mark(void **ptr) | |||
150 | 228 | ||
151 | static void gzip_release(void **ptr) | 229 | static void gzip_release(void **ptr) |
152 | { | 230 | { |
153 | free_mem_ptr = (long) *ptr; | 231 | free_mem_ptr = (unsigned long) *ptr; |
154 | } | 232 | } |
155 | 233 | ||
156 | static void scroll(void) | 234 | static void scroll(void) |
@@ -178,7 +256,7 @@ static void putstr(const char *s) | |||
178 | y--; | 256 | y--; |
179 | } | 257 | } |
180 | } else { | 258 | } else { |
181 | vidmem [ ( x + cols * y ) * 2 ] = c; | 259 | vidmem [ ( x + cols * y ) * 2 ] = c; |
182 | if ( ++x >= cols ) { | 260 | if ( ++x >= cols ) { |
183 | x = 0; | 261 | x = 0; |
184 | if ( ++y >= lines ) { | 262 | if ( ++y >= lines ) { |
@@ -223,58 +301,31 @@ static void* memcpy(void* dest, const void* src, unsigned n) | |||
223 | */ | 301 | */ |
224 | static int fill_inbuf(void) | 302 | static int fill_inbuf(void) |
225 | { | 303 | { |
226 | if (insize != 0) { | 304 | error("ran out of input data"); |
227 | error("ran out of input data"); | 305 | return 0; |
228 | } | ||
229 | |||
230 | inbuf = input_data; | ||
231 | insize = input_len; | ||
232 | inptr = 1; | ||
233 | return inbuf[0]; | ||
234 | } | 306 | } |
235 | 307 | ||
236 | /* =========================================================================== | 308 | /* =========================================================================== |
237 | * Write the output window window[0..outcnt-1] and update crc and bytes_out. | 309 | * Write the output window window[0..outcnt-1] and update crc and bytes_out. |
238 | * (Used for the decompressed data only.) | 310 | * (Used for the decompressed data only.) |
239 | */ | 311 | */ |
240 | static void flush_window_low(void) | ||
241 | { | ||
242 | ulg c = crc; /* temporary variable */ | ||
243 | unsigned n; | ||
244 | uch *in, *out, ch; | ||
245 | |||
246 | in = window; | ||
247 | out = &output_data[output_ptr]; | ||
248 | for (n = 0; n < outcnt; n++) { | ||
249 | ch = *out++ = *in++; | ||
250 | c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); | ||
251 | } | ||
252 | crc = c; | ||
253 | bytes_out += (ulg)outcnt; | ||
254 | output_ptr += (ulg)outcnt; | ||
255 | outcnt = 0; | ||
256 | } | ||
257 | |||
258 | static void flush_window_high(void) | ||
259 | { | ||
260 | ulg c = crc; /* temporary variable */ | ||
261 | unsigned n; | ||
262 | uch *in, ch; | ||
263 | in = window; | ||
264 | for (n = 0; n < outcnt; n++) { | ||
265 | ch = *output_data++ = *in++; | ||
266 | if ((ulg)output_data == low_buffer_end) output_data=high_buffer_start; | ||
267 | c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); | ||
268 | } | ||
269 | crc = c; | ||
270 | bytes_out += (ulg)outcnt; | ||
271 | outcnt = 0; | ||
272 | } | ||
273 | |||
274 | static void flush_window(void) | 312 | static void flush_window(void) |
275 | { | 313 | { |
276 | if (high_loaded) flush_window_high(); | 314 | /* With my window equal to my output buffer |
277 | else flush_window_low(); | 315 | * I only need to compute the crc here. |
316 | */ | ||
317 | ulg c = crc; /* temporary variable */ | ||
318 | unsigned n; | ||
319 | uch *in, ch; | ||
320 | |||
321 | in = window; | ||
322 | for (n = 0; n < outcnt; n++) { | ||
323 | ch = *in++; | ||
324 | c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); | ||
325 | } | ||
326 | crc = c; | ||
327 | bytes_out += (ulg)outcnt; | ||
328 | outcnt = 0; | ||
278 | } | 329 | } |
279 | 330 | ||
280 | static void error(char *x) | 331 | static void error(char *x) |
@@ -286,66 +337,8 @@ static void error(char *x) | |||
286 | while(1); /* Halt */ | 337 | while(1); /* Halt */ |
287 | } | 338 | } |
288 | 339 | ||
289 | #define STACK_SIZE (4096) | 340 | asmlinkage void decompress_kernel(void *rmode, unsigned long end, |
290 | 341 | uch *input_data, unsigned long input_len, uch *output) | |
291 | long user_stack [STACK_SIZE]; | ||
292 | |||
293 | struct { | ||
294 | long * a; | ||
295 | short b; | ||
296 | } stack_start = { & user_stack [STACK_SIZE] , __BOOT_DS }; | ||
297 | |||
298 | static void setup_normal_output_buffer(void) | ||
299 | { | ||
300 | #ifdef STANDARD_MEMORY_BIOS_CALL | ||
301 | if (RM_EXT_MEM_K < 1024) error("Less than 2MB of memory"); | ||
302 | #else | ||
303 | if ((RM_ALT_MEM_K > RM_EXT_MEM_K ? RM_ALT_MEM_K : RM_EXT_MEM_K) < 1024) error("Less than 2MB of memory"); | ||
304 | #endif | ||
305 | output_data = (unsigned char *)CONFIG_PHYSICAL_START; /* Normally Points to 1M */ | ||
306 | free_mem_end_ptr = (long)real_mode; | ||
307 | } | ||
308 | |||
309 | struct moveparams { | ||
310 | uch *low_buffer_start; int lcount; | ||
311 | uch *high_buffer_start; int hcount; | ||
312 | }; | ||
313 | |||
314 | static void setup_output_buffer_if_we_run_high(struct moveparams *mv) | ||
315 | { | ||
316 | high_buffer_start = (uch *)(((ulg)&end) + HEAP_SIZE); | ||
317 | #ifdef STANDARD_MEMORY_BIOS_CALL | ||
318 | if (RM_EXT_MEM_K < (3*1024)) error("Less than 4MB of memory"); | ||
319 | #else | ||
320 | if ((RM_ALT_MEM_K > RM_EXT_MEM_K ? RM_ALT_MEM_K : RM_EXT_MEM_K) < (3*1024)) error("Less than 4MB of memory"); | ||
321 | #endif | ||
322 | mv->low_buffer_start = output_data = (unsigned char *)LOW_BUFFER_START; | ||
323 | low_buffer_end = ((unsigned int)real_mode > LOW_BUFFER_MAX | ||
324 | ? LOW_BUFFER_MAX : (unsigned int)real_mode) & ~0xfff; | ||
325 | low_buffer_size = low_buffer_end - LOW_BUFFER_START; | ||
326 | high_loaded = 1; | ||
327 | free_mem_end_ptr = (long)high_buffer_start; | ||
328 | if ( (CONFIG_PHYSICAL_START + low_buffer_size) > ((ulg)high_buffer_start)) { | ||
329 | high_buffer_start = (uch *)(CONFIG_PHYSICAL_START + low_buffer_size); | ||
330 | mv->hcount = 0; /* say: we need not to move high_buffer */ | ||
331 | } | ||
332 | else mv->hcount = -1; | ||
333 | mv->high_buffer_start = high_buffer_start; | ||
334 | } | ||
335 | |||
336 | static void close_output_buffer_if_we_run_high(struct moveparams *mv) | ||
337 | { | ||
338 | if (bytes_out > low_buffer_size) { | ||
339 | mv->lcount = low_buffer_size; | ||
340 | if (mv->hcount) | ||
341 | mv->hcount = bytes_out - low_buffer_size; | ||
342 | } else { | ||
343 | mv->lcount = bytes_out; | ||
344 | mv->hcount = 0; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | asmlinkage int decompress_kernel(struct moveparams *mv, void *rmode) | ||
349 | { | 342 | { |
350 | real_mode = rmode; | 343 | real_mode = rmode; |
351 | 344 | ||
@@ -360,13 +353,25 @@ asmlinkage int decompress_kernel(struct moveparams *mv, void *rmode) | |||
360 | lines = RM_SCREEN_INFO.orig_video_lines; | 353 | lines = RM_SCREEN_INFO.orig_video_lines; |
361 | cols = RM_SCREEN_INFO.orig_video_cols; | 354 | cols = RM_SCREEN_INFO.orig_video_cols; |
362 | 355 | ||
363 | if (free_mem_ptr < 0x100000) setup_normal_output_buffer(); | 356 | window = output; /* Output buffer (Normally at 1M) */ |
364 | else setup_output_buffer_if_we_run_high(mv); | 357 | free_mem_ptr = end; /* Heap */ |
358 | free_mem_end_ptr = end + HEAP_SIZE; | ||
359 | inbuf = input_data; /* Input buffer */ | ||
360 | insize = input_len; | ||
361 | inptr = 0; | ||
362 | |||
363 | if (((u32)output - CONFIG_PHYSICAL_START) & 0x3fffff) | ||
364 | error("Destination address not 4M aligned"); | ||
365 | if (end > ((-__PAGE_OFFSET-(512 <<20)-1) & 0x7fffffff)) | ||
366 | error("Destination address too large"); | ||
367 | #ifndef CONFIG_RELOCATABLE | ||
368 | if ((u32)output != CONFIG_PHYSICAL_START) | ||
369 | error("Wrong destination address"); | ||
370 | #endif | ||
365 | 371 | ||
366 | makecrc(); | 372 | makecrc(); |
367 | putstr("Uncompressing Linux... "); | 373 | putstr("Uncompressing Linux... "); |
368 | gunzip(); | 374 | gunzip(); |
369 | putstr("Ok, booting the kernel.\n"); | 375 | putstr("Ok, booting the kernel.\n"); |
370 | if (high_loaded) close_output_buffer_if_we_run_high(mv); | 376 | return; |
371 | return high_loaded; | ||
372 | } | 377 | } |