1778cb929SIan Campbell /* 2778cb929SIan Campbell * misc.c 3778cb929SIan Campbell * 4778cb929SIan Campbell * This is a collection of several routines from gzip-1.0.3 5778cb929SIan Campbell * adapted for Linux. 6778cb929SIan Campbell * 7778cb929SIan Campbell * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 8778cb929SIan Campbell * puts by Nick Holloway 1993, better puts by Martin Mares 1995 9778cb929SIan Campbell * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 10778cb929SIan Campbell */ 11778cb929SIan Campbell 128fee13a4SYinghai Lu #include "misc.h" 13820e8fecSVivek Goyal #include "../string.h" 14778cb929SIan Campbell 15778cb929SIan Campbell /* WARNING!! 16778cb929SIan Campbell * This code is compiled with -fPIC and it is relocated dynamically 17778cb929SIan Campbell * at run time, but no relocation processing is performed. 18778cb929SIan Campbell * This means that it is not safe to place pointers in static structures. 19778cb929SIan Campbell */ 20778cb929SIan Campbell 21778cb929SIan Campbell /* 22778cb929SIan Campbell * Getting to provable safe in place decompression is hard. 23778cb929SIan Campbell * Worst case behaviours need to be analyzed. 24778cb929SIan Campbell * Background information: 25778cb929SIan Campbell * 26778cb929SIan Campbell * The file layout is: 27778cb929SIan Campbell * magic[2] 28778cb929SIan Campbell * method[1] 29778cb929SIan Campbell * flags[1] 30778cb929SIan Campbell * timestamp[4] 31778cb929SIan Campbell * extraflags[1] 32778cb929SIan Campbell * os[1] 33778cb929SIan Campbell * compressed data blocks[N] 34778cb929SIan Campbell * crc[4] orig_len[4] 35778cb929SIan Campbell * 36778cb929SIan Campbell * resulting in 18 bytes of non compressed data overhead. 37778cb929SIan Campbell * 38778cb929SIan Campbell * Files divided into blocks 39778cb929SIan Campbell * 1 bit (last block flag) 40778cb929SIan Campbell * 2 bits (block type) 41778cb929SIan Campbell * 421180e01dSIngo Molnar * 1 block occurs every 32K -1 bytes or when there 50% compression 431180e01dSIngo Molnar * has been achieved. The smallest block type encoding is always used. 44778cb929SIan Campbell * 45778cb929SIan Campbell * stored: 46778cb929SIan Campbell * 32 bits length in bytes. 47778cb929SIan Campbell * 48778cb929SIan Campbell * fixed: 49778cb929SIan Campbell * magic fixed tree. 50778cb929SIan Campbell * symbols. 51778cb929SIan Campbell * 52778cb929SIan Campbell * dynamic: 53778cb929SIan Campbell * dynamic tree encoding. 54778cb929SIan Campbell * symbols. 55778cb929SIan Campbell * 56778cb929SIan Campbell * 57778cb929SIan Campbell * The buffer for decompression in place is the length of the 58778cb929SIan Campbell * uncompressed data, plus a small amount extra to keep the algorithm safe. 59778cb929SIan Campbell * The compressed data is placed at the end of the buffer. The output 60778cb929SIan Campbell * pointer is placed at the start of the buffer and the input pointer 61778cb929SIan Campbell * is placed where the compressed data starts. Problems will occur 62778cb929SIan Campbell * when the output pointer overruns the input pointer. 63778cb929SIan Campbell * 64778cb929SIan Campbell * The output pointer can only overrun the input pointer if the input 65778cb929SIan Campbell * pointer is moving faster than the output pointer. A condition only 66778cb929SIan Campbell * triggered by data whose compressed form is larger than the uncompressed 67778cb929SIan Campbell * form. 68778cb929SIan Campbell * 69778cb929SIan Campbell * The worst case at the block level is a growth of the compressed data 70778cb929SIan Campbell * of 5 bytes per 32767 bytes. 71778cb929SIan Campbell * 72778cb929SIan Campbell * The worst case internal to a compressed block is very hard to figure. 73778cb929SIan Campbell * The worst case can at least be boundined by having one bit that represents 74778cb929SIan Campbell * 32764 bytes and then all of the rest of the bytes representing the very 75778cb929SIan Campbell * very last byte. 76778cb929SIan Campbell * 77778cb929SIan Campbell * All of which is enough to compute an amount of extra data that is required 78778cb929SIan Campbell * to be safe. To avoid problems at the block level allocating 5 extra bytes 791180e01dSIngo Molnar * per 32767 bytes of data is sufficient. To avoind problems internal to a 801180e01dSIngo Molnar * block adding an extra 32767 bytes (the worst case uncompressed block size) 811180e01dSIngo Molnar * is sufficient, to ensure that in the worst case the decompressed data for 82778cb929SIan Campbell * block will stop the byte before the compressed data for a block begins. 83778cb929SIan Campbell * To avoid problems with the compressed data's meta information an extra 18 84778cb929SIan Campbell * bytes are needed. Leading to the formula: 85778cb929SIan Campbell * 86778cb929SIan Campbell * extra_bytes = (uncompressed_size >> 12) + 32768 + 18 + decompressor_size. 87778cb929SIan Campbell * 88778cb929SIan Campbell * Adding 8 bytes per 32K is a bit excessive but much easier to calculate. 89778cb929SIan Campbell * Adding 32768 instead of 32767 just makes for round numbers. 90778cb929SIan Campbell * Adding the decompressor_size is necessary as it musht live after all 91778cb929SIan Campbell * of the data as well. Last I measured the decompressor is about 14K. 92778cb929SIan Campbell * 10K of actual data and 4K of bss. 93778cb929SIan Campbell * 94778cb929SIan Campbell */ 95778cb929SIan Campbell 96778cb929SIan Campbell /* 97778cb929SIan Campbell * gzip declarations 98778cb929SIan Campbell */ 99778cb929SIan Campbell #define STATIC static 100778cb929SIan Campbell 101778cb929SIan Campbell #undef memcpy 10204999550SVivek Goyal 10304999550SVivek Goyal /* 10404999550SVivek Goyal * Use a normal definition of memset() from string.c. There are already 10504999550SVivek Goyal * included header files which expect a definition of memset() and by 10604999550SVivek Goyal * the time we define memset macro, it is too late. 10704999550SVivek Goyal */ 10804999550SVivek Goyal #undef memset 109778cb929SIan Campbell #define memzero(s, n) memset((s), 0, (n)) 110778cb929SIan Campbell 111778cb929SIan Campbell 112778cb929SIan Campbell static void error(char *m); 113778cb929SIan Campbell 114778cb929SIan Campbell /* 115778cb929SIan Campbell * This is set up by the setup-routine at boot-time 116778cb929SIan Campbell */ 1178fee13a4SYinghai Lu struct boot_params *real_mode; /* Pointer to real-mode data */ 118778cb929SIan Campbell 11982fa9637SKees Cook memptr free_mem_ptr; 12082fa9637SKees Cook memptr free_mem_end_ptr; 121778cb929SIan Campbell 12203056c88SAlexander van Heukelum static char *vidmem; 123778cb929SIan Campbell static int vidport; 124778cb929SIan Campbell static int lines, cols; 125778cb929SIan Campbell 126ae03c499SAlain Knaff #ifdef CONFIG_KERNEL_GZIP 127ae03c499SAlain Knaff #include "../../../../lib/decompress_inflate.c" 128ae03c499SAlain Knaff #endif 129ae03c499SAlain Knaff 130ae03c499SAlain Knaff #ifdef CONFIG_KERNEL_BZIP2 131ae03c499SAlain Knaff #include "../../../../lib/decompress_bunzip2.c" 132ae03c499SAlain Knaff #endif 133ae03c499SAlain Knaff 134ae03c499SAlain Knaff #ifdef CONFIG_KERNEL_LZMA 135ae03c499SAlain Knaff #include "../../../../lib/decompress_unlzma.c" 136ae03c499SAlain Knaff #endif 137778cb929SIan Campbell 13830314804SLasse Collin #ifdef CONFIG_KERNEL_XZ 13930314804SLasse Collin #include "../../../../lib/decompress_unxz.c" 14030314804SLasse Collin #endif 14130314804SLasse Collin 14213510997SAlbin Tonnerre #ifdef CONFIG_KERNEL_LZO 14313510997SAlbin Tonnerre #include "../../../../lib/decompress_unlzo.c" 14413510997SAlbin Tonnerre #endif 14513510997SAlbin Tonnerre 146f9b493acSKyungsik Lee #ifdef CONFIG_KERNEL_LZ4 147f9b493acSKyungsik Lee #include "../../../../lib/decompress_unlz4.c" 148f9b493acSKyungsik Lee #endif 149f9b493acSKyungsik Lee 150778cb929SIan Campbell static void scroll(void) 151778cb929SIan Campbell { 152778cb929SIan Campbell int i; 153778cb929SIan Campbell 154778cb929SIan Campbell memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2); 155778cb929SIan Campbell for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2) 156778cb929SIan Campbell vidmem[i] = ' '; 157778cb929SIan Campbell } 158778cb929SIan Campbell 1598fee13a4SYinghai Lu #define XMTRDY 0x20 1608fee13a4SYinghai Lu 1618fee13a4SYinghai Lu #define TXR 0 /* Transmit register (WRITE) */ 1628fee13a4SYinghai Lu #define LSR 5 /* Line Status */ 1638fee13a4SYinghai Lu static void serial_putchar(int ch) 1648fee13a4SYinghai Lu { 1658fee13a4SYinghai Lu unsigned timeout = 0xffff; 1668fee13a4SYinghai Lu 1678fee13a4SYinghai Lu while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) 1688fee13a4SYinghai Lu cpu_relax(); 1698fee13a4SYinghai Lu 1708fee13a4SYinghai Lu outb(ch, early_serial_base + TXR); 1718fee13a4SYinghai Lu } 1728fee13a4SYinghai Lu 1737aac3015SJoe Millenbach void __putstr(const char *s) 174778cb929SIan Campbell { 175778cb929SIan Campbell int x, y, pos; 176778cb929SIan Campbell char c; 177778cb929SIan Campbell 1788fee13a4SYinghai Lu if (early_serial_base) { 1798fee13a4SYinghai Lu const char *str = s; 1808fee13a4SYinghai Lu while (*str) { 1818fee13a4SYinghai Lu if (*str == '\n') 1828fee13a4SYinghai Lu serial_putchar('\r'); 1838fee13a4SYinghai Lu serial_putchar(*str++); 1848fee13a4SYinghai Lu } 1858fee13a4SYinghai Lu } 1866bcb13b3SBen Collins 18723968f71SKristian Høgsberg if (real_mode->screen_info.orig_video_mode == 0 && 18823968f71SKristian Høgsberg lines == 0 && cols == 0) 189778cb929SIan Campbell return; 190778cb929SIan Campbell 19123968f71SKristian Høgsberg x = real_mode->screen_info.orig_x; 19223968f71SKristian Høgsberg y = real_mode->screen_info.orig_y; 193778cb929SIan Campbell 194778cb929SIan Campbell while ((c = *s++) != '\0') { 195778cb929SIan Campbell if (c == '\n') { 196778cb929SIan Campbell x = 0; 197778cb929SIan Campbell if (++y >= lines) { 198778cb929SIan Campbell scroll(); 199778cb929SIan Campbell y--; 200778cb929SIan Campbell } 201778cb929SIan Campbell } else { 202778cb929SIan Campbell vidmem[(x + cols * y) * 2] = c; 203778cb929SIan Campbell if (++x >= cols) { 204778cb929SIan Campbell x = 0; 205778cb929SIan Campbell if (++y >= lines) { 206778cb929SIan Campbell scroll(); 207778cb929SIan Campbell y--; 208778cb929SIan Campbell } 209778cb929SIan Campbell } 210778cb929SIan Campbell } 211778cb929SIan Campbell } 212778cb929SIan Campbell 21323968f71SKristian Høgsberg real_mode->screen_info.orig_x = x; 21423968f71SKristian Høgsberg real_mode->screen_info.orig_y = y; 215778cb929SIan Campbell 216778cb929SIan Campbell pos = (x + cols * y) * 2; /* Update cursor position */ 217778cb929SIan Campbell outb(14, vidport); 218778cb929SIan Campbell outb(0xff & (pos >> 9), vidport+1); 219778cb929SIan Campbell outb(15, vidport); 220778cb929SIan Campbell outb(0xff & (pos >> 1), vidport+1); 221778cb929SIan Campbell } 222778cb929SIan Campbell 223778cb929SIan Campbell static void error(char *x) 224778cb929SIan Campbell { 225cb454fe1SJoe Millenbach error_putstr("\n\n"); 226cb454fe1SJoe Millenbach error_putstr(x); 227cb454fe1SJoe Millenbach error_putstr("\n\n -- System halted"); 228778cb929SIan Campbell 229778cb929SIan Campbell while (1) 230778cb929SIan Campbell asm("hlt"); 231778cb929SIan Campbell } 232778cb929SIan Campbell 233a0215061SKees Cook #if CONFIG_X86_NEED_RELOCS 234a0215061SKees Cook static void handle_relocations(void *output, unsigned long output_len) 235a0215061SKees Cook { 236a0215061SKees Cook int *reloc; 237a0215061SKees Cook unsigned long delta, map, ptr; 238a0215061SKees Cook unsigned long min_addr = (unsigned long)output; 239a0215061SKees Cook unsigned long max_addr = min_addr + output_len; 240a0215061SKees Cook 241a0215061SKees Cook /* 242a0215061SKees Cook * Calculate the delta between where vmlinux was linked to load 243a0215061SKees Cook * and where it was actually loaded. 244a0215061SKees Cook */ 245a0215061SKees Cook delta = min_addr - LOAD_PHYSICAL_ADDR; 246a0215061SKees Cook if (!delta) { 247a0215061SKees Cook debug_putstr("No relocation needed... "); 248a0215061SKees Cook return; 249a0215061SKees Cook } 250a0215061SKees Cook debug_putstr("Performing relocations... "); 251a0215061SKees Cook 252a0215061SKees Cook /* 253a0215061SKees Cook * The kernel contains a table of relocation addresses. Those 254a0215061SKees Cook * addresses have the final load address of the kernel in virtual 255a0215061SKees Cook * memory. We are currently working in the self map. So we need to 256a0215061SKees Cook * create an adjustment for kernel memory addresses to the self map. 257a0215061SKees Cook * This will involve subtracting out the base address of the kernel. 258a0215061SKees Cook */ 259a0215061SKees Cook map = delta - __START_KERNEL_map; 260a0215061SKees Cook 261a0215061SKees Cook /* 262a0215061SKees Cook * Process relocations: 32 bit relocations first then 64 bit after. 263a0215061SKees Cook * Two sets of binary relocations are added to the end of the kernel 264a0215061SKees Cook * before compression. Each relocation table entry is the kernel 265a0215061SKees Cook * address of the location which needs to be updated stored as a 266a0215061SKees Cook * 32-bit value which is sign extended to 64 bits. 267a0215061SKees Cook * 268a0215061SKees Cook * Format is: 269a0215061SKees Cook * 270a0215061SKees Cook * kernel bits... 271a0215061SKees Cook * 0 - zero terminator for 64 bit relocations 272a0215061SKees Cook * 64 bit relocation repeated 273a0215061SKees Cook * 0 - zero terminator for 32 bit relocations 274a0215061SKees Cook * 32 bit relocation repeated 275a0215061SKees Cook * 276a0215061SKees Cook * So we work backwards from the end of the decompressed image. 277a0215061SKees Cook */ 278a0215061SKees Cook for (reloc = output + output_len - sizeof(*reloc); *reloc; reloc--) { 279a0215061SKees Cook int extended = *reloc; 280a0215061SKees Cook extended += map; 281a0215061SKees Cook 282a0215061SKees Cook ptr = (unsigned long)extended; 283a0215061SKees Cook if (ptr < min_addr || ptr > max_addr) 284a0215061SKees Cook error("32-bit relocation outside of kernel!\n"); 285a0215061SKees Cook 286a0215061SKees Cook *(uint32_t *)ptr += delta; 287a0215061SKees Cook } 288a0215061SKees Cook #ifdef CONFIG_X86_64 289a0215061SKees Cook for (reloc--; *reloc; reloc--) { 290a0215061SKees Cook long extended = *reloc; 291a0215061SKees Cook extended += map; 292a0215061SKees Cook 293a0215061SKees Cook ptr = (unsigned long)extended; 294a0215061SKees Cook if (ptr < min_addr || ptr > max_addr) 295a0215061SKees Cook error("64-bit relocation outside of kernel!\n"); 296a0215061SKees Cook 297a0215061SKees Cook *(uint64_t *)ptr += delta; 298a0215061SKees Cook } 299a0215061SKees Cook #endif 300a0215061SKees Cook } 301a0215061SKees Cook #else 302a0215061SKees Cook static inline void handle_relocations(void *output, unsigned long output_len) 303a0215061SKees Cook { } 304a0215061SKees Cook #endif 305a0215061SKees Cook 306099e1377SIan Campbell static void parse_elf(void *output) 307099e1377SIan Campbell { 308099e1377SIan Campbell #ifdef CONFIG_X86_64 309099e1377SIan Campbell Elf64_Ehdr ehdr; 310099e1377SIan Campbell Elf64_Phdr *phdrs, *phdr; 311099e1377SIan Campbell #else 312099e1377SIan Campbell Elf32_Ehdr ehdr; 313099e1377SIan Campbell Elf32_Phdr *phdrs, *phdr; 314099e1377SIan Campbell #endif 315099e1377SIan Campbell void *dest; 316099e1377SIan Campbell int i; 317099e1377SIan Campbell 318099e1377SIan Campbell memcpy(&ehdr, output, sizeof(ehdr)); 319099e1377SIan Campbell if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || 320099e1377SIan Campbell ehdr.e_ident[EI_MAG1] != ELFMAG1 || 321099e1377SIan Campbell ehdr.e_ident[EI_MAG2] != ELFMAG2 || 322fd77c7caSPaolo Ciarrocchi ehdr.e_ident[EI_MAG3] != ELFMAG3) { 323099e1377SIan Campbell error("Kernel is not a valid ELF file"); 324099e1377SIan Campbell return; 325099e1377SIan Campbell } 326099e1377SIan Campbell 327e605a425SJoe Millenbach debug_putstr("Parsing ELF... "); 328099e1377SIan Campbell 329099e1377SIan Campbell phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum); 330099e1377SIan Campbell if (!phdrs) 331099e1377SIan Campbell error("Failed to allocate space for phdrs"); 332099e1377SIan Campbell 333099e1377SIan Campbell memcpy(phdrs, output + ehdr.e_phoff, sizeof(*phdrs) * ehdr.e_phnum); 334099e1377SIan Campbell 335099e1377SIan Campbell for (i = 0; i < ehdr.e_phnum; i++) { 336099e1377SIan Campbell phdr = &phdrs[i]; 337099e1377SIan Campbell 338099e1377SIan Campbell switch (phdr->p_type) { 339099e1377SIan Campbell case PT_LOAD: 340099e1377SIan Campbell #ifdef CONFIG_RELOCATABLE 341099e1377SIan Campbell dest = output; 342099e1377SIan Campbell dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR); 343099e1377SIan Campbell #else 344099e1377SIan Campbell dest = (void *)(phdr->p_paddr); 345099e1377SIan Campbell #endif 346099e1377SIan Campbell memcpy(dest, 347099e1377SIan Campbell output + phdr->p_offset, 348099e1377SIan Campbell phdr->p_filesz); 349099e1377SIan Campbell break; 350099e1377SIan Campbell default: /* Ignore other PT_* */ break; 351099e1377SIan Campbell } 352099e1377SIan Campbell } 3535067cf53SJesper Juhl 3545067cf53SJesper Juhl free(phdrs); 355099e1377SIan Campbell } 356099e1377SIan Campbell 357*2605fc21SAndi Kleen asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap, 3581180e01dSIngo Molnar unsigned char *input_data, 3591180e01dSIngo Molnar unsigned long input_len, 360a0215061SKees Cook unsigned char *output, 361a0215061SKees Cook unsigned long output_len) 362778cb929SIan Campbell { 363778cb929SIan Campbell real_mode = rmode; 364778cb929SIan Campbell 3655dcd14ecSH. Peter Anvin sanitize_boot_params(real_mode); 3665dcd14ecSH. Peter Anvin 36723968f71SKristian Høgsberg if (real_mode->screen_info.orig_video_mode == 7) { 368778cb929SIan Campbell vidmem = (char *) 0xb0000; 369778cb929SIan Campbell vidport = 0x3b4; 370778cb929SIan Campbell } else { 371778cb929SIan Campbell vidmem = (char *) 0xb8000; 372778cb929SIan Campbell vidport = 0x3d4; 373778cb929SIan Campbell } 374778cb929SIan Campbell 37523968f71SKristian Høgsberg lines = real_mode->screen_info.orig_video_lines; 37623968f71SKristian Høgsberg cols = real_mode->screen_info.orig_video_cols; 377778cb929SIan Campbell 3788fee13a4SYinghai Lu console_init(); 379e605a425SJoe Millenbach debug_putstr("early console in decompress_kernel\n"); 3808fee13a4SYinghai Lu 381778cb929SIan Campbell free_mem_ptr = heap; /* Heap */ 3827c539764SAlexander van Heukelum free_mem_end_ptr = heap + BOOT_HEAP_SIZE; 383778cb929SIan Campbell 3848ab3820fSKees Cook output = choose_kernel_location(input_data, input_len, 3858ab3820fSKees Cook output, output_len); 3868ab3820fSKees Cook 3878ab3820fSKees Cook /* Validate memory location choices. */ 3887ed42a28SH. Peter Anvin if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1)) 3897ed42a28SH. Peter Anvin error("Destination address inappropriately aligned"); 390778cb929SIan Campbell #ifdef CONFIG_X86_64 3917ed42a28SH. Peter Anvin if (heap > 0x3fffffffffffUL) 392778cb929SIan Campbell error("Destination address too large"); 393778cb929SIan Campbell #else 394147dd561SH. Peter Anvin if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff)) 395778cb929SIan Campbell error("Destination address too large"); 396778cb929SIan Campbell #endif 3977ed42a28SH. Peter Anvin #ifndef CONFIG_RELOCATABLE 3987ed42a28SH. Peter Anvin if ((unsigned long)output != LOAD_PHYSICAL_ADDR) 3997ed42a28SH. Peter Anvin error("Wrong destination address"); 400778cb929SIan Campbell #endif 401778cb929SIan Campbell 402e605a425SJoe Millenbach debug_putstr("\nDecompressing Linux... "); 403ae03c499SAlain Knaff decompress(input_data, input_len, NULL, NULL, output, NULL, error); 404099e1377SIan Campbell parse_elf(output); 405a0215061SKees Cook handle_relocations(output, output_len); 406e605a425SJoe Millenbach debug_putstr("done.\nBooting the kernel.\n"); 4078ab3820fSKees Cook return output; 408778cb929SIan Campbell } 409