1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2012 Gary Mills
23 *
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28 #include <sys/types.h>
29 #include <sys/systm.h>
30 #include <sys/archsystm.h>
31 #include <sys/framebuffer.h>
32 #include <sys/boot_console.h>
33 #include <sys/panic.h>
34 #include <sys/ctype.h>
35 #include <sys/ascii.h>
36 #include <sys/vgareg.h>
37 #if defined(__xpv)
38 #include <sys/hypervisor.h>
39 #endif /* __xpv */
40
41 #include "boot_console_impl.h"
42 #include "boot_serial.h"
43
44 #if defined(_BOOT)
45 #include <dboot/dboot_asm.h>
46 #include <dboot/dboot_xboot.h>
47 #else /* _BOOT */
48 #include <sys/bootconf.h>
49 #if defined(__xpv)
50 #include <sys/evtchn_impl.h>
51 #endif /* __xpv */
52 static char *defcons_buf;
53 static char *defcons_cur;
54 #endif /* _BOOT */
55
56 #if defined(__xpv)
57 extern void bcons_init_xen(char *);
58 extern void bcons_putchar_xen(int);
59 extern int bcons_getchar_xen(void);
60 extern int bcons_ischar_xen(void);
61 #endif /* __xpv */
62
63 fb_info_t fb_info;
64 static bcons_dev_t bcons_dev; /* Device callbacks */
65 static int console = CONS_SCREEN_TEXT;
66 static int diag = CONS_INVALID;
67 static int tty_num = 0;
68 static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
69 static char *boot_line;
70 static struct boot_env {
71 char *be_env; /* ends with double ascii nul */
72 size_t be_size; /* size of the environment, including nul */
73 } boot_env;
74
75 /*
76 * Simple console terminal emulator for early boot.
77 * We need this to support kmdb, all other console output is supposed
78 * to be simple text output.
79 */
80 typedef enum btem_state_type {
81 A_STATE_START,
82 A_STATE_ESC,
83 A_STATE_CSI,
84 A_STATE_CSI_QMARK,
85 A_STATE_CSI_EQUAL
86 } btem_state_type_t;
87
88 #define BTEM_MAXPARAMS 5
89 typedef struct btem_state {
90 btem_state_type_t btem_state;
91 boolean_t btem_gotparam;
92 int btem_curparam;
93 int btem_paramval;
94 int btem_params[BTEM_MAXPARAMS];
95 } btem_state_t;
96
97 static btem_state_t boot_tem;
98
99 static int serial_ischar(void);
100 static int serial_getchar(void);
101 static void serial_putchar(int);
102 static void serial_adjust_prop(void);
103
104 #if !defined(_BOOT)
105 /* Set if the console or mode are expressed in the boot line */
106 static int console_set, console_mode_set;
107 #endif
108
109 #if defined(__xpv)
110 static int console_hypervisor_redirect = B_FALSE;
111 static int console_hypervisor_device = CONS_INVALID;
112 static int console_hypervisor_tty_num = 0;
113
114 /* Obtain the hypervisor console type */
115 int
116 console_hypervisor_dev_type(int *tnum)
117 {
118 if (tnum != NULL)
119 *tnum = console_hypervisor_tty_num;
120 return (console_hypervisor_device);
121 }
122 #endif /* __xpv */
123
124 static int port;
125
126 static void
127 serial_init(void)
128 {
129 port = tty_addr[tty_num];
130
131 outb(port + ISR, 0x20);
132 if (inb(port + ISR) & 0x20) {
133 /*
134 * 82510 chip is present
135 */
136 outb(port + DAT+7, 0x04); /* clear status */
137 outb(port + ISR, 0x40); /* set to bank 2 */
138 outb(port + MCR, 0x08); /* IMD */
139 outb(port + DAT, 0x21); /* FMD */
140 outb(port + ISR, 0x00); /* set to bank 0 */
141 } else {
142 /*
143 * set the UART in FIFO mode if it has FIFO buffers.
144 * use 16550 fifo reset sequence specified in NS
145 * application note. disable fifos until chip is
146 * initialized.
147 */
148 outb(port + FIFOR, 0x00); /* clear */
149 outb(port + FIFOR, FIFO_ON); /* enable */
150 outb(port + FIFOR, FIFO_ON|FIFORXFLSH); /* reset */
151 outb(port + FIFOR,
152 FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80);
153 if ((inb(port + ISR) & 0xc0) != 0xc0) {
154 /*
155 * no fifo buffers so disable fifos.
156 * this is true for 8250's
157 */
158 outb(port + FIFOR, 0x00);
159 }
160 }
161
162 /* disable interrupts */
163 outb(port + ICR, 0);
164
165 #if !defined(_BOOT)
166 if (IN_XPV_PANIC())
167 return;
168 #endif
169
170 /* adjust setting based on tty properties */
171 serial_adjust_prop();
172 }
173
174 /* Advance str pointer past white space */
175 #define EAT_WHITE_SPACE(str) { \
176 while ((*str != '\0') && ISSPACE(*str)) \
177 str++; \
178 }
179
180 /*
181 * boot_line is set when we call here. Search it for the argument name,
182 * and if found, return a pointer to it.
183 */
184 static char *
185 find_boot_line_prop(const char *name)
186 {
187 char *ptr;
188 char *ret = NULL;
189 char end_char;
190 size_t len;
191
192 if (boot_line == NULL)
193 return (NULL);
194
195 len = strlen(name);
196
197 /*
198 * We have two nested loops here: the outer loop discards all options
199 * except -B, and the inner loop parses the -B options looking for
200 * the one we're interested in.
201 */
202 for (ptr = boot_line; *ptr != '\0'; ptr++) {
203 EAT_WHITE_SPACE(ptr);
204
205 if (*ptr == '-') {
206 ptr++;
207 while ((*ptr != '\0') && (*ptr != 'B') &&
208 !ISSPACE(*ptr))
209 ptr++;
210 if (*ptr == '\0')
211 goto out;
212 else if (*ptr != 'B')
213 continue;
214 } else {
215 while ((*ptr != '\0') && !ISSPACE(*ptr))
216 ptr++;
217 if (*ptr == '\0')
218 goto out;
219 continue;
220 }
221
222 do {
223 ptr++;
224 EAT_WHITE_SPACE(ptr);
225
226 if ((strncmp(ptr, name, len) == 0) &&
227 (ptr[len] == '=')) {
228 ptr += len + 1;
229 if ((*ptr == '\'') || (*ptr == '"')) {
230 ret = ptr + 1;
231 end_char = *ptr;
232 ptr++;
233 } else {
234 ret = ptr;
235 end_char = ',';
236 }
237 goto consume_property;
238 }
239
240 /*
241 * We have a property, and it's not the one we're
242 * interested in. Skip the property name. A name
243 * can end with '=', a comma, or white space.
244 */
245 while ((*ptr != '\0') && (*ptr != '=') &&
246 (*ptr != ',') && (!ISSPACE(*ptr)))
247 ptr++;
248
249 /*
250 * We only want to go through the rest of the inner
251 * loop if we have a comma. If we have a property
252 * name without a value, either continue or break.
253 */
254 if (*ptr == '\0')
255 goto out;
256 else if (*ptr == ',')
257 continue;
258 else if (ISSPACE(*ptr))
259 break;
260 ptr++;
261
262 /*
263 * Is the property quoted?
264 */
265 if ((*ptr == '\'') || (*ptr == '"')) {
266 end_char = *ptr;
267 ptr++;
268 } else {
269 /*
270 * Not quoted, so the string ends at a comma
271 * or at white space. Deal with white space
272 * later.
273 */
274 end_char = ',';
275 }
276
277 /*
278 * Now, we can ignore any characters until we find
279 * end_char.
280 */
281 consume_property:
282 for (; (*ptr != '\0') && (*ptr != end_char); ptr++) {
283 if ((end_char == ',') && ISSPACE(*ptr))
284 break;
285 }
286 if (*ptr && (*ptr != ',') && !ISSPACE(*ptr))
287 ptr++;
288 } while (*ptr == ',');
289 }
290 out:
291 return (ret);
292 }
293
294 /*
295 * Find prop from boot env module. The data in module is list of C strings
296 * name=value, the list is terminated by double nul.
297 */
298 static const char *
299 find_boot_env_prop(const char *name)
300 {
301 char *ptr;
302 size_t len;
303 uintptr_t size;
304
305 if (boot_env.be_env == NULL)
306 return (NULL);
307
308 ptr = boot_env.be_env;
309 len = strlen(name);
310
311 /*
312 * Make sure we have at least len + 2 bytes in the environment.
313 * We are looking for name=value\0 constructs, and the environment
314 * itself is terminated by '\0'.
315 */
316 if (boot_env.be_size < len + 2)
317 return (NULL);
318
319 do {
320 if ((strncmp(ptr, name, len) == 0) && (ptr[len] == '=')) {
321 ptr += len + 1;
322 return (ptr);
323 }
324 /* find the first '\0' */
325 while (*ptr != '\0') {
326 ptr++;
327 size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
328 if (size > boot_env.be_size)
329 return (NULL);
330 }
331 ptr++;
332
333 /* If the remainder is shorter than name + 2, get out. */
334 size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
335 if (boot_env.be_size - size < len + 2)
336 return (NULL);
337 } while (*ptr != '\0');
338 return (NULL);
339 }
340
341 /*
342 * Get prop value from either command line or boot environment.
343 * We always check kernel command line first, as this will keep the
344 * functionality and will allow user to override the values in environment.
345 */
346 const char *
347 find_boot_prop(const char *name)
348 {
349 const char *value = find_boot_line_prop(name);
350
351 if (value == NULL)
352 value = find_boot_env_prop(name);
353 return (value);
354 }
355
356 #define MATCHES(p, pat) \
357 (strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
358
359 #define SKIP(p, c) \
360 while (*(p) != 0 && *p != (c)) \
361 ++(p); \
362 if (*(p) == (c)) \
363 ++(p);
364
365 /*
366 * find a tty mode property either from cmdline or from boot properties
367 */
368 static const char *
369 get_mode_value(char *name)
370 {
371 /*
372 * when specified on boot line it looks like "name" "="....
373 */
374 if (boot_line != NULL) {
375 return (find_boot_prop(name));
376 }
377
378 #if defined(_BOOT)
379 return (NULL);
380 #else
381 /*
382 * if we're running in the full kernel we check the bootenv.rc settings
383 */
384 {
385 static char propval[20];
386
387 propval[0] = 0;
388 if (do_bsys_getproplen(NULL, name) <= 0)
389 return (NULL);
390 (void) do_bsys_getprop(NULL, name, propval);
391 return (propval);
392 }
393 #endif
394 }
395
396 /*
397 * adjust serial port based on properties
398 * These come either from the cmdline or from boot properties.
399 */
400 static void
401 serial_adjust_prop(void)
402 {
403 char propname[20];
404 const char *propval;
405 const char *p;
406 ulong_t baud;
407 uchar_t lcr = 0;
408 uchar_t mcr = DTR | RTS;
409
410 (void) strcpy(propname, "ttyX-mode");
411 propname[3] = 'a' + tty_num;
412 propval = get_mode_value(propname);
413 if (propval == NULL)
414 propval = "9600,8,n,1,-";
415 #if !defined(_BOOT)
416 else
417 console_mode_set = 1;
418 #endif
419
420 /* property is of the form: "9600,8,n,1,-" */
421 p = propval;
422 if (MATCHES(p, "110,"))
423 baud = ASY110;
424 else if (MATCHES(p, "150,"))
425 baud = ASY150;
426 else if (MATCHES(p, "300,"))
427 baud = ASY300;
428 else if (MATCHES(p, "600,"))
429 baud = ASY600;
430 else if (MATCHES(p, "1200,"))
431 baud = ASY1200;
432 else if (MATCHES(p, "2400,"))
433 baud = ASY2400;
434 else if (MATCHES(p, "4800,"))
435 baud = ASY4800;
436 else if (MATCHES(p, "19200,"))
437 baud = ASY19200;
438 else if (MATCHES(p, "38400,"))
439 baud = ASY38400;
440 else if (MATCHES(p, "57600,"))
441 baud = ASY57600;
442 else if (MATCHES(p, "115200,"))
443 baud = ASY115200;
444 else {
445 baud = ASY9600;
446 SKIP(p, ',');
447 }
448 outb(port + LCR, DLAB);
449 outb(port + DAT + DLL, baud & 0xff);
450 outb(port + DAT + DLH, (baud >> 8) & 0xff);
451
452 switch (*p) {
453 case '5':
454 lcr |= BITS5;
455 ++p;
456 break;
457 case '6':
458 lcr |= BITS6;
459 ++p;
460 break;
461 case '7':
462 lcr |= BITS7;
463 ++p;
464 break;
465 case '8':
466 ++p;
467 /* FALLTHROUGH */
468 default:
469 lcr |= BITS8;
470 break;
471 }
472
473 SKIP(p, ',');
474
475 switch (*p) {
476 case 'n':
477 lcr |= PARITY_NONE;
478 ++p;
479 break;
480 case 'o':
481 lcr |= PARITY_ODD;
482 ++p;
483 break;
484 case 'e':
485 ++p;
486 /* FALLTHROUGH */
487 default:
488 lcr |= PARITY_EVEN;
489 break;
490 }
491
492
493 SKIP(p, ',');
494
495 switch (*p) {
496 case '1':
497 /* STOP1 is 0 */
498 ++p;
499 break;
500 default:
501 lcr |= STOP2;
502 break;
503 }
504 /* set parity bits */
505 outb(port + LCR, lcr);
506
507 (void) strcpy(propname, "ttyX-rts-dtr-off");
508 propname[3] = 'a' + tty_num;
509 propval = get_mode_value(propname);
510 if (propval == NULL)
511 propval = "false";
512 if (propval[0] != 'f' && propval[0] != 'F')
513 mcr = 0;
514 /* set modem control bits */
515 outb(port + MCR, mcr | OUT2);
516 }
517
518 /* Obtain the console type */
519 int
520 boot_console_type(int *tnum)
521 {
522 if (tnum != NULL)
523 *tnum = tty_num;
524 return (console);
525 }
526
527 /*
528 * A structure to map console names to values.
529 */
530 typedef struct {
531 char *name;
532 int value;
533 } console_value_t;
534
535 console_value_t console_devices[] = {
536 { "ttya", CONS_TTY }, /* 0 */
537 { "ttyb", CONS_TTY }, /* 1 */
538 { "ttyc", CONS_TTY }, /* 2 */
539 { "ttyd", CONS_TTY }, /* 3 */
540 { "text", CONS_SCREEN_TEXT },
541 { "graphics", CONS_SCREEN_GRAPHICS },
542 #if defined(__xpv)
543 { "hypervisor", CONS_HYPERVISOR },
544 #endif
545 #if !defined(_BOOT)
546 { "usb-serial", CONS_USBSER },
547 #endif
548 { NULL, CONS_INVALID }
549 };
550
551 static void
552 bcons_init_env(struct xboot_info *xbi)
553 {
554 uint32_t i;
555 struct boot_modules *modules;
556
557 modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules;
558 for (i = 0; i < xbi->bi_module_cnt; i++) {
559 if (modules[i].bm_type == BMT_ENV)
560 break;
561 }
562 if (i == xbi->bi_module_cnt)
563 return;
564
565 boot_env.be_env = (char *)(uintptr_t)modules[i].bm_addr;
566 boot_env.be_size = modules[i].bm_size;
567 }
568
569 int
570 boot_fb(struct xboot_info *xbi, int console)
571 {
572 if (xbi_fb_init(xbi, &bcons_dev) == B_FALSE)
573 return (console);
574
575 /* FB address is not set, fall back to serial terminal. */
576 if (fb_info.paddr == 0)
577 return (CONS_TTY);
578
579 fb_info.terminal.x = VGA_TEXT_COLS;
580 fb_info.terminal.y = VGA_TEXT_ROWS;
581 boot_fb_init(CONS_FRAMEBUFFER);
582
583 if (console == CONS_SCREEN_TEXT)
584 return (CONS_FRAMEBUFFER);
585 return (console);
586 }
587
588 /*
589 * TODO.
590 * quick and dirty local atoi. Perhaps should build with strtol, but
591 * dboot & early boot mix does overcomplicate things much.
592 * Stolen from libc anyhow.
593 */
594 static int
595 atoi(const char *p)
596 {
597 int n, c, neg = 0;
598 unsigned char *up = (unsigned char *)p;
599
600 if (!isdigit(c = *up)) {
601 while (isspace(c))
602 c = *++up;
603 switch (c) {
604 case '-':
605 neg++;
606 /* FALLTHROUGH */
607 case '+':
608 c = *++up;
609 }
610 if (!isdigit(c))
611 return (0);
612 }
613 for (n = '0' - c; isdigit(c = *++up); ) {
614 n *= 10; /* two steps to avoid unnecessary overflow */
615 n += '0' - c; /* accum neg to avoid surprises at MAX */
616 }
617 return (neg ? n : -n);
618 }
619
620 static void
621 bcons_init_fb(void)
622 {
623 const char *propval;
624 int intval;
625
626 /* initialize with explicit default values */
627 fb_info.fg_color = CONS_COLOR;
628 fb_info.bg_color = 0;
629 fb_info.inverse = B_FALSE;
630 fb_info.inverse_screen = B_FALSE;
631
632 /* color values are 0 - 255 */
633 propval = find_boot_prop("tem.fg_color");
634 if (propval != NULL) {
635 intval = atoi(propval);
636 if (intval >= 0 && intval <= 255)
637 fb_info.fg_color = intval;
638 }
639
640 /* color values are 0 - 255 */
641 propval = find_boot_prop("tem.bg_color");
642 if (propval != NULL && ISDIGIT(*propval)) {
643 intval = atoi(propval);
644 if (intval >= 0 && intval <= 255)
645 fb_info.bg_color = intval;
646 }
647
648 /* get inverses. allow 0, 1, true, false */
649 propval = find_boot_prop("tem.inverse");
650 if (propval != NULL) {
651 if (*propval == '1' || MATCHES(propval, "true"))
652 fb_info.inverse = B_TRUE;
653 }
654
655 propval = find_boot_prop("tem.inverse-screen");
656 if (propval != NULL) {
657 if (*propval == '1' || MATCHES(propval, "true"))
658 fb_info.inverse_screen = B_TRUE;
659 }
660
661 #if defined(_BOOT)
662 /*
663 * Load cursor position from bootloader only in dboot,
664 * dboot will pass cursor position to kernel via xboot info.
665 */
666 propval = find_boot_prop("tem.cursor.row");
667 if (propval != NULL) {
668 intval = atoi(propval);
669 if (intval >= 0 && intval <= 0xFFFF)
670 fb_info.cursor.pos.y = intval;
671 }
672
673 propval = find_boot_prop("tem.cursor.col");
674 if (propval != NULL) {
675 intval = atoi(propval);
676 if (intval >= 0 && intval <= 0xFFFF)
677 fb_info.cursor.pos.x = intval;
678 }
679 #endif
680 }
681
682 /*
683 * Go through the console_devices array trying to match the string
684 * we were given. The string on the command line must end with
685 * a comma or white space.
686 *
687 * This function does set tty_num as an side effect, this does imply that
688 * only one of the main console and the diag-device can be using serial.
689 */
690 static int
691 lookup_console_devices(const char *cons_str)
692 {
693 int n, cons;
694 size_t len, cons_len;
695 console_value_t *consolep;
696
697 cons = CONS_INVALID;
698 if (cons_str != NULL) {
699
700 cons_len = strlen(cons_str);
701 for (n = 0; console_devices[n].name != NULL; n++) {
702 consolep = &console_devices[n];
703 len = strlen(consolep->name);
704 if ((len <= cons_len) && ((cons_str[len] == '\0') ||
705 (cons_str[len] == ',') || (cons_str[len] == '\'') ||
706 (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
707 (strncmp(cons_str, consolep->name, len) == 0)) {
708 cons = consolep->value;
709 if (cons == CONS_TTY)
710 tty_num = n;
711 break;
712 }
713 }
714 }
715 return (cons);
716 }
717
718 void
719 bcons_init(struct xboot_info *xbi)
720 {
721 const char *cons_str;
722 #if !defined(_BOOT)
723 static char console_text[] = "text";
724 extern int post_fastreboot;
725 #endif
726
727 if (xbi == NULL) {
728 /* This is very early dboot console, set up ttya. */
729 console = CONS_TTY;
730 serial_init();
731 return;
732 }
733
734 /* Set up data to fetch properties from commad line and boot env. */
735 boot_line = (char *)(uintptr_t)xbi->bi_cmdline;
736 bcons_init_env(xbi);
737 console = CONS_INVALID;
738
739 /* set up initial fb_info */
740 bcons_init_fb();
741
742 #if defined(__xpv)
743 bcons_init_xen(boot_line);
744 #endif /* __xpv */
745
746 /*
747 * First check for diag-device.
748 */
749 cons_str = find_boot_prop("diag-device");
750 if (cons_str != NULL)
751 diag = lookup_console_devices(cons_str);
752
753 cons_str = find_boot_prop("console");
754 if (cons_str == NULL)
755 cons_str = find_boot_prop("output-device");
756
757 #if !defined(_BOOT)
758 if (post_fastreboot && strcmp(cons_str, "graphics") == 0)
759 cons_str = console_text;
760 #endif
761
762 if (cons_str != NULL)
763 console = lookup_console_devices(cons_str);
764
765 #if defined(__xpv)
766 /*
767 * domU's always use the hypervisor regardless of what
768 * the console variable may be set to.
769 */
770 if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
771 console = CONS_HYPERVISOR;
772 console_hypervisor_redirect = B_TRUE;
773 }
774 #endif /* __xpv */
775
776 /*
777 * If no console device specified, default to text.
778 * Remember what was specified for second phase.
779 */
780 if (console == CONS_INVALID)
781 console = CONS_SCREEN_TEXT;
782 #if !defined(_BOOT)
783 else
784 console_set = 1;
785 #endif
786
787 #if defined(__xpv)
788 if (DOMAIN_IS_INITDOMAIN(xen_info)) {
789 switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
790 case XEN_CONSOLE_COM1:
791 case XEN_CONSOLE_COM2:
792 console_hypervisor_device = CONS_TTY;
793 console_hypervisor_tty_num = tty_num;
794 break;
795 case XEN_CONSOLE_VGA:
796 /*
797 * Currently xen doesn't really support
798 * keyboard/display console devices.
799 * What this setting means is that
800 * "vga=keep" has been enabled, which is
801 * more of a xen debugging tool that a
802 * true console mode. Hence, we're going
803 * to ignore this xen "console" setting.
804 */
805 /*FALLTHROUGH*/
806 default:
807 console_hypervisor_device = CONS_INVALID;
808 }
809 }
810
811 /*
812 * if the hypervisor is using the currently selected serial
813 * port then default to using the hypervisor as the console
814 * device.
815 */
816 if (console == console_hypervisor_device) {
817 console = CONS_HYPERVISOR;
818 console_hypervisor_redirect = B_TRUE;
819 }
820 #endif /* __xpv */
821
822 /* make sure the FB is set up if present */
823 console = boot_fb(xbi, console);
824 switch (console) {
825 case CONS_TTY:
826 serial_init();
827 break;
828
829 case CONS_HYPERVISOR:
830 break;
831
832 #if !defined(_BOOT)
833 case CONS_USBSER:
834 /*
835 * We can't do anything with the usb serial
836 * until we have memory management.
837 */
838 break;
839 #endif
840 case CONS_SCREEN_GRAPHICS:
841 kb_init();
842 break;
843 case CONS_SCREEN_TEXT:
844 boot_vga_init(&bcons_dev);
845 /* Fall through */
846 default:
847 kb_init();
848 break;
849 }
850
851 /*
852 * Initialize diag device unless already done.
853 */
854 switch (diag) {
855 case CONS_TTY:
856 if (console != CONS_TTY)
857 serial_init();
858 break;
859 case CONS_SCREEN_GRAPHICS:
860 case CONS_SCREEN_TEXT:
861 if (console != CONS_SCREEN_GRAPHICS &&
862 console != CONS_SCREEN_TEXT)
863 kb_init();
864 break;
865 default:
866 break;
867 }
868 }
869
870 #if !defined(_BOOT)
871 /*
872 * 2nd part of console initialization.
873 * In the kernel (ie. fakebop), this can be used only to switch to
874 * using a serial port instead of screen based on the contents
875 * of the bootenv.rc file.
876 */
877 /*ARGSUSED*/
878 void
879 bcons_init2(char *inputdev, char *outputdev, char *consoledev)
880 {
881 int cons = CONS_INVALID;
882 int ttyn;
883 char *devnames[] = { consoledev, outputdev, inputdev, NULL };
884 console_value_t *consolep;
885 int i;
886 extern int post_fastreboot;
887
888 if (post_fastreboot && console == CONS_SCREEN_GRAPHICS)
889 console = CONS_SCREEN_TEXT;
890
891 if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
892 if (console_set) {
893 /*
894 * If the console was set on the command line,
895 * but the ttyX-mode was not, we only need to
896 * check bootenv.rc for that setting.
897 */
898 if ((!console_mode_set) && (console == CONS_TTY))
899 serial_init();
900 return;
901 }
902
903 for (i = 0; devnames[i] != NULL; i++) {
904 int n;
905
906 for (n = 0; console_devices[n].name != NULL; n++) {
907 consolep = &console_devices[n];
908 if (strcmp(devnames[i], consolep->name) == 0) {
909 cons = consolep->value;
910 if (cons == CONS_TTY)
911 ttyn = n;
912 }
913 }
914 if (cons != CONS_INVALID)
915 break;
916 }
917
918 #if defined(__xpv)
919 /*
920 * if the hypervisor is using the currently selected console
921 * device then default to using the hypervisor as the console
922 * device.
923 */
924 if (cons == console_hypervisor_device) {
925 cons = CONS_HYPERVISOR;
926 console_hypervisor_redirect = B_TRUE;
927 }
928 #endif /* __xpv */
929
930 if ((cons == CONS_INVALID) || (cons == console)) {
931 /*
932 * we're sticking with whatever the current setting is
933 */
934 return;
935 }
936
937 console = cons;
938 if (cons == CONS_TTY) {
939 tty_num = ttyn;
940 serial_init();
941 return;
942 }
943 } else {
944 /*
945 * USB serial and GRAPHICS console
946 * we just collect data into a buffer
947 */
948 extern void *defcons_init(size_t);
949 defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
950 }
951 }
952
953 #if defined(__xpv)
954 boolean_t
955 bcons_hypervisor_redirect(void)
956 {
957 return (console_hypervisor_redirect);
958 }
959
960 void
961 bcons_device_change(int new_console)
962 {
963 if (new_console < CONS_MIN || new_console > CONS_MAX)
964 return;
965
966 /*
967 * If we are asked to switch the console to the hypervisor, that
968 * really means to switch the console to whichever device the
969 * hypervisor is/was using.
970 */
971 if (new_console == CONS_HYPERVISOR)
972 new_console = console_hypervisor_device;
973
974 console = new_console;
975
976 if (new_console == CONS_TTY) {
977 tty_num = console_hypervisor_tty_num;
978 serial_init();
979 }
980 }
981 #endif /* __xpv */
982
983 static void
984 defcons_putchar(int c)
985 {
986 if (defcons_buf != NULL &&
987 defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
988 *defcons_cur++ = c;
989 *defcons_cur = 0;
990 }
991 }
992 #endif /* _BOOT */
993
994 static void
995 serial_putchar(int c)
996 {
997 int checks = 10000;
998
999 while (((inb(port + LSR) & XHRE) == 0) && checks--)
1000 ;
1001 outb(port + DAT, (char)c);
1002 }
1003
1004 static int
1005 serial_getchar(void)
1006 {
1007 uchar_t lsr;
1008
1009 while (serial_ischar() == 0)
1010 ;
1011
1012 lsr = inb(port + LSR);
1013 if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
1014 SERIAL_PARITY | SERIAL_OVERRUN)) {
1015 if (lsr & SERIAL_OVERRUN) {
1016 return (inb(port + DAT));
1017 } else {
1018 /* Toss the garbage */
1019 (void) inb(port + DAT);
1020 return (0);
1021 }
1022 }
1023 return (inb(port + DAT));
1024 }
1025
1026 static int
1027 serial_ischar(void)
1028 {
1029 return (inb(port + LSR) & RCA);
1030 }
1031
1032 static void
1033 btem_control(btem_state_t *btem, int c)
1034 {
1035 int y, rows, cols;
1036
1037 rows = fb_info.cursor.pos.y;
1038 cols = fb_info.cursor.pos.x;
1039
1040 btem->btem_state = A_STATE_START;
1041 switch (c) {
1042 case A_BS:
1043 bcons_dev.bd_setpos(rows, cols - 1);
1044 break;
1045
1046 case A_HT:
1047 cols += 8 - (cols % 8);
1048 if (cols >= fb_info.terminal.x)
1049 cols = fb_info.terminal.x - 1;
1050 bcons_dev.bd_setpos(rows, cols);
1051 break;
1052
1053 case A_CR:
1054 bcons_dev.bd_setpos(rows, 0);
1055 break;
1056
1057 case A_FF:
1058 for (y = 0; y < fb_info.terminal.y; y++) {
1059 bcons_dev.bd_setpos(y, 0);
1060 bcons_dev.bd_eraseline();
1061 }
1062 bcons_dev.bd_setpos(0, 0);
1063 break;
1064
1065 case A_ESC:
1066 btem->btem_state = A_STATE_ESC;
1067 break;
1068
1069 default:
1070 bcons_dev.bd_putchar(c);
1071 break;
1072 }
1073 }
1074
1075 /*
1076 * if parameters [0..count - 1] are not set, set them to the value
1077 * of newparam.
1078 */
1079 static void
1080 btem_setparam(btem_state_t *btem, int count, int newparam)
1081 {
1082 int i;
1083
1084 for (i = 0; i < count; i++) {
1085 if (btem->btem_params[i] == -1)
1086 btem->btem_params[i] = newparam;
1087 }
1088 }
1089
1090 static void
1091 btem_chkparam(btem_state_t *btem, int c)
1092 {
1093 int rows, cols;
1094
1095 rows = fb_info.cursor.pos.y;
1096 cols = fb_info.cursor.pos.x;
1097 switch (c) {
1098 case '@': /* insert char */
1099 btem_setparam(btem, 1, 1);
1100 bcons_dev.bd_shift(btem->btem_params[0]);
1101 break;
1102
1103 case 'A': /* cursor up */
1104 btem_setparam(btem, 1, 1);
1105 bcons_dev.bd_setpos(rows - btem->btem_params[0], cols);
1106 break;
1107
1108 case 'B': /* cursor down */
1109 btem_setparam(btem, 1, 1);
1110 bcons_dev.bd_setpos(rows + btem->btem_params[0], cols);
1111 break;
1112
1113 case 'C': /* cursor right */
1114 btem_setparam(btem, 1, 1);
1115 bcons_dev.bd_setpos(rows, cols + btem->btem_params[0]);
1116 break;
1117
1118 case 'D': /* cursor left */
1119 btem_setparam(btem, 1, 1);
1120 bcons_dev.bd_setpos(rows, cols - btem->btem_params[0]);
1121 break;
1122
1123 case 'K':
1124 bcons_dev.bd_eraseline();
1125 break;
1126 default:
1127 /* bcons_dev.bd_putchar(c); */
1128 break;
1129 }
1130 btem->btem_state = A_STATE_START;
1131 }
1132
1133 static void
1134 btem_getparams(btem_state_t *btem, int c)
1135 {
1136 if (isdigit(c)) {
1137 btem->btem_paramval = btem->btem_paramval * 10 + c - '0';
1138 btem->btem_gotparam = B_TRUE;
1139 return;
1140 }
1141
1142 if (btem->btem_curparam < BTEM_MAXPARAMS) {
1143 if (btem->btem_gotparam == B_TRUE) {
1144 btem->btem_params[btem->btem_curparam] =
1145 btem->btem_paramval;
1146 }
1147 btem->btem_curparam++;
1148 }
1149
1150 if (c == ';') {
1151 /* Restart parameter search */
1152 btem->btem_gotparam = B_FALSE;
1153 btem->btem_paramval = 0;
1154 } else {
1155 btem_chkparam(btem, c);
1156 }
1157 }
1158
1159 /* Simple boot terminal parser. */
1160 static void
1161 btem_parse(btem_state_t *btem, int c)
1162 {
1163 int i;
1164
1165 /* Normal state? */
1166 if (btem->btem_state == A_STATE_START) {
1167 if (c == A_CSI || c < ' ')
1168 btem_control(btem, c);
1169 else
1170 bcons_dev.bd_putchar(c);
1171 return;
1172 }
1173
1174 /* In <ESC> sequence */
1175 if (btem->btem_state != A_STATE_ESC) {
1176 btem_getparams(btem, c);
1177 return;
1178 }
1179
1180 /* Previous char was <ESC> */
1181 switch (c) {
1182 case '[':
1183 btem->btem_curparam = 0;
1184 btem->btem_paramval = 0;
1185 btem->btem_gotparam = B_FALSE;
1186 /* clear the parameters */
1187 for (i = 0; i < BTEM_MAXPARAMS; i++)
1188 btem->btem_params[i] = -1;
1189 btem->btem_state = A_STATE_CSI;
1190 return;
1191
1192 case 'Q': /* <ESC>Q */
1193 case 'C': /* <ESC>C */
1194 btem->btem_state = A_STATE_START;
1195 return;
1196
1197 default:
1198 btem->btem_state = A_STATE_START;
1199 break;
1200 }
1201
1202 if (c < ' ')
1203 btem_control(btem, c);
1204 else
1205 bcons_dev.bd_putchar(c);
1206 }
1207
1208 static void
1209 _doputchar(int device, int c)
1210 {
1211 switch (device) {
1212 case CONS_TTY:
1213 serial_putchar(c);
1214 return;
1215 case CONS_SCREEN_TEXT:
1216 case CONS_FRAMEBUFFER:
1217 bcons_dev.bd_cursor(B_FALSE);
1218 btem_parse(&boot_tem, c);
1219 bcons_dev.bd_cursor(B_TRUE);
1220 return;
1221 case CONS_SCREEN_GRAPHICS:
1222 #if !defined(_BOOT)
1223 case CONS_USBSER:
1224 defcons_putchar(c);
1225 #endif /* _BOOT */
1226 default:
1227 return;
1228 }
1229 }
1230
1231 void
1232 bcons_putchar(int c)
1233 {
1234 #if defined(__xpv)
1235 if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1236 console == CONS_HYPERVISOR) {
1237 bcons_putchar_xen(c);
1238 return;
1239 }
1240 #endif /* __xpv */
1241
1242 if (c == '\n') {
1243 _doputchar(console, '\r');
1244 if (diag != console)
1245 _doputchar(diag, '\r');
1246 }
1247 _doputchar(console, c);
1248 if (diag != console)
1249 _doputchar(diag, c);
1250 }
1251
1252 /*
1253 * kernel character input functions
1254 */
1255 int
1256 bcons_getchar(void)
1257 {
1258 #if defined(__xpv)
1259 if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1260 console == CONS_HYPERVISOR)
1261 return (bcons_getchar_xen());
1262 #endif /* __xpv */
1263
1264 for (;;) {
1265 if (console == CONS_TTY || diag == CONS_TTY) {
1266 if (serial_ischar())
1267 return (serial_getchar());
1268 }
1269 if (console != CONS_INVALID || diag != CONS_INVALID) {
1270 if (kb_ischar())
1271 return (kb_getchar());
1272 }
1273 }
1274 }
1275
1276 #if !defined(_BOOT)
1277
1278 int
1279 bcons_ischar(void)
1280 {
1281 int c = 0;
1282
1283 #if defined(__xpv)
1284 if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1285 console == CONS_HYPERVISOR)
1286 return (bcons_ischar_xen());
1287 #endif /* __xpv */
1288
1289 switch (console) {
1290 case CONS_TTY:
1291 c = serial_ischar();
1292 break;
1293
1294 case CONS_INVALID:
1295 break;
1296
1297 default:
1298 c = kb_ischar();
1299 }
1300 if (c != 0)
1301 return (c);
1302
1303 switch (diag) {
1304 case CONS_TTY:
1305 c = serial_ischar();
1306 break;
1307
1308 case CONS_INVALID:
1309 break;
1310
1311 default:
1312 c = kb_ischar();
1313 }
1314
1315 return (c);
1316 }
1317
1318 #endif /* _BOOT */