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