Pull bugzilla-5737 into release branch
[linux-2.6] / arch / ppc / boot / common / misc-common.c
1 /*
2  * Misc. bootloader code (almost) all platforms can use
3  *
4  * Author: Johnnie Peters <jpeters@mvista.com>
5  * Editor: Tom Rini <trini@mvista.com>
6  *
7  * Derived from arch/ppc/boot/prep/misc.c
8  *
9  * 2000-2001 (c) MontaVista, Software, Inc.  This file is licensed under
10  * the terms of the GNU General Public License version 2.  This program
11  * is licensed "as is" without any warranty of any kind, whether express
12  * or implied.
13  */
14
15 #include <stdarg.h>     /* for va_ bits */
16 #include <linux/config.h>
17 #include <linux/string.h>
18 #include <linux/zlib.h>
19 #include "nonstdio.h"
20
21 /* If we're on a PReP, assume we have a keyboard controller
22  * Also note, if we're not PReP, we assume you are a serial
23  * console - Tom */
24 #if defined(CONFIG_PPC_PREP) && defined(CONFIG_VGA_CONSOLE)
25 extern void cursor(int x, int y);
26 extern void scroll(void);
27 extern char *vidmem;
28 extern int lines, cols;
29 extern int orig_x, orig_y;
30 extern int keyb_present;
31 extern int CRT_tstc(void);
32 extern int CRT_getc(void);
33 #else
34 int cursor(int x, int y) {return 0;}
35 void scroll(void) {}
36 char vidmem[1];
37 #define lines 0
38 #define cols 0
39 int orig_x = 0;
40 int orig_y = 0;
41 #define keyb_present 0
42 int CRT_tstc(void) {return 0;}
43 int CRT_getc(void) {return 0;}
44 #endif
45
46 extern char *avail_ram;
47 extern char *end_avail;
48 extern char _end[];
49
50 void puts(const char *);
51 void putc(const char c);
52 void puthex(unsigned long val);
53 void gunzip(void *, int, unsigned char *, int *);
54 static int _cvt(unsigned long val, char *buf, long radix, char *digits);
55
56 void _vprintk(void(*putc)(const char), const char *fmt0, va_list ap);
57 unsigned char *ISA_io = NULL;
58
59 #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
60         || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
61         || defined(CONFIG_SERIAL_MPSC_CONSOLE)
62 extern unsigned long com_port;
63
64 extern int serial_tstc(unsigned long com_port);
65 extern unsigned char serial_getc(unsigned long com_port);
66 extern void serial_putc(unsigned long com_port, unsigned char c);
67 #endif
68
69 void pause(void)
70 {
71         puts("pause\n");
72 }
73
74 void exit(void)
75 {
76         puts("exit\n");
77         while(1);
78 }
79
80 int tstc(void)
81 {
82 #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
83         || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
84         || defined(CONFIG_SERIAL_MPSC_CONSOLE)
85         if(keyb_present)
86                 return (CRT_tstc() || serial_tstc(com_port));
87         else
88                 return (serial_tstc(com_port));
89 #else
90         return CRT_tstc();
91 #endif
92 }
93
94 int getc(void)
95 {
96         while (1) {
97 #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
98         || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
99         || defined(CONFIG_SERIAL_MPSC_CONSOLE)
100                 if (serial_tstc(com_port))
101                         return (serial_getc(com_port));
102 #endif /* serial console */
103                 if (keyb_present)
104                         if(CRT_tstc())
105                                 return (CRT_getc());
106         }
107 }
108
109 void
110 putc(const char c)
111 {
112         int x,y;
113
114 #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
115         || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
116         || defined(CONFIG_SERIAL_MPSC_CONSOLE)
117         serial_putc(com_port, c);
118         if ( c == '\n' )
119                 serial_putc(com_port, '\r');
120 #endif /* serial console */
121
122         x = orig_x;
123         y = orig_y;
124
125         if ( c == '\n' ) {
126                 x = 0;
127                 if ( ++y >= lines ) {
128                         scroll();
129                         y--;
130                 }
131         } else if (c == '\r') {
132                 x = 0;
133         } else if (c == '\b') {
134                 if (x > 0) {
135                         x--;
136                 }
137         } else {
138                 vidmem [ ( x + cols * y ) * 2 ] = c;
139                 if ( ++x >= cols ) {
140                         x = 0;
141                         if ( ++y >= lines ) {
142                                 scroll();
143                                 y--;
144                         }
145                 }
146         }
147
148         cursor(x, y);
149
150         orig_x = x;
151         orig_y = y;
152 }
153
154 void puts(const char *s)
155 {
156         int x,y;
157         char c;
158
159         x = orig_x;
160         y = orig_y;
161
162         while ( ( c = *s++ ) != '\0' ) {
163 #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
164         || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
165         || defined(CONFIG_SERIAL_MPSC_CONSOLE)
166                 serial_putc(com_port, c);
167                 if ( c == '\n' ) serial_putc(com_port, '\r');
168 #endif /* serial console */
169
170                 if ( c == '\n' ) {
171                         x = 0;
172                         if ( ++y >= lines ) {
173                                 scroll();
174                                 y--;
175                         }
176                 } else if (c == '\b') {
177                   if (x > 0) {
178                     x--;
179                   }
180                 } else {
181                         vidmem [ ( x + cols * y ) * 2 ] = c;
182                         if ( ++x >= cols ) {
183                                 x = 0;
184                                 if ( ++y >= lines ) {
185                                         scroll();
186                                         y--;
187                                 }
188                         }
189                 }
190         }
191
192         cursor(x, y);
193
194         orig_x = x;
195         orig_y = y;
196 }
197
198 void error(char *x)
199 {
200         puts("\n\n");
201         puts(x);
202         puts("\n\n -- System halted");
203
204         while(1);       /* Halt */
205 }
206
207 static void *zalloc(unsigned size)
208 {
209         void *p = avail_ram;
210
211         size = (size + 7) & -8;
212         avail_ram += size;
213         if (avail_ram > end_avail) {
214                 puts("oops... out of memory\n");
215                 pause();
216         }
217         return p;
218 }
219
220 #define HEAD_CRC        2
221 #define EXTRA_FIELD     4
222 #define ORIG_NAME       8
223 #define COMMENT         0x10
224 #define RESERVED        0xe0
225
226 void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
227 {
228         z_stream s;
229         int r, i, flags;
230
231         /* skip header */
232         i = 10;
233         flags = src[3];
234         if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) {
235                 puts("bad gzipped data\n");
236                 exit();
237         }
238         if ((flags & EXTRA_FIELD) != 0)
239                 i = 12 + src[10] + (src[11] << 8);
240         if ((flags & ORIG_NAME) != 0)
241                 while (src[i++] != 0)
242                         ;
243         if ((flags & COMMENT) != 0)
244                 while (src[i++] != 0)
245                         ;
246         if ((flags & HEAD_CRC) != 0)
247                 i += 2;
248         if (i >= *lenp) {
249                 puts("gunzip: ran out of data in header\n");
250                 exit();
251         }
252
253         /* Initialize ourself. */
254         s.workspace = zalloc(zlib_inflate_workspacesize());
255         r = zlib_inflateInit2(&s, -MAX_WBITS);
256         if (r != Z_OK) {
257                 puts("zlib_inflateInit2 returned "); puthex(r); puts("\n");
258                 exit();
259         }
260         s.next_in = src + i;
261         s.avail_in = *lenp - i;
262         s.next_out = dst;
263         s.avail_out = dstlen;
264         r = zlib_inflate(&s, Z_FINISH);
265         if (r != Z_OK && r != Z_STREAM_END) {
266                 puts("inflate returned "); puthex(r); puts("\n");
267                 exit();
268         }
269         *lenp = s.next_out - (unsigned char *) dst;
270         zlib_inflateEnd(&s);
271 }
272
273 void
274 puthex(unsigned long val)
275 {
276
277         unsigned char buf[10];
278         int i;
279         for (i = 7;  i >= 0;  i--)
280         {
281                 buf[i] = "0123456789ABCDEF"[val & 0x0F];
282                 val >>= 4;
283         }
284         buf[8] = '\0';
285         puts(buf);
286 }
287
288 #define FALSE 0
289 #define TRUE  1
290
291 void
292 _printk(char const *fmt, ...)
293 {
294         va_list ap;
295
296         va_start(ap, fmt);
297         _vprintk(putc, fmt, ap);
298         va_end(ap);
299         return;
300 }
301
302 #define is_digit(c) ((c >= '0') && (c <= '9'))
303
304 void
305 _vprintk(void(*putc)(const char), const char *fmt0, va_list ap)
306 {
307         char c, sign, *cp = 0;
308         int left_prec, right_prec, zero_fill, length = 0, pad, pad_on_right;
309         char buf[32];
310         long val;
311         while ((c = *fmt0++))
312         {
313                 if (c == '%')
314                 {
315                         c = *fmt0++;
316                         left_prec = right_prec = pad_on_right = 0;
317                         if (c == '-')
318                         {
319                                 c = *fmt0++;
320                                 pad_on_right++;
321                         }
322                         if (c == '0')
323                         {
324                                 zero_fill = TRUE;
325                                 c = *fmt0++;
326                         } else
327                         {
328                                 zero_fill = FALSE;
329                         }
330                         while (is_digit(c))
331                         {
332                                 left_prec = (left_prec * 10) + (c - '0');
333                                 c = *fmt0++;
334                         }
335                         if (c == '.')
336                         {
337                                 c = *fmt0++;
338                                 zero_fill++;
339                                 while (is_digit(c))
340                                 {
341                                         right_prec = (right_prec * 10) + (c - '0');
342                                         c = *fmt0++;
343                                 }
344                         } else
345                         {
346                                 right_prec = left_prec;
347                         }
348                         sign = '\0';
349                         switch (c)
350                         {
351                         case 'd':
352                         case 'x':
353                         case 'X':
354                                 val = va_arg(ap, long);
355                                 switch (c)
356                                 {
357                                 case 'd':
358                                         if (val < 0)
359                                         {
360                                                 sign = '-';
361                                                 val = -val;
362                                         }
363                                         length = _cvt(val, buf, 10, "0123456789");
364                                         break;
365                                 case 'x':
366                                         length = _cvt(val, buf, 16, "0123456789abcdef");
367                                         break;
368                                 case 'X':
369                                         length = _cvt(val, buf, 16, "0123456789ABCDEF");
370                                         break;
371                                 }
372                                 cp = buf;
373                                 break;
374                         case 's':
375                                 cp = va_arg(ap, char *);
376                                 length = strlen(cp);
377                                 break;
378                         case 'c':
379                                 c = va_arg(ap, long /*char*/);
380                                 (*putc)(c);
381                                 continue;
382                         default:
383                                 (*putc)('?');
384                         }
385                         pad = left_prec - length;
386                         if (sign != '\0')
387                         {
388                                 pad--;
389                         }
390                         if (zero_fill)
391                         {
392                                 c = '0';
393                                 if (sign != '\0')
394                                 {
395                                         (*putc)(sign);
396                                         sign = '\0';
397                                 }
398                         } else
399                         {
400                                 c = ' ';
401                         }
402                         if (!pad_on_right)
403                         {
404                                 while (pad-- > 0)
405                                 {
406                                         (*putc)(c);
407                                 }
408                         }
409                         if (sign != '\0')
410                         {
411                                 (*putc)(sign);
412                         }
413                         while (length-- > 0)
414                         {
415                                 (*putc)(c = *cp++);
416                                 if (c == '\n')
417                                 {
418                                         (*putc)('\r');
419                                 }
420                         }
421                         if (pad_on_right)
422                         {
423                                 while (pad-- > 0)
424                                 {
425                                         (*putc)(c);
426                                 }
427                         }
428                 } else
429                 {
430                         (*putc)(c);
431                         if (c == '\n')
432                         {
433                                 (*putc)('\r');
434                         }
435                 }
436         }
437 }
438
439 int
440 _cvt(unsigned long val, char *buf, long radix, char *digits)
441 {
442         char temp[80];
443         char *cp = temp;
444         int length = 0;
445         if (val == 0)
446         { /* Special case */
447                 *cp++ = '0';
448         } else
449                 while (val)
450                 {
451                         *cp++ = digits[val % radix];
452                         val /= radix;
453                 }
454         while (cp != temp)
455         {
456                 *buf++ = *--cp;
457                 length++;
458         }
459         *buf = '\0';
460         return (length);
461 }
462
463 void
464 _dump_buf_with_offset(unsigned char *p, int s, unsigned char *base)
465 {
466         int i, c;
467         if ((unsigned int)s > (unsigned int)p)
468         {
469                 s = (unsigned int)s - (unsigned int)p;
470         }
471         while (s > 0)
472         {
473                 if (base)
474                 {
475                         _printk("%06X: ", (int)p - (int)base);
476                 } else
477                 {
478                         _printk("%06X: ", p);
479                 }
480                 for (i = 0;  i < 16;  i++)
481                 {
482                         if (i < s)
483                         {
484                                 _printk("%02X", p[i] & 0xFF);
485                         } else
486                         {
487                                 _printk("  ");
488                         }
489                         if ((i % 2) == 1) _printk(" ");
490                         if ((i % 8) == 7) _printk(" ");
491                 }
492                 _printk(" |");
493                 for (i = 0;  i < 16;  i++)
494                 {
495                         if (i < s)
496                         {
497                                 c = p[i] & 0xFF;
498                                 if ((c < 0x20) || (c >= 0x7F)) c = '.';
499                         } else
500                         {
501                                 c = ' ';
502                         }
503                         _printk("%c", c);
504                 }
505                 _printk("|\n");
506                 s -= 16;
507                 p += 16;
508         }
509 }
510
511 void
512 _dump_buf(unsigned char *p, int s)
513 {
514         _printk("\n");
515         _dump_buf_with_offset(p, s, 0);
516 }
517
518 /* Very simple inb/outb routines.  We declare ISA_io to be 0 above, and
519  * then modify it on platforms which need to.  We do it like this
520  * because on some platforms we give inb/outb an exact location, and
521  * on others it's an offset from a given location. -- Tom
522  */
523
524 void ISA_init(unsigned long base)
525 {
526         ISA_io = (unsigned char *)base;
527 }
528
529 void
530 outb(int port, unsigned char val)
531 {
532         /* Ensure I/O operations complete */
533         __asm__ volatile("eieio");
534         ISA_io[port] = val;
535 }
536
537 unsigned char
538 inb(int port)
539 {
540         /* Ensure I/O operations complete */
541         __asm__ volatile("eieio");
542         return (ISA_io[port]);
543 }
544
545 /*
546  * Local variables:
547  *  c-indent-level: 8
548  *  c-basic-offset: 8
549  *  tab-width: 8
550  * End:
551  */