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/boot_console.h>
32 #include <sys/panic.h>
33 #include <sys/ctype.h>
34 #if defined(__xpv)
35 #include <sys/hypervisor.h>
36 #endif /* __xpv */
37
38 #include "boot_serial.h"
39 #include "boot_vga.h"
40
41 #if defined(_BOOT)
42 #include <dboot/dboot_asm.h>
43 #include <dboot/dboot_xboot.h>
44 #else /* _BOOT */
45 #include <sys/bootconf.h>
46 #if defined(__xpv)
47 #include <sys/evtchn_impl.h>
48 #endif /* __xpv */
49 static char *defcons_buf;
50 static char *defcons_cur;
51 #endif /* _BOOT */
52
53 #if defined(__xpv)
54 extern void bcons_init_xen(char *);
55 extern void bcons_putchar_xen(int);
56 extern int bcons_getchar_xen(void);
57 extern int bcons_ischar_xen(void);
58 #endif /* __xpv */
59
60 static int cons_color = CONS_COLOR;
61 static int console = CONS_SCREEN_TEXT;
62 static int tty_num = 0;
63 static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
64 static char *boot_line;
65 static struct boot_env {
66 char *be_env; /* ends with double ascii nul */
67 size_t be_size; /* size of the environment, including nul */
68 } boot_env;
69
70 static int serial_ischar(void);
71 static int serial_getchar(void);
72 static void serial_putchar(int);
73 static void serial_adjust_prop(void);
74
75 #if !defined(_BOOT)
76 /* Set if the console or mode are expressed in the boot line */
77 static int console_set, console_mode_set;
78 #endif
79
80 #if defined(__xpv)
81 static int console_hypervisor_redirect = B_FALSE;
82 static int console_hypervisor_device = CONS_INVALID;
83 static int console_hypervisor_tty_num = 0;
84
85 /* Obtain the hypervisor console type */
86 int
console_hypervisor_dev_type(int * tnum)87 console_hypervisor_dev_type(int *tnum)
88 {
89 if (tnum != NULL)
90 *tnum = console_hypervisor_tty_num;
91 return (console_hypervisor_device);
92 }
93 #endif /* __xpv */
94
95 /* Clear the screen and initialize VIDEO, XPOS and YPOS. */
96 void
clear_screen(void)97 clear_screen(void)
98 {
99 /*
100 * XXX should set vga mode so we don't depend on the
101 * state left by the boot loader. Note that we have to
102 * enable the cursor before clearing the screen since
103 * the cursor position is dependant upon the cursor
104 * skew, which is initialized by vga_cursor_display()
105 */
106 vga_cursor_display();
107 vga_clear(cons_color);
108 vga_setpos(0, 0);
109 }
110
111 /* Put the character C on the screen. */
112 static void
screen_putchar(int c)113 screen_putchar(int c)
114 {
115 int row, col;
116
117 vga_getpos(&row, &col);
118 switch (c) {
119 case '\t':
120 col += 8 - (col % 8);
121 if (col == VGA_TEXT_COLS)
122 col = 79;
123 vga_setpos(row, col);
124 break;
125
126 case '\r':
127 vga_setpos(row, 0);
128 break;
129
130 case '\b':
131 if (col > 0)
132 vga_setpos(row, col - 1);
133 break;
134
135 case '\n':
136 if (row < VGA_TEXT_ROWS - 1)
137 vga_setpos(row + 1, col);
138 else
139 vga_scroll(cons_color);
140 break;
141
142 default:
143 vga_drawc(c, cons_color);
144 if (col < VGA_TEXT_COLS -1)
145 vga_setpos(row, col + 1);
146 else if (row < VGA_TEXT_ROWS - 1)
147 vga_setpos(row + 1, 0);
148 else {
149 vga_setpos(row, 0);
150 vga_scroll(cons_color);
151 }
152 break;
153 }
154 }
155
156 static int port;
157
158 static void
serial_init(void)159 serial_init(void)
160 {
161 port = tty_addr[tty_num];
162
163 outb(port + ISR, 0x20);
164 if (inb(port + ISR) & 0x20) {
165 /*
166 * 82510 chip is present
167 */
168 outb(port + DAT+7, 0x04); /* clear status */
169 outb(port + ISR, 0x40); /* set to bank 2 */
170 outb(port + MCR, 0x08); /* IMD */
171 outb(port + DAT, 0x21); /* FMD */
172 outb(port + ISR, 0x00); /* set to bank 0 */
173 } else {
174 /*
175 * set the UART in FIFO mode if it has FIFO buffers.
176 * use 16550 fifo reset sequence specified in NS
177 * application note. disable fifos until chip is
178 * initialized.
179 */
180 outb(port + FIFOR, 0x00); /* clear */
181 outb(port + FIFOR, FIFO_ON); /* enable */
182 outb(port + FIFOR, FIFO_ON|FIFORXFLSH); /* reset */
183 outb(port + FIFOR,
184 FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80);
185 if ((inb(port + ISR) & 0xc0) != 0xc0) {
186 /*
187 * no fifo buffers so disable fifos.
188 * this is true for 8250's
189 */
190 outb(port + FIFOR, 0x00);
191 }
192 }
193
194 /* disable interrupts */
195 outb(port + ICR, 0);
196
197 #if !defined(_BOOT)
198 if (IN_XPV_PANIC())
199 return;
200 #endif
201
202 /* adjust setting based on tty properties */
203 serial_adjust_prop();
204
205 #if defined(_BOOT)
206 /*
207 * Do a full reset to match console behavior.
208 * 0x1B + c - reset everything
209 */
210 serial_putchar(0x1B);
211 serial_putchar('c');
212 #endif
213 }
214
215 /* Advance str pointer past white space */
216 #define EAT_WHITE_SPACE(str) { \
217 while ((*str != '\0') && ISSPACE(*str)) \
218 str++; \
219 }
220
221 /*
222 * boot_line is set when we call here. Search it for the argument name,
223 * and if found, return a pointer to it.
224 */
225 static char *
find_boot_line_prop(const char * name)226 find_boot_line_prop(const char *name)
227 {
228 char *ptr;
229 char *ret = NULL;
230 char end_char;
231 size_t len;
232
233 if (boot_line == NULL)
234 return (NULL);
235
236 len = strlen(name);
237
238 /*
239 * We have two nested loops here: the outer loop discards all options
240 * except -B, and the inner loop parses the -B options looking for
241 * the one we're interested in.
242 */
243 for (ptr = boot_line; *ptr != '\0'; ptr++) {
244 EAT_WHITE_SPACE(ptr);
245
246 if (*ptr == '-') {
247 ptr++;
248 while ((*ptr != '\0') && (*ptr != 'B') &&
249 !ISSPACE(*ptr))
250 ptr++;
251 if (*ptr == '\0')
252 goto out;
253 else if (*ptr != 'B')
254 continue;
255 } else {
256 while ((*ptr != '\0') && !ISSPACE(*ptr))
257 ptr++;
258 if (*ptr == '\0')
259 goto out;
260 continue;
261 }
262
263 do {
264 ptr++;
265 EAT_WHITE_SPACE(ptr);
266
267 if ((strncmp(ptr, name, len) == 0) &&
268 (ptr[len] == '=')) {
269 ptr += len + 1;
270 if ((*ptr == '\'') || (*ptr == '"')) {
271 ret = ptr + 1;
272 end_char = *ptr;
273 ptr++;
274 } else {
275 ret = ptr;
276 end_char = ',';
277 }
278 goto consume_property;
279 }
280
281 /*
282 * We have a property, and it's not the one we're
283 * interested in. Skip the property name. A name
284 * can end with '=', a comma, or white space.
285 */
286 while ((*ptr != '\0') && (*ptr != '=') &&
287 (*ptr != ',') && (!ISSPACE(*ptr)))
288 ptr++;
289
290 /*
291 * We only want to go through the rest of the inner
292 * loop if we have a comma. If we have a property
293 * name without a value, either continue or break.
294 */
295 if (*ptr == '\0')
296 goto out;
297 else if (*ptr == ',')
298 continue;
299 else if (ISSPACE(*ptr))
300 break;
301 ptr++;
302
303 /*
304 * Is the property quoted?
305 */
306 if ((*ptr == '\'') || (*ptr == '"')) {
307 end_char = *ptr;
308 ptr++;
309 } else {
310 /*
311 * Not quoted, so the string ends at a comma
312 * or at white space. Deal with white space
313 * later.
314 */
315 end_char = ',';
316 }
317
318 /*
319 * Now, we can ignore any characters until we find
320 * end_char.
321 */
322 consume_property:
323 for (; (*ptr != '\0') && (*ptr != end_char); ptr++) {
324 if ((end_char == ',') && ISSPACE(*ptr))
325 break;
326 }
327 if (*ptr && (*ptr != ',') && !ISSPACE(*ptr))
328 ptr++;
329 } while (*ptr == ',');
330 }
331 out:
332 return (ret);
333 }
334
335 /*
336 * Find prop from boot env module. The data in module is list of C strings
337 * name=value, the list is terminated by double nul.
338 */
339 static const char *
find_boot_env_prop(const char * name)340 find_boot_env_prop(const char *name)
341 {
342 char *ptr;
343 size_t len;
344 uintptr_t size;
345
346 if (boot_env.be_env == NULL)
347 return (NULL);
348
349 ptr = boot_env.be_env;
350 len = strlen(name);
351
352 /*
353 * Make sure we have at least len + 2 bytes in the environment.
354 * We are looking for name=value\0 constructs, and the environment
355 * itself is terminated by '\0'.
356 */
357 if (boot_env.be_size < len + 2)
358 return (NULL);
359
360 do {
361 if ((strncmp(ptr, name, len) == 0) && (ptr[len] == '=')) {
362 ptr += len + 1;
363 return (ptr);
364 }
365 /* find the first '\0' */
366 while (*ptr != '\0') {
367 ptr++;
368 size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
369 if (size > boot_env.be_size)
370 return (NULL);
371 }
372 ptr++;
373
374 /* If the remainder is shorter than name + 2, get out. */
375 size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
376 if (boot_env.be_size - size < len + 2)
377 return (NULL);
378 } while (*ptr != '\0');
379 return (NULL);
380 }
381
382 /*
383 * Get prop value from either command line or boot environment.
384 * We always check kernel command line first, as this will keep the
385 * functionality and will allow user to override the values in environment.
386 */
387 const char *
find_boot_prop(const char * name)388 find_boot_prop(const char *name)
389 {
390 const char *value = find_boot_line_prop(name);
391
392 if (value == NULL)
393 value = find_boot_env_prop(name);
394 return (value);
395 }
396
397 #define MATCHES(p, pat) \
398 (strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
399
400 #define SKIP(p, c) \
401 while (*(p) != 0 && *p != (c)) \
402 ++(p); \
403 if (*(p) == (c)) \
404 ++(p);
405
406 /*
407 * find a tty mode property either from cmdline or from boot properties
408 */
409 static const char *
get_mode_value(char * name)410 get_mode_value(char *name)
411 {
412 /*
413 * when specified on boot line it looks like "name" "="....
414 */
415 if (boot_line != NULL) {
416 return (find_boot_prop(name));
417 }
418
419 #if defined(_BOOT)
420 return (NULL);
421 #else
422 /*
423 * if we're running in the full kernel we check the bootenv.rc settings
424 */
425 {
426 static char propval[20];
427
428 propval[0] = 0;
429 if (do_bsys_getproplen(NULL, name) <= 0)
430 return (NULL);
431 (void) do_bsys_getprop(NULL, name, propval);
432 return (propval);
433 }
434 #endif
435 }
436
437 /*
438 * adjust serial port based on properties
439 * These come either from the cmdline or from boot properties.
440 */
441 static void
serial_adjust_prop(void)442 serial_adjust_prop(void)
443 {
444 char propname[20];
445 const char *propval;
446 const char *p;
447 ulong_t baud;
448 uchar_t lcr = 0;
449 uchar_t mcr = DTR | RTS;
450
451 (void) strcpy(propname, "ttyX-mode");
452 propname[3] = 'a' + tty_num;
453 propval = get_mode_value(propname);
454 if (propval == NULL)
455 propval = "9600,8,n,1,-";
456 #if !defined(_BOOT)
457 else
458 console_mode_set = 1;
459 #endif
460
461 /* property is of the form: "9600,8,n,1,-" */
462 p = propval;
463 if (MATCHES(p, "110,"))
464 baud = ASY110;
465 else if (MATCHES(p, "150,"))
466 baud = ASY150;
467 else if (MATCHES(p, "300,"))
468 baud = ASY300;
469 else if (MATCHES(p, "600,"))
470 baud = ASY600;
471 else if (MATCHES(p, "1200,"))
472 baud = ASY1200;
473 else if (MATCHES(p, "2400,"))
474 baud = ASY2400;
475 else if (MATCHES(p, "4800,"))
476 baud = ASY4800;
477 else if (MATCHES(p, "19200,"))
478 baud = ASY19200;
479 else if (MATCHES(p, "38400,"))
480 baud = ASY38400;
481 else if (MATCHES(p, "57600,"))
482 baud = ASY57600;
483 else if (MATCHES(p, "115200,"))
484 baud = ASY115200;
485 else {
486 baud = ASY9600;
487 SKIP(p, ',');
488 }
489 outb(port + LCR, DLAB);
490 outb(port + DAT + DLL, baud & 0xff);
491 outb(port + DAT + DLH, (baud >> 8) & 0xff);
492
493 switch (*p) {
494 case '5':
495 lcr |= BITS5;
496 ++p;
497 break;
498 case '6':
499 lcr |= BITS6;
500 ++p;
501 break;
502 case '7':
503 lcr |= BITS7;
504 ++p;
505 break;
506 case '8':
507 ++p;
508 default:
509 lcr |= BITS8;
510 break;
511 }
512
513 SKIP(p, ',');
514
515 switch (*p) {
516 case 'n':
517 lcr |= PARITY_NONE;
518 ++p;
519 break;
520 case 'o':
521 lcr |= PARITY_ODD;
522 ++p;
523 break;
524 case 'e':
525 ++p;
526 default:
527 lcr |= PARITY_EVEN;
528 break;
529 }
530
531
532 SKIP(p, ',');
533
534 switch (*p) {
535 case '1':
536 /* STOP1 is 0 */
537 ++p;
538 break;
539 default:
540 lcr |= STOP2;
541 break;
542 }
543 /* set parity bits */
544 outb(port + LCR, lcr);
545
546 (void) strcpy(propname, "ttyX-rts-dtr-off");
547 propname[3] = 'a' + tty_num;
548 propval = get_mode_value(propname);
549 if (propval == NULL)
550 propval = "false";
551 if (propval[0] != 'f' && propval[0] != 'F')
552 mcr = 0;
553 /* set modem control bits */
554 outb(port + MCR, mcr | OUT2);
555 }
556
557 /* Obtain the console type */
558 int
boot_console_type(int * tnum)559 boot_console_type(int *tnum)
560 {
561 if (tnum != NULL)
562 *tnum = tty_num;
563 return (console);
564 }
565
566 /*
567 * A structure to map console names to values.
568 */
569 typedef struct {
570 char *name;
571 int value;
572 } console_value_t;
573
574 console_value_t console_devices[] = {
575 { "ttya", CONS_TTY }, /* 0 */
576 { "ttyb", CONS_TTY }, /* 1 */
577 { "ttyc", CONS_TTY }, /* 2 */
578 { "ttyd", CONS_TTY }, /* 3 */
579 { "text", CONS_SCREEN_TEXT },
580 { "graphics", CONS_SCREEN_GRAPHICS },
581 #if defined(__xpv)
582 { "hypervisor", CONS_HYPERVISOR },
583 #endif
584 #if !defined(_BOOT)
585 { "usb-serial", CONS_USBSER },
586 #endif
587 { NULL, CONS_INVALID }
588 };
589
590 static void
bcons_init_env(struct xboot_info * xbi)591 bcons_init_env(struct xboot_info *xbi)
592 {
593 uint32_t i;
594 struct boot_modules *modules;
595
596 modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules;
597 for (i = 0; i < xbi->bi_module_cnt; i++) {
598 if (modules[i].bm_type == BMT_ENV)
599 break;
600 }
601 if (i == xbi->bi_module_cnt)
602 return;
603
604 boot_env.be_env = (char *)(uintptr_t)modules[i].bm_addr;
605 boot_env.be_size = modules[i].bm_size;
606 }
607
608 void
bcons_init(struct xboot_info * xbi)609 bcons_init(struct xboot_info *xbi)
610 {
611 console_value_t *consolep;
612 size_t len, cons_len;
613 const char *cons_str;
614 #if !defined(_BOOT)
615 static char console_text[] = "text";
616 extern int post_fastreboot;
617 #endif
618
619 /* Set up data to fetch properties from commad line and boot env. */
620 boot_line = (char *)(uintptr_t)xbi->bi_cmdline;
621 bcons_init_env(xbi);
622 console = CONS_INVALID;
623
624 #if defined(__xpv)
625 bcons_init_xen(boot_line);
626 #endif /* __xpv */
627
628 cons_str = find_boot_prop("console");
629 if (cons_str == NULL)
630 cons_str = find_boot_prop("output-device");
631
632 #if !defined(_BOOT)
633 if (post_fastreboot && strcmp(cons_str, "graphics") == 0)
634 cons_str = console_text;
635 #endif
636
637 /*
638 * Go through the console_devices array trying to match the string
639 * we were given. The string on the command line must end with
640 * a comma or white space.
641 */
642 if (cons_str != NULL) {
643 int n;
644
645 cons_len = strlen(cons_str);
646 for (n = 0; console_devices[n].name != NULL; n++) {
647 consolep = &console_devices[n];
648 len = strlen(consolep->name);
649 if ((len <= cons_len) && ((cons_str[len] == '\0') ||
650 (cons_str[len] == ',') || (cons_str[len] == '\'') ||
651 (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
652 (strncmp(cons_str, consolep->name, len) == 0)) {
653 console = consolep->value;
654 if (console == CONS_TTY)
655 tty_num = n;
656 break;
657 }
658 }
659 }
660
661 #if defined(__xpv)
662 /*
663 * domU's always use the hypervisor regardless of what
664 * the console variable may be set to.
665 */
666 if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
667 console = CONS_HYPERVISOR;
668 console_hypervisor_redirect = B_TRUE;
669 }
670 #endif /* __xpv */
671
672 /*
673 * If no console device specified, default to text.
674 * Remember what was specified for second phase.
675 */
676 if (console == CONS_INVALID)
677 console = CONS_SCREEN_TEXT;
678 #if !defined(_BOOT)
679 else
680 console_set = 1;
681 #endif
682
683 #if defined(__xpv)
684 if (DOMAIN_IS_INITDOMAIN(xen_info)) {
685 switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
686 case XEN_CONSOLE_COM1:
687 case XEN_CONSOLE_COM2:
688 console_hypervisor_device = CONS_TTY;
689 console_hypervisor_tty_num = tty_num;
690 break;
691 case XEN_CONSOLE_VGA:
692 /*
693 * Currently xen doesn't really support
694 * keyboard/display console devices.
695 * What this setting means is that
696 * "vga=keep" has been enabled, which is
697 * more of a xen debugging tool that a
698 * true console mode. Hence, we're going
699 * to ignore this xen "console" setting.
700 */
701 /*FALLTHROUGH*/
702 default:
703 console_hypervisor_device = CONS_INVALID;
704 }
705 }
706
707 /*
708 * if the hypervisor is using the currently selected serial
709 * port then default to using the hypervisor as the console
710 * device.
711 */
712 if (console == console_hypervisor_device) {
713 console = CONS_HYPERVISOR;
714 console_hypervisor_redirect = B_TRUE;
715 }
716 #endif /* __xpv */
717
718 switch (console) {
719 case CONS_TTY:
720 serial_init();
721 break;
722
723 case CONS_HYPERVISOR:
724 break;
725
726 #if !defined(_BOOT)
727 case CONS_USBSER:
728 /*
729 * We can't do anything with the usb serial
730 * until we have memory management.
731 */
732 break;
733 #endif
734 case CONS_SCREEN_GRAPHICS:
735 kb_init();
736 break;
737 case CONS_SCREEN_TEXT:
738 default:
739 #if defined(_BOOT)
740 clear_screen(); /* clears the grub or xen screen */
741 #endif /* _BOOT */
742 kb_init();
743 break;
744 }
745 }
746
747 #if !defined(_BOOT)
748 /*
749 * 2nd part of console initialization.
750 * In the kernel (ie. fakebop), this can be used only to switch to
751 * using a serial port instead of screen based on the contents
752 * of the bootenv.rc file.
753 */
754 /*ARGSUSED*/
755 void
bcons_init2(char * inputdev,char * outputdev,char * consoledev)756 bcons_init2(char *inputdev, char *outputdev, char *consoledev)
757 {
758 int cons = CONS_INVALID;
759 int ttyn;
760 char *devnames[] = { consoledev, outputdev, inputdev, NULL };
761 console_value_t *consolep;
762 int i;
763 extern int post_fastreboot;
764
765 if (post_fastreboot && console == CONS_SCREEN_GRAPHICS)
766 console = CONS_SCREEN_TEXT;
767
768 if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
769 if (console_set) {
770 /*
771 * If the console was set on the command line,
772 * but the ttyX-mode was not, we only need to
773 * check bootenv.rc for that setting.
774 */
775 if ((!console_mode_set) && (console == CONS_TTY))
776 serial_init();
777 return;
778 }
779
780 for (i = 0; devnames[i] != NULL; i++) {
781 int n;
782
783 for (n = 0; console_devices[n].name != NULL; n++) {
784 consolep = &console_devices[n];
785 if (strcmp(devnames[i], consolep->name) == 0) {
786 cons = consolep->value;
787 if (cons == CONS_TTY)
788 ttyn = n;
789 }
790 }
791 if (cons != CONS_INVALID)
792 break;
793 }
794
795 #if defined(__xpv)
796 /*
797 * if the hypervisor is using the currently selected console
798 * device then default to using the hypervisor as the console
799 * device.
800 */
801 if (cons == console_hypervisor_device) {
802 cons = CONS_HYPERVISOR;
803 console_hypervisor_redirect = B_TRUE;
804 }
805 #endif /* __xpv */
806
807 if ((cons == CONS_INVALID) || (cons == console)) {
808 /*
809 * we're sticking with whatever the current setting is
810 */
811 return;
812 }
813
814 console = cons;
815 if (cons == CONS_TTY) {
816 tty_num = ttyn;
817 serial_init();
818 return;
819 }
820 } else {
821 /*
822 * USB serial and GRAPHICS console
823 * we just collect data into a buffer
824 */
825 extern void *defcons_init(size_t);
826 defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
827 }
828 }
829
830 #if defined(__xpv)
831 boolean_t
bcons_hypervisor_redirect(void)832 bcons_hypervisor_redirect(void)
833 {
834 return (console_hypervisor_redirect);
835 }
836
837 void
bcons_device_change(int new_console)838 bcons_device_change(int new_console)
839 {
840 if (new_console < CONS_MIN || new_console > CONS_MAX)
841 return;
842
843 /*
844 * If we are asked to switch the console to the hypervisor, that
845 * really means to switch the console to whichever device the
846 * hypervisor is/was using.
847 */
848 if (new_console == CONS_HYPERVISOR)
849 new_console = console_hypervisor_device;
850
851 console = new_console;
852
853 if (new_console == CONS_TTY) {
854 tty_num = console_hypervisor_tty_num;
855 serial_init();
856 }
857 }
858 #endif /* __xpv */
859
860 static void
defcons_putchar(int c)861 defcons_putchar(int c)
862 {
863 if (defcons_buf != NULL &&
864 defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
865 *defcons_cur++ = c;
866 *defcons_cur = 0;
867 }
868 }
869 #endif /* _BOOT */
870
871 static void
serial_putchar(int c)872 serial_putchar(int c)
873 {
874 int checks = 10000;
875
876 while (((inb(port + LSR) & XHRE) == 0) && checks--)
877 ;
878 outb(port + DAT, (char)c);
879 }
880
881 static int
serial_getchar(void)882 serial_getchar(void)
883 {
884 uchar_t lsr;
885
886 while (serial_ischar() == 0)
887 ;
888
889 lsr = inb(port + LSR);
890 if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
891 SERIAL_PARITY | SERIAL_OVERRUN)) {
892 if (lsr & SERIAL_OVERRUN) {
893 return (inb(port + DAT));
894 } else {
895 /* Toss the garbage */
896 (void) inb(port + DAT);
897 return (0);
898 }
899 }
900 return (inb(port + DAT));
901 }
902
903 static int
serial_ischar(void)904 serial_ischar(void)
905 {
906 return (inb(port + LSR) & RCA);
907 }
908
909 static void
_doputchar(int c)910 _doputchar(int c)
911 {
912 switch (console) {
913 case CONS_TTY:
914 serial_putchar(c);
915 return;
916 case CONS_SCREEN_TEXT:
917 screen_putchar(c);
918 return;
919 case CONS_SCREEN_GRAPHICS:
920 #if !defined(_BOOT)
921 case CONS_USBSER:
922 defcons_putchar(c);
923 #endif /* _BOOT */
924 return;
925 }
926 }
927
928 void
bcons_putchar(int c)929 bcons_putchar(int c)
930 {
931 static int bhcharpos = 0;
932
933 #if defined(__xpv)
934 if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
935 console == CONS_HYPERVISOR) {
936 bcons_putchar_xen(c);
937 return;
938 }
939 #endif /* __xpv */
940
941 if (c == '\t') {
942 do {
943 _doputchar(' ');
944 } while (++bhcharpos % 8);
945 return;
946 } else if (c == '\n' || c == '\r') {
947 bhcharpos = 0;
948 _doputchar('\r');
949 _doputchar(c);
950 return;
951 } else if (c == '\b') {
952 if (bhcharpos)
953 bhcharpos--;
954 _doputchar(c);
955 return;
956 }
957
958 bhcharpos++;
959 _doputchar(c);
960 }
961
962 /*
963 * kernel character input functions
964 */
965 int
bcons_getchar(void)966 bcons_getchar(void)
967 {
968 #if defined(__xpv)
969 if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
970 console == CONS_HYPERVISOR)
971 return (bcons_getchar_xen());
972 #endif /* __xpv */
973
974 switch (console) {
975 case CONS_TTY:
976 return (serial_getchar());
977 default:
978 return (kb_getchar());
979 }
980 }
981
982 #if !defined(_BOOT)
983
984 int
bcons_ischar(void)985 bcons_ischar(void)
986 {
987
988 #if defined(__xpv)
989 if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
990 console == CONS_HYPERVISOR)
991 return (bcons_ischar_xen());
992 #endif /* __xpv */
993
994 switch (console) {
995 case CONS_TTY:
996 return (serial_ischar());
997 default:
998 return (kb_ischar());
999 }
1000 }
1001
1002 #endif /* _BOOT */
1003