dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / arch / x86 / kernel / platform / i86pc / boot / boot_console.c
blobcefc4a2b1303b66b75e11b85af6ab248ee4c1650
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2012 Gary Mills
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 #include <sys/types.h>
29 #include <sys/systm.h>
30 #include <sys/archsystm.h>
31 #include <sys/boot_console.h>
32 #include <sys/panic.h>
33 #include <sys/ctype.h>
35 #include "boot_serial.h"
36 #include "boot_vga.h"
38 #if defined(_BOOT)
39 #include <dboot_asm.h>
40 #include <dboot_xboot.h>
41 #else /* _BOOT */
42 #include <sys/bootconf.h>
43 #if defined(__xpv)
44 #include <sys/evtchn_impl.h>
45 #endif /* __xpv */
46 static char *defcons_buf;
47 static char *defcons_cur;
48 #endif /* _BOOT */
51 static int cons_color = CONS_COLOR;
52 static int console = CONS_SCREEN_TEXT;
53 static int tty_num = 0;
54 static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
55 static char *boot_line;
56 static struct boot_env {
57 char *be_env; /* ends with double ascii nul */
58 size_t be_size; /* size of the environment, including nul */
59 } boot_env;
61 static int serial_ischar(void);
62 static int serial_getchar(void);
63 static void serial_putchar(int);
64 static void serial_adjust_prop(void);
66 #if !defined(_BOOT)
67 /* Set if the console or mode are expressed in the boot line */
68 static int console_set, console_mode_set;
69 #endif
72 /* Clear the screen and initialize VIDEO, XPOS and YPOS. */
73 void
74 clear_screen(void)
77 * XXX should set vga mode so we don't depend on the
78 * state left by the boot loader. Note that we have to
79 * enable the cursor before clearing the screen since
80 * the cursor position is dependant upon the cursor
81 * skew, which is initialized by vga_cursor_display()
83 vga_cursor_display();
84 vga_clear(cons_color);
85 vga_setpos(0, 0);
88 /* Put the character C on the screen. */
89 static void
90 screen_putchar(int c)
92 int row, col;
94 vga_getpos(&row, &col);
95 switch (c) {
96 case '\t':
97 col += 8 - (col % 8);
98 if (col == VGA_TEXT_COLS)
99 col = 79;
100 vga_setpos(row, col);
101 break;
103 case '\r':
104 vga_setpos(row, 0);
105 break;
107 case '\b':
108 if (col > 0)
109 vga_setpos(row, col - 1);
110 break;
112 case '\n':
113 if (row < VGA_TEXT_ROWS - 1)
114 vga_setpos(row + 1, col);
115 else
116 vga_scroll(cons_color);
117 break;
119 default:
120 vga_drawc(c, cons_color);
121 if (col < VGA_TEXT_COLS -1)
122 vga_setpos(row, col + 1);
123 else if (row < VGA_TEXT_ROWS - 1)
124 vga_setpos(row + 1, 0);
125 else {
126 vga_setpos(row, 0);
127 vga_scroll(cons_color);
129 break;
133 static int port;
135 static void
136 serial_init(void)
138 port = tty_addr[tty_num];
140 outb(port + ISR, 0x20);
141 if (inb(port + ISR) & 0x20) {
143 * 82510 chip is present
145 outb(port + DAT+7, 0x04); /* clear status */
146 outb(port + ISR, 0x40); /* set to bank 2 */
147 outb(port + MCR, 0x08); /* IMD */
148 outb(port + DAT, 0x21); /* FMD */
149 outb(port + ISR, 0x00); /* set to bank 0 */
150 } else {
152 * set the UART in FIFO mode if it has FIFO buffers.
153 * use 16550 fifo reset sequence specified in NS
154 * application note. disable fifos until chip is
155 * initialized.
157 outb(port + FIFOR, 0x00); /* clear */
158 outb(port + FIFOR, FIFO_ON); /* enable */
159 outb(port + FIFOR, FIFO_ON|FIFORXFLSH); /* reset */
160 outb(port + FIFOR,
161 FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80);
162 if ((inb(port + ISR) & 0xc0) != 0xc0) {
164 * no fifo buffers so disable fifos.
165 * this is true for 8250's
167 outb(port + FIFOR, 0x00);
171 /* disable interrupts */
172 outb(port + ICR, 0);
174 #if !defined(_BOOT)
175 if (IN_XPV_PANIC())
176 return;
177 #endif
179 /* adjust setting based on tty properties */
180 serial_adjust_prop();
182 #if defined(_BOOT)
184 * Do a full reset to match console behavior.
185 * 0x1B + c - reset everything
187 serial_putchar(0x1B);
188 serial_putchar('c');
189 #endif
192 /* Advance str pointer past white space */
193 #define EAT_WHITE_SPACE(str) { \
194 while ((*str != '\0') && ISSPACE(*str)) \
195 str++; \
199 * boot_line is set when we call here. Search it for the argument name,
200 * and if found, return a pointer to it.
202 static char *
203 find_boot_line_prop(const char *name)
205 char *ptr;
206 char *ret = NULL;
207 char end_char;
208 size_t len;
210 if (boot_line == NULL)
211 return (NULL);
213 len = strlen(name);
216 * We have two nested loops here: the outer loop discards all options
217 * except -B, and the inner loop parses the -B options looking for
218 * the one we're interested in.
220 for (ptr = boot_line; *ptr != '\0'; ptr++) {
221 EAT_WHITE_SPACE(ptr);
223 if (*ptr == '-') {
224 ptr++;
225 while ((*ptr != '\0') && (*ptr != 'B') &&
226 !ISSPACE(*ptr))
227 ptr++;
228 if (*ptr == '\0')
229 goto out;
230 else if (*ptr != 'B')
231 continue;
232 } else {
233 while ((*ptr != '\0') && !ISSPACE(*ptr))
234 ptr++;
235 if (*ptr == '\0')
236 goto out;
237 continue;
240 do {
241 ptr++;
242 EAT_WHITE_SPACE(ptr);
244 if ((strncmp(ptr, name, len) == 0) &&
245 (ptr[len] == '=')) {
246 ptr += len + 1;
247 if ((*ptr == '\'') || (*ptr == '"')) {
248 ret = ptr + 1;
249 end_char = *ptr;
250 ptr++;
251 } else {
252 ret = ptr;
253 end_char = ',';
255 goto consume_property;
259 * We have a property, and it's not the one we're
260 * interested in. Skip the property name. A name
261 * can end with '=', a comma, or white space.
263 while ((*ptr != '\0') && (*ptr != '=') &&
264 (*ptr != ',') && (!ISSPACE(*ptr)))
265 ptr++;
268 * We only want to go through the rest of the inner
269 * loop if we have a comma. If we have a property
270 * name without a value, either continue or break.
272 if (*ptr == '\0')
273 goto out;
274 else if (*ptr == ',')
275 continue;
276 else if (ISSPACE(*ptr))
277 break;
278 ptr++;
281 * Is the property quoted?
283 if ((*ptr == '\'') || (*ptr == '"')) {
284 end_char = *ptr;
285 ptr++;
286 } else {
288 * Not quoted, so the string ends at a comma
289 * or at white space. Deal with white space
290 * later.
292 end_char = ',';
296 * Now, we can ignore any characters until we find
297 * end_char.
299 consume_property:
300 for (; (*ptr != '\0') && (*ptr != end_char); ptr++) {
301 if ((end_char == ',') && ISSPACE(*ptr))
302 break;
304 if (*ptr && (*ptr != ',') && !ISSPACE(*ptr))
305 ptr++;
306 } while (*ptr == ',');
308 out:
309 return (ret);
313 * Find prop from boot env module. The data in module is list of C strings
314 * name=value, the list is terminated by double nul.
316 static const char *
317 find_boot_env_prop(const char *name)
319 char *ptr;
320 size_t len;
321 uintptr_t size;
323 if (boot_env.be_env == NULL)
324 return (NULL);
326 ptr = boot_env.be_env;
327 len = strlen(name);
330 * Make sure we have at least len + 2 bytes in the environment.
331 * We are looking for name=value\0 constructs, and the environment
332 * itself is terminated by '\0'.
334 if (boot_env.be_size < len + 2)
335 return (NULL);
337 do {
338 if ((strncmp(ptr, name, len) == 0) && (ptr[len] == '=')) {
339 ptr += len + 1;
340 return (ptr);
342 /* find the first '\0' */
343 while (*ptr != '\0') {
344 ptr++;
345 size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
346 if (size > boot_env.be_size)
347 return (NULL);
349 ptr++;
351 /* If the remainder is shorter than name + 2, get out. */
352 size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
353 if (boot_env.be_size - size < len + 2)
354 return (NULL);
355 } while (*ptr != '\0');
356 return (NULL);
360 * Get prop value from either command line or boot environment.
361 * We always check kernel command line first, as this will keep the
362 * functionality and will allow user to override the values in environment.
364 const char *
365 find_boot_prop(const char *name)
367 const char *value = find_boot_line_prop(name);
369 if (value == NULL)
370 value = find_boot_env_prop(name);
371 return (value);
374 #define MATCHES(p, pat) \
375 (strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
377 #define SKIP(p, c) \
378 while (*(p) != 0 && *p != (c)) \
379 ++(p); \
380 if (*(p) == (c)) \
381 ++(p);
384 * find a tty mode property either from cmdline or from boot properties
386 static const char *
387 get_mode_value(char *name)
390 * when specified on boot line it looks like "name" "="....
392 if (boot_line != NULL) {
393 return (find_boot_prop(name));
396 #if defined(_BOOT)
397 return (NULL);
398 #else
400 * if we're running in the full kernel we check the bootenv.rc settings
403 static char propval[20];
405 propval[0] = 0;
406 if (do_bsys_getproplen(NULL, name) <= 0)
407 return (NULL);
408 (void) do_bsys_getprop(NULL, name, propval);
409 return (propval);
411 #endif
415 * adjust serial port based on properties
416 * These come either from the cmdline or from boot properties.
418 static void
419 serial_adjust_prop(void)
421 char propname[20];
422 const char *propval;
423 const char *p;
424 ulong_t baud;
425 uchar_t lcr = 0;
426 uchar_t mcr = DTR | RTS;
428 (void) strcpy(propname, "ttyX-mode");
429 propname[3] = 'a' + tty_num;
430 propval = get_mode_value(propname);
431 if (propval == NULL)
432 propval = "9600,8,n,1,-";
433 #if !defined(_BOOT)
434 else
435 console_mode_set = 1;
436 #endif
438 /* property is of the form: "9600,8,n,1,-" */
439 p = propval;
440 if (MATCHES(p, "110,"))
441 baud = ASY110;
442 else if (MATCHES(p, "150,"))
443 baud = ASY150;
444 else if (MATCHES(p, "300,"))
445 baud = ASY300;
446 else if (MATCHES(p, "600,"))
447 baud = ASY600;
448 else if (MATCHES(p, "1200,"))
449 baud = ASY1200;
450 else if (MATCHES(p, "2400,"))
451 baud = ASY2400;
452 else if (MATCHES(p, "4800,"))
453 baud = ASY4800;
454 else if (MATCHES(p, "19200,"))
455 baud = ASY19200;
456 else if (MATCHES(p, "38400,"))
457 baud = ASY38400;
458 else if (MATCHES(p, "57600,"))
459 baud = ASY57600;
460 else if (MATCHES(p, "115200,"))
461 baud = ASY115200;
462 else {
463 baud = ASY9600;
464 SKIP(p, ',');
466 outb(port + LCR, DLAB);
467 outb(port + DAT + DLL, baud & 0xff);
468 outb(port + DAT + DLH, (baud >> 8) & 0xff);
470 switch (*p) {
471 case '5':
472 lcr |= BITS5;
473 ++p;
474 break;
475 case '6':
476 lcr |= BITS6;
477 ++p;
478 break;
479 case '7':
480 lcr |= BITS7;
481 ++p;
482 break;
483 case '8':
484 ++p;
485 default:
486 lcr |= BITS8;
487 break;
490 SKIP(p, ',');
492 switch (*p) {
493 case 'n':
494 lcr |= PARITY_NONE;
495 ++p;
496 break;
497 case 'o':
498 lcr |= PARITY_ODD;
499 ++p;
500 break;
501 case 'e':
502 ++p;
503 default:
504 lcr |= PARITY_EVEN;
505 break;
509 SKIP(p, ',');
511 switch (*p) {
512 case '1':
513 /* STOP1 is 0 */
514 ++p;
515 break;
516 default:
517 lcr |= STOP2;
518 break;
520 /* set parity bits */
521 outb(port + LCR, lcr);
523 (void) strcpy(propname, "ttyX-rts-dtr-off");
524 propname[3] = 'a' + tty_num;
525 propval = get_mode_value(propname);
526 if (propval == NULL)
527 propval = "false";
528 if (propval[0] != 'f' && propval[0] != 'F')
529 mcr = 0;
530 /* set modem control bits */
531 outb(port + MCR, mcr | OUT2);
534 /* Obtain the console type */
536 boot_console_type(int *tnum)
538 if (tnum != NULL)
539 *tnum = tty_num;
540 return (console);
544 * A structure to map console names to values.
546 typedef struct {
547 char *name;
548 int value;
549 } console_value_t;
551 console_value_t console_devices[] = {
552 { "ttya", CONS_TTY }, /* 0 */
553 { "ttyb", CONS_TTY }, /* 1 */
554 { "ttyc", CONS_TTY }, /* 2 */
555 { "ttyd", CONS_TTY }, /* 3 */
556 { "text", CONS_SCREEN_TEXT },
557 { "graphics", CONS_SCREEN_GRAPHICS },
558 #if !defined(_BOOT)
559 { "usb-serial", CONS_USBSER },
560 #endif
561 { NULL, CONS_INVALID }
564 static void
565 bcons_init_env(struct xboot_info *xbi)
567 uint32_t i;
568 struct boot_modules *modules;
570 modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules;
571 for (i = 0; i < xbi->bi_module_cnt; i++) {
572 if (modules[i].bm_type == BMT_ENV)
573 break;
575 if (i == xbi->bi_module_cnt)
576 return;
578 boot_env.be_env = (char *)(uintptr_t)modules[i].bm_addr;
579 boot_env.be_size = modules[i].bm_size;
582 void
583 bcons_init(struct xboot_info *xbi)
585 console_value_t *consolep;
586 size_t len, cons_len;
587 const char *cons_str;
588 #if !defined(_BOOT)
589 static char console_text[] = "text";
590 extern int post_fastreboot;
591 #endif
593 /* Set up data to fetch properties from commad line and boot env. */
594 boot_line = (char *)(uintptr_t)xbi->bi_cmdline;
595 bcons_init_env(xbi);
596 console = CONS_INVALID;
599 cons_str = find_boot_prop("console");
600 if (cons_str == NULL)
601 cons_str = find_boot_prop("output-device");
603 #if !defined(_BOOT)
604 if (post_fastreboot && strcmp(cons_str, "graphics") == 0)
605 cons_str = console_text;
606 #endif
609 * Go through the console_devices array trying to match the string
610 * we were given. The string on the command line must end with
611 * a comma or white space.
613 if (cons_str != NULL) {
614 int n;
616 cons_len = strlen(cons_str);
617 for (n = 0; console_devices[n].name != NULL; n++) {
618 consolep = &console_devices[n];
619 len = strlen(consolep->name);
620 if ((len <= cons_len) && ((cons_str[len] == '\0') ||
621 (cons_str[len] == ',') || (cons_str[len] == '\'') ||
622 (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
623 (strncmp(cons_str, consolep->name, len) == 0)) {
624 console = consolep->value;
625 if (console == CONS_TTY)
626 tty_num = n;
627 break;
634 * If no console device specified, default to text.
635 * Remember what was specified for second phase.
637 if (console == CONS_INVALID)
638 console = CONS_SCREEN_TEXT;
639 #if !defined(_BOOT)
640 else
641 console_set = 1;
642 #endif
645 switch (console) {
646 case CONS_TTY:
647 serial_init();
648 break;
650 case CONS_HYPERVISOR:
651 break;
653 #if !defined(_BOOT)
654 case CONS_USBSER:
656 * We can't do anything with the usb serial
657 * until we have memory management.
659 break;
660 #endif
661 case CONS_SCREEN_GRAPHICS:
662 kb_init();
663 break;
664 case CONS_SCREEN_TEXT:
665 default:
666 #if defined(_BOOT)
667 clear_screen(); /* clears the grub or xen screen */
668 #endif /* _BOOT */
669 kb_init();
670 break;
674 #if !defined(_BOOT)
676 * 2nd part of console initialization.
677 * In the kernel (ie. fakebop), this can be used only to switch to
678 * using a serial port instead of screen based on the contents
679 * of the bootenv.rc file.
681 /*ARGSUSED*/
682 void
683 bcons_init2(char *inputdev, char *outputdev, char *consoledev)
685 int cons = CONS_INVALID;
686 int ttyn;
687 char *devnames[] = { consoledev, outputdev, inputdev, NULL };
688 console_value_t *consolep;
689 int i;
690 extern int post_fastreboot;
692 if (post_fastreboot && console == CONS_SCREEN_GRAPHICS)
693 console = CONS_SCREEN_TEXT;
695 if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
696 if (console_set) {
698 * If the console was set on the command line,
699 * but the ttyX-mode was not, we only need to
700 * check bootenv.rc for that setting.
702 if ((!console_mode_set) && (console == CONS_TTY))
703 serial_init();
704 return;
707 for (i = 0; devnames[i] != NULL; i++) {
708 int n;
710 for (n = 0; console_devices[n].name != NULL; n++) {
711 consolep = &console_devices[n];
712 if (strcmp(devnames[i], consolep->name) == 0) {
713 cons = consolep->value;
714 if (cons == CONS_TTY)
715 ttyn = n;
718 if (cons != CONS_INVALID)
719 break;
722 #if defined(__xpv)
724 * if the hypervisor is using the currently selected console
725 * device then default to using the hypervisor as the console
726 * device.
728 if (cons == console_hypervisor_device) {
729 cons = CONS_HYPERVISOR;
730 console_hypervisor_redirect = B_TRUE;
732 #endif /* __xpv */
734 if ((cons == CONS_INVALID) || (cons == console)) {
736 * we're sticking with whatever the current setting is
738 return;
741 console = cons;
742 if (cons == CONS_TTY) {
743 tty_num = ttyn;
744 serial_init();
745 return;
747 } else {
749 * USB serial and GRAPHICS console
750 * we just collect data into a buffer
752 extern void *defcons_init(size_t);
753 defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
757 #if defined(__xpv)
758 boolean_t
759 bcons_hypervisor_redirect(void)
761 return (console_hypervisor_redirect);
764 void
765 bcons_device_change(int new_console)
767 if (new_console < CONS_MIN || new_console > CONS_MAX)
768 return;
771 * If we are asked to switch the console to the hypervisor, that
772 * really means to switch the console to whichever device the
773 * hypervisor is/was using.
775 if (new_console == CONS_HYPERVISOR)
776 new_console = console_hypervisor_device;
778 console = new_console;
780 if (new_console == CONS_TTY) {
781 tty_num = console_hypervisor_tty_num;
782 serial_init();
785 #endif /* __xpv */
787 static void
788 defcons_putchar(int c)
790 if (defcons_buf != NULL &&
791 defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
792 *defcons_cur++ = c;
793 *defcons_cur = 0;
796 #endif /* _BOOT */
798 static void
799 serial_putchar(int c)
801 int checks = 10000;
803 while (((inb(port + LSR) & XHRE) == 0) && checks--)
805 outb(port + DAT, (char)c);
808 static int
809 serial_getchar(void)
811 uchar_t lsr;
813 while (serial_ischar() == 0)
816 lsr = inb(port + LSR);
817 if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
818 SERIAL_PARITY | SERIAL_OVERRUN)) {
819 if (lsr & SERIAL_OVERRUN) {
820 return (inb(port + DAT));
821 } else {
822 /* Toss the garbage */
823 (void) inb(port + DAT);
824 return (0);
827 return (inb(port + DAT));
830 static int
831 serial_ischar(void)
833 return (inb(port + LSR) & RCA);
836 static void
837 _doputchar(int c)
839 switch (console) {
840 case CONS_TTY:
841 serial_putchar(c);
842 return;
843 case CONS_SCREEN_TEXT:
844 screen_putchar(c);
845 return;
846 case CONS_SCREEN_GRAPHICS:
847 #if !defined(_BOOT)
848 case CONS_USBSER:
849 defcons_putchar(c);
850 #endif /* _BOOT */
851 return;
855 void
856 bcons_putchar(int c)
858 static int bhcharpos = 0;
861 if (c == '\t') {
862 do {
863 _doputchar(' ');
864 } while (++bhcharpos % 8);
865 return;
866 } else if (c == '\n' || c == '\r') {
867 bhcharpos = 0;
868 _doputchar('\r');
869 _doputchar(c);
870 return;
871 } else if (c == '\b') {
872 if (bhcharpos)
873 bhcharpos--;
874 _doputchar(c);
875 return;
878 bhcharpos++;
879 _doputchar(c);
883 * kernel character input functions
886 bcons_getchar(void)
889 switch (console) {
890 case CONS_TTY:
891 return (serial_getchar());
892 default:
893 return (kb_getchar());
897 #if !defined(_BOOT)
900 bcons_ischar(void)
903 #if defined(__xpv)
904 if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
905 console == CONS_HYPERVISOR)
906 return (bcons_ischar_xen());
907 #endif /* __xpv */
909 switch (console) {
910 case CONS_TTY:
911 return (serial_ischar());
912 default:
913 return (kb_ischar());
917 #endif /* _BOOT */