aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPekka Enberg <penberg@cs.helsinki.fi>2010-07-11 04:06:57 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2010-07-12 17:46:00 -0400
commitfa97bdf92709adaaf8b9a5164a895e262a4fcf60 (patch)
treee34f1b6232c302e39065f3dd7ef8bbf4c8ac3d3d
parent589643be6693c46fbc54bae77745f336c8ed4bcc (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.h16
-rw-r--r--arch/x86/boot/main.c3
-rw-r--r--arch/x86/boot/string.c41
-rw-r--r--arch/x86/boot/tty.c111
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 @@
37extern struct setup_header hdr; 37extern struct setup_header hdr;
38extern struct boot_params boot_params; 38extern 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 */
41static inline void outb(u8 v, u16 port) 43static 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
208static 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. */
207extern char _end[]; 220extern char _end[];
208extern char *HEAP; 221extern char *HEAP;
@@ -329,10 +342,13 @@ void initregs(struct biosregs *regs);
329 342
330/* string.c */ 343/* string.c */
331int strcmp(const char *str1, const char *str2); 344int strcmp(const char *str1, const char *str2);
345int strncmp(const char *cs, const char *ct, size_t count);
332size_t strnlen(const char *s, size_t maxlen); 346size_t strnlen(const char *s, size_t maxlen);
333unsigned int atou(const char *s); 347unsigned int atou(const char *s);
348unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base);
334 349
335/* tty.c */ 350/* tty.c */
351void console_init(void);
336void puts(const char *); 352void puts(const char *);
337void putchar(int); 353void putchar(int);
338int getchar(void); 354int 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
33int 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
33size_t strnlen(const char *s, size_t maxlen) 49size_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
71unsigned 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
20static 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
24void __attribute__((section(".inittext"))) putchar(int ch) 45static 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
55static 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
67void __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
39void __attribute__((section(".inittext"))) puts(const char *str) 78void __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
155static 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
173void 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}