556 lines
9.9 KiB
C
556 lines
9.9 KiB
C
/*
|
|
* Misc. bootloader code (almost) all platforms can use
|
|
*
|
|
* Author: Johnnie Peters <jpeters@mvista.com>
|
|
* Editor: Tom Rini <trini@mvista.com>
|
|
*
|
|
* 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 <stdarg.h> /* for va_ bits */
|
|
#include <linux/string.h>
|
|
#include <linux/zlib.h>
|
|
#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) \
|
|
|| defined(CONFIG_SERIAL_UARTLITE_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) \
|
|
|| defined(CONFIG_SERIAL_UARTLITE_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) \
|
|
|| defined(CONFIG_SERIAL_UARTLITE_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) \
|
|
|| defined(CONFIG_SERIAL_UARTLITE_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) \
|
|
|| defined(CONFIG_SERIAL_UARTLITE_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:
|
|
*/
|