aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/boot/compressed/misc.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2006-12-06 20:14:04 -0500
committerAndi Kleen <andi@basil.nowhere.org>2006-12-06 20:14:04 -0500
commit968de4f02621db35b8ae5239c8cfc6664fb872d8 (patch)
tree9388da7f18f9511e1bbfeaf934cba8dbc696e9f4 /arch/i386/boot/compressed/misc.c
parentfd593d12770d4a0d1ff095d44b96436c18479ee8 (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.c261
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;
29typedef unsigned short ush; 111typedef unsigned short ush;
30typedef unsigned long ulg; 112typedef 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
35static uch *inbuf; /* input buffer */ 122static uch *inbuf; /* input buffer */
36static uch window[WSIZE]; /* Sliding window buffer */ 123static uch *window; /* Sliding window buffer, (and final output buffer) */
37 124
38static unsigned insize = 0; /* valid bytes in inbuf */ 125static unsigned insize; /* valid bytes in inbuf */
39static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ 126static unsigned inptr; /* index of next byte to be processed in inbuf */
40static unsigned outcnt = 0; /* bytes in output buffer */ 127static 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[];
88extern int input_len; 175extern int input_len;
89 176
90static long bytes_out = 0; 177static long bytes_out = 0;
91static uch *output_data;
92static unsigned long output_ptr = 0;
93 178
94static void *malloc(int size); 179static void *malloc(int size);
95static void free(void *where); 180static void free(void *where);
@@ -99,17 +184,10 @@ static void *memcpy(void *dest, const void *src, unsigned n);
99 184
100static void putstr(const char *); 185static void putstr(const char *);
101 186
102extern int end; 187static unsigned long free_mem_ptr;
103static long free_mem_ptr = (long)&end; 188static unsigned long free_mem_end_ptr;
104static 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
110static unsigned int low_buffer_end, low_buffer_size;
111static int high_loaded =0;
112static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/;
113 191
114static char *vidmem = (char *)0xb8000; 192static char *vidmem = (char *)0xb8000;
115static int vidport; 193static int vidport;
@@ -150,7 +228,7 @@ static void gzip_mark(void **ptr)
150 228
151static void gzip_release(void **ptr) 229static void gzip_release(void **ptr)
152{ 230{
153 free_mem_ptr = (long) *ptr; 231 free_mem_ptr = (unsigned long) *ptr;
154} 232}
155 233
156static void scroll(void) 234static 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 */
224static int fill_inbuf(void) 302static 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 */
240static 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
258static 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
274static void flush_window(void) 312static 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
280static void error(char *x) 331static 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) 340asmlinkage void decompress_kernel(void *rmode, unsigned long end,
290 341 uch *input_data, unsigned long input_len, uch *output)
291long user_stack [STACK_SIZE];
292
293struct {
294 long * a;
295 short b;
296 } stack_start = { & user_stack [STACK_SIZE] , __BOOT_DS };
297
298static 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
309struct moveparams {
310 uch *low_buffer_start; int lcount;
311 uch *high_buffer_start; int hcount;
312};
313
314static 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
336static 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
348asmlinkage 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}