diff options
Diffstat (limited to 'arch/x86/boot/compressed/misc.c')
| -rw-r--r-- | arch/x86/boot/compressed/misc.c | 333 |
1 files changed, 151 insertions, 182 deletions
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index 9fea7370647..57ab74df7ee 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c @@ -9,28 +9,8 @@ * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 */ -/* - * 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 - */ -#undef CONFIG_PARAVIRT -#ifdef CONFIG_X86_32 -#define _ASM_DESC_H_ 1 -#endif - -#ifdef CONFIG_X86_64 -#define _LINUX_STRING_H_ 1 -#define __LINUX_BITMAP_H 1 -#endif - -#include <linux/linkage.h> -#include <linux/screen_info.h> -#include <linux/elf.h> -#include <asm/io.h> -#include <asm/page.h> -#include <asm/boot.h> -#include <asm/bootparam.h> +#include "misc.h" +#include "../string.h" /* WARNING!! * This code is compiled with -fPIC and it is relocated dynamically @@ -116,104 +96,56 @@ /* * gzip declarations */ - -#define OF(args) args #define STATIC static -#undef memset #undef memcpy -#define memzero(s, n) memset((s), 0, (n)) - -typedef unsigned char uch; -typedef unsigned short ush; -typedef unsigned long ulg; /* - * 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: + * 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. */ -#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; - -/* Index of next byte to be processed in inbuf: */ -static unsigned inptr; - -/* Bytes in output buffer: */ -static unsigned outcnt; - -/* gzip flag byte */ -#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 */ - -#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) - -/* Diagnostic functions */ -#ifdef DEBUG -# 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) -#else -# define Assert(cond, msg) -# define Trace(x) -# define Tracev(x) -# define Tracevv(x) -# define Tracec(c, x) -# define Tracecv(c, x) -#endif +#undef memset +#define memzero(s, n) memset((s), 0, (n)) + -static int fill_inbuf(void); -static void flush_window(void); static void error(char *m); /* * This is set up by the setup-routine at boot-time */ -static struct boot_params *real_mode; /* Pointer to real-mode data */ -static int quiet; +struct boot_params *real_mode; /* Pointer to real-mode data */ -extern unsigned char input_data[]; -extern int input_len; +memptr free_mem_ptr; +memptr free_mem_end_ptr; -static long bytes_out; +static char *vidmem; +static int vidport; +static int lines, cols; -static void *memset(void *s, int c, unsigned n); -static void *memcpy(void *dest, const void *src, unsigned n); +#ifdef CONFIG_KERNEL_GZIP +#include "../../../../lib/decompress_inflate.c" +#endif -static void __putstr(int, const char *); -#define putstr(__x) __putstr(0, __x) +#ifdef CONFIG_KERNEL_BZIP2 +#include "../../../../lib/decompress_bunzip2.c" +#endif -#ifdef CONFIG_X86_64 -#define memptr long -#else -#define memptr unsigned +#ifdef CONFIG_KERNEL_LZMA +#include "../../../../lib/decompress_unlzma.c" #endif -static memptr free_mem_ptr; -static memptr free_mem_end_ptr; +#ifdef CONFIG_KERNEL_XZ +#include "../../../../lib/decompress_unxz.c" +#endif -static char *vidmem; -static int vidport; -static int lines, cols; +#ifdef CONFIG_KERNEL_LZO +#include "../../../../lib/decompress_unlzo.c" +#endif -#include "../../../../lib/inflate.c" +#ifdef CONFIG_KERNEL_LZ4 +#include "../../../../lib/decompress_unlz4.c" +#endif static void scroll(void) { @@ -224,21 +156,37 @@ static void scroll(void) vidmem[i] = ' '; } -static void __putstr(int error, const char *s) +#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); +} + +void __putstr(const char *s) { int x, y, pos; char c; -#ifndef CONFIG_X86_VERBOSE_BOOTUP - if (!error) - return; -#endif + if (early_serial_base) { + const char *str = s; + while (*str) { + if (*str == '\n') + serial_putchar('\r'); + serial_putchar(*str++); + } + } -#ifdef CONFIG_X86_32 if (real_mode->screen_info.orig_video_mode == 0 && lines == 0 && cols == 0) return; -#endif x = real_mode->screen_info.orig_x; y = real_mode->screen_info.orig_y; @@ -251,7 +199,7 @@ static void __putstr(int error, const char *s) y--; } } else { - vidmem [(x + cols * y) * 2] = c; + vidmem[(x + cols * y) * 2] = c; if (++x >= cols) { x = 0; if (++y >= lines) { @@ -272,67 +220,88 @@ static void __putstr(int error, const char *s) outb(0xff & (pos >> 1), vidport+1); } -static void *memset(void *s, int c, unsigned n) +static void error(char *x) { - int i; - char *ss = s; + error_putstr("\n\n"); + error_putstr(x); + error_putstr("\n\n -- System halted"); - for (i = 0; i < n; i++) ss[i] = c; - return s; + while (1) + asm("hlt"); } -static void *memcpy(void *dest, const void *src, unsigned n) +#if CONFIG_X86_NEED_RELOCS +static void handle_relocations(void *output, unsigned long output_len) { - int i; - const char *s = src; - char *d = dest; - - for (i = 0; i < n; i++) d[i] = s[i]; - return dest; -} + 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; -/* =========================================================================== - * 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) -{ - error("ran out of input data"); - return 0; -} + ptr = (unsigned long)extended; + if (ptr < min_addr || ptr > max_addr) + error("32-bit relocation outside of kernel!\n"); -/* =========================================================================== - * 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) -{ - /* With my window equal to my output buffer - * I only need to compute the crc here. - */ - unsigned long c = crc; /* temporary variable */ - unsigned n; - unsigned char *in, ch; - - in = window; - for (n = 0; n < outcnt; n++) { - ch = *in++; - c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + *(uint32_t *)ptr += delta; } - crc = c; - bytes_out += (unsigned long)outcnt; - outcnt = 0; -} +#ifdef CONFIG_X86_64 + for (reloc--; *reloc; reloc--) { + long extended = *reloc; + extended += map; -static void error(char *x) -{ - __putstr(1, "\n\n"); - __putstr(1, x); - __putstr(1, "\n\n -- System halted"); + ptr = (unsigned long)extended; + if (ptr < min_addr || ptr > max_addr) + error("64-bit relocation outside of kernel!\n"); - while (1) - asm("hlt"); + *(uint64_t *)ptr += delta; + } +#endif } +#else +static inline void handle_relocations(void *output, unsigned long output_len) +{ } +#endif static void parse_elf(void *output) { @@ -355,8 +324,7 @@ static void parse_elf(void *output) return; } - if (!quiet) - putstr("Parsing ELF... "); + debug_putstr("Parsing ELF... "); phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum); if (!phdrs) @@ -382,17 +350,19 @@ static void parse_elf(void *output) default: /* Ignore other PT_* */ break; } } + + free(phdrs); } -asmlinkage void decompress_kernel(void *rmode, memptr heap, +asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap, unsigned char *input_data, unsigned long input_len, - unsigned char *output) + unsigned char *output, + unsigned long output_len) { real_mode = rmode; - if (real_mode->hdr.loadflags & QUIET_FLAG) - quiet = 1; + sanitize_boot_params(real_mode); if (real_mode->screen_info.orig_video_mode == 7) { vidmem = (char *) 0xb0000; @@ -405,35 +375,34 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap, lines = real_mode->screen_info.orig_video_lines; cols = real_mode->screen_info.orig_video_cols; - window = output; /* Output buffer (Normally at 1M) */ + console_init(); + debug_putstr("early console in decompress_kernel\n"); + free_mem_ptr = heap; /* Heap */ free_mem_end_ptr = heap + BOOT_HEAP_SIZE; - inbuf = input_data; /* Input buffer */ - insize = input_len; - inptr = 0; + output = choose_kernel_location(input_data, input_len, + output, output_len); + + /* Validate memory location choices. */ + if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1)) + error("Destination address inappropriately aligned"); #ifdef CONFIG_X86_64 - if ((unsigned long)output & (__KERNEL_ALIGN - 1)) - error("Destination address not 2M aligned"); - if ((unsigned long)output >= 0xffffffffffUL) + if (heap > 0x3fffffffffffUL) error("Destination address too large"); #else - if ((u32)output & (CONFIG_PHYSICAL_ALIGN - 1)) - error("Destination address not CONFIG_PHYSICAL_ALIGN aligned"); - if (heap > ((-__PAGE_OFFSET-(512<<20)-1) & 0x7fffffff)) + if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff)) error("Destination address too large"); +#endif #ifndef CONFIG_RELOCATABLE - if ((u32)output != LOAD_PHYSICAL_ADDR) + if ((unsigned long)output != LOAD_PHYSICAL_ADDR) error("Wrong destination address"); #endif -#endif - makecrc(); - if (!quiet) - putstr("\nDecompressing Linux... "); - gunzip(); + debug_putstr("\nDecompressing Linux... "); + decompress(input_data, input_len, NULL, NULL, output, NULL, error); parse_elf(output); - if (!quiet) - putstr("done.\nBooting the kernel.\n"); - return; + handle_relocations(output, output_len); + debug_putstr("done.\nBooting the kernel.\n"); + return output; } |
