misc.c 11.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 * misc.c
3 4
 *
 * This is a collection of several routines from gzip-1.0.3
L
Linus Torvalds 已提交
5 6 7 8 9 10 11
 * adapted for Linux.
 *
 * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
 * puts by Nick Holloway 1993, better puts by Martin Mares 1995
 * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
 */

12 13 14 15 16
/*
 * we have to be careful, because no indirections are allowed here, and
 * paravirt_ops is a kind of one. As it will only run in baremetal anyway,
 * we just keep it from happening
 */
17
#undef CONFIG_PARAVIRT
18 19 20 21
#ifdef CONFIG_X86_32
#define _ASM_DESC_H_ 1
#endif

22 23 24 25 26
#ifdef CONFIG_X86_64
#define _LINUX_STRING_H_ 1
#define __LINUX_BITMAP_H 1
#endif

L
Linus Torvalds 已提交
27
#include <linux/linkage.h>
28
#include <linux/screen_info.h>
29
#include <linux/elf.h>
L
Linus Torvalds 已提交
30
#include <asm/io.h>
31
#include <asm/page.h>
32
#include <asm/boot.h>
33 34 35 36 37 38 39 40 41

/* WARNING!!
 * This code is compiled with -fPIC and it is relocated dynamically
 * at run time, but no relocation processing is performed.
 * This means that it is not safe to place pointers in static structures.
 */

/*
 * Getting to provable safe in place decompression is hard.
S
Simon Arlott 已提交
42
 * Worst case behaviours need to be analyzed.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
 * Background information:
 *
 * The file layout is:
 *    magic[2]
 *    method[1]
 *    flags[1]
 *    timestamp[4]
 *    extraflags[1]
 *    os[1]
 *    compressed data blocks[N]
 *    crc[4] orig_len[4]
 *
 * resulting in 18 bytes of non compressed data overhead.
 *
 * Files divided into blocks
 * 1 bit (last block flag)
 * 2 bits (block type)
 *
61 62
 * 1 block occurs every 32K -1 bytes or when there 50% compression
 * has been achieved. The smallest block type encoding is always used.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
 *
 * stored:
 *    32 bits length in bytes.
 *
 * fixed:
 *    magic fixed tree.
 *    symbols.
 *
 * dynamic:
 *    dynamic tree encoding.
 *    symbols.
 *
 *
 * The buffer for decompression in place is the length of the
 * uncompressed data, plus a small amount extra to keep the algorithm safe.
 * The compressed data is placed at the end of the buffer.  The output
 * pointer is placed at the start of the buffer and the input pointer
 * is placed where the compressed data starts.  Problems will occur
 * when the output pointer overruns the input pointer.
 *
 * The output pointer can only overrun the input pointer if the input
 * pointer is moving faster than the output pointer.  A condition only
 * triggered by data whose compressed form is larger than the uncompressed
 * form.
 *
 * The worst case at the block level is a growth of the compressed data
 * of 5 bytes per 32767 bytes.
 *
 * The worst case internal to a compressed block is very hard to figure.
 * The worst case can at least be boundined by having one bit that represents
 * 32764 bytes and then all of the rest of the bytes representing the very
 * very last byte.
 *
 * All of which is enough to compute an amount of extra data that is required
 * to be safe.  To avoid problems at the block level allocating 5 extra bytes
98 99 100
 * per 32767 bytes of data is sufficient.  To avoind problems internal to a
 * block adding an extra 32767 bytes (the worst case uncompressed block size)
 * is sufficient, to ensure that in the worst case the decompressed data for
101 102 103 104 105 106 107 108 109 110
 * block will stop the byte before the compressed data for a block begins.
 * To avoid problems with the compressed data's meta information an extra 18
 * bytes are needed.  Leading to the formula:
 *
 * extra_bytes = (uncompressed_size >> 12) + 32768 + 18 + decompressor_size.
 *
 * Adding 8 bytes per 32K is a bit excessive but much easier to calculate.
 * Adding 32768 instead of 32767 just makes for round numbers.
 * Adding the decompressor_size is necessary as it musht live after all
 * of the data as well.  Last I measured the decompressor is about 14K.
S
Simon Arlott 已提交
111
 * 10K of actual data and 4K of bss.
112 113
 *
 */
L
Linus Torvalds 已提交
114 115 116 117 118

/*
 * gzip declarations
 */

