1/*
2 * Definitions and wrapper functions for kernel decompressor
3 *
4 *   (C) 2017 Helge Deller <deller@gmx.de>
5 */
6
7#include <linux/uaccess.h>
8#include <linux/elf.h>
9#include <asm/unaligned.h>
10#include <asm/page.h>
11#include "sizes.h"
12
13/*
14 * gzip declarations
15 */
16#define STATIC static
17
18#undef memmove
19#define memmove memmove
20#define memzero(s, n) memset((s), 0, (n))
21
22#define malloc	malloc_gzip
23#define free	free_gzip
24
25/* Symbols defined by linker scripts */
26extern char input_data[];
27extern int input_len;
28/* output_len is inserted by the linker possibly at an unaligned address */
29extern char output_len;
30extern char _text, _end;
31extern char _bss, _ebss;
32extern char _startcode_end;
33extern void startup_continue(void *entry, unsigned long cmdline,
34	unsigned long rd_start, unsigned long rd_end) __noreturn;
35
36void error(char *m) __noreturn;
37
38static unsigned long free_mem_ptr;
39static unsigned long free_mem_end_ptr;
40
41#ifdef CONFIG_KERNEL_GZIP
42#include "../../../../lib/decompress_inflate.c"
43#endif
44
45#ifdef CONFIG_KERNEL_BZIP2
46#include "../../../../lib/decompress_bunzip2.c"
47#endif
48
49#ifdef CONFIG_KERNEL_LZ4
50#include "../../../../lib/decompress_unlz4.c"
51#endif
52
53#ifdef CONFIG_KERNEL_LZMA
54#include "../../../../lib/decompress_unlzma.c"
55#endif
56
57#ifdef CONFIG_KERNEL_LZO
58#include "../../../../lib/decompress_unlzo.c"
59#endif
60
61#ifdef CONFIG_KERNEL_XZ
62#include "../../../../lib/decompress_unxz.c"
63#endif
64
65void *memmove(void *dest, const void *src, size_t n)
66{
67	const char *s = src;
68	char *d = dest;
69
70	if (d <= s) {
71		while (n--)
72			*d++ = *s++;
73	} else {
74		d += n;
75		s += n;
76		while (n--)
77			*--d = *--s;
78	}
79	return dest;
80}
81
82void *memset(void *s, int c, size_t count)
83{
84	char *xs = (char *)s;
85
86	while (count--)
87		*xs++ = c;
88	return s;
89}
90
91void *memcpy(void *d, const void *s, size_t len)
92{
93	char *dest = (char *)d;
94	const char *source = (const char *)s;
95
96	while (len--)
97		*dest++ = *source++;
98	return d;
99}
100
101size_t strlen(const char *s)
102{
103	const char *sc;
104
105	for (sc = s; *sc != '\0'; ++sc)
106		;
107	return sc - s;
108}
109
110char *strchr(const char *s, int c)
111{
112	while (*s) {
113		if (*s == (char)c)
114			return (char *)s;
115		++s;
116	}
117	return NULL;
118}
119
120static int puts(const char *s)
121{
122	const char *nuline = s;
123
124	while ((nuline = strchr(s, '\n')) != NULL) {
125		if (nuline != s)
126			pdc_iodc_print(s, nuline - s);
127		pdc_iodc_print("\r\n", 2);
128		s = nuline + 1;
129	}
130	if (*s != '\0')
131		pdc_iodc_print(s, strlen(s));
132
133	return 0;
134}
135
136static int putchar(int c)
137{
138	char buf[2];
139
140	buf[0] = c;
141	buf[1] = '\0';
142	puts(buf);
143	return c;
144}
145
146void __noreturn error(char *x)
147{
148	if (x) puts(x);
149	puts("\n -- System halted\n");
150	while (1)	/* wait forever */
151		;
152}
153
154static int print_num(unsigned long num, int base)
155{
156	const char hex[] = "0123456789abcdef";
157	char str[40];
158	int i = sizeof(str)-1;
159
160	str[i--] = '\0';
161	do {
162		str[i--] = hex[num % base];
163		num = num / base;
164	} while (num);
165
166	if (base == 16) {
167		str[i--] = 'x';
168		str[i] = '0';
169	} else i++;
170	puts(&str[i]);
171
172	return 0;
173}
174
175static int printf(const char *fmt, ...)
176{
177	va_list args;
178	int i = 0;
179
180	va_start(args, fmt);
181
182	while (fmt[i]) {
183		if (fmt[i] != '%') {
184put:
185			putchar(fmt[i++]);
186			continue;
187		}
188
189		if (fmt[++i] == '%')
190			goto put;
191		print_num(va_arg(args, unsigned long),
192			fmt[i] == 'x' ? 16:10);
193		++i;
194	}
195
196	va_end(args);
197	return 0;
198}
199
200/* helper functions for libgcc */
201void abort(void)
202{
203	error("aborted.");
204}
205
206#undef malloc
207static void *malloc(size_t size)
208{
209	return malloc_gzip(size);
210}
211
212#undef free
213static void free(void *ptr)
214{
215	return free_gzip(ptr);
216}
217
218
219static void flush_data_cache(char *start, unsigned long length)
220{
221	char *end = start + length;
222
223	do {
224		asm volatile("fdc 0(%0)" : : "r" (start));
225		asm volatile("fic 0(%%sr0,%0)" : : "r" (start));
226		start += 16;
227	} while (start < end);
228	asm volatile("fdc 0(%0)" : : "r" (end));
229
230	asm ("sync");
231}
232
233static void parse_elf(void *output)
234{
235#ifdef CONFIG_64BIT
236	Elf64_Ehdr ehdr;
237	Elf64_Phdr *phdrs, *phdr;
238#else
239	Elf32_Ehdr ehdr;
240	Elf32_Phdr *phdrs, *phdr;
241#endif
242	void *dest;
243	int i;
244
245	memcpy(&ehdr, output, sizeof(ehdr));
246	if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
247	   ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
248	   ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
249	   ehdr.e_ident[EI_MAG3] != ELFMAG3) {
250		error("Kernel is not a valid ELF file");
251		return;
252	}
253
254#ifdef DEBUG
255	printf("Parsing ELF... ");
256#endif
257
258	phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum);
259	if (!phdrs)
260		error("Failed to allocate space for phdrs");
261
262	memcpy(phdrs, output + ehdr.e_phoff, sizeof(*phdrs) * ehdr.e_phnum);
263
264	for (i = 0; i < ehdr.e_phnum; i++) {
265		phdr = &phdrs[i];
266
267		switch (phdr->p_type) {
268		case PT_LOAD:
269			dest = (void *)((unsigned long) phdr->p_paddr &
270					(__PAGE_OFFSET_DEFAULT-1));
271			memmove(dest, output + phdr->p_offset, phdr->p_filesz);
272			break;
273		default:
274			break;
275		}
276	}
277
278	free(phdrs);
279}
280
281asmlinkage unsigned long __visible decompress_kernel(unsigned int started_wide,
282		unsigned int command_line,
283		const unsigned int rd_start,
284		const unsigned int rd_end)
285{
286	char *output;
287	unsigned long vmlinux_addr, vmlinux_len;
288	unsigned long kernel_addr, kernel_len;
289
290#ifdef CONFIG_64BIT
291	parisc_narrow_firmware = 0;
292#endif
293
294	set_firmware_width_unlocked();
295
296	putchar('D');	/* if you get this D and no more, string storage */
297			/* in $GLOBAL$ is wrong or %dp is wrong */
298	puts("ecompressing Linux... ");
299
300	/* where the final bits are stored */
301	kernel_addr = KERNEL_BINARY_TEXT_START;
302	kernel_len = __pa(SZ_end) - __pa(SZparisc_kernel_start);
303	if ((unsigned long) &_startcode_end > kernel_addr)
304		error("Bootcode overlaps kernel code");
305
306	/*
307	 * Calculate addr to where the vmlinux ELF file shall be decompressed.
308	 * Assembly code in head.S positioned the stack directly behind bss, so
309	 * leave 2 MB for the stack.
310	 */
311	vmlinux_addr = (unsigned long) &_ebss + 2*1024*1024;
312	vmlinux_len = get_unaligned_le32(&output_len);
313	output = (char *) vmlinux_addr;
314
315	/*
316	 * Initialize free_mem_ptr and free_mem_end_ptr.
317	 */
318	free_mem_ptr = vmlinux_addr + vmlinux_len;
319
320	/* Limit memory for bootoader to 1GB */
321	#define ARTIFICIAL_LIMIT (1*1024*1024*1024)
322	free_mem_end_ptr = PAGE0->imm_max_mem;
323	if (free_mem_end_ptr > ARTIFICIAL_LIMIT)
324		free_mem_end_ptr = ARTIFICIAL_LIMIT;
325
326#ifdef CONFIG_BLK_DEV_INITRD
327	/* if we have ramdisk this is at end of memory */
328	if (rd_start && rd_start < free_mem_end_ptr)
329		free_mem_end_ptr = rd_start;
330#endif
331
332	if (free_mem_ptr >= free_mem_end_ptr) {
333		int free_ram;
334		free_ram = (free_mem_ptr >> 20) + 1;
335		if (free_ram < 32)
336			free_ram = 32;
337		printf("\nKernel requires at least %d MB RAM.\n",
338			free_ram);
339		error(NULL);
340	}
341
342#ifdef DEBUG
343	printf("\n");
344	printf("startcode_end = %x\n", &_startcode_end);
345	printf("commandline   = %x\n", command_line);
346	printf("rd_start      = %x\n", rd_start);
347	printf("rd_end        = %x\n", rd_end);
348
349	printf("free_ptr      = %x\n", free_mem_ptr);
350	printf("free_ptr_end  = %x\n", free_mem_end_ptr);
351
352	printf("input_data    = %x\n", input_data);
353	printf("input_len     = %x\n", input_len);
354	printf("output        = %x\n", output);
355	printf("output_len    = %x\n", vmlinux_len);
356	printf("kernel_addr   = %x\n", kernel_addr);
357	printf("kernel_len    = %x\n", kernel_len);
358#endif
359
360	__decompress(input_data, input_len, NULL, NULL,
361			output, 0, NULL, error);
362	parse_elf(output);
363
364	output = (char *) kernel_addr;
365	flush_data_cache(output, kernel_len);
366
367	printf("done.\nBooting the kernel.\n");
368
369	return (unsigned long) output;
370}
371