2007-10-11 13:17:24 +04:00
# include <linux/console.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/string.h>
# include <linux/screen_info.h>
# include <asm/io.h>
# include <asm/processor.h>
# include <asm/fcntl.h>
2007-10-16 04:13:22 +04:00
# include <asm/setup.h>
2007-10-11 13:17:24 +04:00
# include <xen/hvc-console.h>
2005-04-17 02:20:36 +04:00
2007-10-11 13:17:24 +04:00
/* Simple VGA output */
# define VGABASE (__ISA_IO_base + 0xb8000)
static int max_ypos = 25 , max_xpos = 80 ;
static int current_ypos = 25 , current_xpos = 0 ;
static void early_vga_write ( struct console * con , const char * str , unsigned n )
{
char c ;
int i , k , j ;
while ( ( c = * str + + ) ! = ' \0 ' & & n - - > 0 ) {
if ( current_ypos > = max_ypos ) {
/* scroll 1 line up */
for ( k = 1 , j = 0 ; k < max_ypos ; k + + , j + + ) {
for ( i = 0 ; i < max_xpos ; i + + ) {
writew ( readw ( VGABASE + 2 * ( max_xpos * k + i ) ) ,
VGABASE + 2 * ( max_xpos * j + i ) ) ;
}
}
for ( i = 0 ; i < max_xpos ; i + + )
writew ( 0x720 , VGABASE + 2 * ( max_xpos * j + i ) ) ;
current_ypos = max_ypos - 1 ;
}
if ( c = = ' \n ' ) {
current_xpos = 0 ;
current_ypos + + ;
} else if ( c ! = ' \r ' ) {
writew ( ( ( 0x7 < < 8 ) | ( unsigned short ) c ) ,
VGABASE + 2 * ( max_xpos * current_ypos +
current_xpos + + ) ) ;
if ( current_xpos > = max_xpos ) {
current_xpos = 0 ;
current_ypos + + ;
}
}
}
}
static struct console early_vga_console = {
. name = " earlyvga " ,
. write = early_vga_write ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
} ;
/* Serial functions loosely based on a similar package from Klaus P. Gerlicher */
static int early_serial_base = 0x3f8 ; /* ttyS0 */
# define XMTRDY 0x20
# define DLAB 0x80
# define TXR 0 /* Transmit register (WRITE) */
# define RXR 0 /* Receive register (READ) */
# define IER 1 /* Interrupt Enable */
# define IIR 2 /* Interrupt ID */
# define FCR 2 /* FIFO control */
# define LCR 3 /* Line control */
# define MCR 4 /* Modem control */
# define LSR 5 /* Line Status */
# define MSR 6 /* Modem Status */
# define DLL 0 /* Divisor Latch Low */
# define DLH 1 /* Divisor latch High */
static int early_serial_putc ( unsigned char ch )
{
unsigned timeout = 0xffff ;
while ( ( inb ( early_serial_base + LSR ) & XMTRDY ) = = 0 & & - - timeout )
cpu_relax ( ) ;
outb ( ch , early_serial_base + TXR ) ;
return timeout ? 0 : - 1 ;
}
static void early_serial_write ( struct console * con , const char * s , unsigned n )
{
while ( * s & & n - - > 0 ) {
if ( * s = = ' \n ' )
early_serial_putc ( ' \r ' ) ;
early_serial_putc ( * s ) ;
s + + ;
}
}
# define DEFAULT_BAUD 9600
static __init void early_serial_init ( char * s )
{
unsigned char c ;
unsigned divisor ;
unsigned baud = DEFAULT_BAUD ;
char * e ;
if ( * s = = ' , ' )
+ + s ;
if ( * s ) {
unsigned port ;
2008-02-29 15:25:30 +03:00
if ( ! strncmp ( s , " 0x " , 2 ) ) {
2007-10-11 13:17:24 +04:00
early_serial_base = simple_strtoul ( s , & e , 16 ) ;
} else {
static int bases [ ] = { 0x3f8 , 0x2f8 } ;
2008-02-29 15:25:30 +03:00
if ( ! strncmp ( s , " ttyS " , 4 ) )
2007-10-11 13:17:24 +04:00
s + = 4 ;
port = simple_strtoul ( s , & e , 10 ) ;
if ( port > 1 | | s = = e )
port = 0 ;
early_serial_base = bases [ port ] ;
}
s + = strcspn ( s , " , " ) ;
if ( * s = = ' , ' )
s + + ;
}
outb ( 0x3 , early_serial_base + LCR ) ; /* 8n1 */
outb ( 0 , early_serial_base + IER ) ; /* no interrupt */
outb ( 0 , early_serial_base + FCR ) ; /* no fifo */
outb ( 0x3 , early_serial_base + MCR ) ; /* DTR + RTS */
if ( * s ) {
baud = simple_strtoul ( s , & e , 0 ) ;
if ( baud = = 0 | | s = = e )
baud = DEFAULT_BAUD ;
}
divisor = 115200 / baud ;
c = inb ( early_serial_base + LCR ) ;
outb ( c | DLAB , early_serial_base + LCR ) ;
outb ( divisor & 0xff , early_serial_base + DLL ) ;
outb ( ( divisor > > 8 ) & 0xff , early_serial_base + DLH ) ;
outb ( c & ~ DLAB , early_serial_base + LCR ) ;
}
static struct console early_serial_console = {
. name = " earlyser " ,
. write = early_serial_write ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
} ;
/* Console interface to a host file on AMD's SimNow! */
static int simnow_fd ;
enum {
MAGIC1 = 0xBACCD00A ,
MAGIC2 = 0xCA110000 ,
XOPEN = 5 ,
XWRITE = 4 ,
} ;
static noinline long simnow ( long cmd , long a , long b , long c )
{
long ret ;
asm volatile ( " cpuid " :
" =a " ( ret ) :
" b " ( a ) , " c " ( b ) , " d " ( c ) , " 0 " ( MAGIC1 ) , " D " ( cmd + MAGIC2 ) ) ;
return ret ;
}
static void __init simnow_init ( char * str )
{
char * fn = " klog " ;
if ( * str = = ' = ' )
fn = + + str ;
/* error ignored */
simnow_fd = simnow ( XOPEN , ( unsigned long ) fn , O_WRONLY | O_APPEND | O_CREAT , 0644 ) ;
}
static void simnow_write ( struct console * con , const char * s , unsigned n )
{
simnow ( XWRITE , simnow_fd , ( unsigned long ) s , n ) ;
}
static struct console simnow_console = {
. name = " simnow " ,
. write = simnow_write ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
} ;
/* Direct interface for emergencies */
2008-02-01 19:49:42 +03:00
static struct console * early_console = & early_vga_console ;
2007-10-11 13:17:24 +04:00
static int early_console_initialized = 0 ;
void early_printk ( const char * fmt , . . . )
{
char buf [ 512 ] ;
int n ;
va_list ap ;
2008-02-29 15:25:30 +03:00
va_start ( ap , fmt ) ;
n = vscnprintf ( buf , 512 , fmt , ap ) ;
early_console - > write ( early_console , buf , n ) ;
2007-10-11 13:17:24 +04:00
va_end ( ap ) ;
}
static int __initdata keep_early ;
static int __init setup_early_printk ( char * buf )
{
if ( ! buf )
return 0 ;
if ( early_console_initialized )
return 0 ;
early_console_initialized = 1 ;
if ( strstr ( buf , " keep " ) )
keep_early = 1 ;
if ( ! strncmp ( buf , " serial " , 6 ) ) {
early_serial_init ( buf + 6 ) ;
early_console = & early_serial_console ;
} else if ( ! strncmp ( buf , " ttyS " , 4 ) ) {
early_serial_init ( buf ) ;
early_console = & early_serial_console ;
} else if ( ! strncmp ( buf , " vga " , 3 )
2008-02-29 15:25:30 +03:00
& & boot_params . screen_info . orig_video_isVGA = = 1 ) {
2007-10-16 04:13:22 +04:00
max_xpos = boot_params . screen_info . orig_video_cols ;
max_ypos = boot_params . screen_info . orig_video_lines ;
current_ypos = boot_params . screen_info . orig_y ;
2007-10-11 13:17:24 +04:00
early_console = & early_vga_console ;
2008-02-29 15:25:30 +03:00
} else if ( ! strncmp ( buf , " simnow " , 6 ) ) {
simnow_init ( buf + 6 ) ;
early_console = & simnow_console ;
keep_early = 1 ;
2007-10-11 13:17:24 +04:00
# ifdef CONFIG_HVC_XEN
} else if ( ! strncmp ( buf , " xen " , 3 ) ) {
early_console = & xenboot_console ;
# endif
}
if ( keep_early )
early_console - > flags & = ~ CON_BOOT ;
else
early_console - > flags | = CON_BOOT ;
register_console ( early_console ) ;
return 0 ;
}
early_param ( " earlyprintk " , setup_early_printk ) ;