| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * misc.c | 
					
						
							| 
									
										
										
										
											2008-01-30 13:33:38 +01:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This is a collection of several routines from gzip-1.0.3 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * 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 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-02 16:21:22 -07:00
										 |  |  | #include "misc.h"
 | 
					
						
							| 
									
										
										
										
											2014-03-18 15:26:38 -04:00
										 |  |  | #include "../string.h"
 | 
					
						
							| 
									
										
										
										
											2006-12-07 02:14:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* 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. | 
					
						
							| 
									
										
										
										
											2007-10-20 01:13:56 +02:00
										 |  |  |  * Worst case behaviours need to be analyzed. | 
					
						
							| 
									
										
										
										
											2006-12-07 02:14:04 +01:00
										 |  |  |  * 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) | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2008-02-21 05:03:48 +01:00
										 |  |  |  * 1 block occurs every 32K -1 bytes or when there 50% compression | 
					
						
							|  |  |  |  * has been achieved. The smallest block type encoding is always used. | 
					
						
							| 
									
										
										
										
											2006-12-07 02:14:04 +01:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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 | 
					
						
							| 
									
										
										
										
											2008-02-21 05:03:48 +01:00
										 |  |  |  * 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 | 
					
						
							| 
									
										
										
										
											2006-12-07 02:14:04 +01:00
										 |  |  |  * 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. | 
					
						
							| 
									
										
										
										
											2007-10-20 01:13:56 +02:00
										 |  |  |  * 10K of actual data and 4K of bss. | 
					
						
							| 
									
										
										
										
											2006-12-07 02:14:04 +01:00
										 |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * gzip declarations | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-02-21 05:03:48 +01:00
										 |  |  | #define STATIC		static
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #undef memcpy
 | 
					
						
							| 
									
										
										
										
											2014-03-18 15:26:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Use a normal definition of memset() from string.c. There are already | 
					
						
							|  |  |  |  * included header files which expect a definition of memset() and by | 
					
						
							|  |  |  |  * the time we define memset macro, it is too late. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #undef memset
 | 
					
						
							| 
									
										
										
										
											2008-02-21 05:03:48 +01:00
										 |  |  | #define memzero(s, n)	memset((s), 0, (n))
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void error(char *m); | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * This is set up by the setup-routine at boot-time | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-08-02 16:21:22 -07:00
										 |  |  | struct boot_params *real_mode;		/* Pointer to real-mode data */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-10 17:18:16 -07:00
										 |  |  | memptr free_mem_ptr; | 
					
						
							|  |  |  | memptr free_mem_end_ptr; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-06 14:47:00 +02:00
										 |  |  | static char *vidmem; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static int vidport; | 
					
						
							|  |  |  | static int lines, cols; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-04 22:46:17 +01:00
										 |  |  | #ifdef CONFIG_KERNEL_GZIP
 | 
					
						
							|  |  |  | #include "../../../../lib/decompress_inflate.c"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_KERNEL_BZIP2
 | 
					
						
							|  |  |  | #include "../../../../lib/decompress_bunzip2.c"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_KERNEL_LZMA
 | 
					
						
							|  |  |  | #include "../../../../lib/decompress_unlzma.c"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-12 17:01:24 -08:00
										 |  |  | #ifdef CONFIG_KERNEL_XZ
 | 
					
						
							|  |  |  | #include "../../../../lib/decompress_unxz.c"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-08 14:42:45 -08:00
										 |  |  | #ifdef CONFIG_KERNEL_LZO
 | 
					
						
							|  |  |  | #include "../../../../lib/decompress_unlzo.c"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-08 16:01:48 -07:00
										 |  |  | #ifdef CONFIG_KERNEL_LZ4
 | 
					
						
							|  |  |  | #include "../../../../lib/decompress_unlz4.c"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static void scroll(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 	memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2); | 
					
						
							|  |  |  | 	for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		vidmem[i] = ' '; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-02 16:21:22 -07:00
										 |  |  | #define XMTRDY          0x20
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define TXR             0       /*  Transmit register (WRITE) */
 | 
					
						
							|  |  |  | #define LSR             5       /*  Line Status               */
 | 
					
						
							|  |  |  | static void serial_putchar(int ch) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned timeout = 0xffff; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) | 
					
						
							|  |  |  | 		cpu_relax(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outb(ch, early_serial_base + TXR); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-19 18:04:39 -07:00
										 |  |  | void __putstr(const char *s) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 	int x, y, pos; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	char c; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-02 16:21:22 -07:00
										 |  |  | 	if (early_serial_base) { | 
					
						
							|  |  |  | 		const char *str = s; | 
					
						
							|  |  |  | 		while (*str) { | 
					
						
							|  |  |  | 			if (*str == '\n') | 
					
						
							|  |  |  | 				serial_putchar('\r'); | 
					
						
							|  |  |  | 			serial_putchar(*str++); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-06-18 14:04:35 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-29 18:31:14 -04:00
										 |  |  | 	if (real_mode->screen_info.orig_video_mode == 0 && | 
					
						
							|  |  |  | 	    lines == 0 && cols == 0) | 
					
						
							| 
									
										
										
										
											2007-10-21 16:41:35 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-29 18:31:14 -04:00
										 |  |  | 	x = real_mode->screen_info.orig_x; | 
					
						
							|  |  |  | 	y = real_mode->screen_info.orig_y; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 	while ((c = *s++) != '\0') { | 
					
						
							|  |  |  | 		if (c == '\n') { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			x = 0; | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 			if (++y >= lines) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				scroll(); | 
					
						
							|  |  |  | 				y--; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2008-08-02 21:23:36 +02:00
										 |  |  | 			vidmem[(x + cols * y) * 2] = c; | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 			if (++x >= cols) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				x = 0; | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 				if (++y >= lines) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 					scroll(); | 
					
						
							|  |  |  | 					y--; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-29 18:31:14 -04:00
										 |  |  | 	real_mode->screen_info.orig_x = x; | 
					
						
							|  |  |  | 	real_mode->screen_info.orig_y = y; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pos = (x + cols * y) * 2;	/* Update cursor position */ | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:05 +01:00
										 |  |  | 	outb(14, vidport); | 
					
						
							|  |  |  | 	outb(0xff & (pos >> 9), vidport+1); | 
					
						
							|  |  |  | 	outb(15, vidport); | 
					
						
							|  |  |  | 	outb(0xff & (pos >> 1), vidport+1); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void error(char *x) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-19 18:04:38 -07:00
										 |  |  | 	error_putstr("\n\n"); | 
					
						
							|  |  |  | 	error_putstr(x); | 
					
						
							|  |  |  | 	error_putstr("\n\n -- System halted"); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:31 +01:00
										 |  |  | 	while (1) | 
					
						
							|  |  |  | 		asm("hlt"); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-08 09:15:17 -07:00
										 |  |  | #if CONFIG_X86_NEED_RELOCS
 | 
					
						
							|  |  |  | static void handle_relocations(void *output, unsigned long output_len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int *reloc; | 
					
						
							|  |  |  | 	unsigned long delta, map, ptr; | 
					
						
							|  |  |  | 	unsigned long min_addr = (unsigned long)output; | 
					
						
							|  |  |  | 	unsigned long max_addr = min_addr + output_len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Calculate the delta between where vmlinux was linked to load | 
					
						
							|  |  |  | 	 * and where it was actually loaded. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	delta = min_addr - LOAD_PHYSICAL_ADDR; | 
					
						
							|  |  |  | 	if (!delta) { | 
					
						
							|  |  |  | 		debug_putstr("No relocation needed... "); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	debug_putstr("Performing relocations... "); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The kernel contains a table of relocation addresses. Those | 
					
						
							|  |  |  | 	 * addresses have the final load address of the kernel in virtual | 
					
						
							|  |  |  | 	 * memory. We are currently working in the self map. So we need to | 
					
						
							|  |  |  | 	 * create an adjustment for kernel memory addresses to the self map. | 
					
						
							|  |  |  | 	 * This will involve subtracting out the base address of the kernel. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	map = delta - __START_KERNEL_map; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Process relocations: 32 bit relocations first then 64 bit after. | 
					
						
							|  |  |  | 	 * Two sets of binary relocations are added to the end of the kernel | 
					
						
							|  |  |  | 	 * before compression. Each relocation table entry is the kernel | 
					
						
							|  |  |  | 	 * address of the location which needs to be updated stored as a | 
					
						
							|  |  |  | 	 * 32-bit value which is sign extended to 64 bits. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * Format is: | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * kernel bits... | 
					
						
							|  |  |  | 	 * 0 - zero terminator for 64 bit relocations | 
					
						
							|  |  |  | 	 * 64 bit relocation repeated | 
					
						
							|  |  |  | 	 * 0 - zero terminator for 32 bit relocations | 
					
						
							|  |  |  | 	 * 32 bit relocation repeated | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * So we work backwards from the end of the decompressed image. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	for (reloc = output + output_len - sizeof(*reloc); *reloc; reloc--) { | 
					
						
							|  |  |  | 		int extended = *reloc; | 
					
						
							|  |  |  | 		extended += map; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ptr = (unsigned long)extended; | 
					
						
							|  |  |  | 		if (ptr < min_addr || ptr > max_addr) | 
					
						
							|  |  |  | 			error("32-bit relocation outside of kernel!\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		*(uint32_t *)ptr += delta; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #ifdef CONFIG_X86_64
 | 
					
						
							|  |  |  | 	for (reloc--; *reloc; reloc--) { | 
					
						
							|  |  |  | 		long extended = *reloc; | 
					
						
							|  |  |  | 		extended += map; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ptr = (unsigned long)extended; | 
					
						
							|  |  |  | 		if (ptr < min_addr || ptr > max_addr) | 
					
						
							|  |  |  | 			error("64-bit relocation outside of kernel!\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		*(uint64_t *)ptr += delta; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | static inline void handle_relocations(void *output, unsigned long output_len) | 
					
						
							|  |  |  | { } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-13 20:54:58 +00:00
										 |  |  | 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)); | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 	if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || | 
					
						
							| 
									
										
										
										
											2008-02-13 20:54:58 +00:00
										 |  |  | 	   ehdr.e_ident[EI_MAG1] != ELFMAG1 || | 
					
						
							|  |  |  | 	   ehdr.e_ident[EI_MAG2] != ELFMAG2 || | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 	   ehdr.e_ident[EI_MAG3] != ELFMAG3) { | 
					
						
							| 
									
										
										
										
											2008-02-13 20:54:58 +00:00
										 |  |  | 		error("Kernel is not a valid ELF file"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-19 18:04:37 -07:00
										 |  |  | 	debug_putstr("Parsing ELF... "); | 
					
						
							| 
									
										
										
										
											2008-02-13 20:54:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 	for (i = 0; i < ehdr.e_phnum; i++) { | 
					
						
							| 
									
										
										
										
											2008-02-13 20:54:58 +00:00
										 |  |  | 		phdr = &phdrs[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (phdr->p_type) { | 
					
						
							|  |  |  | 		case PT_LOAD: | 
					
						
							|  |  |  | #ifdef CONFIG_RELOCATABLE
 | 
					
						
							|  |  |  | 			dest = output; | 
					
						
							|  |  |  | 			dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR); | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2008-02-21 00:19:10 +01:00
										 |  |  | 			dest = (void *)(phdr->p_paddr); | 
					
						
							| 
									
										
										
										
											2008-02-13 20:54:58 +00:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 			memcpy(dest, | 
					
						
							|  |  |  | 			       output + phdr->p_offset, | 
					
						
							|  |  |  | 			       phdr->p_filesz); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: /* Ignore other PT_* */ break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-01-23 23:34:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	free(phdrs); | 
					
						
							| 
									
										
										
										
											2008-02-13 20:54:58 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-02 00:44:37 +02:00
										 |  |  | asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap, | 
					
						
							| 
									
										
										
										
											2008-02-21 05:03:48 +01:00
										 |  |  | 				  unsigned char *input_data, | 
					
						
							|  |  |  | 				  unsigned long input_len, | 
					
						
							| 
									
										
										
										
											2013-07-08 09:15:17 -07:00
										 |  |  | 				  unsigned char *output, | 
					
						
							|  |  |  | 				  unsigned long output_len) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	real_mode = rmode; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-29 01:05:24 -08:00
										 |  |  | 	sanitize_boot_params(real_mode); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-29 18:31:14 -04:00
										 |  |  | 	if (real_mode->screen_info.orig_video_mode == 7) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		vidmem = (char *) 0xb0000; | 
					
						
							|  |  |  | 		vidport = 0x3b4; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		vidmem = (char *) 0xb8000; | 
					
						
							|  |  |  | 		vidport = 0x3d4; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-29 18:31:14 -04:00
										 |  |  | 	lines = real_mode->screen_info.orig_video_lines; | 
					
						
							|  |  |  | 	cols = real_mode->screen_info.orig_video_cols; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-02 16:21:22 -07:00
										 |  |  | 	console_init(); | 
					
						
							| 
									
										
										
										
											2012-07-19 18:04:37 -07:00
										 |  |  | 	debug_putstr("early console in decompress_kernel\n"); | 
					
						
							| 
									
										
										
										
											2010-08-02 16:21:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:33:38 +01:00
										 |  |  | 	free_mem_ptr     = heap;	/* Heap */ | 
					
						
							| 
									
										
										
										
											2008-04-08 12:54:30 +02:00
										 |  |  | 	free_mem_end_ptr = heap + BOOT_HEAP_SIZE; | 
					
						
							| 
									
										
										
										
											2006-12-07 02:14:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-10 17:18:14 -07:00
										 |  |  | 	output = choose_kernel_location(input_data, input_len, | 
					
						
							|  |  |  | 					output, output_len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Validate memory location choices. */ | 
					
						
							| 
									
										
										
										
											2009-05-12 11:33:08 -07:00
										 |  |  | 	if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1)) | 
					
						
							|  |  |  | 		error("Destination address inappropriately aligned"); | 
					
						
							| 
									
										
										
										
											2008-01-30 13:33:38 +01:00
										 |  |  | #ifdef CONFIG_X86_64
 | 
					
						
							| 
									
										
										
										
											2009-05-12 11:33:08 -07:00
										 |  |  | 	if (heap > 0x3fffffffffffUL) | 
					
						
							| 
									
										
										
										
											2008-01-30 13:33:38 +01:00
										 |  |  | 		error("Destination address too large"); | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2010-12-16 19:11:09 -08:00
										 |  |  | 	if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff)) | 
					
						
							| 
									
										
										
										
											2006-12-07 02:14:04 +01:00
										 |  |  | 		error("Destination address too large"); | 
					
						
							| 
									
										
										
										
											2009-05-12 11:33:08 -07:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2006-12-07 02:14:04 +01:00
										 |  |  | #ifndef CONFIG_RELOCATABLE
 | 
					
						
							| 
									
										
										
										
											2009-05-12 11:33:08 -07:00
										 |  |  | 	if ((unsigned long)output != LOAD_PHYSICAL_ADDR) | 
					
						
							| 
									
										
										
										
											2006-12-07 02:14:04 +01:00
										 |  |  | 		error("Wrong destination address"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-19 18:04:37 -07:00
										 |  |  | 	debug_putstr("\nDecompressing Linux... "); | 
					
						
							| 
									
										
										
										
											2009-01-04 22:46:17 +01:00
										 |  |  | 	decompress(input_data, input_len, NULL, NULL, output, NULL, error); | 
					
						
							| 
									
										
										
										
											2008-02-13 20:54:58 +00:00
										 |  |  | 	parse_elf(output); | 
					
						
							| 
									
										
										
										
											2013-07-08 09:15:17 -07:00
										 |  |  | 	handle_relocations(output, output_len); | 
					
						
							| 
									
										
										
										
											2012-07-19 18:04:37 -07:00
										 |  |  | 	debug_putstr("done.\nBooting the kernel.\n"); | 
					
						
							| 
									
										
										
										
											2013-10-10 17:18:14 -07:00
										 |  |  | 	return output; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } |