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