/* * Misc. bootloader code (almost) all platforms can use * * Author: Johnnie Peters * Editor: Tom Rini * * Derived from arch/ppc/boot/prep/misc.c * * 2000-2001 (c) MontaVista, Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */ #include /* for va_ bits */ #include #include #include #include "nonstdio.h" /* If we're on a PReP, assume we have a keyboard controller * Also note, if we're not PReP, we assume you are a serial * console - Tom */ #if defined(CONFIG_PPC_PREP) && defined(CONFIG_VGA_CONSOLE) extern void cursor(int x, int y); extern void scroll(void); extern char *vidmem; extern int lines, cols; extern int orig_x, orig_y; extern int keyb_present; extern int CRT_tstc(void); extern int CRT_getc(void); #else int cursor(int x, int y) {return 0;} void scroll(void) {} char vidmem[1]; #define lines 0 #define cols 0 int orig_x = 0; int orig_y = 0; #define keyb_present 0 int CRT_tstc(void) {return 0;} int CRT_getc(void) {return 0;} #endif extern char *avail_ram; extern char *end_avail; extern char _end[]; void puts(const char *); void putc(const char c); void puthex(unsigned long val); void gunzip(void *, int, unsigned char *, int *); static int _cvt(unsigned long val, char *buf, long radix, char *digits); void _vprintk(void(*putc)(const char), const char *fmt0, va_list ap); unsigned char *ISA_io = NULL; #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ || defined(CONFIG_SERIAL_MPSC_CONSOLE) extern unsigned long com_port; extern int serial_tstc(unsigned long com_port); extern unsigned char serial_getc(unsigned long com_port); extern void serial_putc(unsigned long com_port, unsigned char c); #endif void pause(void) { puts("pause\n"); } void exit(void) { puts("exit\n"); while(1); } int tstc(void) { #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ || defined(CONFIG_SERIAL_MPSC_CONSOLE) if(keyb_present) return (CRT_tstc() || serial_tstc(com_port)); else return (serial_tstc(com_port)); #else return CRT_tstc(); #endif } int getc(void) { while (1) { #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ || defined(CONFIG_SERIAL_MPSC_CONSOLE) if (serial_tstc(com_port)) return (serial_getc(com_port)); #endif /* serial console */ if (keyb_present) if(CRT_tstc()) return (CRT_getc()); } } void putc(const char c) { int x,y; #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ || defined(CONFIG_SERIAL_MPSC_CONSOLE) serial_putc(com_port, c); if ( c == '\n' ) serial_putc(com_port, '\r'); #endif /* serial console */ x = orig_x; y = orig_y; if ( c == '\n' ) { x = 0; if ( ++y >= lines ) { scroll(); y--; } } else if (c == '\r') { x = 0; } else if (c == '\b') { if (x > 0) { x--; } } else { vidmem [ ( x + cols * y ) * 2 ] = c; if ( ++x >= cols ) { x = 0; if ( ++y >= lines ) { scroll(); y--; } } } cursor(x, y); orig_x = x; orig_y = y; } void puts(const char *s) { int x,y; char c; x = orig_x; y = orig_y; while ( ( c = *s++ ) != '\0' ) { #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ || defined(CONFIG_SERIAL_MPSC_CONSOLE) serial_putc(com_port, c); if ( c == '\n' ) serial_putc(com_port, '\r'); #endif /* serial console */ if ( c == '\n' ) { x = 0; if ( ++y >= lines ) { scroll(); y--; } } else if (c == '\b') { if (x > 0) { x--; } } else { vidmem [ ( x + cols * y ) * 2 ] = c; if ( ++x >= cols ) { x = 0; if ( ++y >= lines ) { scroll(); y--; } } } } cursor(x, y); orig_x = x; orig_y = y; } void error(char *x) { puts("\n\n"); puts(x); puts("\n\n -- System halted"); while(1); /* Halt */ } static void *zalloc(unsigned size) { void *p = avail_ram; size = (size + 7) & -8; avail_ram += size; if (avail_ram > end_avail) { puts("oops... out of memory\n"); pause(); } return p; } #define HEAD_CRC 2 #define EXTRA_FIELD 4 #define ORIG_NAME 8 #define COMMENT 0x10 #define RESERVED 0xe0 void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) { z_stream s; int r, i, flags; /* skip header */ i = 10; flags = src[3]; if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) { puts("bad gzipped data\n"); exit(); } if ((flags & EXTRA_FIELD) != 0) i = 12 + src[10] + (src[11] << 8); if ((flags & ORIG_NAME) != 0) while (src[i++] != 0) ; if ((flags & COMMENT) != 0) while (src[i++] != 0) ; if ((flags & HEAD_CRC) != 0) i += 2; if (i >= *lenp) { puts("gunzip: ran out of data in header\n"); exit(); } /* Initialize ourself. */ s.workspace = zalloc(zlib_inflate_workspacesize()); r = zlib_inflateInit2(&s, -MAX_WBITS); if (r != Z_OK) { puts("zlib_inflateInit2 returned "); puthex(r); puts("\n"); exit(); } s.next_in = src + i; s.avail_in = *lenp - i; s.next_out = dst; s.avail_out = dstlen; r = zlib_inflate(&s, Z_FINISH); if (r != Z_OK && r != Z_STREAM_END) { puts("inflate returned "); puthex(r); puts("\n"); exit(); } *lenp = s.next_out - (unsigned char *) dst; zlib_inflateEnd(&s); } void puthex(unsigned long val) { unsigned char buf[10]; int i; for (i = 7; i >= 0; i--) { buf[i] = "0123456789ABCDEF"[val & 0x0F]; val >>= 4; } buf[8] = '\0'; puts(buf); } #define FALSE 0 #define TRUE 1 void _printk(char const *fmt, ...) { va_list ap; va_start(ap, fmt); _vprintk(putc, fmt, ap); va_end(ap); return; } #define is_digit(c) ((c >= '0') && (c <= '9')) void _vprintk(void(*putc)(const char), const char *fmt0, va_list ap) { char c, sign, *cp = 0; int left_prec, right_prec, zero_fill, length = 0, pad, pad_on_right; char buf[32]; long val; while ((c = *fmt0++)) { if (c == '%') { c = *fmt0++; left_prec = right_prec = pad_on_right = 0; if (c == '-') { c = *fmt0++; pad_on_right++; } if (c == '0') { zero_fill = TRUE; c = *fmt0++; } else { zero_fill = FALSE; } while (is_digit(c)) { left_prec = (left_prec * 10) + (c - '0'); c = *fmt0++; } if (c == '.') { c = *fmt0++; zero_fill++; while (is_digit(c)) { right_prec = (right_prec * 10) + (c - '0'); c = *fmt0++; } } else { right_prec = left_prec; } sign = '\0'; switch (c) { case 'd': case 'x': case 'X': val = va_arg(ap, long); switch (c) { case 'd': if (val < 0) { sign = '-'; val = -val; } length = _cvt(val, buf, 10, "0123456789"); break; case 'x': length = _cvt(val, buf, 16, "0123456789abcdef"); break; case 'X': length = _cvt(val, buf, 16, "0123456789ABCDEF"); break; } cp = buf; break; case 's': cp = va_arg(ap, char *); length = strlen(cp); break; case 'c': c = va_arg(ap, long /*char*/); (*putc)(c); continue; default: (*putc)('?'); } pad = left_prec - length; if (sign != '\0') { pad--; } if (zero_fill) { c = '0'; if (sign != '\0') { (*putc)(sign); sign = '\0'; } } else { c = ' '; } if (!pad_on_right) { while (pad-- > 0) { (*putc)(c); } } if (sign != '\0') { (*putc)(sign); } while (length-- > 0) { (*putc)(c = *cp++); if (c == '\n') { (*putc)('\r'); } } if (pad_on_right) { while (pad-- > 0) { (*putc)(c); } } } else { (*putc)(c); if (c == '\n') { (*putc)('\r'); } } } } int _cvt(unsigned long val, char *buf, long radix, char *digits) { char temp[80]; char *cp = temp; int length = 0; if (val == 0) { /* Special case */ *cp++ = '0'; } else while (val) { *cp++ = digits[val % radix]; val /= radix; } while (cp != temp) { *buf++ = *--cp; length++; } *buf = '\0'; return (length); } void _dump_buf_with_offset(unsigned char *p, int s, unsigned char *base) { int i, c; if ((unsigned int)s > (unsigned int)p) { s = (unsigned int)s - (unsigned int)p; } while (s > 0) { if (base) { _printk("%06X: ", (int)p - (int)base); } else { _printk("%06X: ", p); } for (i = 0; i < 16; i++) { if (i < s) { _printk("%02X", p[i] & 0xFF); } else { _printk(" "); } if ((i % 2) == 1) _printk(" "); if ((i % 8) == 7) _printk(" "); } _printk(" |"); for (i = 0; i < 16; i++) { if (i < s) { c = p[i] & 0xFF; if ((c < 0x20) || (c >= 0x7F)) c = '.'; } else { c = ' '; } _printk("%c", c); } _printk("|\n"); s -= 16; p += 16; } } void _dump_buf(unsigned char *p, int s) { _printk("\n"); _dump_buf_with_offset(p, s, 0); } /* Very simple inb/outb routines. We declare ISA_io to be 0 above, and * then modify it on platforms which need to. We do it like this * because on some platforms we give inb/outb an exact location, and * on others it's an offset from a given location. -- Tom */ void ISA_init(unsigned long base) { ISA_io = (unsigned char *)base; } void outb(int port, unsigned char val) { /* Ensure I/O operations complete */ __asm__ volatile("eieio"); ISA_io[port] = val; } unsigned char inb(int port) { /* Ensure I/O operations complete */ __asm__ volatile("eieio"); return (ISA_io[port]); } /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */