misc.c 8.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 * misc.c
3 4
 *
 * This is a collection of several routines from gzip-1.0.3
L
Linus Torvalds 已提交
5 6 7 8 9 10 11
 * adapted for Linux.
 *
 * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
 * puts by Nick Holloway 1993, better puts by Martin Mares 1995
 * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
 */

12 13 14 15 16
/*
 * we have to be careful, because no indirections are allowed here, and
 * paravirt_ops is a kind of one. As it will only run in baremetal anyway,
 * we just keep it from happening
 */
17
#undef CONFIG_PARAVIRT
18
#ifdef CONFIG_X86_32
H
H. Peter Anvin 已提交
19
#define _ASM_X86_DESC_H 1
20 21
#endif

22 23 24 25 26
#ifdef CONFIG_X86_64
#define _LINUX_STRING_H_ 1
#define __LINUX_BITMAP_H 1
#endif

L
Linus Torvalds 已提交
27
#include <linux/linkage.h>
28
#include <linux/screen_info.h>
29
#include <linux/elf.h>
30
#include <linux/io.h>
31
#include <asm/page.h>
32
#include <asm/boot.h>
33
#include <asm/bootparam.h>
34 35 36 37 38 39 40 41 42

/* WARNING!!
 * This code is compiled with -fPIC and it is relocated dynamically
 * at run time, but no relocation processing is performed.
 * This means that it is not safe to place pointers in static structures.
 */

/*
 * Getting to provable safe in place decompression is hard.
S
Simon Arlott 已提交
43
 * Worst case behaviours need to be analyzed.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
 * Background information:
 *
 * The file layout is:
 *    magic[2]
 *    method[1]
 *    flags[1]
 *    timestamp[4]
 *    extraflags[1]
 *    os[1]
 *    compressed data blocks[N]
 *    crc[4] orig_len[4]
 *
 * resulting in 18 bytes of non compressed data overhead.
 *
 * Files divided into blocks
 * 1 bit (last block flag)
 * 2 bits (block type)
 *
62 63
 * 1 block occurs every 32K -1 bytes or when there 50% compression
 * has been achieved. The smallest block type encoding is always used.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
 *
 * stored:
 *    32 bits length in bytes.
 *
 * fixed:
 *    magic fixed tree.
 *    symbols.
 *
 * dynamic:
 *    dynamic tree encoding.
 *    symbols.
 *
 *
 * The buffer for decompression in place is the length of the
 * uncompressed data, plus a small amount extra to keep the algorithm safe.
 * The compressed data is placed at the end of the buffer.  The output
 * pointer is placed at the start of the buffer and the input pointer
 * is placed where the compressed data starts.  Problems will occur
 * when the output pointer overruns the input pointer.
 *
 * The output pointer can only overrun the input pointer if the input
 * pointer is moving faster than the output pointer.  A condition only
 * triggered by data whose compressed form is larger than the uncompressed
 * form.
 *
 * The worst case at the block level is a growth of the compressed data
 * of 5 bytes per 32767 bytes.
 *
 * The worst case internal to a compressed block is very hard to figure.
 * The worst case can at least be boundined by having one bit that represents
 * 32764 bytes and then all of the rest of the bytes representing the very
 * very last byte.
 *
 * All of which is enough to compute an amount of extra data that is required
 * to be safe.  To avoid problems at the block level allocating 5 extra bytes
99 100 101
 * per 32767 bytes of data is sufficient.  To avoind problems internal to a
 * block adding an extra 32767 bytes (the worst case uncompressed block size)
 * is sufficient, to ensure that in the worst case the decompressed data for
102 103 104 105 106 107 108 109 110 111
 * block will stop the byte before the compressed data for a block begins.
 * To avoid problems with the compressed data's meta information an extra 18
 * bytes are needed.  Leading to the formula:
 *
 * extra_bytes = (uncompressed_size >> 12) + 32768 + 18 + decompressor_size.
 *
 * Adding 8 bytes per 32K is a bit excessive but much easier to calculate.
 * Adding 32768 instead of 32767 just makes for round numbers.
 * Adding the decompressor_size is necessary as it musht live after all
 * of the data as well.  Last I measured the decompressor is about 14K.
S
Simon Arlott 已提交
112
 * 10K of actual data and 4K of bss.
113 114
 *
 */
