diff options
author | Pekka Enberg <penberg@cs.helsinki.fi> | 2010-07-11 04:06:57 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2010-07-12 17:46:00 -0400 |
commit | fa97bdf92709adaaf8b9a5164a895e262a4fcf60 (patch) | |
tree | e34f1b6232c302e39065f3dd7ef8bbf4c8ac3d3d | |
parent | 589643be6693c46fbc54bae77745f336c8ed4bcc (diff) |
x86, setup: Early-boot serial I/O support
This patch adds serial I/O support to the real-mode setup (very early
boot) printf(). It's useful for debugging boot code when running Linux
under KVM, for example. The actual code was lifted from early printk.
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Pekka Enberg <penberg@cs.helsinki.fi>
LKML-Reference: <1278835617-11368-1-git-send-email-penberg@cs.helsinki.fi>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r-- | arch/x86/boot/boot.h | 16 | ||||
-rw-r--r-- | arch/x86/boot/main.c | 3 | ||||
-rw-r--r-- | arch/x86/boot/string.c | 41 | ||||
-rw-r--r-- | arch/x86/boot/tty.c | 111 |
4 files changed, 165 insertions, 6 deletions
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h index 98239d2658f2..46c4c5c71af7 100644 --- a/arch/x86/boot/boot.h +++ b/arch/x86/boot/boot.h | |||
@@ -37,6 +37,8 @@ | |||
37 | extern struct setup_header hdr; | 37 | extern struct setup_header hdr; |
38 | extern struct boot_params boot_params; | 38 | extern struct boot_params boot_params; |
39 | 39 | ||
40 | #define cpu_relax() asm volatile("rep; nop") | ||
41 | |||
40 | /* Basic port I/O */ | 42 | /* Basic port I/O */ |
41 | static inline void outb(u8 v, u16 port) | 43 | static inline void outb(u8 v, u16 port) |
42 | { | 44 | { |
@@ -203,6 +205,17 @@ static inline int isdigit(int ch) | |||
203 | return (ch >= '0') && (ch <= '9'); | 205 | return (ch >= '0') && (ch <= '9'); |
204 | } | 206 | } |
205 | 207 | ||
208 | static inline int isxdigit(int ch) | ||
209 | { | ||
210 | if (isdigit(ch)) | ||
211 | return true; | ||
212 | |||
213 | if ((ch >= 'a') && (ch <= 'f')) | ||
214 | return true; | ||
215 | |||
216 | return (ch >= 'A') && (ch <= 'F'); | ||
217 | } | ||
218 | |||
206 | /* Heap -- available for dynamic lists. */ | 219 | /* Heap -- available for dynamic lists. */ |
207 | extern char _end[]; | 220 | extern char _end[]; |
208 | extern char *HEAP; | 221 | extern char *HEAP; |
@@ -329,10 +342,13 @@ void initregs(struct biosregs *regs); | |||
329 | 342 | ||
330 | /* string.c */ | 343 | /* string.c */ |
331 | int strcmp(const char *str1, const char *str2); | 344 | int strcmp(const char *str1, const char *str2); |
345 | int strncmp(const char *cs, const char *ct, size_t count); | ||
332 | size_t strnlen(const char *s, size_t maxlen); | 346 | size_t strnlen(const char *s, size_t maxlen); |
333 | unsigned int atou(const char *s); | 347 | unsigned int atou(const char *s); |
348 | unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base); | ||
334 | 349 | ||
335 | /* tty.c */ | 350 | /* tty.c */ |
351 | void console_init(void); | ||
336 | void puts(const char *); | 352 | void puts(const char *); |
337 | void putchar(int); | 353 | void putchar(int); |
338 | int getchar(void); | 354 | int getchar(void); |
diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c index 140172b895bd..4ef1a33e8572 100644 --- a/arch/x86/boot/main.c +++ b/arch/x86/boot/main.c | |||
@@ -130,6 +130,9 @@ void main(void) | |||
130 | /* First, copy the boot header into the "zeropage" */ | 130 | /* First, copy the boot header into the "zeropage" */ |
131 | copy_boot_params(); | 131 | copy_boot_params(); |
132 | 132 | ||
133 | /* Initialize the early-boot console */ | ||
134 | console_init(); | ||
135 | |||
133 | /* End of heap check */ | 136 | /* End of heap check */ |
134 | init_heap(); | 137 | init_heap(); |
135 | 138 | ||
diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c index f94b7a0c2abf..aba29df4a7bb 100644 --- a/arch/x86/boot/string.c +++ b/arch/x86/boot/string.c | |||
@@ -30,6 +30,22 @@ int strcmp(const char *str1, const char *str2) | |||
30 | return 0; | 30 | return 0; |
31 | } | 31 | } |
32 | 32 | ||
33 | int strncmp(const char *cs, const char *ct, size_t count) | ||
34 | { | ||
35 | unsigned char c1, c2; | ||
36 | |||
37 | while (count) { | ||
38 | c1 = *cs++; | ||
39 | c2 = *ct++; | ||
40 | if (c1 != c2) | ||
41 | return c1 < c2 ? -1 : 1; | ||
42 | if (!c1) | ||
43 | break; | ||
44 | count--; | ||
45 | } | ||
46 | return 0; | ||
47 | } | ||
48 | |||
33 | size_t strnlen(const char *s, size_t maxlen) | 49 | size_t strnlen(const char *s, size_t maxlen) |
34 | { | 50 | { |
35 | const char *es = s; | 51 | const char *es = s; |
@@ -48,3 +64,28 @@ unsigned int atou(const char *s) | |||
48 | i = i * 10 + (*s++ - '0'); | 64 | i = i * 10 + (*s++ - '0'); |
49 | return i; | 65 | return i; |
50 | } | 66 | } |
67 | |||
68 | /* Works only for digits and letters, but small and fast */ | ||
69 | #define TOLOWER(x) ((x) | 0x20) | ||
70 | |||
71 | unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base) | ||
72 | { | ||
73 | unsigned long long result = 0; | ||
74 | |||
75 | if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x') | ||
76 | cp += 2; | ||
77 | |||
78 | while (isxdigit(*cp)) { | ||
79 | unsigned int value; | ||
80 | |||
81 | value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10; | ||
82 | if (value >= base) | ||
83 | break; | ||
84 | result = result * base + value; | ||
85 | cp++; | ||
86 | } | ||
87 | if (endp) | ||
88 | *endp = (char *)cp; | ||
89 | |||
90 | return result; | ||
91 | } | ||
diff --git a/arch/x86/boot/tty.c b/arch/x86/boot/tty.c index 01ec69c901c7..f3ceee20ff12 100644 --- a/arch/x86/boot/tty.c +++ b/arch/x86/boot/tty.c | |||
@@ -10,23 +10,51 @@ | |||
10 | * ----------------------------------------------------------------------- */ | 10 | * ----------------------------------------------------------------------- */ |
11 | 11 | ||
12 | /* | 12 | /* |
13 | * Very simple screen I/O | 13 | * Very simple screen and serial I/O |
14 | * XXX: Probably should add very simple serial I/O? | ||
15 | */ | 14 | */ |
16 | 15 | ||
17 | #include "boot.h" | 16 | #include "boot.h" |
18 | 17 | ||
18 | #define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */ | ||
19 | |||
20 | static int early_serial_base; | ||
21 | |||
22 | #define XMTRDY 0x20 | ||
23 | |||
24 | #define DLAB 0x80 | ||
25 | |||
26 | #define TXR 0 /* Transmit register (WRITE) */ | ||
27 | #define RXR 0 /* Receive register (READ) */ | ||
28 | #define IER 1 /* Interrupt Enable */ | ||
29 | #define IIR 2 /* Interrupt ID */ | ||
30 | #define FCR 2 /* FIFO control */ | ||
31 | #define LCR 3 /* Line control */ | ||
32 | #define MCR 4 /* Modem control */ | ||
33 | #define LSR 5 /* Line Status */ | ||
34 | #define MSR 6 /* Modem Status */ | ||
35 | #define DLL 0 /* Divisor Latch Low */ | ||
36 | #define DLH 1 /* Divisor latch High */ | ||
37 | |||
38 | #define DEFAULT_BAUD 9600 | ||
39 | |||
19 | /* | 40 | /* |
20 | * These functions are in .inittext so they can be used to signal | 41 | * These functions are in .inittext so they can be used to signal |
21 | * error during initialization. | 42 | * error during initialization. |
22 | */ | 43 | */ |
23 | 44 | ||
24 | void __attribute__((section(".inittext"))) putchar(int ch) | 45 | static void __attribute__((section(".inittext"))) serial_putchar(int ch) |
25 | { | 46 | { |
26 | struct biosregs ireg; | 47 | unsigned timeout = 0xffff; |
27 | 48 | ||
28 | if (ch == '\n') | 49 | while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) |
29 | putchar('\r'); /* \n -> \r\n */ | 50 | cpu_relax(); |
51 | |||
52 | outb(ch, early_serial_base + TXR); | ||
53 | } | ||
54 | |||
55 | static void __attribute__((section(".inittext"))) bios_putchar(int ch) | ||
56 | { | ||
57 | struct biosregs ireg; | ||
30 | 58 | ||
31 | initregs(&ireg); | 59 | initregs(&ireg); |
32 | ireg.bx = 0x0007; | 60 | ireg.bx = 0x0007; |
@@ -36,6 +64,17 @@ void __attribute__((section(".inittext"))) putchar(int ch) | |||
36 | intcall(0x10, &ireg, NULL); | 64 | intcall(0x10, &ireg, NULL); |
37 | } | 65 | } |
38 | 66 | ||
67 | void __attribute__((section(".inittext"))) putchar(int ch) | ||
68 | { | ||
69 | if (ch == '\n') | ||
70 | putchar('\r'); /* \n -> \r\n */ | ||
71 | |||
72 | bios_putchar(ch); | ||
73 | |||
74 | if (early_serial_base != 0) | ||
75 | serial_putchar(ch); | ||
76 | } | ||
77 | |||
39 | void __attribute__((section(".inittext"))) puts(const char *str) | 78 | void __attribute__((section(".inittext"))) puts(const char *str) |
40 | { | 79 | { |
41 | while (*str) | 80 | while (*str) |
@@ -112,3 +151,63 @@ int getchar_timeout(void) | |||
112 | 151 | ||
113 | return 0; /* Timeout! */ | 152 | return 0; /* Timeout! */ |
114 | } | 153 | } |
154 | |||
155 | static void early_serial_init(int baud) | ||
156 | { | ||
157 | unsigned char c; | ||
158 | unsigned divisor; | ||
159 | |||
160 | outb(0x3, early_serial_base + LCR); /* 8n1 */ | ||
161 | outb(0, early_serial_base + IER); /* no interrupt */ | ||
162 | outb(0, early_serial_base + FCR); /* no fifo */ | ||
163 | outb(0x3, early_serial_base + MCR); /* DTR + RTS */ | ||
164 | |||
165 | divisor = 115200 / baud; | ||
166 | c = inb(early_serial_base + LCR); | ||
167 | outb(c | DLAB, early_serial_base + LCR); | ||
168 | outb(divisor & 0xff, early_serial_base + DLL); | ||
169 | outb((divisor >> 8) & 0xff, early_serial_base + DLH); | ||
170 | outb(c & ~DLAB, early_serial_base + LCR); | ||
171 | } | ||
172 | |||
173 | void console_init(void) | ||
174 | { | ||
175 | int baud = DEFAULT_BAUD; | ||
176 | char arg[32]; | ||
177 | int pos = 0; | ||
178 | |||
179 | if (cmdline_find_option("earlyprintk", arg, sizeof arg) > 0) { | ||
180 | char *e; | ||
181 | |||
182 | if (!strncmp(arg, "serial", 6)) { | ||
183 | early_serial_base = DEFAULT_SERIAL_PORT; | ||
184 | pos += 6; | ||
185 | } | ||
186 | |||
187 | if (arg[pos] == ',') | ||
188 | pos++; | ||
189 | |||
190 | if (!strncmp(arg, "ttyS", 4)) { | ||
191 | static const int bases[] = { 0x3f8, 0x2f8 }; | ||
192 | int port = 0; | ||
193 | |||
194 | if (!strncmp(arg + pos, "ttyS", 4)) | ||
195 | pos += 4; | ||
196 | |||
197 | if (arg[pos++] == '1') | ||
198 | port = 1; | ||
199 | |||
200 | early_serial_base = bases[port]; | ||
201 | } | ||
202 | |||
203 | if (arg[pos] == ',') | ||
204 | pos++; | ||
205 | |||
206 | baud = simple_strtoull(arg + pos, &e, 0); | ||
207 | if (baud == 0 || arg + pos == e) | ||
208 | baud = DEFAULT_BAUD; | ||
209 | } | ||
210 | |||
211 | if (early_serial_base != 0) | ||
212 | early_serial_init(baud); | ||
213 | } | ||