xref: /openbmc/linux/arch/parisc/boot/compressed/misc.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
12f3c7b81SHelge Deller /*
22f3c7b81SHelge Deller  * Definitions and wrapper functions for kernel decompressor
32f3c7b81SHelge Deller  *
42f3c7b81SHelge Deller  *   (C) 2017 Helge Deller <deller@gmx.de>
52f3c7b81SHelge Deller  */
62f3c7b81SHelge Deller 
72f3c7b81SHelge Deller #include <linux/uaccess.h>
834c201aeSHelge Deller #include <linux/elf.h>
92f3c7b81SHelge Deller #include <asm/unaligned.h>
102f3c7b81SHelge Deller #include <asm/page.h>
112f3c7b81SHelge Deller #include "sizes.h"
122f3c7b81SHelge Deller 
132f3c7b81SHelge Deller /*
142f3c7b81SHelge Deller  * gzip declarations
152f3c7b81SHelge Deller  */
162f3c7b81SHelge Deller #define STATIC static
172f3c7b81SHelge Deller 
182f3c7b81SHelge Deller #undef memmove
192f3c7b81SHelge Deller #define memmove memmove
202f3c7b81SHelge Deller #define memzero(s, n) memset((s), 0, (n))
212f3c7b81SHelge Deller 
222f3c7b81SHelge Deller #define malloc	malloc_gzip
232f3c7b81SHelge Deller #define free	free_gzip
242f3c7b81SHelge Deller 
252f3c7b81SHelge Deller /* Symbols defined by linker scripts */
262f3c7b81SHelge Deller extern char input_data[];
272f3c7b81SHelge Deller extern int input_len;
288c031ba6SHelge Deller /* output_len is inserted by the linker possibly at an unaligned address */
29c42813b7SHelge Deller extern char output_len;
302f3c7b81SHelge Deller extern char _text, _end;
312f3c7b81SHelge Deller extern char _bss, _ebss;
322f3c7b81SHelge Deller extern char _startcode_end;
332f3c7b81SHelge Deller extern void startup_continue(void *entry, unsigned long cmdline,
342f3c7b81SHelge Deller 	unsigned long rd_start, unsigned long rd_end) __noreturn;
352f3c7b81SHelge Deller 
362f3c7b81SHelge Deller void error(char *m) __noreturn;
372f3c7b81SHelge Deller 
382f3c7b81SHelge Deller static unsigned long free_mem_ptr;
392f3c7b81SHelge Deller static unsigned long free_mem_end_ptr;
402f3c7b81SHelge Deller 
412f3c7b81SHelge Deller #ifdef CONFIG_KERNEL_GZIP
422f3c7b81SHelge Deller #include "../../../../lib/decompress_inflate.c"
432f3c7b81SHelge Deller #endif
442f3c7b81SHelge Deller 
452f3c7b81SHelge Deller #ifdef CONFIG_KERNEL_BZIP2
462f3c7b81SHelge Deller #include "../../../../lib/decompress_bunzip2.c"
472f3c7b81SHelge Deller #endif
482f3c7b81SHelge Deller 
492f3c7b81SHelge Deller #ifdef CONFIG_KERNEL_LZ4
502f3c7b81SHelge Deller #include "../../../../lib/decompress_unlz4.c"
512f3c7b81SHelge Deller #endif
522f3c7b81SHelge Deller 
532f3c7b81SHelge Deller #ifdef CONFIG_KERNEL_LZMA
542f3c7b81SHelge Deller #include "../../../../lib/decompress_unlzma.c"
552f3c7b81SHelge Deller #endif
562f3c7b81SHelge Deller 
572f3c7b81SHelge Deller #ifdef CONFIG_KERNEL_LZO
582f3c7b81SHelge Deller #include "../../../../lib/decompress_unlzo.c"
592f3c7b81SHelge Deller #endif
602f3c7b81SHelge Deller 
612f3c7b81SHelge Deller #ifdef CONFIG_KERNEL_XZ
622f3c7b81SHelge Deller #include "../../../../lib/decompress_unxz.c"
632f3c7b81SHelge Deller #endif
642f3c7b81SHelge Deller 
memmove(void * dest,const void * src,size_t n)652f3c7b81SHelge Deller void *memmove(void *dest, const void *src, size_t n)
662f3c7b81SHelge Deller {
672f3c7b81SHelge Deller 	const char *s = src;
682f3c7b81SHelge Deller 	char *d = dest;
692f3c7b81SHelge Deller 
702f3c7b81SHelge Deller 	if (d <= s) {
712f3c7b81SHelge Deller 		while (n--)
722f3c7b81SHelge Deller 			*d++ = *s++;
732f3c7b81SHelge Deller 	} else {
742f3c7b81SHelge Deller 		d += n;
752f3c7b81SHelge Deller 		s += n;
762f3c7b81SHelge Deller 		while (n--)
772f3c7b81SHelge Deller 			*--d = *--s;
782f3c7b81SHelge Deller 	}
792f3c7b81SHelge Deller 	return dest;
802f3c7b81SHelge Deller }
812f3c7b81SHelge Deller 
memset(void * s,int c,size_t count)822f3c7b81SHelge Deller void *memset(void *s, int c, size_t count)
832f3c7b81SHelge Deller {
842f3c7b81SHelge Deller 	char *xs = (char *)s;
852f3c7b81SHelge Deller 
862f3c7b81SHelge Deller 	while (count--)
872f3c7b81SHelge Deller 		*xs++ = c;
882f3c7b81SHelge Deller 	return s;
892f3c7b81SHelge Deller }
902f3c7b81SHelge Deller 
memcpy(void * d,const void * s,size_t len)912f3c7b81SHelge Deller void *memcpy(void *d, const void *s, size_t len)
922f3c7b81SHelge Deller {
932f3c7b81SHelge Deller 	char *dest = (char *)d;
942f3c7b81SHelge Deller 	const char *source = (const char *)s;
952f3c7b81SHelge Deller 
962f3c7b81SHelge Deller 	while (len--)
972f3c7b81SHelge Deller 		*dest++ = *source++;
982f3c7b81SHelge Deller 	return d;
992f3c7b81SHelge Deller }
1002f3c7b81SHelge Deller 
strlen(const char * s)1012f3c7b81SHelge Deller size_t strlen(const char *s)
1022f3c7b81SHelge Deller {
1032f3c7b81SHelge Deller 	const char *sc;
1042f3c7b81SHelge Deller 
1052f3c7b81SHelge Deller 	for (sc = s; *sc != '\0'; ++sc)
1062f3c7b81SHelge Deller 		;
1072f3c7b81SHelge Deller 	return sc - s;
1082f3c7b81SHelge Deller }
1092f3c7b81SHelge Deller 
strchr(const char * s,int c)1102f3c7b81SHelge Deller char *strchr(const char *s, int c)
1112f3c7b81SHelge Deller {
1122f3c7b81SHelge Deller 	while (*s) {
1132f3c7b81SHelge Deller 		if (*s == (char)c)
1142f3c7b81SHelge Deller 			return (char *)s;
1152f3c7b81SHelge Deller 		++s;
1162f3c7b81SHelge Deller 	}
1172f3c7b81SHelge Deller 	return NULL;
1182f3c7b81SHelge Deller }
1192f3c7b81SHelge Deller 
puts(const char * s)120*b967f48dSHelge Deller static int puts(const char *s)
1212f3c7b81SHelge Deller {
1222f3c7b81SHelge Deller 	const char *nuline = s;
1232f3c7b81SHelge Deller 
1242f3c7b81SHelge Deller 	while ((nuline = strchr(s, '\n')) != NULL) {
1252f3c7b81SHelge Deller 		if (nuline != s)
1262f3c7b81SHelge Deller 			pdc_iodc_print(s, nuline - s);
1272f3c7b81SHelge Deller 		pdc_iodc_print("\r\n", 2);
1282f3c7b81SHelge Deller 		s = nuline + 1;
1292f3c7b81SHelge Deller 	}
1302f3c7b81SHelge Deller 	if (*s != '\0')
1312f3c7b81SHelge Deller 		pdc_iodc_print(s, strlen(s));
1322f3c7b81SHelge Deller 
1332f3c7b81SHelge Deller 	return 0;
1342f3c7b81SHelge Deller }
1352f3c7b81SHelge Deller 
putchar(int c)1362f3c7b81SHelge Deller static int putchar(int c)
1372f3c7b81SHelge Deller {
1382f3c7b81SHelge Deller 	char buf[2];
1392f3c7b81SHelge Deller 
1402f3c7b81SHelge Deller 	buf[0] = c;
1412f3c7b81SHelge Deller 	buf[1] = '\0';
1422f3c7b81SHelge Deller 	puts(buf);
1432f3c7b81SHelge Deller 	return c;
1442f3c7b81SHelge Deller }
1452f3c7b81SHelge Deller 
error(char * x)1462f3c7b81SHelge Deller void __noreturn error(char *x)
1472f3c7b81SHelge Deller {
14882d96bf6SHelge Deller 	if (x) puts(x);
14982d96bf6SHelge Deller 	puts("\n -- System halted\n");
1502f3c7b81SHelge Deller 	while (1)	/* wait forever */
1512f3c7b81SHelge Deller 		;
1522f3c7b81SHelge Deller }
1532f3c7b81SHelge Deller 
print_num(unsigned long num,int base)15482d96bf6SHelge Deller static int print_num(unsigned long num, int base)
1552f3c7b81SHelge Deller {
1562f3c7b81SHelge Deller 	const char hex[] = "0123456789abcdef";
1572f3c7b81SHelge Deller 	char str[40];
1582f3c7b81SHelge Deller 	int i = sizeof(str)-1;
1592f3c7b81SHelge Deller 
1602f3c7b81SHelge Deller 	str[i--] = '\0';
1612f3c7b81SHelge Deller 	do {
16282d96bf6SHelge Deller 		str[i--] = hex[num % base];
16382d96bf6SHelge Deller 		num = num / base;
1642f3c7b81SHelge Deller 	} while (num);
1652f3c7b81SHelge Deller 
16682d96bf6SHelge Deller 	if (base == 16) {
1672f3c7b81SHelge Deller 		str[i--] = 'x';
1682f3c7b81SHelge Deller 		str[i] = '0';
16982d96bf6SHelge Deller 	} else i++;
1702f3c7b81SHelge Deller 	puts(&str[i]);
1712f3c7b81SHelge Deller 
1722f3c7b81SHelge Deller 	return 0;
1732f3c7b81SHelge Deller }
1742f3c7b81SHelge Deller 
printf(const char * fmt,...)175*b967f48dSHelge Deller static int printf(const char *fmt, ...)
1762f3c7b81SHelge Deller {
1772f3c7b81SHelge Deller 	va_list args;
1782f3c7b81SHelge Deller 	int i = 0;
1792f3c7b81SHelge Deller 
1802f3c7b81SHelge Deller 	va_start(args, fmt);
1812f3c7b81SHelge Deller 
1822f3c7b81SHelge Deller 	while (fmt[i]) {
1832f3c7b81SHelge Deller 		if (fmt[i] != '%') {
1842f3c7b81SHelge Deller put:
1852f3c7b81SHelge Deller 			putchar(fmt[i++]);
1862f3c7b81SHelge Deller 			continue;
1872f3c7b81SHelge Deller 		}
1882f3c7b81SHelge Deller 
1892f3c7b81SHelge Deller 		if (fmt[++i] == '%')
1902f3c7b81SHelge Deller 			goto put;
19182d96bf6SHelge Deller 		print_num(va_arg(args, unsigned long),
19282d96bf6SHelge Deller 			fmt[i] == 'x' ? 16:10);
1932f3c7b81SHelge Deller 		++i;
1942f3c7b81SHelge Deller 	}
1952f3c7b81SHelge Deller 
1962f3c7b81SHelge Deller 	va_end(args);
1972f3c7b81SHelge Deller 	return 0;
1982f3c7b81SHelge Deller }
1992f3c7b81SHelge Deller 
2002f3c7b81SHelge Deller /* helper functions for libgcc */
abort(void)2012f3c7b81SHelge Deller void abort(void)
2022f3c7b81SHelge Deller {
2032f3c7b81SHelge Deller 	error("aborted.");
2042f3c7b81SHelge Deller }
2052f3c7b81SHelge Deller 
2062f3c7b81SHelge Deller #undef malloc
malloc(size_t size)207*b967f48dSHelge Deller static void *malloc(size_t size)
2082f3c7b81SHelge Deller {
2092f3c7b81SHelge Deller 	return malloc_gzip(size);
2102f3c7b81SHelge Deller }
2112f3c7b81SHelge Deller 
2122f3c7b81SHelge Deller #undef free
free(void * ptr)213*b967f48dSHelge Deller static void free(void *ptr)
2142f3c7b81SHelge Deller {
2152f3c7b81SHelge Deller 	return free_gzip(ptr);
2162f3c7b81SHelge Deller }
2172f3c7b81SHelge Deller 
2182f3c7b81SHelge Deller 
flush_data_cache(char * start,unsigned long length)2192f3c7b81SHelge Deller static void flush_data_cache(char *start, unsigned long length)
2202f3c7b81SHelge Deller {
2212f3c7b81SHelge Deller 	char *end = start + length;
2222f3c7b81SHelge Deller 
2232f3c7b81SHelge Deller 	do {
2242f3c7b81SHelge Deller 		asm volatile("fdc 0(%0)" : : "r" (start));
2252f3c7b81SHelge Deller 		asm volatile("fic 0(%%sr0,%0)" : : "r" (start));
2262f3c7b81SHelge Deller 		start += 16;
2272f3c7b81SHelge Deller 	} while (start < end);
2282f3c7b81SHelge Deller 	asm volatile("fdc 0(%0)" : : "r" (end));
2292f3c7b81SHelge Deller 
2302f3c7b81SHelge Deller 	asm ("sync");
2312f3c7b81SHelge Deller }
2322f3c7b81SHelge Deller 
parse_elf(void * output)23334c201aeSHelge Deller static void parse_elf(void *output)
23434c201aeSHelge Deller {
23534c201aeSHelge Deller #ifdef CONFIG_64BIT
23634c201aeSHelge Deller 	Elf64_Ehdr ehdr;
23734c201aeSHelge Deller 	Elf64_Phdr *phdrs, *phdr;
23834c201aeSHelge Deller #else
23934c201aeSHelge Deller 	Elf32_Ehdr ehdr;
24034c201aeSHelge Deller 	Elf32_Phdr *phdrs, *phdr;
24134c201aeSHelge Deller #endif
24234c201aeSHelge Deller 	void *dest;
24334c201aeSHelge Deller 	int i;
24434c201aeSHelge Deller 
24534c201aeSHelge Deller 	memcpy(&ehdr, output, sizeof(ehdr));
24634c201aeSHelge Deller 	if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
24734c201aeSHelge Deller 	   ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
24834c201aeSHelge Deller 	   ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
24934c201aeSHelge Deller 	   ehdr.e_ident[EI_MAG3] != ELFMAG3) {
25034c201aeSHelge Deller 		error("Kernel is not a valid ELF file");
25134c201aeSHelge Deller 		return;
25234c201aeSHelge Deller 	}
25334c201aeSHelge Deller 
25434c201aeSHelge Deller #ifdef DEBUG
25534c201aeSHelge Deller 	printf("Parsing ELF... ");
25634c201aeSHelge Deller #endif
25734c201aeSHelge Deller 
25834c201aeSHelge Deller 	phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum);
25934c201aeSHelge Deller 	if (!phdrs)
26034c201aeSHelge Deller 		error("Failed to allocate space for phdrs");
26134c201aeSHelge Deller 
26234c201aeSHelge Deller 	memcpy(phdrs, output + ehdr.e_phoff, sizeof(*phdrs) * ehdr.e_phnum);
26334c201aeSHelge Deller 
26434c201aeSHelge Deller 	for (i = 0; i < ehdr.e_phnum; i++) {
26534c201aeSHelge Deller 		phdr = &phdrs[i];
26634c201aeSHelge Deller 
26734c201aeSHelge Deller 		switch (phdr->p_type) {
26834c201aeSHelge Deller 		case PT_LOAD:
26934c201aeSHelge Deller 			dest = (void *)((unsigned long) phdr->p_paddr &
27034c201aeSHelge Deller 					(__PAGE_OFFSET_DEFAULT-1));
27134c201aeSHelge Deller 			memmove(dest, output + phdr->p_offset, phdr->p_filesz);
27234c201aeSHelge Deller 			break;
27334c201aeSHelge Deller 		default:
27434c201aeSHelge Deller 			break;
27534c201aeSHelge Deller 		}
27634c201aeSHelge Deller 	}
27734c201aeSHelge Deller 
27834c201aeSHelge Deller 	free(phdrs);
27934c201aeSHelge Deller }
28034c201aeSHelge Deller 
decompress_kernel(unsigned int started_wide,unsigned int command_line,const unsigned int rd_start,const unsigned int rd_end)281*b967f48dSHelge Deller asmlinkage unsigned long __visible decompress_kernel(unsigned int started_wide,
2822f3c7b81SHelge Deller 		unsigned int command_line,
2832f3c7b81SHelge Deller 		const unsigned int rd_start,
2842f3c7b81SHelge Deller 		const unsigned int rd_end)
2852f3c7b81SHelge Deller {
2862f3c7b81SHelge Deller 	char *output;
28734c201aeSHelge Deller 	unsigned long vmlinux_addr, vmlinux_len;
28834c201aeSHelge Deller 	unsigned long kernel_addr, kernel_len;
2892f3c7b81SHelge Deller 
2902f3c7b81SHelge Deller #ifdef CONFIG_64BIT
2912f3c7b81SHelge Deller 	parisc_narrow_firmware = 0;
2922f3c7b81SHelge Deller #endif
2932f3c7b81SHelge Deller 
2942f3c7b81SHelge Deller 	set_firmware_width_unlocked();
2952f3c7b81SHelge Deller 
29634c201aeSHelge Deller 	putchar('D');	/* if you get this D and no more, string storage */
2972f3c7b81SHelge Deller 			/* in $GLOBAL$ is wrong or %dp is wrong */
29834c201aeSHelge Deller 	puts("ecompressing Linux... ");
2992f3c7b81SHelge Deller 
30034c201aeSHelge Deller 	/* where the final bits are stored */
30134c201aeSHelge Deller 	kernel_addr = KERNEL_BINARY_TEXT_START;
30234c201aeSHelge Deller 	kernel_len = __pa(SZ_end) - __pa(SZparisc_kernel_start);
30334c201aeSHelge Deller 	if ((unsigned long) &_startcode_end > kernel_addr)
3042f3c7b81SHelge Deller 		error("Bootcode overlaps kernel code");
3052f3c7b81SHelge Deller 
30634c201aeSHelge Deller 	/*
30734c201aeSHelge Deller 	 * Calculate addr to where the vmlinux ELF file shall be decompressed.
30834c201aeSHelge Deller 	 * Assembly code in head.S positioned the stack directly behind bss, so
30934c201aeSHelge Deller 	 * leave 2 MB for the stack.
31034c201aeSHelge Deller 	 */
31134c201aeSHelge Deller 	vmlinux_addr = (unsigned long) &_ebss + 2*1024*1024;
31234c201aeSHelge Deller 	vmlinux_len = get_unaligned_le32(&output_len);
31334c201aeSHelge Deller 	output = (char *) vmlinux_addr;
3142f3c7b81SHelge Deller 
3152f3c7b81SHelge Deller 	/*
3162f3c7b81SHelge Deller 	 * Initialize free_mem_ptr and free_mem_end_ptr.
3172f3c7b81SHelge Deller 	 */
31834c201aeSHelge Deller 	free_mem_ptr = vmlinux_addr + vmlinux_len;
3192f3c7b81SHelge Deller 
3202f3c7b81SHelge Deller 	/* Limit memory for bootoader to 1GB */
3212f3c7b81SHelge Deller 	#define ARTIFICIAL_LIMIT (1*1024*1024*1024)
3222f3c7b81SHelge Deller 	free_mem_end_ptr = PAGE0->imm_max_mem;
3232f3c7b81SHelge Deller 	if (free_mem_end_ptr > ARTIFICIAL_LIMIT)
3242f3c7b81SHelge Deller 		free_mem_end_ptr = ARTIFICIAL_LIMIT;
3252f3c7b81SHelge Deller 
3262f3c7b81SHelge Deller #ifdef CONFIG_BLK_DEV_INITRD
3272f3c7b81SHelge Deller 	/* if we have ramdisk this is at end of memory */
3282f3c7b81SHelge Deller 	if (rd_start && rd_start < free_mem_end_ptr)
3292f3c7b81SHelge Deller 		free_mem_end_ptr = rd_start;
3302f3c7b81SHelge Deller #endif
3312f3c7b81SHelge Deller 
33282d96bf6SHelge Deller 	if (free_mem_ptr >= free_mem_end_ptr) {
33382d96bf6SHelge Deller 		int free_ram;
33482d96bf6SHelge Deller 		free_ram = (free_mem_ptr >> 20) + 1;
33582d96bf6SHelge Deller 		if (free_ram < 32)
33682d96bf6SHelge Deller 			free_ram = 32;
33782d96bf6SHelge Deller 		printf("\nKernel requires at least %d MB RAM.\n",
33882d96bf6SHelge Deller 			free_ram);
33982d96bf6SHelge Deller 		error(NULL);
34082d96bf6SHelge Deller 	}
34134c201aeSHelge Deller 
3422f3c7b81SHelge Deller #ifdef DEBUG
34334c201aeSHelge Deller 	printf("\n");
3442f3c7b81SHelge Deller 	printf("startcode_end = %x\n", &_startcode_end);
3452f3c7b81SHelge Deller 	printf("commandline   = %x\n", command_line);
3462f3c7b81SHelge Deller 	printf("rd_start      = %x\n", rd_start);
3472f3c7b81SHelge Deller 	printf("rd_end        = %x\n", rd_end);
3482f3c7b81SHelge Deller 
3492f3c7b81SHelge Deller 	printf("free_ptr      = %x\n", free_mem_ptr);
3502f3c7b81SHelge Deller 	printf("free_ptr_end  = %x\n", free_mem_end_ptr);
3512f3c7b81SHelge Deller 
3522f3c7b81SHelge Deller 	printf("input_data    = %x\n", input_data);
3532f3c7b81SHelge Deller 	printf("input_len     = %x\n", input_len);
3542f3c7b81SHelge Deller 	printf("output        = %x\n", output);
35534c201aeSHelge Deller 	printf("output_len    = %x\n", vmlinux_len);
35634c201aeSHelge Deller 	printf("kernel_addr   = %x\n", kernel_addr);
35734c201aeSHelge Deller 	printf("kernel_len    = %x\n", kernel_len);
3582f3c7b81SHelge Deller #endif
3592f3c7b81SHelge Deller 
3602f3c7b81SHelge Deller 	__decompress(input_data, input_len, NULL, NULL,
3612f3c7b81SHelge Deller 			output, 0, NULL, error);
36234c201aeSHelge Deller 	parse_elf(output);
3632f3c7b81SHelge Deller 
36434c201aeSHelge Deller 	output = (char *) kernel_addr;
36534c201aeSHelge Deller 	flush_data_cache(output, kernel_len);
3662f3c7b81SHelge Deller 
36734c201aeSHelge Deller 	printf("done.\nBooting the kernel.\n");
3682f3c7b81SHelge Deller 
3692f3c7b81SHelge Deller 	return (unsigned long) output;
3702f3c7b81SHelge Deller }
371