|  | /* | 
|  | * Definitions and wrapper functions for kernel decompressor | 
|  | * | 
|  | *   (C) 2017 Helge Deller <deller@gmx.de> | 
|  | */ | 
|  |  | 
|  | #include <linux/uaccess.h> | 
|  | #include <asm/unaligned.h> | 
|  | #include <asm/page.h> | 
|  | #include "sizes.h" | 
|  |  | 
|  | /* | 
|  | * gzip declarations | 
|  | */ | 
|  | #define STATIC static | 
|  |  | 
|  | #undef memmove | 
|  | #define memmove memmove | 
|  | #define memzero(s, n) memset((s), 0, (n)) | 
|  |  | 
|  | #define malloc	malloc_gzip | 
|  | #define free	free_gzip | 
|  |  | 
|  | /* Symbols defined by linker scripts */ | 
|  | extern char input_data[]; | 
|  | extern int input_len; | 
|  | /* output_len is inserted by the linker possibly at an unaligned address */ | 
|  | extern __le32 output_len __aligned(1); | 
|  | extern char _text, _end; | 
|  | extern char _bss, _ebss; | 
|  | extern char _startcode_end; | 
|  | extern void startup_continue(void *entry, unsigned long cmdline, | 
|  | unsigned long rd_start, unsigned long rd_end) __noreturn; | 
|  |  | 
|  | void error(char *m) __noreturn; | 
|  |  | 
|  | static unsigned long free_mem_ptr; | 
|  | static unsigned long free_mem_end_ptr; | 
|  |  | 
|  | #ifdef CONFIG_KERNEL_GZIP | 
|  | #include "../../../../lib/decompress_inflate.c" | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_KERNEL_BZIP2 | 
|  | #include "../../../../lib/decompress_bunzip2.c" | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_KERNEL_LZ4 | 
|  | #include "../../../../lib/decompress_unlz4.c" | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_KERNEL_LZMA | 
|  | #include "../../../../lib/decompress_unlzma.c" | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_KERNEL_LZO | 
|  | #include "../../../../lib/decompress_unlzo.c" | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_KERNEL_XZ | 
|  | #include "../../../../lib/decompress_unxz.c" | 
|  | #endif | 
|  |  | 
|  | void *memmove(void *dest, const void *src, size_t n) | 
|  | { | 
|  | const char *s = src; | 
|  | char *d = dest; | 
|  |  | 
|  | if (d <= s) { | 
|  | while (n--) | 
|  | *d++ = *s++; | 
|  | } else { | 
|  | d += n; | 
|  | s += n; | 
|  | while (n--) | 
|  | *--d = *--s; | 
|  | } | 
|  | return dest; | 
|  | } | 
|  |  | 
|  | void *memset(void *s, int c, size_t count) | 
|  | { | 
|  | char *xs = (char *)s; | 
|  |  | 
|  | while (count--) | 
|  | *xs++ = c; | 
|  | return s; | 
|  | } | 
|  |  | 
|  | void *memcpy(void *d, const void *s, size_t len) | 
|  | { | 
|  | char *dest = (char *)d; | 
|  | const char *source = (const char *)s; | 
|  |  | 
|  | while (len--) | 
|  | *dest++ = *source++; | 
|  | return d; | 
|  | } | 
|  |  | 
|  | size_t strlen(const char *s) | 
|  | { | 
|  | const char *sc; | 
|  |  | 
|  | for (sc = s; *sc != '\0'; ++sc) | 
|  | ; | 
|  | return sc - s; | 
|  | } | 
|  |  | 
|  | char *strchr(const char *s, int c) | 
|  | { | 
|  | while (*s) { | 
|  | if (*s == (char)c) | 
|  | return (char *)s; | 
|  | ++s; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | int puts(const char *s) | 
|  | { | 
|  | const char *nuline = s; | 
|  |  | 
|  | while ((nuline = strchr(s, '\n')) != NULL) { | 
|  | if (nuline != s) | 
|  | pdc_iodc_print(s, nuline - s); | 
|  | pdc_iodc_print("\r\n", 2); | 
|  | s = nuline + 1; | 
|  | } | 
|  | if (*s != '\0') | 
|  | pdc_iodc_print(s, strlen(s)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int putchar(int c) | 
|  | { | 
|  | char buf[2]; | 
|  |  | 
|  | buf[0] = c; | 
|  | buf[1] = '\0'; | 
|  | puts(buf); | 
|  | return c; | 
|  | } | 
|  |  | 
|  | void __noreturn error(char *x) | 
|  | { | 
|  | puts("\n\n"); | 
|  | puts(x); | 
|  | puts("\n\n -- System halted"); | 
|  | while (1)	/* wait forever */ | 
|  | ; | 
|  | } | 
|  |  | 
|  | static int print_hex(unsigned long num) | 
|  | { | 
|  | const char hex[] = "0123456789abcdef"; | 
|  | char str[40]; | 
|  | int i = sizeof(str)-1; | 
|  |  | 
|  | str[i--] = '\0'; | 
|  | do { | 
|  | str[i--] = hex[num & 0x0f]; | 
|  | num >>= 4; | 
|  | } while (num); | 
|  |  | 
|  | str[i--] = 'x'; | 
|  | str[i] = '0'; | 
|  | puts(&str[i]); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int printf(const char *fmt, ...) | 
|  | { | 
|  | va_list args; | 
|  | int i = 0; | 
|  |  | 
|  | va_start(args, fmt); | 
|  |  | 
|  | while (fmt[i]) { | 
|  | if (fmt[i] != '%') { | 
|  | put: | 
|  | putchar(fmt[i++]); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (fmt[++i] == '%') | 
|  | goto put; | 
|  | ++i; | 
|  | print_hex(va_arg(args, unsigned long)); | 
|  | } | 
|  |  | 
|  | va_end(args); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* helper functions for libgcc */ | 
|  | void abort(void) | 
|  | { | 
|  | error("aborted."); | 
|  | } | 
|  |  | 
|  | #undef malloc | 
|  | void *malloc(size_t size) | 
|  | { | 
|  | return malloc_gzip(size); | 
|  | } | 
|  |  | 
|  | #undef free | 
|  | void free(void *ptr) | 
|  | { | 
|  | return free_gzip(ptr); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void flush_data_cache(char *start, unsigned long length) | 
|  | { | 
|  | char *end = start + length; | 
|  |  | 
|  | do { | 
|  | asm volatile("fdc 0(%0)" : : "r" (start)); | 
|  | asm volatile("fic 0(%%sr0,%0)" : : "r" (start)); | 
|  | start += 16; | 
|  | } while (start < end); | 
|  | asm volatile("fdc 0(%0)" : : "r" (end)); | 
|  |  | 
|  | asm ("sync"); | 
|  | } | 
|  |  | 
|  | unsigned long decompress_kernel(unsigned int started_wide, | 
|  | unsigned int command_line, | 
|  | const unsigned int rd_start, | 
|  | const unsigned int rd_end) | 
|  | { | 
|  | char *output; | 
|  | unsigned long len, len_all; | 
|  |  | 
|  | #ifdef CONFIG_64BIT | 
|  | parisc_narrow_firmware = 0; | 
|  | #endif | 
|  |  | 
|  | set_firmware_width_unlocked(); | 
|  |  | 
|  | putchar('U');	/* if you get this p and no more, string storage */ | 
|  | /* in $GLOBAL$ is wrong or %dp is wrong */ | 
|  | puts("ncompressing ...\n"); | 
|  |  | 
|  | output = (char *) KERNEL_BINARY_TEXT_START; | 
|  | len_all = __pa(SZ_end) - __pa(SZparisc_kernel_start); | 
|  |  | 
|  | if ((unsigned long) &_startcode_end > (unsigned long) output) | 
|  | error("Bootcode overlaps kernel code"); | 
|  |  | 
|  | len = get_unaligned_le32(&output_len); | 
|  | if (len > len_all) | 
|  | error("Output len too big."); | 
|  | else | 
|  | memset(&output[len], 0, len_all - len); | 
|  |  | 
|  | /* | 
|  | * Initialize free_mem_ptr and free_mem_end_ptr. | 
|  | */ | 
|  | free_mem_ptr = (unsigned long) &_ebss; | 
|  | free_mem_ptr += 2*1024*1024;	/* leave 2 MB for stack */ | 
|  |  | 
|  | /* Limit memory for bootoader to 1GB */ | 
|  | #define ARTIFICIAL_LIMIT (1*1024*1024*1024) | 
|  | free_mem_end_ptr = PAGE0->imm_max_mem; | 
|  | if (free_mem_end_ptr > ARTIFICIAL_LIMIT) | 
|  | free_mem_end_ptr = ARTIFICIAL_LIMIT; | 
|  |  | 
|  | #ifdef CONFIG_BLK_DEV_INITRD | 
|  | /* if we have ramdisk this is at end of memory */ | 
|  | if (rd_start && rd_start < free_mem_end_ptr) | 
|  | free_mem_end_ptr = rd_start; | 
|  | #endif | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printf("startcode_end = %x\n", &_startcode_end); | 
|  | printf("commandline   = %x\n", command_line); | 
|  | printf("rd_start      = %x\n", rd_start); | 
|  | printf("rd_end        = %x\n", rd_end); | 
|  |  | 
|  | printf("free_ptr      = %x\n", free_mem_ptr); | 
|  | printf("free_ptr_end  = %x\n", free_mem_end_ptr); | 
|  |  | 
|  | printf("input_data    = %x\n", input_data); | 
|  | printf("input_len     = %x\n", input_len); | 
|  | printf("output        = %x\n", output); | 
|  | printf("output_len    = %x\n", len); | 
|  | printf("output_max    = %x\n", len_all); | 
|  | #endif | 
|  |  | 
|  | __decompress(input_data, input_len, NULL, NULL, | 
|  | output, 0, NULL, error); | 
|  |  | 
|  | flush_data_cache(output, len); | 
|  |  | 
|  | printf("Booting kernel ...\n\n"); | 
|  |  | 
|  | return (unsigned long) output; | 
|  | } |