1 #include <linux/console.h>
2 #include <linux/kernel.h>
3 #include <linux/init.h>
4 #include <linux/string.h>
5 #include <asm/io.h>
6 #include <asm/proto.h>
7 
8 /* Simple VGA output */
9 
10 #define VGABASE		0xffffffff800b8000UL
11 
12 #define MAX_YPOS	25
13 #define MAX_XPOS	80
14 
15 static int current_ypos = 1, current_xpos = 0;
16 
early_vga_write(struct console * con,const char * str,unsigned n)17 static void early_vga_write(struct console *con, const char *str, unsigned n)
18 {
19 	char c;
20 	int  i, k, j;
21 
22 	while ((c = *str++) != '\0' && n-- > 0) {
23 		if (current_ypos >= MAX_YPOS) {
24 			/* scroll 1 line up */
25 			for(k = 1, j = 0; k < MAX_YPOS; k++, j++) {
26 				for(i = 0; i < MAX_XPOS; i++) {
27 					writew(readw(VGABASE + 2*(MAX_XPOS*k + i)),
28 					       VGABASE + 2*(MAX_XPOS*j + i));
29 				}
30 			}
31 			for(i = 0; i < MAX_XPOS; i++) {
32 				writew(0x720, VGABASE + 2*(MAX_XPOS*j + i));
33 			}
34 			current_ypos = MAX_YPOS-1;
35 		}
36 		if (c == '\n') {
37 			current_xpos = 0;
38 			current_ypos++;
39 		} else if (c != '\r')  {
40 			writew(((0x7 << 8) | (unsigned short) c),
41 			       VGABASE + 2*(MAX_XPOS*current_ypos + current_xpos++));
42 			if (current_xpos >= MAX_XPOS) {
43 				current_xpos = 0;
44 				current_ypos++;
45 			}
46 		}
47 	}
48 }
49 
50 static struct console early_vga_console = {
51 	name:		"earlyvga",
52 	write:		early_vga_write,
53 	flags:		CON_PRINTBUFFER,
54 	index:		-1,
55 };
56 
57 /* Serial functions losely based on a similar package from Klaus P. Gerlicher */
58 
59 int early_serial_base = 0x3f8;  /* ttyS0 */
60 
61 #define XMTRDY          0x20
62 
63 #define DLAB		0x80
64 
65 #define TXR             0       /*  Transmit register (WRITE) */
66 #define RXR             0       /*  Receive register  (READ)  */
67 #define IER             1       /*  Interrupt Enable          */
68 #define IIR             2       /*  Interrupt ID              */
69 #define FCR             2       /*  FIFO control              */
70 #define LCR             3       /*  Line control              */
71 #define MCR             4       /*  Modem control             */
72 #define LSR             5       /*  Line Status               */
73 #define MSR             6       /*  Modem Status              */
74 #define DLL             0       /*  Divisor Latch Low         */
75 #define DLH             1       /*  Divisor latch High        */
76 
early_serial_putc(unsigned char ch)77 static int early_serial_putc(unsigned char ch)
78 {
79 	unsigned timeout = 0xffff;
80 	while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
81 		rep_nop();
82 	outb(ch, early_serial_base + TXR);
83 	return timeout ? 0 : -1;
84 }
85 
early_serial_write(struct console * con,const char * s,unsigned n)86 static void early_serial_write(struct console *con, const char *s, unsigned n)
87 {
88 	while (*s && n-- > 0) {
89 		early_serial_putc(*s);
90 		if (*s == '\n')
91 			early_serial_putc('\r');
92 		s++;
93 	}
94 }
95 
early_serial_init(char * opt)96 static __init void early_serial_init(char *opt)
97 {
98 	unsigned char c;
99 	unsigned divisor, baud = 38400;
100 	char *s, *e;
101 
102 	if (*opt == ',')
103 		++opt;
104 
105 	s = strsep(&opt, ",");
106 	if (s != NULL) {
107 		unsigned port;
108 		if (!strncmp(s,"0x",2))
109 			early_serial_base = simple_strtoul(s, &e, 16);
110 		else {
111 			static int bases[] = { 0x3f8, 0x2f8 };
112 			if (!strncmp(s,"ttyS",4))
113 				s+=4;
114 			port = simple_strtoul(s, &e, 10);
115 			if (port > 1 || s == e)
116 				port = 0;
117 			early_serial_base = bases[port];
118 		}
119 	}
120 
121 	outb(0x3, early_serial_base + LCR); /* 8n1 */
122 	outb(0, early_serial_base + IER); /* no interrupt */
123 	outb(0, early_serial_base + FCR); /* no fifo */
124 	outb(0x3, early_serial_base + MCR); /* DTR + RTS */
125 
126 	s = strsep(&opt, ",");
127 	if (s != NULL) {
128 		baud = simple_strtoul(s, &e, 0);
129 		if (baud == 0 || s == e)
130 			baud = 38400;
131 	}
132 
133 	divisor = 115200 / baud;
134 	c = inb(early_serial_base + LCR);
135 	outb(c | DLAB, early_serial_base + LCR);
136 	outb(divisor & 0xff, early_serial_base + DLL);
137 	outb((divisor >> 8) & 0xff, early_serial_base + DLH);
138 	outb(c & ~DLAB, early_serial_base + LCR);
139 }
140 
141 static struct console early_serial_console = {
142 	name:		"earlyser",
143 	write:		early_serial_write,
144 	flags:		CON_PRINTBUFFER,
145 	index:		-1,
146 };
147 
148 /* Direct interface for emergencies */
149 struct console *early_console = &early_vga_console;
150 static int early_console_initialized = 0;
151 
early_printk(const char * fmt,...)152 void early_printk(const char *fmt, ...)
153 {
154 	char buf[512];
155 	int n;
156 	va_list ap;
157 	va_start(ap,fmt);
158 	n = vsnprintf(buf,512,fmt,ap);
159 	early_console->write(early_console,buf,n);
160 	va_end(ap);
161 }
162 
163 static int keep_early;
164 
setup_early_printk(char * opt)165 int __init setup_early_printk(char *opt)
166 {
167 	char *space;
168 	char buf[256];
169 
170 	if (early_console_initialized)
171 		return -1;
172 
173 	strncpy(buf,opt,256);
174 	buf[255] = 0;
175 	space = strchr(buf, ' ');
176 	if (space)
177 		*space = 0;
178 
179 	if (strstr(buf,"keep"))
180 		keep_early = 1;
181 
182 	if (!strncmp(buf, "serial", 6)) {
183 		early_serial_init(buf + 6);
184 		early_console = &early_serial_console;
185 	} else if (!strncmp(buf, "vga", 3)) {
186 		early_console = &early_vga_console;
187 	} else {
188 		early_console = NULL;
189 		return -1;
190 	}
191 	early_console_initialized = 1;
192 	register_console(early_console);
193 	return 0;
194 }
195 
disable_early_printk(void)196 void __init disable_early_printk(void)
197 {
198 	if (!early_console_initialized || !early_console)
199 		return;
200 	if (!keep_early) {
201 		printk("Disabling early console\n");
202 		unregister_console(early_console);
203 		early_console_initialized = 0;
204 	}
205 }
206 
207 /* syntax: earlyprintk=vga
208            earlyprintk=serial[,ttySn[,baudrate]]
209    Append ,keep to not disable it when the real console takes over.
210    Only vga or serial at a time, not both.
211    Currently only ttyS0 and ttyS1 are supported.
212    Interaction with the standard serial driver is not very good.
213    The VGA output is eventually overwritten by the real console. */
214 __setup("earlyprintk=", setup_early_printk);
215