119 120
#define OF(args)	args
#define STATIC		static
L
Linus Torvalds 已提交
121 122 123

#undef memset
#undef memcpy
124
#define memzero(s, n)	memset((s), 0, (n))
L
Linus Torvalds 已提交
125

126 127 128
typedef unsigned char	uch;
typedef unsigned short	ush;
typedef unsigned long	ulg;
L
Linus Torvalds 已提交
129

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
/*
 * Window size must be at least 32k, and a power of two.
 * We don't actually have a window just a huge output buffer,
 * so we report a 2G window size, as that should always be
 * larger than our output buffer:
 */
#define WSIZE		0x80000000

/* Input buffer: */
static unsigned char	*inbuf;

/* Sliding window buffer (and final output buffer): */
static unsigned char	*window;

/* Valid bytes in inbuf: */
static unsigned		insize;
L
Linus Torvalds 已提交
146

147 148
/* Index of next byte to be processed in inbuf: */
static unsigned		inptr;
L
Linus Torvalds 已提交
149

150 151
/* Bytes in output buffer: */
static unsigned		outcnt;
L
Linus Torvalds 已提交
152 153

/* gzip flag byte */
154 155 156 157 158 159 160
#define ASCII_FLAG	0x01 /* bit 0 set: file probably ASCII text */
#define CONTINUATION	0x02 /* bit 1 set: continuation of multi-part gz file */
#define EXTRA_FIELD	0x04 /* bit 2 set: extra field present */
#define ORIG_NAM	0x08 /* bit 3 set: original file name present */
#define COMMENT		0x10 /* bit 4 set: file comment present */
#define ENCRYPTED	0x20 /* bit 5 set: file is encrypted */
#define RESERVED	0xC0 /* bit 6, 7:  reserved */
L
Linus Torvalds 已提交
161

162
#define get_byte()	(inptr < insize ? inbuf[inptr++] : fill_inbuf())
163

L
Linus Torvalds 已提交
164 165
/* Diagnostic functions */
#ifdef DEBUG
166 167 168 169 170 171
#  define Assert(cond, msg) do { if (!(cond)) error(msg); } while (0)
#  define Trace(x)	do { fprintf x; } while (0)
#  define Tracev(x)	do { if (verbose) fprintf x ; } while (0)
#  define Tracevv(x)	do { if (verbose > 1) fprintf x ; } while (0)
#  define Tracec(c, x)	do { if (verbose && (c)) fprintf x ; } while (0)
#  define Tracecv(c, x)	do { if (verbose > 1 && (c)) fprintf x ; } while (0)
L
Linus Torvalds 已提交
172
#else
173
#  define Assert(cond, msg)
L
Linus Torvalds 已提交
174 175 176
#  define Trace(x)
#  define Tracev(x)
#  define Tracevv(x)
177 178
#  define Tracec(c, x)
#  define Tracecv(c, x)
L
Linus Torvalds 已提交
179 180 181 182 183 184 185
#endif

static int  fill_inbuf(void);
static void flush_window(void);
static void error(char *m);
static void gzip_mark(void **);
static void gzip_release(void **);
186

L
Linus Torvalds 已提交
187 188 189 190 191 192 193 194 195 196 197
/*
 * This is set up by the setup-routine at boot-time
 */
static unsigned char *real_mode; /* Pointer to real-mode data */

#define RM_EXT_MEM_K   (*(unsigned short *)(real_mode + 0x2))
#ifndef STANDARD_MEMORY_BIOS_CALL
#define RM_ALT_MEM_K   (*(unsigned long *)(real_mode + 0x1e0))
#endif
#define RM_SCREEN_INFO (*(struct screen_info *)(real_mode+0))

198
extern unsigned char input_data[];
L
Linus Torvalds 已提交
199 200
extern int input_len;

201
static long bytes_out;
L
Linus Torvalds 已提交
202 203 204 205

static void *malloc(int size);
static void free(void *where);

206 207 208
static void *memset(void *s, int c, unsigned n);
static void *memcpy(void *dest, const void *src, unsigned n);

L
Linus Torvalds 已提交
209 210
static void putstr(const char *);

211 212 213 214 215 216 217 218
#ifdef CONFIG_X86_64
#define memptr long
#else
#define memptr unsigned
#endif

static memptr free_mem_ptr;
static memptr free_mem_end_ptr;
L
Linus Torvalds 已提交
219

