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" 13778cb929SIan Campbell 14778cb929SIan Campbell /* WARNING!! 15778cb929SIan Campbell * This code is compiled with -fPIC and it is relocated dynamically 16778cb929SIan Campbell * at run time, but no relocation processing is performed. 17778cb929SIan Campbell * This means that it is not safe to place pointers in static structures. 18778cb929SIan Campbell */ 19778cb929SIan Campbell 20778cb929SIan Campbell /* 21778cb929SIan Campbell * Getting to provable safe in place decompression is hard. 22778cb929SIan Campbell * Worst case behaviours need to be analyzed. 23778cb929SIan Campbell * Background information: 24778cb929SIan Campbell * 25778cb929SIan Campbell * The file layout is: 26778cb929SIan Campbell * magic[2] 27778cb929SIan Campbell * method[1] 28778cb929SIan Campbell * flags[1] 29778cb929SIan Campbell * timestamp[4] 30778cb929SIan Campbell * extraflags[1] 31778cb929SIan Campbell * os[1] 32778cb929SIan Campbell * compressed data blocks[N] 33778cb929SIan Campbell * crc[4] orig_len[4] 34778cb929SIan Campbell * 35778cb929SIan Campbell * resulting in 18 bytes of non compressed data overhead. 36778cb929SIan Campbell * 37778cb929SIan Campbell * Files divided into blocks 38778cb929SIan Campbell * 1 bit (last block flag) 39778cb929SIan Campbell * 2 bits (block type) 40778cb929SIan Campbell * 411180e01dSIngo Molnar * 1 block occurs every 32K -1 bytes or when there 50% compression 421180e01dSIngo Molnar * has been achieved. The smallest block type encoding is always used. 43778cb929SIan Campbell * 44778cb929SIan Campbell * stored: 45778cb929SIan Campbell * 32 bits length in bytes. 46778cb929SIan Campbell * 47778cb929SIan Campbell * fixed: 48778cb929SIan Campbell * magic fixed tree. 49778cb929SIan Campbell * symbols. 50778cb929SIan Campbell * 51778cb929SIan Campbell * dynamic: 52778cb929SIan Campbell * dynamic tree encoding. 53778cb929SIan Campbell * symbols. 54778cb929SIan Campbell * 55778cb929SIan Campbell * 56778cb929SIan Campbell * The buffer for decompression in place is the length of the 57778cb929SIan Campbell * uncompressed data, plus a small amount extra to keep the algorithm safe. 58778cb929SIan Campbell * The compressed data is placed at the end of the buffer. The output 59778cb929SIan Campbell * pointer is placed at the start of the buffer and the input pointer 60778cb929SIan Campbell * is placed where the compressed data starts. Problems will occur 61778cb929SIan Campbell * when the output pointer overruns the input pointer. 62778cb929SIan Campbell * 63778cb929SIan Campbell * The output pointer can only overrun the input pointer if the input 64778cb929SIan Campbell * pointer is moving faster than the output pointer. A condition only 65778cb929SIan Campbell * triggered by data whose compressed form is larger than the uncompressed 66778cb929SIan Campbell * form. 67778cb929SIan Campbell * 68778cb929SIan Campbell * The worst case at the block level is a growth of the compressed data 69778cb929SIan Campbell * of 5 bytes per 32767 bytes. 70778cb929SIan Campbell * 71778cb929SIan Campbell * The worst case internal to a compressed block is very hard to figure. 72778cb929SIan Campbell * The worst case can at least be boundined by having one bit that represents 73778cb929SIan Campbell * 32764 bytes and then all of the rest of the bytes representing the very 74778cb929SIan Campbell * very last byte. 75778cb929SIan Campbell * 76778cb929SIan Campbell * All of which is enough to compute an amount of extra data that is required 77778cb929SIan Campbell * to be safe. To avoid problems at the block level allocating 5 extra bytes 781180e01dSIngo Molnar * per 32767 bytes of data is sufficient. To avoind problems internal to a 791180e01dSIngo Molnar * block adding an extra 32767 bytes (the worst case uncompressed block size) 801180e01dSIngo Molnar * is sufficient, to ensure that in the worst case the decompressed data for 81778cb929SIan Campbell * block will stop the byte before the compressed data for a block begins. 82778cb929SIan Campbell * To avoid problems with the compressed data's meta information an extra 18 83778cb929SIan Campbell * bytes are needed. Leading to the formula: 84778cb929SIan Campbell * 85778cb929SIan Campbell * extra_bytes = (uncompressed_size >> 12) + 32768 + 18 + decompressor_size. 86778cb929SIan Campbell * 87778cb929SIan Campbell * Adding 8 bytes per 32K is a bit excessive but much easier to calculate. 88778cb929SIan Campbell * Adding 32768 instead of 32767 just makes for round numbers. 89778cb929SIan Campbell * Adding the decompressor_size is necessary as it musht live after all 90778cb929SIan Campbell * of the data as well. Last I measured the decompressor is about 14K. 91778cb929SIan Campbell * 10K of actual data and 4K of bss. 92778cb929SIan Campbell * 93778cb929SIan Campbell */ 94778cb929SIan Campbell 95778cb929SIan Campbell /* 96778cb929SIan Campbell * gzip declarations 97778cb929SIan Campbell */ 98778cb929SIan Campbell #define STATIC static 99778cb929SIan Campbell 100778cb929SIan Campbell #undef memset 101778cb929SIan Campbell #undef memcpy 102778cb929SIan Campbell #define memzero(s, n) memset((s), 0, (n)) 103778cb929SIan Campbell 104778cb929SIan Campbell 105778cb929SIan Campbell static void error(char *m); 106778cb929SIan Campbell 107778cb929SIan Campbell /* 108778cb929SIan Campbell * This is set up by the setup-routine at boot-time 109778cb929SIan Campbell */ 1108fee13a4SYinghai Lu struct boot_params *real_mode; /* Pointer to real-mode data */ 1118fee13a4SYinghai Lu static int debug; 112778cb929SIan Campbell 1136175ddf0SBrian Gerst void *memset(void *s, int c, size_t n); 1146175ddf0SBrian Gerst void *memcpy(void *dest, const void *src, size_t n); 115778cb929SIan Campbell 116778cb929SIan Campbell #ifdef CONFIG_X86_64 117778cb929SIan Campbell #define memptr long 118778cb929SIan Campbell #else 119778cb929SIan Campbell #define memptr unsigned 120778cb929SIan Campbell #endif 121778cb929SIan Campbell 122778cb929SIan Campbell static memptr free_mem_ptr; 123778cb929SIan Campbell static memptr free_mem_end_ptr; 124778cb929SIan Campbell 12503056c88SAlexander van Heukelum static char *vidmem; 126778cb929SIan Campbell static int vidport; 127778cb929SIan Campbell static int lines, cols; 128778cb929SIan Campbell 129ae03c499SAlain Knaff #ifdef CONFIG_KERNEL_GZIP 130ae03c499SAlain Knaff #include "../../../../lib/decompress_inflate.c" 131ae03c499SAlain Knaff #endif 132ae03c499SAlain Knaff 133ae03c499SAlain Knaff #ifdef CONFIG_KERNEL_BZIP2 134ae03c499SAlain Knaff #include "../../../../lib/decompress_bunzip2.c" 135ae03c499SAlain Knaff #endif 136ae03c499SAlain Knaff 137ae03c499SAlain Knaff #ifdef CONFIG_KERNEL_LZMA 138ae03c499SAlain Knaff #include "../../../../lib/decompress_unlzma.c" 139ae03c499SAlain Knaff #endif 140778cb929SIan Campbell 14130314804SLasse Collin #ifdef CONFIG_KERNEL_XZ 14230314804SLasse Collin #include "../../../../lib/decompress_unxz.c" 14330314804SLasse Collin #endif 14430314804SLasse Collin 14513510997SAlbin Tonnerre #ifdef CONFIG_KERNEL_LZO 14613510997SAlbin Tonnerre #include "../../../../lib/decompress_unlzo.c" 14713510997SAlbin Tonnerre #endif 14813510997SAlbin Tonnerre 149778cb929SIan Campbell static void scroll(void) 150778cb929SIan Campbell { 151778cb929SIan Campbell int i; 152778cb929SIan Campbell 153778cb929SIan Campbell memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2); 154778cb929SIan Campbell for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2) 155778cb929SIan Campbell vidmem[i] = ' '; 156778cb929SIan Campbell } 157778cb929SIan Campbell 1588fee13a4SYinghai Lu #define XMTRDY 0x20 1598fee13a4SYinghai Lu 1608fee13a4SYinghai Lu #define TXR 0 /* Transmit register (WRITE) */ 1618fee13a4SYinghai Lu #define LSR 5 /* Line Status */ 1628fee13a4SYinghai Lu static void serial_putchar(int ch) 1638fee13a4SYinghai Lu { 1648fee13a4SYinghai Lu unsigned timeout = 0xffff; 1658fee13a4SYinghai Lu 1668fee13a4SYinghai Lu while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) 1678fee13a4SYinghai Lu cpu_relax(); 1688fee13a4SYinghai Lu 1698fee13a4SYinghai Lu outb(ch, early_serial_base + TXR); 1708fee13a4SYinghai Lu } 1718fee13a4SYinghai Lu 1728fee13a4SYinghai Lu void __putstr(int error, const char *s) 173778cb929SIan Campbell { 174778cb929SIan Campbell int x, y, pos; 175778cb929SIan Campbell char c; 176778cb929SIan Campbell 1776bcb13b3SBen Collins #ifndef CONFIG_X86_VERBOSE_BOOTUP 1786bcb13b3SBen Collins if (!error) 1796bcb13b3SBen Collins return; 1806bcb13b3SBen Collins #endif 1818fee13a4SYinghai Lu if (early_serial_base) { 1828fee13a4SYinghai Lu const char *str = s; 1838fee13a4SYinghai Lu while (*str) { 1848fee13a4SYinghai Lu if (*str == '\n') 1858fee13a4SYinghai Lu serial_putchar('\r'); 1868fee13a4SYinghai Lu serial_putchar(*str++); 1878fee13a4SYinghai Lu } 1888fee13a4SYinghai Lu } 1896bcb13b3SBen Collins 19023968f71SKristian Høgsberg if (real_mode->screen_info.orig_video_mode == 0 && 19123968f71SKristian Høgsberg lines == 0 && cols == 0) 192778cb929SIan Campbell return; 193778cb929SIan Campbell 19423968f71SKristian Høgsberg x = real_mode->screen_info.orig_x; 19523968f71SKristian Høgsberg y = real_mode->screen_info.orig_y; 196778cb929SIan Campbell 197778cb929SIan Campbell while ((c = *s++) != '\0') { 198778cb929SIan Campbell if (c == '\n') { 199778cb929SIan Campbell x = 0; 200778cb929SIan Campbell if (++y >= lines) { 201778cb929SIan Campbell scroll(); 202778cb929SIan Campbell y--; 203778cb929SIan Campbell } 204778cb929SIan Campbell } else { 205778cb929SIan Campbell vidmem[(x + cols * y) * 2] = c; 206778cb929SIan Campbell if (++x >= cols) { 207778cb929SIan Campbell x = 0; 208778cb929SIan Campbell if (++y >= lines) { 209778cb929SIan Campbell scroll(); 210778cb929SIan Campbell y--; 211778cb929SIan Campbell } 212778cb929SIan Campbell } 213778cb929SIan Campbell } 214778cb929SIan Campbell } 215778cb929SIan Campbell 21623968f71SKristian Høgsberg real_mode->screen_info.orig_x = x; 21723968f71SKristian Høgsberg real_mode->screen_info.orig_y = y; 218778cb929SIan Campbell 219778cb929SIan Campbell pos = (x + cols * y) * 2; /* Update cursor position */ 220778cb929SIan Campbell outb(14, vidport); 221778cb929SIan Campbell outb(0xff & (pos >> 9), vidport+1); 222778cb929SIan Campbell outb(15, vidport); 223778cb929SIan Campbell outb(0xff & (pos >> 1), vidport+1); 224778cb929SIan Campbell } 225778cb929SIan Campbell 226e605a425SJoe Millenbach static void debug_putstr(const char *s) 227e605a425SJoe Millenbach { 228e605a425SJoe Millenbach if (debug) 229e605a425SJoe Millenbach putstr(s); 230e605a425SJoe Millenbach } 231e605a425SJoe Millenbach 2326175ddf0SBrian Gerst void *memset(void *s, int c, size_t n) 233778cb929SIan Campbell { 234778cb929SIan Campbell int i; 235778cb929SIan Campbell char *ss = s; 236778cb929SIan Campbell 237020878acSPaolo Ciarrocchi for (i = 0; i < n; i++) 238020878acSPaolo Ciarrocchi ss[i] = c; 239778cb929SIan Campbell return s; 240778cb929SIan Campbell } 24168f4d5a0SZhao Yakui #ifdef CONFIG_X86_32 2426175ddf0SBrian Gerst void *memcpy(void *dest, const void *src, size_t n) 243778cb929SIan Campbell { 24468f4d5a0SZhao Yakui int d0, d1, d2; 24568f4d5a0SZhao Yakui asm volatile( 24668f4d5a0SZhao Yakui "rep ; movsl\n\t" 24768f4d5a0SZhao Yakui "movl %4,%%ecx\n\t" 24868f4d5a0SZhao Yakui "rep ; movsb\n\t" 24968f4d5a0SZhao Yakui : "=&c" (d0), "=&D" (d1), "=&S" (d2) 25068f4d5a0SZhao Yakui : "0" (n >> 2), "g" (n & 3), "1" (dest), "2" (src) 25168f4d5a0SZhao Yakui : "memory"); 252778cb929SIan Campbell 253778cb929SIan Campbell return dest; 254778cb929SIan Campbell } 25568f4d5a0SZhao Yakui #else 25668f4d5a0SZhao Yakui void *memcpy(void *dest, const void *src, size_t n) 25768f4d5a0SZhao Yakui { 25868f4d5a0SZhao Yakui long d0, d1, d2; 25968f4d5a0SZhao Yakui asm volatile( 26068f4d5a0SZhao Yakui "rep ; movsq\n\t" 26168f4d5a0SZhao Yakui "movq %4,%%rcx\n\t" 26268f4d5a0SZhao Yakui "rep ; movsb\n\t" 26368f4d5a0SZhao Yakui : "=&c" (d0), "=&D" (d1), "=&S" (d2) 26468f4d5a0SZhao Yakui : "0" (n >> 3), "g" (n & 7), "1" (dest), "2" (src) 26568f4d5a0SZhao Yakui : "memory"); 266778cb929SIan Campbell 26768f4d5a0SZhao Yakui return dest; 26868f4d5a0SZhao Yakui } 26968f4d5a0SZhao Yakui #endif 270778cb929SIan Campbell 271778cb929SIan Campbell static void error(char *x) 272778cb929SIan Campbell { 273*cb454fe1SJoe Millenbach error_putstr("\n\n"); 274*cb454fe1SJoe Millenbach error_putstr(x); 275*cb454fe1SJoe Millenbach error_putstr("\n\n -- System halted"); 276778cb929SIan Campbell 277778cb929SIan Campbell while (1) 278778cb929SIan Campbell asm("hlt"); 279778cb929SIan Campbell } 280778cb929SIan Campbell 281099e1377SIan Campbell static void parse_elf(void *output) 282099e1377SIan Campbell { 283099e1377SIan Campbell #ifdef CONFIG_X86_64 284099e1377SIan Campbell Elf64_Ehdr ehdr; 285099e1377SIan Campbell Elf64_Phdr *phdrs, *phdr; 286099e1377SIan Campbell #else 287099e1377SIan Campbell Elf32_Ehdr ehdr; 288099e1377SIan Campbell Elf32_Phdr *phdrs, *phdr; 289099e1377SIan Campbell #endif 290099e1377SIan Campbell void *dest; 291099e1377SIan Campbell int i; 292099e1377SIan Campbell 293099e1377SIan Campbell memcpy(&ehdr, output, sizeof(ehdr)); 294099e1377SIan Campbell if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || 295099e1377SIan Campbell ehdr.e_ident[EI_MAG1] != ELFMAG1 || 296099e1377SIan Campbell ehdr.e_ident[EI_MAG2] != ELFMAG2 || 297fd77c7caSPaolo Ciarrocchi ehdr.e_ident[EI_MAG3] != ELFMAG3) { 298099e1377SIan Campbell error("Kernel is not a valid ELF file"); 299099e1377SIan Campbell return; 300099e1377SIan Campbell } 301099e1377SIan Campbell 302e605a425SJoe Millenbach debug_putstr("Parsing ELF... "); 303099e1377SIan Campbell 304099e1377SIan Campbell phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum); 305099e1377SIan Campbell if (!phdrs) 306099e1377SIan Campbell error("Failed to allocate space for phdrs"); 307099e1377SIan Campbell 308099e1377SIan Campbell memcpy(phdrs, output + ehdr.e_phoff, sizeof(*phdrs) * ehdr.e_phnum); 309099e1377SIan Campbell 310099e1377SIan Campbell for (i = 0; i < ehdr.e_phnum; i++) { 311099e1377SIan Campbell phdr = &phdrs[i]; 312099e1377SIan Campbell 313099e1377SIan Campbell switch (phdr->p_type) { 314099e1377SIan Campbell case PT_LOAD: 315099e1377SIan Campbell #ifdef CONFIG_RELOCATABLE 316099e1377SIan Campbell dest = output; 317099e1377SIan Campbell dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR); 318099e1377SIan Campbell #else 319099e1377SIan Campbell dest = (void *)(phdr->p_paddr); 320099e1377SIan Campbell #endif 321099e1377SIan Campbell memcpy(dest, 322099e1377SIan Campbell output + phdr->p_offset, 323099e1377SIan Campbell phdr->p_filesz); 324099e1377SIan Campbell break; 325099e1377SIan Campbell default: /* Ignore other PT_* */ break; 326099e1377SIan Campbell } 327099e1377SIan Campbell } 3285067cf53SJesper Juhl 3295067cf53SJesper Juhl free(phdrs); 330099e1377SIan Campbell } 331099e1377SIan Campbell 332778cb929SIan Campbell asmlinkage void decompress_kernel(void *rmode, memptr heap, 3331180e01dSIngo Molnar unsigned char *input_data, 3341180e01dSIngo Molnar unsigned long input_len, 3351180e01dSIngo Molnar unsigned char *output) 336778cb929SIan Campbell { 337778cb929SIan Campbell real_mode = rmode; 338778cb929SIan Campbell 3398fee13a4SYinghai Lu if (cmdline_find_option_bool("debug")) 3408fee13a4SYinghai Lu debug = 1; 3413b6b9293SKristian Høgsberg 34223968f71SKristian Høgsberg if (real_mode->screen_info.orig_video_mode == 7) { 343778cb929SIan Campbell vidmem = (char *) 0xb0000; 344778cb929SIan Campbell vidport = 0x3b4; 345778cb929SIan Campbell } else { 346778cb929SIan Campbell vidmem = (char *) 0xb8000; 347778cb929SIan Campbell vidport = 0x3d4; 348778cb929SIan Campbell } 349778cb929SIan Campbell 35023968f71SKristian Høgsberg lines = real_mode->screen_info.orig_video_lines; 35123968f71SKristian Høgsberg cols = real_mode->screen_info.orig_video_cols; 352778cb929SIan Campbell 3538fee13a4SYinghai Lu console_init(); 354e605a425SJoe Millenbach debug_putstr("early console in decompress_kernel\n"); 3558fee13a4SYinghai Lu 356778cb929SIan Campbell free_mem_ptr = heap; /* Heap */ 3577c539764SAlexander van Heukelum free_mem_end_ptr = heap + BOOT_HEAP_SIZE; 358778cb929SIan Campbell 3597ed42a28SH. Peter Anvin if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1)) 3607ed42a28SH. Peter Anvin error("Destination address inappropriately aligned"); 361778cb929SIan Campbell #ifdef CONFIG_X86_64 3627ed42a28SH. Peter Anvin if (heap > 0x3fffffffffffUL) 363778cb929SIan Campbell error("Destination address too large"); 364778cb929SIan Campbell #else 365147dd561SH. Peter Anvin if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff)) 366778cb929SIan Campbell error("Destination address too large"); 367778cb929SIan Campbell #endif 3687ed42a28SH. Peter Anvin #ifndef CONFIG_RELOCATABLE 3697ed42a28SH. Peter Anvin if ((unsigned long)output != LOAD_PHYSICAL_ADDR) 3707ed42a28SH. Peter Anvin error("Wrong destination address"); 371778cb929SIan Campbell #endif 372778cb929SIan Campbell 373e605a425SJoe Millenbach debug_putstr("\nDecompressing Linux... "); 374ae03c499SAlain Knaff decompress(input_data, input_len, NULL, NULL, output, NULL, error); 375099e1377SIan Campbell parse_elf(output); 376e605a425SJoe Millenbach debug_putstr("done.\nBooting the kernel.\n"); 377778cb929SIan Campbell return; 378778cb929SIan Campbell } 379