1
2
3
4
5
6
7
8
9
10
11
12#include "misc.h"
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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#define STATIC static
99
100#undef memset
101#undef memcpy
102#define memzero(s, n) memset((s), 0, (n))
103
104
105static void error(char *m);
106
107
108
109
110struct boot_params *real_mode;
111
112void *memset(void *s, int c, size_t n);
113void *memcpy(void *dest, const void *src, size_t n);
114
115#ifdef CONFIG_X86_64
116#define memptr long
117#else
118#define memptr unsigned
119#endif
120
121static memptr free_mem_ptr;
122static memptr free_mem_end_ptr;
123
124static char *vidmem;
125static int vidport;
126static int lines, cols;
127
128#ifdef CONFIG_KERNEL_GZIP
129#include "../../../../lib/decompress_inflate.c"
130#endif
131
132#ifdef CONFIG_KERNEL_BZIP2
133#include "../../../../lib/decompress_bunzip2.c"
134#endif
135
136#ifdef CONFIG_KERNEL_LZMA
137#include "../../../../lib/decompress_unlzma.c"
138#endif
139
140#ifdef CONFIG_KERNEL_XZ
141#include "../../../../lib/decompress_unxz.c"
142#endif
143
144#ifdef CONFIG_KERNEL_LZO
145#include "../../../../lib/decompress_unlzo.c"
146#endif
147
148#ifdef CONFIG_KERNEL_LZ4
149#include "../../../../lib/decompress_unlz4.c"
150#endif
151
152static void scroll(void)
153{
154 int i;
155
156 memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2);
157 for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2)
158 vidmem[i] = ' ';
159}
160
161#define XMTRDY 0x20
162
163#define TXR 0
164#define LSR 5
165static void serial_putchar(int ch)
166{
167 unsigned timeout = 0xffff;
168
169 while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
170 cpu_relax();
171
172 outb(ch, early_serial_base + TXR);
173}
174
175void __putstr(const char *s)
176{
177 int x, y, pos;
178 char c;
179
180 if (early_serial_base) {
181 const char *str = s;
182 while (*str) {
183 if (*str == '\n')
184 serial_putchar('\r');
185 serial_putchar(*str++);
186 }
187 }
188
189 if (real_mode->screen_info.orig_video_mode == 0 &&
190 lines == 0 && cols == 0)
191 return;
192
193 x = real_mode->screen_info.orig_x;
194 y = real_mode->screen_info.orig_y;
195
196 while ((c = *s++) != '\0') {
197 if (c == '\n') {
198 x = 0;
199 if (++y >= lines) {
200 scroll();
201 y--;
202 }
203 } else {
204 vidmem[(x + cols * y) * 2] = c;
205 if (++x >= cols) {
206 x = 0;
207 if (++y >= lines) {
208 scroll();
209 y--;
210 }
211 }
212 }
213 }
214
215 real_mode->screen_info.orig_x = x;
216 real_mode->screen_info.orig_y = y;
217
218 pos = (x + cols * y) * 2;
219 outb(14, vidport);
220 outb(0xff & (pos >> 9), vidport+1);
221 outb(15, vidport);
222 outb(0xff & (pos >> 1), vidport+1);
223}
224
225void *memset(void *s, int c, size_t n)
226{
227 int i;
228 char *ss = s;
229
230 for (i = 0; i < n; i++)
231 ss[i] = c;
232 return s;
233}
234#ifdef CONFIG_X86_32
235void *memcpy(void *dest, const void *src, size_t n)
236{
237 int d0, d1, d2;
238 asm volatile(
239 "rep ; movsl\n\t"
240 "movl %4,%%ecx\n\t"
241 "rep ; movsb\n\t"
242 : "=&c" (d0), "=&D" (d1), "=&S" (d2)
243 : "0" (n >> 2), "g" (n & 3), "1" (dest), "2" (src)
244 : "memory");
245
246 return dest;
247}
248#else
249void *memcpy(void *dest, const void *src, size_t n)
250{
251 long d0, d1, d2;
252 asm volatile(
253 "rep ; movsq\n\t"
254 "movq %4,%%rcx\n\t"
255 "rep ; movsb\n\t"
256 : "=&c" (d0), "=&D" (d1), "=&S" (d2)
257 : "0" (n >> 3), "g" (n & 7), "1" (dest), "2" (src)
258 : "memory");
259
260 return dest;
261}
262#endif
263
264static void error(char *x)
265{
266 error_putstr("\n\n");
267 error_putstr(x);
268 error_putstr("\n\n -- System halted");
269
270 while (1)
271 asm("hlt");
272}
273
274#if CONFIG_X86_NEED_RELOCS
275static void handle_relocations(void *output, unsigned long output_len)
276{
277 int *reloc;
278 unsigned long delta, map, ptr;
279 unsigned long min_addr = (unsigned long)output;
280 unsigned long max_addr = min_addr + output_len;
281
282
283
284
285
286 delta = min_addr - LOAD_PHYSICAL_ADDR;
287 if (!delta) {
288 debug_putstr("No relocation needed... ");
289 return;
290 }
291 debug_putstr("Performing relocations... ");
292
293
294
295
296
297
298
299
300 map = delta - __START_KERNEL_map;
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319 for (reloc = output + output_len - sizeof(*reloc); *reloc; reloc--) {
320 int extended = *reloc;
321 extended += map;
322
323 ptr = (unsigned long)extended;
324 if (ptr < min_addr || ptr > max_addr)
325 error("32-bit relocation outside of kernel!\n");
326
327 *(uint32_t *)ptr += delta;
328 }
329#ifdef CONFIG_X86_64
330 for (reloc--; *reloc; reloc--) {
331 long extended = *reloc;
332 extended += map;
333
334 ptr = (unsigned long)extended;
335 if (ptr < min_addr || ptr > max_addr)
336 error("64-bit relocation outside of kernel!\n");
337
338 *(uint64_t *)ptr += delta;
339 }
340#endif
341}
342#else
343static inline void handle_relocations(void *output, unsigned long output_len)
344{ }
345#endif
346
347static void parse_elf(void *output)
348{
349#ifdef CONFIG_X86_64
350 Elf64_Ehdr ehdr;
351 Elf64_Phdr *phdrs, *phdr;
352#else
353 Elf32_Ehdr ehdr;
354 Elf32_Phdr *phdrs, *phdr;
355#endif
356 void *dest;
357 int i;
358
359 memcpy(&ehdr, output, sizeof(ehdr));
360 if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
361 ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
362 ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
363 ehdr.e_ident[EI_MAG3] != ELFMAG3) {
364 error("Kernel is not a valid ELF file");
365 return;
366 }
367
368 debug_putstr("Parsing ELF... ");
369
370 phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum);
371 if (!phdrs)
372 error("Failed to allocate space for phdrs");
373
374 memcpy(phdrs, output + ehdr.e_phoff, sizeof(*phdrs) * ehdr.e_phnum);
375
376 for (i = 0; i < ehdr.e_phnum; i++) {
377 phdr = &phdrs[i];
378
379 switch (phdr->p_type) {
380 case PT_LOAD:
381#ifdef CONFIG_RELOCATABLE
382 dest = output;
383 dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR);
384#else
385 dest = (void *)(phdr->p_paddr);
386#endif
387 memcpy(dest,
388 output + phdr->p_offset,
389 phdr->p_filesz);
390 break;
391 default: break;
392 }
393 }
394
395 free(phdrs);
396}
397
398asmlinkage void decompress_kernel(void *rmode, memptr heap,
399 unsigned char *input_data,
400 unsigned long input_len,
401 unsigned char *output,
402 unsigned long output_len)
403{
404 real_mode = rmode;
405
406 sanitize_boot_params(real_mode);
407
408 if (real_mode->screen_info.orig_video_mode == 7) {
409 vidmem = (char *) 0xb0000;
410 vidport = 0x3b4;
411 } else {
412 vidmem = (char *) 0xb8000;
413 vidport = 0x3d4;
414 }
415
416 lines = real_mode->screen_info.orig_video_lines;
417 cols = real_mode->screen_info.orig_video_cols;
418
419 console_init();
420 debug_putstr("early console in decompress_kernel\n");
421
422 free_mem_ptr = heap;
423 free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
424
425 if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
426 error("Destination address inappropriately aligned");
427#ifdef CONFIG_X86_64
428 if (heap > 0x3fffffffffffUL)
429 error("Destination address too large");
430#else
431 if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff))
432 error("Destination address too large");
433#endif
434#ifndef CONFIG_RELOCATABLE
435 if ((unsigned long)output != LOAD_PHYSICAL_ADDR)
436 error("Wrong destination address");
437#endif
438
439 debug_putstr("\nDecompressing Linux... ");
440 decompress(input_data, input_len, NULL, NULL, output, NULL, error);
441 parse_elf(output);
442 handle_relocations(output, output_len);
443 debug_putstr("done.\nBooting the kernel.\n");
444 return;
445}
446