220
static char *vidmem;
L
Linus Torvalds 已提交
221 222 223 224
static int vidport;
static int lines, cols;

#ifdef CONFIG_X86_NUMAQ
225
void *xquad_portio;
L
Linus Torvalds 已提交
226 227 228 229 230 231 232 233
#endif

#include "../../../../lib/inflate.c"

static void *malloc(int size)
{
	void *p;

234 235 236 237
	if (size < 0)
		error("Malloc error");
	if (free_mem_ptr <= 0)
		error("Memory error");
L
Linus Torvalds 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260

	free_mem_ptr = (free_mem_ptr + 3) & ~3;	/* Align */

	p = (void *)free_mem_ptr;
	free_mem_ptr += size;

	if (free_mem_ptr >= free_mem_end_ptr)
		error("Out of memory");

	return p;
}

static void free(void *where)
{	/* Don't care */
}

static void gzip_mark(void **ptr)
{
	*ptr = (void *) free_mem_ptr;
}

static void gzip_release(void **ptr)
{
261
	free_mem_ptr = (memptr) *ptr;
L
Linus Torvalds 已提交
262
}
263

L
Linus Torvalds 已提交
264 265 266 267
static void scroll(void)
{
	int i;

268 269
	memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2);
	for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2)
L
Linus Torvalds 已提交
270 271 272 273 274
		vidmem[i] = ' ';
}

static void putstr(const char *s)
{
275
	int x, y, pos;
L
Linus Torvalds 已提交
276 277
	char c;

278
#ifdef CONFIG_X86_32
R
Rusty Russell 已提交
279 280
	if (RM_SCREEN_INFO.orig_video_mode == 0 && lines == 0 && cols == 0)
		return;
281
#endif
R
Rusty Russell 已提交
282

L
Linus Torvalds 已提交
283 284 285
	x = RM_SCREEN_INFO.orig_x;
	y = RM_SCREEN_INFO.orig_y;

286 287
	while ((c = *s++) != '\0') {
		if (c == '\n') {
L
Linus Torvalds 已提交
288
			x = 0;
289
			if (++y >= lines) {
L
Linus Torvalds 已提交
290 291 292 293
				scroll();
				y--;
			}
		} else {
294
			vidmem [(x + cols * y) * 2] = c;
295
			if (++x >= cols) {
L
Linus Torvalds 已提交
296
				x = 0;
297
				if (++y >= lines) {
L
Linus Torvalds 已提交
298 299 300 301 302 303 304 305 306 307 308
					scroll();
					y--;
				}
			}
		}
	}

	RM_SCREEN_INFO.orig_x = x;
	RM_SCREEN_INFO.orig_y = y;

	pos = (x + cols * y) * 2;	/* Update cursor position */
309 310 311 312
	outb(14, vidport);
	outb(0xff & (pos >> 9), vidport+1);
	outb(15, vidport);
	outb(0xff & (pos >> 1), vidport+1);
L
Linus Torvalds 已提交
313 314
}

315
static void *memset(void *s, int c, unsigned n)
L
Linus Torvalds 已提交
316 317
{
	int i;
J
Jan Engelhardt 已提交
318
	char *ss = s;
L
Linus Torvalds 已提交
319

320
	for (i = 0; i < n; i++) ss[i] = c;
L
Linus Torvalds 已提交
321 322 323
	return s;
}

324
static void *memcpy(void *dest, const void *src, unsigned n)
L
Linus Torvalds 已提交
325 326
{
	int i;
J
Jan Engelhardt 已提交
327 328
	const char *s = src;
	char *d = dest;
L
Linus Torvalds 已提交
329

330
	for (i = 0; i < n; i++) d[i] = s[i];
331
	return dest;
L
Linus Torvalds 已提交
332 333 334 335 336 337 338 339
}

/* ===========================================================================
 * Fill the input buffer. This is called only when the buffer is empty
 * and at least one byte is really needed.
 */
static int fill_inbuf(void)
{
340 341
	error("ran out of input data");
	return 0;
L
Linus Torvalds 已提交
342 343 344 345 346 347 348 349
}

/* ===========================================================================
 * Write the output window window[0..outcnt-1] and update crc and bytes_out.
 * (Used for the decompressed data only.)
 */
