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.
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.
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]
22 * Copyright (c) 2012 Gary Mills
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
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>
35 #include <sys/hypervisor.h>
38 #include "boot_serial.h"
42 #include <dboot_asm.h>
43 #include <dboot_xboot.h>
45 #include <sys/bootconf.h>
47 #include <sys/evtchn_impl.h>
49 static char *defcons_buf
;
50 static char *defcons_cur
;
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);
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 */
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);
76 /* Set if the console or mode are expressed in the boot line */
77 static int console_set
, console_mode_set
;
81 static int console_hypervisor_redirect
= B_FALSE
;
82 static int console_hypervisor_device
= CONS_INVALID
;
83 static int console_hypervisor_tty_num
= 0;
85 /* Obtain the hypervisor console type */
87 console_hypervisor_dev_type(int *tnum
)
90 *tnum
= console_hypervisor_tty_num
;
91 return (console_hypervisor_device
);
95 /* Clear the screen and initialize VIDEO, XPOS and YPOS. */
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()
106 vga_cursor_display();
107 vga_clear(cons_color
);
111 /* Put the character C on the screen. */
113 screen_putchar(int c
)
117 vga_getpos(&row
, &col
);
120 col
+= 8 - (col
% 8);
121 if (col
== VGA_TEXT_COLS
)
123 vga_setpos(row
, col
);
132 vga_setpos(row
, col
- 1);
136 if (row
< VGA_TEXT_ROWS
- 1)
137 vga_setpos(row
+ 1, col
);
139 vga_scroll(cons_color
);
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);
150 vga_scroll(cons_color
);
161 port
= tty_addr
[tty_num
];
163 outb(port
+ ISR
, 0x20);
164 if (inb(port
+ ISR
) & 0x20) {
166 * 82510 chip is present
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 */
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
180 outb(port
+ FIFOR
, 0x00); /* clear */
181 outb(port
+ FIFOR
, FIFO_ON
); /* enable */
182 outb(port
+ FIFOR
, FIFO_ON
|FIFORXFLSH
); /* reset */
184 FIFO_ON
|FIFODMA
|FIFOTXFLSH
|FIFORXFLSH
|0x80);
185 if ((inb(port
+ ISR
) & 0xc0) != 0xc0) {
187 * no fifo buffers so disable fifos.
188 * this is true for 8250's
190 outb(port
+ FIFOR
, 0x00);
194 /* disable interrupts */
202 /* adjust setting based on tty properties */
203 serial_adjust_prop();
207 * Do a full reset to match console behavior.
208 * 0x1B + c - reset everything
210 serial_putchar(0x1B);
215 /* Advance str pointer past white space */
216 #define EAT_WHITE_SPACE(str) { \
217 while ((*str != '\0') && ISSPACE(*str)) \
222 * boot_line is set when we call here. Search it for the argument name,
223 * and if found, return a pointer to it.
226 find_boot_line_prop(const char *name
)
233 if (boot_line
== NULL
)
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.
243 for (ptr
= boot_line
; *ptr
!= '\0'; ptr
++) {
244 EAT_WHITE_SPACE(ptr
);
248 while ((*ptr
!= '\0') && (*ptr
!= 'B') &&
253 else if (*ptr
!= 'B')
256 while ((*ptr
!= '\0') && !ISSPACE(*ptr
))
265 EAT_WHITE_SPACE(ptr
);
267 if ((strncmp(ptr
, name
, len
) == 0) &&
270 if ((*ptr
== '\'') || (*ptr
== '"')) {
278 goto consume_property
;
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.
286 while ((*ptr
!= '\0') && (*ptr
!= '=') &&
287 (*ptr
!= ',') && (!ISSPACE(*ptr
)))
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.
297 else if (*ptr
== ',')
299 else if (ISSPACE(*ptr
))
304 * Is the property quoted?
306 if ((*ptr
== '\'') || (*ptr
== '"')) {
311 * Not quoted, so the string ends at a comma
312 * or at white space. Deal with white space
319 * Now, we can ignore any characters until we find
323 for (; (*ptr
!= '\0') && (*ptr
!= end_char
); ptr
++) {
324 if ((end_char
== ',') && ISSPACE(*ptr
))
327 if (*ptr
&& (*ptr
!= ',') && !ISSPACE(*ptr
))
329 } while (*ptr
== ',');
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.
340 find_boot_env_prop(const char *name
)
346 if (boot_env
.be_env
== NULL
)
349 ptr
= boot_env
.be_env
;
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'.
357 if (boot_env
.be_size
< len
+ 2)
361 if ((strncmp(ptr
, name
, len
) == 0) && (ptr
[len
] == '=')) {
365 /* find the first '\0' */
366 while (*ptr
!= '\0') {
368 size
= (uintptr_t)ptr
- (uintptr_t)boot_env
.be_env
;
369 if (size
> boot_env
.be_size
)
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)
378 } while (*ptr
!= '\0');
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.
388 find_boot_prop(const char *name
)
390 const char *value
= find_boot_line_prop(name
);
393 value
= find_boot_env_prop(name
);
397 #define MATCHES(p, pat) \
398 (strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
401 while (*(p) != 0 && *p != (c)) \
407 * find a tty mode property either from cmdline or from boot properties
410 get_mode_value(char *name
)
413 * when specified on boot line it looks like "name" "="....
415 if (boot_line
!= NULL
) {
416 return (find_boot_prop(name
));
423 * if we're running in the full kernel we check the bootenv.rc settings
426 static char propval
[20];
429 if (do_bsys_getproplen(NULL
, name
) <= 0)
431 (void) do_bsys_getprop(NULL
, name
, propval
);
438 * adjust serial port based on properties
439 * These come either from the cmdline or from boot properties.
442 serial_adjust_prop(void)
449 uchar_t mcr
= DTR
| RTS
;
451 (void) strcpy(propname
, "ttyX-mode");
452 propname
[3] = 'a' + tty_num
;
453 propval
= get_mode_value(propname
);
455 propval
= "9600,8,n,1,-";
458 console_mode_set
= 1;
461 /* property is of the form: "9600,8,n,1,-" */
463 if (MATCHES(p
, "110,"))
465 else if (MATCHES(p
, "150,"))
467 else if (MATCHES(p
, "300,"))
469 else if (MATCHES(p
, "600,"))
471 else if (MATCHES(p
, "1200,"))
473 else if (MATCHES(p
, "2400,"))
475 else if (MATCHES(p
, "4800,"))
477 else if (MATCHES(p
, "19200,"))
479 else if (MATCHES(p
, "38400,"))
481 else if (MATCHES(p
, "57600,"))
483 else if (MATCHES(p
, "115200,"))
489 outb(port
+ LCR
, DLAB
);
490 outb(port
+ DAT
+ DLL
, baud
& 0xff);
491 outb(port
+ DAT
+ DLH
, (baud
>> 8) & 0xff);
543 /* set parity bits */
544 outb(port
+ LCR
, lcr
);
546 (void) strcpy(propname
, "ttyX-rts-dtr-off");
547 propname
[3] = 'a' + tty_num
;
548 propval
= get_mode_value(propname
);
551 if (propval
[0] != 'f' && propval
[0] != 'F')
553 /* set modem control bits */
554 outb(port
+ MCR
, mcr
| OUT2
);
557 /* Obtain the console type */
559 boot_console_type(int *tnum
)
567 * A structure to map console names to values.
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
},
582 { "hypervisor", CONS_HYPERVISOR
},
585 { "usb-serial", CONS_USBSER
},
587 { NULL
, CONS_INVALID
}
591 bcons_init_env(struct xboot_info
*xbi
)
594 struct boot_modules
*modules
;
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
)
601 if (i
== xbi
->bi_module_cnt
)
604 boot_env
.be_env
= (char *)(uintptr_t)modules
[i
].bm_addr
;
605 boot_env
.be_size
= modules
[i
].bm_size
;
609 bcons_init(struct xboot_info
*xbi
)
611 console_value_t
*consolep
;
612 size_t len
, cons_len
;
613 const char *cons_str
;
615 static char console_text
[] = "text";
616 extern int post_fastreboot
;
619 /* Set up data to fetch properties from commad line and boot env. */
620 boot_line
= (char *)(uintptr_t)xbi
->bi_cmdline
;
622 console
= CONS_INVALID
;
625 bcons_init_xen(boot_line
);
628 cons_str
= find_boot_prop("console");
629 if (cons_str
== NULL
)
630 cons_str
= find_boot_prop("output-device");
633 if (post_fastreboot
&& strcmp(cons_str
, "graphics") == 0)
634 cons_str
= console_text
;
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.
642 if (cons_str
!= NULL
) {
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
)
663 * domU's always use the hypervisor regardless of what
664 * the console variable may be set to.
666 if (!DOMAIN_IS_INITDOMAIN(xen_info
)) {
667 console
= CONS_HYPERVISOR
;
668 console_hypervisor_redirect
= B_TRUE
;
673 * If no console device specified, default to text.
674 * Remember what was specified for second phase.
676 if (console
== CONS_INVALID
)
677 console
= CONS_SCREEN_TEXT
;
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
;
691 case XEN_CONSOLE_VGA
:
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.
703 console_hypervisor_device
= CONS_INVALID
;
708 * if the hypervisor is using the currently selected serial
709 * port then default to using the hypervisor as the console
712 if (console
== console_hypervisor_device
) {
713 console
= CONS_HYPERVISOR
;
714 console_hypervisor_redirect
= B_TRUE
;
723 case CONS_HYPERVISOR
:
729 * We can't do anything with the usb serial
730 * until we have memory management.
734 case CONS_SCREEN_GRAPHICS
:
737 case CONS_SCREEN_TEXT
:
740 clear_screen(); /* clears the grub or xen screen */
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.
756 bcons_init2(char *inputdev
, char *outputdev
, char *consoledev
)
758 int cons
= CONS_INVALID
;
760 char *devnames
[] = { consoledev
, outputdev
, inputdev
, NULL
};
761 console_value_t
*consolep
;
763 extern int post_fastreboot
;
765 if (post_fastreboot
&& console
== CONS_SCREEN_GRAPHICS
)
766 console
= CONS_SCREEN_TEXT
;
768 if (console
!= CONS_USBSER
&& console
!= CONS_SCREEN_GRAPHICS
) {
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.
775 if ((!console_mode_set
) && (console
== CONS_TTY
))
780 for (i
= 0; devnames
[i
] != NULL
; i
++) {
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
)
791 if (cons
!= CONS_INVALID
)
797 * if the hypervisor is using the currently selected console
798 * device then default to using the hypervisor as the console
801 if (cons
== console_hypervisor_device
) {
802 cons
= CONS_HYPERVISOR
;
803 console_hypervisor_redirect
= B_TRUE
;
807 if ((cons
== CONS_INVALID
) || (cons
== console
)) {
809 * we're sticking with whatever the current setting is
815 if (cons
== CONS_TTY
) {
822 * USB serial and GRAPHICS console
823 * we just collect data into a buffer
825 extern void *defcons_init(size_t);
826 defcons_buf
= defcons_cur
= defcons_init(MMU_PAGESIZE
);
832 bcons_hypervisor_redirect(void)
834 return (console_hypervisor_redirect
);
838 bcons_device_change(int new_console
)
840 if (new_console
< CONS_MIN
|| new_console
> CONS_MAX
)
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.
848 if (new_console
== CONS_HYPERVISOR
)
849 new_console
= console_hypervisor_device
;
851 console
= new_console
;
853 if (new_console
== CONS_TTY
) {
854 tty_num
= console_hypervisor_tty_num
;
861 defcons_putchar(int c
)
863 if (defcons_buf
!= NULL
&&
864 defcons_cur
+ 1 - defcons_buf
< MMU_PAGESIZE
) {
872 serial_putchar(int c
)
876 while (((inb(port
+ LSR
) & XHRE
) == 0) && checks
--)
878 outb(port
+ DAT
, (char)c
);
886 while (serial_ischar() == 0)
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
));
895 /* Toss the garbage */
896 (void) inb(port
+ DAT
);
900 return (inb(port
+ DAT
));
906 return (inb(port
+ LSR
) & RCA
);
916 case CONS_SCREEN_TEXT
:
919 case CONS_SCREEN_GRAPHICS
:
931 static int bhcharpos
= 0;
934 if (!DOMAIN_IS_INITDOMAIN(xen_info
) ||
935 console
== CONS_HYPERVISOR
) {
936 bcons_putchar_xen(c
);
944 } while (++bhcharpos
% 8);
946 } else if (c
== '\n' || c
== '\r') {
951 } else if (c
== '\b') {
963 * kernel character input functions
969 if (!DOMAIN_IS_INITDOMAIN(xen_info
) ||
970 console
== CONS_HYPERVISOR
)
971 return (bcons_getchar_xen());
976 return (serial_getchar());
978 return (kb_getchar());
989 if (!DOMAIN_IS_INITDOMAIN(xen_info
) ||
990 console
== CONS_HYPERVISOR
)
991 return (bcons_ischar_xen());
996 return (serial_ischar());
998 return (kb_ischar());