diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/x86_64/kernel/early_printk.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/x86_64/kernel/early_printk.c')
-rw-r--r-- | arch/x86_64/kernel/early_printk.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/arch/x86_64/kernel/early_printk.c b/arch/x86_64/kernel/early_printk.c new file mode 100644 index 000000000000..750bcd0655dc --- /dev/null +++ b/arch/x86_64/kernel/early_printk.c | |||
@@ -0,0 +1,220 @@ | |||
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/processor.h> | ||
7 | |||
8 | /* Simple VGA output */ | ||
9 | |||
10 | #ifdef __i386__ | ||
11 | #define VGABASE (__ISA_IO_base + 0xb8000) | ||
12 | #else | ||
13 | #define VGABASE ((void __iomem *)0xffffffff800b8000UL) | ||
14 | #endif | ||
15 | |||
16 | #define MAX_YPOS 25 | ||
17 | #define MAX_XPOS 80 | ||
18 | |||
19 | static int current_ypos = 1, current_xpos = 0; | ||
20 | |||
21 | static void early_vga_write(struct console *con, const char *str, unsigned n) | ||
22 | { | ||
23 | char c; | ||
24 | int i, k, j; | ||
25 | |||
26 | while ((c = *str++) != '\0' && n-- > 0) { | ||
27 | if (current_ypos >= MAX_YPOS) { | ||
28 | /* scroll 1 line up */ | ||
29 | for (k = 1, j = 0; k < MAX_YPOS; k++, j++) { | ||
30 | for (i = 0; i < MAX_XPOS; i++) { | ||
31 | writew(readw(VGABASE + 2*(MAX_XPOS*k + i)), | ||
32 | VGABASE + 2*(MAX_XPOS*j + i)); | ||
33 | } | ||
34 | } | ||
35 | for (i = 0; i < MAX_XPOS; i++) | ||
36 | writew(0x720, VGABASE + 2*(MAX_XPOS*j + i)); | ||
37 | current_ypos = MAX_YPOS-1; | ||
38 | } | ||
39 | if (c == '\n') { | ||
40 | current_xpos = 0; | ||
41 | current_ypos++; | ||
42 | } else if (c != '\r') { | ||
43 | writew(((0x7 << 8) | (unsigned short) c), | ||
44 | VGABASE + 2*(MAX_XPOS*current_ypos + | ||
45 | current_xpos++)); | ||
46 | if (current_xpos >= MAX_XPOS) { | ||
47 | current_xpos = 0; | ||
48 | current_ypos++; | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | static struct console early_vga_console = { | ||
55 | .name = "earlyvga", | ||
56 | .write = early_vga_write, | ||
57 | .flags = CON_PRINTBUFFER, | ||
58 | .index = -1, | ||
59 | }; | ||
60 | |||
61 | /* Serial functions loosely based on a similar package from Klaus P. Gerlicher */ | ||
62 | |||
63 | int early_serial_base = 0x3f8; /* ttyS0 */ | ||
64 | |||
65 | #define XMTRDY 0x20 | ||
66 | |||
67 | #define DLAB 0x80 | ||
68 | |||
69 | #define TXR 0 /* Transmit register (WRITE) */ | ||
70 | #define RXR 0 /* Receive register (READ) */ | ||
71 | #define IER 1 /* Interrupt Enable */ | ||
72 | #define IIR 2 /* Interrupt ID */ | ||
73 | #define FCR 2 /* FIFO control */ | ||
74 | #define LCR 3 /* Line control */ | ||
75 | #define MCR 4 /* Modem control */ | ||
76 | #define LSR 5 /* Line Status */ | ||
77 | #define MSR 6 /* Modem Status */ | ||
78 | #define DLL 0 /* Divisor Latch Low */ | ||
79 | #define DLH 1 /* Divisor latch High */ | ||
80 | |||
81 | static int early_serial_putc(unsigned char ch) | ||
82 | { | ||
83 | unsigned timeout = 0xffff; | ||
84 | while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) | ||
85 | cpu_relax(); | ||
86 | outb(ch, early_serial_base + TXR); | ||
87 | return timeout ? 0 : -1; | ||
88 | } | ||
89 | |||
90 | static void early_serial_write(struct console *con, const char *s, unsigned n) | ||
91 | { | ||
92 | while (*s && n-- > 0) { | ||
93 | early_serial_putc(*s); | ||
94 | if (*s == '\n') | ||
95 | early_serial_putc('\r'); | ||
96 | s++; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | #define DEFAULT_BAUD 9600 | ||
101 | |||
102 | static __init void early_serial_init(char *s) | ||
103 | { | ||
104 | unsigned char c; | ||
105 | unsigned divisor; | ||
106 | unsigned baud = DEFAULT_BAUD; | ||
107 | char *e; | ||
108 | |||
109 | if (*s == ',') | ||
110 | ++s; | ||
111 | |||
112 | if (*s) { | ||
113 | unsigned port; | ||
114 | if (!strncmp(s,"0x",2)) { | ||
115 | early_serial_base = simple_strtoul(s, &e, 16); | ||
116 | } else { | ||
117 | static int bases[] = { 0x3f8, 0x2f8 }; | ||
118 | |||
119 | if (!strncmp(s,"ttyS",4)) | ||
120 | s += 4; | ||
121 | port = simple_strtoul(s, &e, 10); | ||
122 | if (port > 1 || s == e) | ||
123 | port = 0; | ||
124 | early_serial_base = bases[port]; | ||
125 | } | ||
126 | s += strcspn(s, ","); | ||
127 | if (*s == ',') | ||
128 | s++; | ||
129 | } | ||
130 | |||
131 | outb(0x3, early_serial_base + LCR); /* 8n1 */ | ||
132 | outb(0, early_serial_base + IER); /* no interrupt */ | ||
133 | outb(0, early_serial_base + FCR); /* no fifo */ | ||
134 | outb(0x3, early_serial_base + MCR); /* DTR + RTS */ | ||
135 | |||
136 | if (*s) { | ||
137 | baud = simple_strtoul(s, &e, 0); | ||
138 | if (baud == 0 || s == e) | ||
139 | baud = DEFAULT_BAUD; | ||
140 | } | ||
141 | |||
142 | divisor = 115200 / baud; | ||
143 | c = inb(early_serial_base + LCR); | ||
144 | outb(c | DLAB, early_serial_base + LCR); | ||
145 | outb(divisor & 0xff, early_serial_base + DLL); | ||
146 | outb((divisor >> 8) & 0xff, early_serial_base + DLH); | ||
147 | outb(c & ~DLAB, early_serial_base + LCR); | ||
148 | } | ||
149 | |||
150 | static struct console early_serial_console = { | ||
151 | .name = "earlyser", | ||
152 | .write = early_serial_write, | ||
153 | .flags = CON_PRINTBUFFER, | ||
154 | .index = -1, | ||
155 | }; | ||
156 | |||
157 | /* Direct interface for emergencies */ | ||
158 | struct console *early_console = &early_vga_console; | ||
159 | static int early_console_initialized = 0; | ||
160 | |||
161 | void early_printk(const char *fmt, ...) | ||
162 | { | ||
163 | char buf[512]; | ||
164 | int n; | ||
165 | va_list ap; | ||
166 | |||
167 | va_start(ap,fmt); | ||
168 | n = vscnprintf(buf,512,fmt,ap); | ||
169 | early_console->write(early_console,buf,n); | ||
170 | va_end(ap); | ||
171 | } | ||
172 | |||
173 | static int keep_early; | ||
174 | |||
175 | int __init setup_early_printk(char *opt) | ||
176 | { | ||
177 | char *space; | ||
178 | char buf[256]; | ||
179 | |||
180 | if (early_console_initialized) | ||
181 | return -1; | ||
182 | |||
183 | opt = strchr(opt, '=') + 1; | ||
184 | |||
185 | strlcpy(buf,opt,sizeof(buf)); | ||
186 | space = strchr(buf, ' '); | ||
187 | if (space) | ||
188 | *space = 0; | ||
189 | |||
190 | if (strstr(buf,"keep")) | ||
191 | keep_early = 1; | ||
192 | |||
193 | if (!strncmp(buf, "serial", 6)) { | ||
194 | early_serial_init(buf + 6); | ||
195 | early_console = &early_serial_console; | ||
196 | } else if (!strncmp(buf, "ttyS", 4)) { | ||
197 | early_serial_init(buf); | ||
198 | early_console = &early_serial_console; | ||
199 | } else if (!strncmp(buf, "vga", 3)) { | ||
200 | early_console = &early_vga_console; | ||
201 | } | ||
202 | early_console_initialized = 1; | ||
203 | register_console(early_console); | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | void __init disable_early_printk(void) | ||
208 | { | ||
209 | if (!early_console_initialized || !early_console) | ||
210 | return; | ||
211 | if (!keep_early) { | ||
212 | printk("disabling early console\n"); | ||
213 | unregister_console(early_console); | ||
214 | early_console_initialized = 0; | ||
215 | } else { | ||
216 | printk("keeping early console\n"); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | __setup("earlyprintk=", setup_early_printk); | ||