Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Pekka Enberg and committed by
H. Peter Anvin
fa97bdf9 589643be

+165 -6
+16
arch/x86/boot/boot.h
··· 37 37 extern struct setup_header hdr; 38 38 extern struct boot_params boot_params; 39 39 40 + #define cpu_relax() asm volatile("rep; nop") 41 + 40 42 /* Basic port I/O */ 41 43 static inline void outb(u8 v, u16 port) 42 44 { ··· 205 203 return (ch >= '0') && (ch <= '9'); 206 204 } 207 205 206 + static inline int isxdigit(int ch) 207 + { 208 + if (isdigit(ch)) 209 + return true; 210 + 211 + if ((ch >= 'a') && (ch <= 'f')) 212 + return true; 213 + 214 + return (ch >= 'A') && (ch <= 'F'); 215 + } 216 + 208 217 /* Heap -- available for dynamic lists. */ 209 218 extern char _end[]; 210 219 extern char *HEAP; ··· 342 329 343 330 /* string.c */ 344 331 int strcmp(const char *str1, const char *str2); 332 + int strncmp(const char *cs, const char *ct, size_t count); 345 333 size_t strnlen(const char *s, size_t maxlen); 346 334 unsigned int atou(const char *s); 335 + unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base); 347 336 348 337 /* tty.c */ 338 + void console_init(void); 349 339 void puts(const char *); 350 340 void putchar(int); 351 341 int getchar(void);
+3
arch/x86/boot/main.c
··· 130 130 /* First, copy the boot header into the "zeropage" */ 131 131 copy_boot_params(); 132 132 133 + /* Initialize the early-boot console */ 134 + console_init(); 135 + 133 136 /* End of heap check */ 134 137 init_heap(); 135 138
+41
arch/x86/boot/string.c
··· 30 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 49 size_t strnlen(const char *s, size_t maxlen) 34 50 { 35 51 const char *es = s; ··· 63 47 while (isdigit(*s)) 64 48 i = i * 10 + (*s++ - '0'); 65 49 return i; 50 + } 51 + 52 + /* Works only for digits and letters, but small and fast */ 53 + #define TOLOWER(x) ((x) | 0x20) 54 + 55 + unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base) 56 + { 57 + unsigned long long result = 0; 58 + 59 + if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x') 60 + cp += 2; 61 + 62 + while (isxdigit(*cp)) { 63 + unsigned int value; 64 + 65 + value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10; 66 + if (value >= base) 67 + break; 68 + result = result * base + value; 69 + cp++; 70 + } 71 + if (endp) 72 + *endp = (char *)cp; 73 + 74 + return result; 66 75 }
+105 -6
arch/x86/boot/tty.c
··· 10 10 * ----------------------------------------------------------------------- */ 11 11 12 12 /* 13 - * Very simple screen I/O 14 - * XXX: Probably should add very simple serial I/O? 13 + * Very simple screen and serial I/O 15 14 */ 16 15 17 16 #include "boot.h" 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 18 39 19 40 /* 20 41 * These functions are in .inittext so they can be used to signal 21 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) 46 + { 47 + unsigned timeout = 0xffff; 48 + 49 + while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) 50 + cpu_relax(); 51 + 52 + outb(ch, early_serial_base + TXR); 53 + } 54 + 55 + static void __attribute__((section(".inittext"))) bios_putchar(int ch) 25 56 { 26 57 struct biosregs ireg; 27 - 28 - if (ch == '\n') 29 - putchar('\r'); /* \n -> \r\n */ 30 58 31 59 initregs(&ireg); 32 60 ireg.bx = 0x0007; ··· 62 34 ireg.ah = 0x0e; 63 35 ireg.al = ch; 64 36 intcall(0x10, &ireg, NULL); 37 + } 38 + 39 + void __attribute__((section(".inittext"))) putchar(int ch) 40 + { 41 + if (ch == '\n') 42 + putchar('\r'); /* \n -> \r\n */ 43 + 44 + bios_putchar(ch); 45 + 46 + if (early_serial_base != 0) 47 + serial_putchar(ch); 65 48 } 66 49 67 50 void __attribute__((section(".inittext"))) puts(const char *str) ··· 150 111 } 151 112 152 113 return 0; /* Timeout! */ 114 + } 115 + 116 + static void early_serial_init(int baud) 117 + { 118 + unsigned char c; 119 + unsigned divisor; 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 + divisor = 115200 / baud; 127 + c = inb(early_serial_base + LCR); 128 + outb(c | DLAB, early_serial_base + LCR); 129 + outb(divisor & 0xff, early_serial_base + DLL); 130 + outb((divisor >> 8) & 0xff, early_serial_base + DLH); 131 + outb(c & ~DLAB, early_serial_base + LCR); 132 + } 133 + 134 + void console_init(void) 135 + { 136 + int baud = DEFAULT_BAUD; 137 + char arg[32]; 138 + int pos = 0; 139 + 140 + if (cmdline_find_option("earlyprintk", arg, sizeof arg) > 0) { 141 + char *e; 142 + 143 + if (!strncmp(arg, "serial", 6)) { 144 + early_serial_base = DEFAULT_SERIAL_PORT; 145 + pos += 6; 146 + } 147 + 148 + if (arg[pos] == ',') 149 + pos++; 150 + 151 + if (!strncmp(arg, "ttyS", 4)) { 152 + static const int bases[] = { 0x3f8, 0x2f8 }; 153 + int port = 0; 154 + 155 + if (!strncmp(arg + pos, "ttyS", 4)) 156 + pos += 4; 157 + 158 + if (arg[pos++] == '1') 159 + port = 1; 160 + 161 + early_serial_base = bases[port]; 162 + } 163 + 164 + if (arg[pos] == ',') 165 + pos++; 166 + 167 + baud = simple_strtoull(arg + pos, &e, 0); 168 + if (baud == 0 || arg + pos == e) 169 + baud = DEFAULT_BAUD; 170 + } 171 + 172 + if (early_serial_base != 0) 173 + early_serial_init(baud); 153 174 }