]>
Commit | Line | Data |
---|---|---|
b920de1b DH |
1 | /* MN10300 Miscellaneous helper routines for kernel decompressor |
2 | * | |
3 | * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. | |
4 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | |
5 | * Modified by David Howells (dhowells@redhat.com) | |
6 | * - Derived from arch/x86/boot/compressed/misc_32.c | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public Licence | |
10 | * as published by the Free Software Foundation; either version | |
11 | * 2 of the Licence, or (at your option) any later version. | |
12 | */ | |
13 | #include <linux/compiler.h> | |
14 | #include <asm/serial-regs.h> | |
15 | #include "misc.h" | |
16 | ||
17 | #ifndef CONFIG_GDBSTUB_ON_TTYSx | |
18 | /* display 'Uncompressing Linux... ' messages on ttyS0 or ttyS1 */ | |
19 | #if 1 /* ttyS0 */ | |
20 | #define CYG_DEV_BASE 0xA6FB0000 | |
21 | #else /* ttyS1 */ | |
22 | #define CYG_DEV_BASE 0xA6FC0000 | |
23 | #endif | |
24 | ||
25 | #define CYG_DEV_THR (*((volatile __u8*)(CYG_DEV_BASE + 0x00))) | |
26 | #define CYG_DEV_MCR (*((volatile __u8*)(CYG_DEV_BASE + 0x10))) | |
27 | #define SIO_MCR_DTR 0x01 | |
28 | #define SIO_MCR_RTS 0x02 | |
29 | #define CYG_DEV_LSR (*((volatile __u8*)(CYG_DEV_BASE + 0x14))) | |
30 | #define SIO_LSR_THRE 0x20 /* transmitter holding register empty */ | |
31 | #define SIO_LSR_TEMT 0x40 /* transmitter register empty */ | |
32 | #define CYG_DEV_MSR (*((volatile __u8*)(CYG_DEV_BASE + 0x18))) | |
33 | #define SIO_MSR_CTS 0x10 /* clear to send */ | |
34 | #define SIO_MSR_DSR 0x20 /* data set ready */ | |
35 | ||
36 | #define LSR_WAIT_FOR(STATE) \ | |
37 | do { while (!(CYG_DEV_LSR & SIO_LSR_##STATE)) {} } while (0) | |
38 | #define FLOWCTL_QUERY(LINE) \ | |
39 | ({ CYG_DEV_MSR & SIO_MSR_##LINE; }) | |
40 | #define FLOWCTL_WAIT_FOR(LINE) \ | |
41 | do { while (!(CYG_DEV_MSR & SIO_MSR_##LINE)) {} } while (0) | |
42 | #define FLOWCTL_CLEAR(LINE) \ | |
43 | do { CYG_DEV_MCR &= ~SIO_MCR_##LINE; } while (0) | |
44 | #define FLOWCTL_SET(LINE) \ | |
45 | do { CYG_DEV_MCR |= SIO_MCR_##LINE; } while (0) | |
46 | #endif | |
47 | ||
48 | /* | |
49 | * gzip declarations | |
50 | */ | |
51 | ||
52 | #define OF(args) args | |
53 | #define STATIC static | |
54 | ||
55 | #undef memset | |
56 | #undef memcpy | |
57 | ||
58 | static inline void *memset(const void *s, int c, size_t n) | |
59 | { | |
60 | int i; | |
61 | char *ss = (char *) s; | |
62 | ||
63 | for (i = 0; i < n; i++) | |
64 | ss[i] = c; | |
65 | return (void *)s; | |
66 | } | |
67 | ||
68 | #define memzero(s, n) memset((s), 0, (n)) | |
69 | ||
70 | static inline void *memcpy(void *__dest, const void *__src, size_t __n) | |
71 | { | |
72 | int i; | |
73 | const char *s = __src; | |
74 | char *d = __dest; | |
75 | ||
76 | for (i = 0; i < __n; i++) | |
77 | d[i] = s[i]; | |
78 | return __dest; | |
79 | } | |
80 | ||
81 | typedef unsigned char uch; | |
82 | typedef unsigned short ush; | |
83 | typedef unsigned long ulg; | |
84 | ||
85 | #define WSIZE 0x8000 /* Window size must be at least 32k, and a power of | |
86 | * two */ | |
87 | ||
88 | static uch *inbuf; /* input buffer */ | |
89 | static uch window[WSIZE]; /* sliding window buffer */ | |
90 | ||
91 | static unsigned insize; /* valid bytes in inbuf */ | |
92 | static unsigned inptr; /* index of next byte to be processed in inbuf */ | |
93 | static unsigned outcnt; /* bytes in output buffer */ | |
94 | ||
95 | /* gzip flag byte */ | |
96 | #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ | |
97 | #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ | |
98 | #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ | |
99 | #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ | |
100 | #define COMMENT 0x10 /* bit 4 set: file comment present */ | |
101 | #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ | |
102 | #define RESERVED 0xC0 /* bit 6,7: reserved */ | |
103 | ||
104 | /* Diagnostic functions */ | |
105 | #ifdef DEBUG | |
106 | # define Assert(cond, msg) { if (!(cond)) error(msg); } | |
107 | # define Trace(x) fprintf x | |
108 | # define Tracev(x) { if (verbose) fprintf x ; } | |
109 | # define Tracevv(x) { if (verbose > 1) fprintf x ; } | |
110 | # define Tracec(c, x) { if (verbose && (c)) fprintf x ; } | |
111 | # define Tracecv(c, x) { if (verbose > 1 && (c)) fprintf x ; } | |
112 | #else | |
113 | # define Assert(cond, msg) | |
114 | # define Trace(x) | |
115 | # define Tracev(x) | |
116 | # define Tracevv(x) | |
117 | # define Tracec(c, x) | |
118 | # define Tracecv(c, x) | |
119 | #endif | |
120 | ||
121 | static int fill_inbuf(void); | |
122 | static void flush_window(void); | |
123 | static void error(const char *) __attribute__((noreturn)); | |
124 | static void kputs(const char *); | |
125 | ||
126 | static inline unsigned char get_byte(void) | |
127 | { | |
128 | unsigned char ch = inptr < insize ? inbuf[inptr++] : fill_inbuf(); | |
129 | ||
130 | #if 0 | |
131 | char hex[3]; | |
132 | hex[0] = ((ch & 0x0f) > 9) ? | |
133 | ((ch & 0x0f) + 'A' - 0xa) : ((ch & 0x0f) + '0'); | |
134 | hex[1] = ((ch >> 4) > 9) ? | |
135 | ((ch >> 4) + 'A' - 0xa) : ((ch >> 4) + '0'); | |
136 | hex[2] = 0; | |
137 | kputs(hex); | |
138 | #endif | |
139 | return ch; | |
140 | } | |
141 | ||
142 | /* | |
143 | * This is set up by the setup-routine at boot-time | |
144 | */ | |
145 | #define EXT_MEM_K (*(unsigned short *)0x90002) | |
146 | #ifndef STANDARD_MEMORY_BIOS_CALL | |
147 | #define ALT_MEM_K (*(unsigned long *) 0x901e0) | |
148 | #endif | |
149 | #define SCREEN_INFO (*(struct screen_info *)0x90000) | |
150 | ||
151 | static long bytes_out; | |
152 | static uch *output_data; | |
153 | static unsigned long output_ptr; | |
154 | ||
155 | ||
b920de1b DH |
156 | static unsigned long free_mem_ptr = (unsigned long) &end; |
157 | static unsigned long free_mem_end_ptr = (unsigned long) &end + 0x90000; | |
158 | ||
b920de1b DH |
159 | #define INPLACE_MOVE_ROUTINE 0x1000 |
160 | #define LOW_BUFFER_START 0x2000 | |
161 | #define LOW_BUFFER_END 0x90000 | |
162 | #define LOW_BUFFER_SIZE (LOW_BUFFER_END - LOW_BUFFER_START) | |
163 | #define HEAP_SIZE 0x3000 | |
164 | static int high_loaded; | |
165 | static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; | |
166 | ||
167 | static char *vidmem = (char *)0xb8000; | |
168 | static int lines, cols; | |
169 | ||
1490cf5f | 170 | #define BOOTLOADER_INFLATE |
b920de1b DH |
171 | #include "../../../../lib/inflate.c" |
172 | ||
b920de1b DH |
173 | static inline void scroll(void) |
174 | { | |
175 | int i; | |
176 | ||
177 | memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2); | |
178 | for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2) | |
179 | vidmem[i] = ' '; | |
180 | } | |
181 | ||
182 | static inline void kputchar(unsigned char ch) | |
183 | { | |
184 | #ifdef CONFIG_MN10300_UNIT_ASB2305 | |
185 | while (SC0STR & SC01STR_TBF) | |
186 | continue; | |
187 | ||
188 | if (ch == 0x0a) { | |
189 | SC0TXB = 0x0d; | |
190 | while (SC0STR & SC01STR_TBF) | |
191 | continue; | |
192 | } | |
193 | ||
194 | SC0TXB = ch; | |
195 | ||
196 | #else | |
197 | while (SC1STR & SC01STR_TBF) | |
198 | continue; | |
199 | ||
200 | if (ch == 0x0a) { | |
201 | SC1TXB = 0x0d; | |
202 | while (SC1STR & SC01STR_TBF) | |
203 | continue; | |
204 | } | |
205 | ||
206 | SC1TXB = ch; | |
207 | ||
208 | #endif | |
209 | } | |
210 | ||
211 | static void kputs(const char *s) | |
212 | { | |
213 | #ifdef CONFIG_DEBUG_DECOMPRESS_KERNEL | |
214 | #ifndef CONFIG_GDBSTUB_ON_TTYSx | |
215 | char ch; | |
216 | ||
217 | FLOWCTL_SET(DTR); | |
218 | ||
219 | while (*s) { | |
220 | LSR_WAIT_FOR(THRE); | |
221 | ||
222 | ch = *s++; | |
223 | if (ch == 0x0a) { | |
224 | CYG_DEV_THR = 0x0d; | |
225 | LSR_WAIT_FOR(THRE); | |
226 | } | |
227 | CYG_DEV_THR = ch; | |
228 | } | |
229 | ||
230 | FLOWCTL_CLEAR(DTR); | |
231 | #else | |
232 | ||
233 | for (; *s; s++) | |
234 | kputchar(*s); | |
235 | ||
236 | #endif | |
237 | #endif /* CONFIG_DEBUG_DECOMPRESS_KERNEL */ | |
238 | } | |
239 | ||
240 | /* =========================================================================== | |
241 | * Fill the input buffer. This is called only when the buffer is empty | |
242 | * and at least one byte is really needed. | |
243 | */ | |
244 | static int fill_inbuf() | |
245 | { | |
246 | if (insize != 0) | |
247 | error("ran out of input data\n"); | |
248 | ||
249 | inbuf = input_data; | |
250 | insize = input_len; | |
251 | inptr = 1; | |
252 | return inbuf[0]; | |
253 | } | |
254 | ||
255 | /* =========================================================================== | |
256 | * Write the output window window[0..outcnt-1] and update crc and bytes_out. | |
257 | * (Used for the decompressed data only.) | |
258 | */ | |
259 | static void flush_window_low(void) | |
260 | { | |
261 | ulg c = crc; /* temporary variable */ | |
262 | unsigned n; | |
263 | uch *in, *out, ch; | |
264 | ||
265 | in = window; | |
266 | out = &output_data[output_ptr]; | |
267 | for (n = 0; n < outcnt; n++) { | |
268 | ch = *out++ = *in++; | |
269 | c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); | |
270 | } | |
271 | crc = c; | |
272 | bytes_out += (ulg)outcnt; | |
273 | output_ptr += (ulg)outcnt; | |
274 | outcnt = 0; | |
275 | } | |
276 | ||
277 | static void flush_window_high(void) | |
278 | { | |
279 | ulg c = crc; /* temporary variable */ | |
280 | unsigned n; | |
281 | uch *in, ch; | |
282 | in = window; | |
283 | for (n = 0; n < outcnt; n++) { | |
284 | ch = *output_data++ = *in++; | |
285 | if ((ulg) output_data == LOW_BUFFER_END) | |
286 | output_data = high_buffer_start; | |
287 | c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); | |
288 | } | |
289 | crc = c; | |
290 | bytes_out += (ulg)outcnt; | |
291 | outcnt = 0; | |
292 | } | |
293 | ||
294 | static void flush_window(void) | |
295 | { | |
296 | if (high_loaded) | |
297 | flush_window_high(); | |
298 | else | |
299 | flush_window_low(); | |
300 | } | |
301 | ||
302 | static void error(const char *x) | |
303 | { | |
304 | kputs("\n\n"); | |
305 | kputs(x); | |
306 | kputs("\n\n -- System halted"); | |
307 | ||
308 | while (1) | |
309 | /* Halt */; | |
310 | } | |
311 | ||
312 | #define STACK_SIZE (4096) | |
313 | ||
314 | long user_stack[STACK_SIZE]; | |
315 | ||
316 | struct { | |
317 | long *a; | |
318 | short b; | |
319 | } stack_start = { &user_stack[STACK_SIZE], 0 }; | |
320 | ||
321 | void setup_normal_output_buffer(void) | |
322 | { | |
323 | #ifdef STANDARD_MEMORY_BIOS_CALL | |
324 | if (EXT_MEM_K < 1024) | |
325 | error("Less than 2MB of memory.\n"); | |
326 | #else | |
327 | if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) | |
328 | error("Less than 2MB of memory.\n"); | |
329 | #endif | |
330 | output_data = (char *) 0x100000; /* Points to 1M */ | |
331 | } | |
332 | ||
333 | struct moveparams { | |
334 | uch *low_buffer_start; | |
335 | int lcount; | |
336 | uch *high_buffer_start; | |
337 | int hcount; | |
338 | }; | |
339 | ||
340 | void setup_output_buffer_if_we_run_high(struct moveparams *mv) | |
341 | { | |
342 | high_buffer_start = (uch *)(((ulg) &end) + HEAP_SIZE); | |
343 | #ifdef STANDARD_MEMORY_BIOS_CALL | |
344 | if (EXT_MEM_K < (3 * 1024)) | |
345 | error("Less than 4MB of memory.\n"); | |
346 | #else | |
347 | if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3 * 1024)) | |
348 | error("Less than 4MB of memory.\n"); | |
349 | #endif | |
350 | mv->low_buffer_start = output_data = (char *) LOW_BUFFER_START; | |
351 | high_loaded = 1; | |
352 | free_mem_end_ptr = (long) high_buffer_start; | |
353 | if (0x100000 + LOW_BUFFER_SIZE > (ulg) high_buffer_start) { | |
354 | high_buffer_start = (uch *)(0x100000 + LOW_BUFFER_SIZE); | |
355 | mv->hcount = 0; /* say: we need not to move high_buffer */ | |
356 | } else { | |
357 | mv->hcount = -1; | |
358 | } | |
359 | mv->high_buffer_start = high_buffer_start; | |
360 | } | |
361 | ||
362 | void close_output_buffer_if_we_run_high(struct moveparams *mv) | |
363 | { | |
364 | mv->lcount = bytes_out; | |
365 | if (bytes_out > LOW_BUFFER_SIZE) { | |
366 | mv->lcount = LOW_BUFFER_SIZE; | |
367 | if (mv->hcount) | |
368 | mv->hcount = bytes_out - LOW_BUFFER_SIZE; | |
369 | } else { | |
370 | mv->hcount = 0; | |
371 | } | |
372 | } | |
373 | ||
374 | #undef DEBUGFLAG | |
375 | #ifdef DEBUGFLAG | |
376 | int debugflag; | |
377 | #endif | |
378 | ||
379 | int decompress_kernel(struct moveparams *mv) | |
380 | { | |
381 | #ifdef DEBUGFLAG | |
382 | while (!debugflag) | |
383 | barrier(); | |
384 | #endif | |
385 | ||
386 | output_data = (char *) CONFIG_KERNEL_TEXT_ADDRESS; | |
387 | ||
388 | makecrc(); | |
389 | kputs("Uncompressing Linux... "); | |
390 | gunzip(); | |
391 | kputs("Ok, booting the kernel.\n"); | |
392 | return 0; | |
393 | } |