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

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

/*
 * gzip declarations
 */
#define STATIC static

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

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

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

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

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

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

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

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

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

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

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

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

93
void *memmove(void *dest, const void *src, size_t n)
94
{
95 96 97 98 99 100 101 102 103 104 105 106 107
	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;
108 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 145 146
}

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)
{
	unsigned long output_addr;
	unsigned char *output;

147 148
	output_addr = ((unsigned long) &_end + HEAP_SIZE + 4095UL) & -4096UL;
	check_ipl_parmblock((void *) 0, output_addr + SZ__bss_start);
149
	memset(&_bss, 0, &_ebss - &_bss);
150 151
	free_mem_ptr = (unsigned long)&_end;
	free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
152
	output = (unsigned char *) output_addr;
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174

#ifdef CONFIG_BLK_DEV_INITRD
	/*
	 * Move the initrd right behind the end of the decompressed
	 * kernel image.
	 */
	if (INITRD_START && INITRD_SIZE &&
	    INITRD_START < (unsigned long) output + SZ__bss_start) {
		check_ipl_parmblock(output + SZ__bss_start,
				    INITRD_START + INITRD_SIZE);
		memmove(output + SZ__bss_start,
			(void *) INITRD_START, INITRD_SIZE);
		INITRD_START = (unsigned long) output + SZ__bss_start;
	}
#endif

	puts("Uncompressing Linux... ");
	decompress(input_data, input_len, NULL, NULL, output, NULL, error);
	puts("Ok, booting the kernel.\n");
	return (unsigned long) output;
}