static void flush_window(void)
{
350 351 352
	/* With my window equal to my output buffer
	 * I only need to compute the crc here.
	 */
353
	unsigned long c = crc;         /* temporary variable */
354
	unsigned n;
355
	unsigned char *in, ch;
356 357 358 359 360 361 362

	in = window;
	for (n = 0; n < outcnt; n++) {
		ch = *in++;
		c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
	}
	crc = c;
363
	bytes_out += (unsigned long)outcnt;
364
	outcnt = 0;
L
Linus Torvalds 已提交
365 366 367 368 369 370 371 372
}

static void error(char *x)
{
	putstr("\n\n");
	putstr(x);
	putstr("\n\n -- System halted");

I
Ingo Molnar 已提交
373 374
	while (1)
		asm("hlt");
L
Linus Torvalds 已提交
375 376
}

377 378 379 380 381 382 383 384 385 386 387 388 389
static void parse_elf(void *output)
{
#ifdef CONFIG_X86_64
	Elf64_Ehdr ehdr;
	Elf64_Phdr *phdrs, *phdr;
#else
	Elf32_Ehdr ehdr;
	Elf32_Phdr *phdrs, *phdr;
#endif
	void *dest;
	int i;

	memcpy(&ehdr, output, sizeof(ehdr));
390
	if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
391 392
	   ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
	   ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
393
	   ehdr.e_ident[EI_MAG3] != ELFMAG3) {
394 395 396 397 398 399 400 401 402 403 404 405
		error("Kernel is not a valid ELF file");
		return;
	}

	putstr("Parsing ELF... ");

	phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum);
	if (!phdrs)
		error("Failed to allocate space for phdrs");

	memcpy(phdrs, output + ehdr.e_phoff, sizeof(*phdrs) * ehdr.e_phnum);

406
	for (i = 0; i < ehdr.e_phnum; i++) {
407 408 409 410 411 412 413 414
		phdr = &phdrs[i];

		switch (phdr->p_type) {
		case PT_LOAD:
#ifdef CONFIG_RELOCATABLE
			dest = output;
			dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR);
#else
415
			dest = (void *)(phdr->p_paddr);
416 417 418 419 420 421 422 423 424 425
#endif
			memcpy(dest,
			       output + phdr->p_offset,
			       phdr->p_filesz);
			break;
		default: /* Ignore other PT_* */ break;
		}
	}
}

426
asmlinkage void decompress_kernel(void *rmode, memptr heap,
427 428 429
				  unsigned char *input_data,
				  unsigned long input_len,
				  unsigned char *output)
L
Linus Torvalds 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443
{
	real_mode = rmode;

	if (RM_SCREEN_INFO.orig_video_mode == 7) {
		vidmem = (char *) 0xb0000;
		vidport = 0x3b4;
	} else {
		vidmem = (char *) 0xb8000;
		vidport = 0x3d4;
	}

	lines = RM_SCREEN_INFO.orig_video_lines;
	cols = RM_SCREEN_INFO.orig_video_cols;

444
	window = output;		/* Output buffer (Normally at 1M) */
445
	free_mem_ptr     = heap;	/* Heap */
446
	free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
447
	inbuf  = input_data;		/* Input buffer */
448 449 450
	insize = input_len;
	inptr  = 0;

451
#ifdef CONFIG_X86_64
452
	if ((unsigned long)output & (__KERNEL_ALIGN - 1))
453
		error("Destination address not 2M aligned");
454
	if ((unsigned long)output >= 0xffffffffffUL)
455 456
		error("Destination address too large");
#else
457
	if ((u32)output & (CONFIG_PHYSICAL_ALIGN - 1))
458
		error("Destination address not CONFIG_PHYSICAL_ALIGN aligned");
459
	if (heap > ((-__PAGE_OFFSET-(512<<20)-1) & 0x7fffffff))
460 461
		error("Destination address too large");
#ifndef CONFIG_RELOCATABLE
462
	if ((u32)output != LOAD_PHYSICAL_ADDR)
463
		error("Wrong destination address");
464
#endif
465
#endif
L
Linus Torvalds 已提交
466 467

	makecrc();
468
	putstr("\nDecompressing Linux... ");
L
Linus Torvalds 已提交
469
	gunzip();
470
	parse_elf(output);
471
	putstr("done.\nBooting the kernel.\n");
472
	return;
L
Linus Torvalds 已提交
473
}