misc.c 3.8 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2 3 4 5 6 7 8 9
/*
 * Definitions and wrapper functions for kernel decompressor
 *
 * Copyright IBM Corp. 2010
 *
 * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
 */

10
#include <linux/uaccess.h>
11
#include <asm/page.h>
12
#include <asm/sclp.h>
13 14 15 16 17 18 19 20 21 22 23
#include <asm/ipl.h>
#include "sizes.h"

/*
 * gzip declarations
 */
#define STATIC static

#undef memset
#undef memcpy
#undef memmove
24
#define memmove memmove
25 26 27 28 29
#define memzero(s, n) memset((s), 0, (n))

/* Symbols defined by linker scripts */
extern char input_data[];
extern int input_len;
30 31
extern char _text, _end;
extern char _bss, _ebss;
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

static void error(char *m);

static unsigned long free_mem_ptr;
static unsigned long free_mem_end_ptr;

#ifdef CONFIG_HAVE_KERNEL_BZIP2
#define HEAP_SIZE	0x400000
#else
#define HEAP_SIZE	0x10000
#endif

#ifdef CONFIG_KERNEL_GZIP
#include "../../../../lib/decompress_inflate.c"
#endif

#ifdef CONFIG_KERNEL_BZIP2
#include "../../../../lib/decompress_bunzip2.c"
#endif

52 53 54 55
#ifdef CONFIG_KERNEL_LZ4
#include "../../../../lib/decompress_unlz4.c"
#endif

56 57 58 59
#ifdef CONFIG_KERNEL_LZMA
#include "../../../../lib/decompress_unlzma.c"
#endif

60 61 62 63
#ifdef CONFIG_KERNEL_LZO
#include "../../../../lib/decompress_unlzo.c"
#endif

64 65 66 67
#ifdef CONFIG_KERNEL_XZ
#include "../../../../lib/decompress_unxz.c"
#endif

68
static int puts(const char *s)
69
{
70
	sclp_early_printk(s);
71 72 73 74 75 76 77
	return 0;
}

void *memset(void *s, int c, size_t n)
{
	char *xs;

78 79 80
	xs = s;
	while (n--)
		*xs++ = c;
81 82 83
	return s;
}

84
void *memcpy(void *dest, const void *src, size_t n)
85
{
86 87 88 89 90 91
	const char *s = src;
	char *d = dest;

	while (n--)
		*d++ = *s++;
	return dest;
92 93
}

94
void *memmove(void *dest, const void *src, size_t n)
95
{
96 97 98 99 100 101 102 103 104 105 106 107 108
	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;
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
}

static void error(char *x)
{
	unsigned long long psw = 0x000a0000deadbeefULL;

	puts("\n\n");
	puts(x);
	puts("\n\n -- System halted");

	asm volatile("lpsw %0" : : "Q" (psw));
}

/*
 * Safe guard the ipl parameter block against a memory area that will be
 * overwritten. The validity check for the ipl parameter block is complex
 * (see cio_get_iplinfo and ipl_save_parameters) but if the pointer to
 * the ipl parameter block intersects with the passed memory area we can
 * safely assume that we can read from that memory. In that case just copy
 * the memory to IPL_PARMBLOCK_ORIGIN even if there is no ipl parameter
 * block.
 */
static void check_ipl_parmblock(void *start, unsigned long size)
{
	void *src, *dst;

	src = (void *)(unsigned long) S390_lowcore.ipl_parmblock_ptr;
	if (src + PAGE_SIZE <= start || src >= start + size)
		return;
	dst = (void *) IPL_PARMBLOCK_ORIGIN;
	memmove(dst, src, PAGE_SIZE);
	S390_lowcore.ipl_parmblock_ptr = IPL_PARMBLOCK_ORIGIN;
}

unsigned long decompress_kernel(void)
{
145
	void *output, *kernel_end;
146

147 148 149
	output = (void *) ALIGN((unsigned long) &_end + HEAP_SIZE, PAGE_SIZE);
	kernel_end = output + SZ__bss_start;
	check_ipl_parmblock((void *) 0, (unsigned long) kernel_end);
150 151 152 153

#ifdef CONFIG_BLK_DEV_INITRD
	/*
	 * Move the initrd right behind the end of the decompressed
154 155 156
	 * kernel image. This also prevents initrd corruption caused by
	 * bss clearing since kernel_end will always be located behind the
	 * current bss section..
157
	 */
158 159 160 161
	if (INITRD_START && INITRD_SIZE && kernel_end > (void *) INITRD_START) {
		check_ipl_parmblock(kernel_end, INITRD_SIZE);
		memmove(kernel_end, (void *) INITRD_START, INITRD_SIZE);
		INITRD_START = (unsigned long) kernel_end;
162 163 164
	}
#endif

165 166 167 168 169 170 171 172
	/*
	 * Clear bss section. free_mem_ptr and free_mem_end_ptr need to be
	 * initialized afterwards since they reside in bss.
	 */
	memset(&_bss, 0, &_ebss - &_bss);
	free_mem_ptr = (unsigned long) &_end;
	free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;

173
	__decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error);
174 175 176
	return (unsigned long) output;
}