L
Linus Torvalds 已提交
115 116 117 118

/*
 * gzip declarations
 */
119
#define STATIC		static
L
Linus Torvalds 已提交
120 121 122

#undef memset
#undef memcpy
123
#define memzero(s, n)	memset((s), 0, (n))
L
Linus Torvalds 已提交
124 125 126


static void error(char *m);
127

L
Linus Torvalds 已提交
128 129 130
/*
 * This is set up by the setup-routine at boot-time
 */
131
static struct boot_params *real_mode;		/* Pointer to real-mode data */
132
static int quiet;
L
Linus Torvalds 已提交
133

134
static void *memset(void *s, int c, unsigned n);
135
void *memcpy(void *dest, const void *src, unsigned n);
136

137 138
static void __putstr(int, const char *);
#define putstr(__x)  __putstr(0, __x)
L
Linus Torvalds 已提交
139

140 141 142 143 144 145 146 147
#ifdef CONFIG_X86_64
#define memptr long
#else
#define memptr unsigned
#endif

static memptr free_mem_ptr;
static memptr free_mem_end_ptr;
L
Linus Torvalds 已提交
148

149
static char *vidmem;
L
Linus Torvalds 已提交
150 151 152
static int vidport;
static int lines, cols;

153 154 155 156 157 158 159 160 161 162 163
#ifdef CONFIG_KERNEL_GZIP
#include "../../../../lib/decompress_inflate.c"
#endif

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

#ifdef CONFIG_KERNEL_LZMA
#include "../../../../lib/decompress_unlzma.c"
#endif
L
Linus Torvalds 已提交
164

165 166 167 168
#ifdef CONFIG_KERNEL_LZO
#include "../../../../lib/decompress_unlzo.c"
#endif

L
Linus Torvalds 已提交
169 170 171 172
static void scroll(void)
{
	int i;

173 174
	memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2);
	for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2)
L
Linus Torvalds 已提交
175 176 177
		vidmem[i] = ' ';
}

178
static void __putstr(int error, const char *s)
L
Linus Torvalds 已提交
179
{
180
	int x, y, pos;
L
Linus Torvalds 已提交
181 182
	char c;

183 184 185 186 187
#ifndef CONFIG_X86_VERBOSE_BOOTUP
	if (!error)
		return;
#endif

188
#ifdef CONFIG_X86_32
189 190
	if (real_mode->screen_info.orig_video_mode == 0 &&
	    lines == 0 && cols == 0)
R
Rusty Russell 已提交
191
		return;
192
#endif
R
Rusty Russell 已提交
193

194 195
	x = real_mode->screen_info.orig_x;
	y = real_mode->screen_info.orig_y;
L
Linus Torvalds 已提交
196

197 198
	while ((c = *s++) != '\0') {
		if (c == '\n') {
L
Linus Torvalds 已提交
199
			x = 0;
200
			if (++y >= lines) {
L
Linus Torvalds 已提交
201 202 203 204
				scroll();
				y--;
			}
		} else {
205
			vidmem[(x + cols * y) * 2] = c;
206
			if (++x >= cols) {
L
Linus Torvalds 已提交
207
				x = 0;
208
				if (++y >= lines) {
L
Linus Torvalds 已提交
209 210 211 212 213 214 215
					scroll();
					y--;
				}
			}
		}
	}

216 217
	real_mode->screen_info.orig_x = x;
	real_mode->screen_info.orig_y = y;
L
Linus Torvalds 已提交
218 219

	pos = (x + cols * y) * 2;	/* Update cursor position */
220 221 222 223
	outb(14, vidport);
	outb(0xff & (pos >> 9), vidport+1);
	outb(15, vidport);
	outb(0xff & (pos >> 1), vidport+1);
L
Linus Torvalds 已提交
224 225
}

