blob: dc1538931555845b17d53b82d39285624aedc455 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * misc.c
3 *
4 * This is a collection of several routines from gzip-1.0.3
5 * adapted for Linux.
6 *
7 * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
8 * puts by Nick Holloway 1993, better puts by Martin Mares 1995
9 * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
10 */
11
12#include <linux/linkage.h>
13#include <linux/vmalloc.h>
Brian Gerst7e7f3582006-01-08 01:04:54 -080014#include <linux/screen_info.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <asm/io.h>
Eric W. Biederman968de4f2006-12-07 02:14:04 +010016#include <asm/page.h>
Vivek Goyale69f2022006-12-07 02:14:04 +010017#include <asm/boot.h>
Eric W. Biederman968de4f2006-12-07 02:14:04 +010018
19/* WARNING!!
20 * This code is compiled with -fPIC and it is relocated dynamically
21 * at run time, but no relocation processing is performed.
22 * This means that it is not safe to place pointers in static structures.
23 */
24
25/*
26 * Getting to provable safe in place decompression is hard.
27 * Worst case behaviours need to be analized.
28 * Background information:
29 *
30 * The file layout is:
31 * magic[2]
32 * method[1]
33 * flags[1]
34 * timestamp[4]
35 * extraflags[1]
36 * os[1]
37 * compressed data blocks[N]
38 * crc[4] orig_len[4]
39 *
40 * resulting in 18 bytes of non compressed data overhead.
41 *
42 * Files divided into blocks
43 * 1 bit (last block flag)
44 * 2 bits (block type)
45 *
46 * 1 block occurs every 32K -1 bytes or when there 50% compression has been achieved.
47 * The smallest block type encoding is always used.
48 *
49 * stored:
50 * 32 bits length in bytes.
51 *
52 * fixed:
53 * magic fixed tree.
54 * symbols.
55 *
56 * dynamic:
57 * dynamic tree encoding.
58 * symbols.
59 *
60 *
61 * The buffer for decompression in place is the length of the
62 * uncompressed data, plus a small amount extra to keep the algorithm safe.
63 * The compressed data is placed at the end of the buffer. The output
64 * pointer is placed at the start of the buffer and the input pointer
65 * is placed where the compressed data starts. Problems will occur
66 * when the output pointer overruns the input pointer.
67 *
68 * The output pointer can only overrun the input pointer if the input
69 * pointer is moving faster than the output pointer. A condition only
70 * triggered by data whose compressed form is larger than the uncompressed
71 * form.
72 *
73 * The worst case at the block level is a growth of the compressed data
74 * of 5 bytes per 32767 bytes.
75 *
76 * The worst case internal to a compressed block is very hard to figure.
77 * The worst case can at least be boundined by having one bit that represents
78 * 32764 bytes and then all of the rest of the bytes representing the very
79 * very last byte.
80 *
81 * All of which is enough to compute an amount of extra data that is required
82 * to be safe. To avoid problems at the block level allocating 5 extra bytes
83 * per 32767 bytes of data is sufficient. To avoind problems internal to a block
84 * adding an extra 32767 bytes (the worst case uncompressed block size) is
85 * sufficient, to ensure that in the worst case the decompressed data for
86 * block will stop the byte before the compressed data for a block begins.
87 * To avoid problems with the compressed data's meta information an extra 18
88 * bytes are needed. Leading to the formula:
89 *
90 * extra_bytes = (uncompressed_size >> 12) + 32768 + 18 + decompressor_size.
91 *
92 * Adding 8 bytes per 32K is a bit excessive but much easier to calculate.
93 * Adding 32768 instead of 32767 just makes for round numbers.
94 * Adding the decompressor_size is necessary as it musht live after all
95 * of the data as well. Last I measured the decompressor is about 14K.
96 * 10K of actuall data and 4K of bss.
97 *
98 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070099
100/*
101 * gzip declarations
102 */
103
104#define OF(args) args
105#define STATIC static
106
107#undef memset
108#undef memcpy
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109#define memzero(s, n) memset ((s), 0, (n))
110
111typedef unsigned char uch;
112typedef unsigned short ush;
113typedef unsigned long ulg;
114
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100115#define WSIZE 0x80000000 /* Window size must be at least 32k,
116 * and a power of two
117 * We don't actually have a window just
118 * a huge output buffer so I report
119 * a 2G windows size, as that should
120 * always be larger than our output buffer.
121 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100123static uch *inbuf; /* input buffer */
124static uch *window; /* Sliding window buffer, (and final output buffer) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100126static unsigned insize; /* valid bytes in inbuf */
127static unsigned inptr; /* index of next byte to be processed in inbuf */
128static unsigned outcnt; /* bytes in output buffer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
130/* gzip flag byte */
131#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
132#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
133#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
134#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
135#define COMMENT 0x10 /* bit 4 set: file comment present */
136#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
137#define RESERVED 0xC0 /* bit 6,7: reserved */
138
139#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
140
141/* Diagnostic functions */
142#ifdef DEBUG
143# define Assert(cond,msg) {if(!(cond)) error(msg);}
144# define Trace(x) fprintf x
145# define Tracev(x) {if (verbose) fprintf x ;}
146# define Tracevv(x) {if (verbose>1) fprintf x ;}
147# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
148# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
149#else
150# define Assert(cond,msg)
151# define Trace(x)
152# define Tracev(x)
153# define Tracevv(x)
154# define Tracec(c,x)
155# define Tracecv(c,x)
156#endif
157
158static int fill_inbuf(void);
159static void flush_window(void);
160static void error(char *m);
161static void gzip_mark(void **);
162static void gzip_release(void **);
163
164/*
165 * This is set up by the setup-routine at boot-time
166 */
167static unsigned char *real_mode; /* Pointer to real-mode data */
168
169#define RM_EXT_MEM_K (*(unsigned short *)(real_mode + 0x2))
170#ifndef STANDARD_MEMORY_BIOS_CALL
171#define RM_ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0))
172#endif
173#define RM_SCREEN_INFO (*(struct screen_info *)(real_mode+0))
174
Carl-Daniel Hailfingerb79c4df72006-06-26 13:57:53 +0200175extern unsigned char input_data[];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176extern int input_len;
177
178static long bytes_out = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
180static void *malloc(int size);
181static void free(void *where);
182
Carl-Daniel Hailfingerb79c4df72006-06-26 13:57:53 +0200183static void *memset(void *s, int c, unsigned n);
184static void *memcpy(void *dest, const void *src, unsigned n);
185
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186static void putstr(const char *);
187
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100188static unsigned long free_mem_ptr;
189static unsigned long free_mem_end_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191#define HEAP_SIZE 0x3000
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
193static char *vidmem = (char *)0xb8000;
194static int vidport;
195static int lines, cols;
196
197#ifdef CONFIG_X86_NUMAQ
198static void * xquad_portio = NULL;
199#endif
200
201#include "../../../../lib/inflate.c"
202
203static void *malloc(int size)
204{
205 void *p;
206
207 if (size <0) error("Malloc error");
208 if (free_mem_ptr <= 0) error("Memory error");
209
210 free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
211
212 p = (void *)free_mem_ptr;
213 free_mem_ptr += size;
214
215 if (free_mem_ptr >= free_mem_end_ptr)
216 error("Out of memory");
217
218 return p;
219}
220
221static void free(void *where)
222{ /* Don't care */
223}
224
225static void gzip_mark(void **ptr)
226{
227 *ptr = (void *) free_mem_ptr;
228}
229
230static void gzip_release(void **ptr)
231{
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100232 free_mem_ptr = (unsigned long) *ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233}
234
235static void scroll(void)
236{
237 int i;
238
239 memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
240 for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
241 vidmem[i] = ' ';
242}
243
244static void putstr(const char *s)
245{
246 int x,y,pos;
247 char c;
248
249 x = RM_SCREEN_INFO.orig_x;
250 y = RM_SCREEN_INFO.orig_y;
251
252 while ( ( c = *s++ ) != '\0' ) {
253 if ( c == '\n' ) {
254 x = 0;
255 if ( ++y >= lines ) {
256 scroll();
257 y--;
258 }
259 } else {
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100260 vidmem [ ( x + cols * y ) * 2 ] = c;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 if ( ++x >= cols ) {
262 x = 0;
263 if ( ++y >= lines ) {
264 scroll();
265 y--;
266 }
267 }
268 }
269 }
270
271 RM_SCREEN_INFO.orig_x = x;
272 RM_SCREEN_INFO.orig_y = y;
273
274 pos = (x + cols * y) * 2; /* Update cursor position */
275 outb_p(14, vidport);
276 outb_p(0xff & (pos >> 9), vidport+1);
277 outb_p(15, vidport);
278 outb_p(0xff & (pos >> 1), vidport+1);
279}
280
Carl-Daniel Hailfingerb79c4df72006-06-26 13:57:53 +0200281static void* memset(void* s, int c, unsigned n)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282{
283 int i;
284 char *ss = (char*)s;
285
286 for (i=0;i<n;i++) ss[i] = c;
287 return s;
288}
289
Carl-Daniel Hailfingerb79c4df72006-06-26 13:57:53 +0200290static void* memcpy(void* dest, const void* src, unsigned n)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291{
292 int i;
Carl-Daniel Hailfingerb79c4df72006-06-26 13:57:53 +0200293 char *d = (char *)dest, *s = (char *)src;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
Carl-Daniel Hailfingerb79c4df72006-06-26 13:57:53 +0200295 for (i=0;i<n;i++) d[i] = s[i];
296 return dest;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297}
298
299/* ===========================================================================
300 * Fill the input buffer. This is called only when the buffer is empty
301 * and at least one byte is really needed.
302 */
303static int fill_inbuf(void)
304{
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100305 error("ran out of input data");
306 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307}
308
309/* ===========================================================================
310 * Write the output window window[0..outcnt-1] and update crc and bytes_out.
311 * (Used for the decompressed data only.)
312 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313static void flush_window(void)
314{
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100315 /* With my window equal to my output buffer
316 * I only need to compute the crc here.
317 */
318 ulg c = crc; /* temporary variable */
319 unsigned n;
320 uch *in, ch;
321
322 in = window;
323 for (n = 0; n < outcnt; n++) {
324 ch = *in++;
325 c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
326 }
327 crc = c;
328 bytes_out += (ulg)outcnt;
329 outcnt = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330}
331
332static void error(char *x)
333{
334 putstr("\n\n");
335 putstr(x);
336 putstr("\n\n -- System halted");
337
338 while(1); /* Halt */
339}
340
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100341asmlinkage void decompress_kernel(void *rmode, unsigned long end,
342 uch *input_data, unsigned long input_len, uch *output)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343{
344 real_mode = rmode;
345
346 if (RM_SCREEN_INFO.orig_video_mode == 7) {
347 vidmem = (char *) 0xb0000;
348 vidport = 0x3b4;
349 } else {
350 vidmem = (char *) 0xb8000;
351 vidport = 0x3d4;
352 }
353
354 lines = RM_SCREEN_INFO.orig_video_lines;
355 cols = RM_SCREEN_INFO.orig_video_cols;
356
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100357 window = output; /* Output buffer (Normally at 1M) */
358 free_mem_ptr = end; /* Heap */
359 free_mem_end_ptr = end + HEAP_SIZE;
360 inbuf = input_data; /* Input buffer */
361 insize = input_len;
362 inptr = 0;
363
Vivek Goyale69f2022006-12-07 02:14:04 +0100364 if ((u32)output & (CONFIG_PHYSICAL_ALIGN -1))
365 error("Destination address not CONFIG_PHYSICAL_ALIGN aligned");
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100366 if (end > ((-__PAGE_OFFSET-(512 <<20)-1) & 0x7fffffff))
367 error("Destination address too large");
368#ifndef CONFIG_RELOCATABLE
Vivek Goyale69f2022006-12-07 02:14:04 +0100369 if ((u32)output != LOAD_PHYSICAL_ADDR)
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100370 error("Wrong destination address");
371#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
373 makecrc();
374 putstr("Uncompressing Linux... ");
375 gunzip();
376 putstr("Ok, booting the kernel.\n");
Eric W. Biederman968de4f2006-12-07 02:14:04 +0100377 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378}