Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384/* * Definitions and wrapper functions for kernel decompressor * * (C) 2017 Helge Deller <deller@gmx.de> */
#include <linux/uaccess.h>#include <linux/elf.h>#include <linux/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 char output_len;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;}
static 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){ if (x) puts(x); puts("\n -- System halted\n"); while (1) /* wait forever */ ;}
static int print_num(unsigned long num, int base){ const char hex[] = "0123456789abcdef"; char str[40]; int i = sizeof(str)-1;
str[i--] = '\0'; do { str[i--] = hex[num % base]; num = num / base; } while (num);
if (base == 16) { str[i--] = 'x'; str[i] = '0'; } else i++; puts(&str[i]);
return 0;}
static 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; print_num(va_arg(args, unsigned long), fmt[i] == 'x' ? 16:10); ++i; }
va_end(args); return 0;}
/* helper functions for libgcc */void abort(void){ error("aborted.");}
#undef mallocstatic void *malloc(size_t size){ return malloc_gzip(size);}
#undef freestatic 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");}
static void parse_elf(void *output){#ifdef CONFIG_64BIT 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)); if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || ehdr.e_ident[EI_MAG1] != ELFMAG1 || ehdr.e_ident[EI_MAG2] != ELFMAG2 || ehdr.e_ident[EI_MAG3] != ELFMAG3) { error("Kernel is not a valid ELF file"); return; }
#ifdef DEBUG printf("Parsing ELF... ");#endif
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);
for (i = 0; i < ehdr.e_phnum; i++) { phdr = &phdrs[i];
switch (phdr->p_type) { case PT_LOAD: dest = (void *)((unsigned long) phdr->p_paddr & (__PAGE_OFFSET_DEFAULT-1)); memmove(dest, output + phdr->p_offset, phdr->p_filesz); break; default: break; } }
free(phdrs);}
/* * The regular get_unaligned_le32 uses __builtin_memcpy which can trigger * warnings when reading a byte/char output_len as an integer, as the size of a * char is less than that of an integer. Use type punning and the packed * attribute, which requires -fno-strict-aliasing, to work around the problem. */static u32 punned_get_unaligned_le32(const void *p){ const struct { __le32 x; } __packed * __get_pptr = p;
return le32_to_cpu(__get_pptr->x);}
asmlinkage unsigned long __visible decompress_kernel(unsigned int started_wide, unsigned int command_line, const unsigned int rd_start, const unsigned int rd_end){ char *output; unsigned long vmlinux_addr, vmlinux_len; unsigned long kernel_addr, kernel_len;
#ifdef CONFIG_64BIT parisc_narrow_firmware = 0;#endif
set_firmware_width_unlocked();
putchar('D'); /* if you get this D and no more, string storage */ /* in $GLOBAL$ is wrong or %dp is wrong */ puts("ecompressing Linux... ");
/* where the final bits are stored */ kernel_addr = KERNEL_BINARY_TEXT_START; kernel_len = __pa(SZ_end) - __pa(SZparisc_kernel_start); if ((unsigned long) &_startcode_end > kernel_addr) error("Bootcode overlaps kernel code");
/* * Calculate addr to where the vmlinux ELF file shall be decompressed. * Assembly code in head.S positioned the stack directly behind bss, so * leave 2 MB for the stack. */ vmlinux_addr = (unsigned long) &_ebss + 2*1024*1024; vmlinux_len = punned_get_unaligned_le32(&output_len); output = (char *) vmlinux_addr;
/* * Initialize free_mem_ptr and free_mem_end_ptr. */ free_mem_ptr = vmlinux_addr + vmlinux_len;
/* 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
if (free_mem_ptr >= free_mem_end_ptr) { int free_ram; free_ram = (free_mem_ptr >> 20) + 1; if (free_ram < 32) free_ram = 32; printf("\nKernel requires at least %d MB RAM.\n", free_ram); error(NULL); }
#ifdef DEBUG printf("\n"); 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", vmlinux_len); printf("kernel_addr = %x\n", kernel_addr); printf("kernel_len = %x\n", kernel_len);#endif
__decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error); parse_elf(output);
output = (char *) kernel_addr; flush_data_cache(output, kernel_len);
printf("done.\nBooting the kernel.\n");
return (unsigned long) output;}