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