226
static void *memset(void *s, int c, unsigned n)
L
Linus Torvalds 已提交
227 228
{
	int i;
J
Jan Engelhardt 已提交
229
	char *ss = s;
L
Linus Torvalds 已提交
230

231 232
	for (i = 0; i < n; i++)
		ss[i] = c;
L
Linus Torvalds 已提交
233 234 235
	return s;
}

236
void *memcpy(void *dest, const void *src, unsigned n)
L
Linus Torvalds 已提交
237 238
{
	int i;
J
Jan Engelhardt 已提交
239 240
	const char *s = src;
	char *d = dest;
L
Linus Torvalds 已提交
241

242 243
	for (i = 0; i < n; i++)
		d[i] = s[i];
244
	return dest;
L
Linus Torvalds 已提交
245 246 247 248 249
}


static void error(char *x)
{
250 251 252
	__putstr(1, "\n\n");
	__putstr(1, x);
	__putstr(1, "\n\n -- System halted");
L
Linus Torvalds 已提交
253

I
Ingo Molnar 已提交
254 255
	while (1)
		asm("hlt");
L
Linus Torvalds 已提交
256 257
}

258 259 260 261 262 263 264 265 266 267 268 269 270
static void parse_elf(void *output)
{
#ifdef CONFIG_X86_64
	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));
271
	if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
272 273
	   ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
	   ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
274
	   ehdr.e_ident[EI_MAG3] != ELFMAG3) {
275 276 277 278
		error("Kernel is not a valid ELF file");
		return;
	}

279 280
	if (!quiet)
		putstr("Parsing ELF... ");
281 282 283 284 285 286 287

	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);

288
	for (i = 0; i < ehdr.e_phnum; i++) {
289 290 291 292 293 294 295 296
		phdr = &phdrs[i];

		switch (phdr->p_type) {
		case PT_LOAD:
#ifdef CONFIG_RELOCATABLE
			dest = output;
			dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR);
#else
297
			dest = (void *)(phdr->p_paddr);
298 299 300 301 302 303 304 305 306 307
#endif
			memcpy(dest,
			       output + phdr->p_offset,
			       phdr->p_filesz);
			break;
		default: /* Ignore other PT_* */ break;
		}
	}
}

308
asmlinkage void decompress_kernel(void *rmode, memptr heap,
309 310 311
				  unsigned char *input_data,
				  unsigned long input_len,
				  unsigned char *output)
L
Linus Torvalds 已提交
312 313 314
{
	real_mode = rmode;

315 316 317
	if (real_mode->hdr.loadflags & QUIET_FLAG)
		quiet = 1;

318
	if (real_mode->screen_info.orig_video_mode == 7) {
L
Linus Torvalds 已提交
319 320 321 322 323 324 325
		vidmem = (char *) 0xb0000;
		vidport = 0x3b4;
	} else {
		vidmem = (char *) 0xb8000;
		vidport = 0x3d4;
	}

326 327
	lines = real_mode->screen_info.orig_video_lines;
	cols = real_mode->screen_info.orig_video_cols;
L
Linus Torvalds 已提交
328

329
	free_mem_ptr     = heap;	/* Heap */
330
	free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
331

332 333
	if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
		error("Destination address inappropriately aligned");
334
#ifdef CONFIG_X86_64
335
	if (heap > 0x3fffffffffffUL)
336 337
		error("Destination address too large");
#else
338
	if (heap > ((-__PAGE_OFFSET-(512<<20)-1) & 0x7fffffff))
339
		error("Destination address too large");
340
#endif
341
#ifndef CONFIG_RELOCATABLE
342
	if ((unsigned long)output != LOAD_PHYSICAL_ADDR)
343 344
		error("Wrong destination address");
#endif
L
Linus Torvalds 已提交
345

346 347
	if (!quiet)
		putstr("\nDecompressing Linux... ");
348
	decompress(input_data, input_len, NULL, NULL, output, NULL, error);
349
	parse_elf(output);
350 351
	if (!quiet)
		putstr("done.\nBooting the kernel.\n");
352
	return;
L
Linus Torvalds 已提交
353
}