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