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 "boot_serial.h"
39 #include <dboot_asm.h>
40 #include <dboot_xboot.h>
42 #include <sys/bootconf.h>
44 #include <sys/evtchn_impl.h>
46 static char *defcons_buf
;
47 static char *defcons_cur
;
51 static int cons_color
= CONS_COLOR
;
52 static int console
= CONS_SCREEN_TEXT
;
53 static int tty_num
= 0;
54 static int tty_addr
[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
55 static char *boot_line
;
56 static struct boot_env
{
57 char *be_env
; /* ends with double ascii nul */
58 size_t be_size
; /* size of the environment, including nul */
61 static int serial_ischar(void);
62 static int serial_getchar(void);
63 static void serial_putchar(int);
64 static void serial_adjust_prop(void);
67 /* Set if the console or mode are expressed in the boot line */
68 static int console_set
, console_mode_set
;
72 /* Clear the screen and initialize VIDEO, XPOS and YPOS. */
77 * XXX should set vga mode so we don't depend on the
78 * state left by the boot loader. Note that we have to
79 * enable the cursor before clearing the screen since
80 * the cursor position is dependant upon the cursor
81 * skew, which is initialized by vga_cursor_display()
84 vga_clear(cons_color
);
88 /* Put the character C on the screen. */
94 vga_getpos(&row
, &col
);
98 if (col
== VGA_TEXT_COLS
)
100 vga_setpos(row
, col
);
109 vga_setpos(row
, col
- 1);
113 if (row
< VGA_TEXT_ROWS
- 1)
114 vga_setpos(row
+ 1, col
);
116 vga_scroll(cons_color
);
120 vga_drawc(c
, cons_color
);
121 if (col
< VGA_TEXT_COLS
-1)
122 vga_setpos(row
, col
+ 1);
123 else if (row
< VGA_TEXT_ROWS
- 1)
124 vga_setpos(row
+ 1, 0);
127 vga_scroll(cons_color
);
138 port
= tty_addr
[tty_num
];
140 outb(port
+ ISR
, 0x20);
141 if (inb(port
+ ISR
) & 0x20) {
143 * 82510 chip is present
145 outb(port
+ DAT
+7, 0x04); /* clear status */
146 outb(port
+ ISR
, 0x40); /* set to bank 2 */
147 outb(port
+ MCR
, 0x08); /* IMD */
148 outb(port
+ DAT
, 0x21); /* FMD */
149 outb(port
+ ISR
, 0x00); /* set to bank 0 */
152 * set the UART in FIFO mode if it has FIFO buffers.
153 * use 16550 fifo reset sequence specified in NS
154 * application note. disable fifos until chip is
157 outb(port
+ FIFOR
, 0x00); /* clear */
158 outb(port
+ FIFOR
, FIFO_ON
); /* enable */
159 outb(port
+ FIFOR
, FIFO_ON
|FIFORXFLSH
); /* reset */
161 FIFO_ON
|FIFODMA
|FIFOTXFLSH
|FIFORXFLSH
|0x80);
162 if ((inb(port
+ ISR
) & 0xc0) != 0xc0) {
164 * no fifo buffers so disable fifos.
165 * this is true for 8250's
167 outb(port
+ FIFOR
, 0x00);
171 /* disable interrupts */
179 /* adjust setting based on tty properties */
180 serial_adjust_prop();
184 * Do a full reset to match console behavior.
185 * 0x1B + c - reset everything
187 serial_putchar(0x1B);
192 /* Advance str pointer past white space */
193 #define EAT_WHITE_SPACE(str) { \
194 while ((*str != '\0') && ISSPACE(*str)) \
199 * boot_line is set when we call here. Search it for the argument name,
200 * and if found, return a pointer to it.
203 find_boot_line_prop(const char *name
)
210 if (boot_line
== NULL
)
216 * We have two nested loops here: the outer loop discards all options
217 * except -B, and the inner loop parses the -B options looking for
218 * the one we're interested in.
220 for (ptr
= boot_line
; *ptr
!= '\0'; ptr
++) {
221 EAT_WHITE_SPACE(ptr
);
225 while ((*ptr
!= '\0') && (*ptr
!= 'B') &&
230 else if (*ptr
!= 'B')
233 while ((*ptr
!= '\0') && !ISSPACE(*ptr
))
242 EAT_WHITE_SPACE(ptr
);
244 if ((strncmp(ptr
, name
, len
) == 0) &&
247 if ((*ptr
== '\'') || (*ptr
== '"')) {
255 goto consume_property
;
259 * We have a property, and it's not the one we're
260 * interested in. Skip the property name. A name
261 * can end with '=', a comma, or white space.
263 while ((*ptr
!= '\0') && (*ptr
!= '=') &&
264 (*ptr
!= ',') && (!ISSPACE(*ptr
)))
268 * We only want to go through the rest of the inner
269 * loop if we have a comma. If we have a property
270 * name without a value, either continue or break.
274 else if (*ptr
== ',')
276 else if (ISSPACE(*ptr
))
281 * Is the property quoted?
283 if ((*ptr
== '\'') || (*ptr
== '"')) {
288 * Not quoted, so the string ends at a comma
289 * or at white space. Deal with white space
296 * Now, we can ignore any characters until we find
300 for (; (*ptr
!= '\0') && (*ptr
!= end_char
); ptr
++) {
301 if ((end_char
== ',') && ISSPACE(*ptr
))
304 if (*ptr
&& (*ptr
!= ',') && !ISSPACE(*ptr
))
306 } while (*ptr
== ',');
313 * Find prop from boot env module. The data in module is list of C strings
314 * name=value, the list is terminated by double nul.
317 find_boot_env_prop(const char *name
)
323 if (boot_env
.be_env
== NULL
)
326 ptr
= boot_env
.be_env
;
330 * Make sure we have at least len + 2 bytes in the environment.
331 * We are looking for name=value\0 constructs, and the environment
332 * itself is terminated by '\0'.
334 if (boot_env
.be_size
< len
+ 2)
338 if ((strncmp(ptr
, name
, len
) == 0) && (ptr
[len
] == '=')) {
342 /* find the first '\0' */
343 while (*ptr
!= '\0') {
345 size
= (uintptr_t)ptr
- (uintptr_t)boot_env
.be_env
;
346 if (size
> boot_env
.be_size
)
351 /* If the remainder is shorter than name + 2, get out. */
352 size
= (uintptr_t)ptr
- (uintptr_t)boot_env
.be_env
;
353 if (boot_env
.be_size
- size
< len
+ 2)
355 } while (*ptr
!= '\0');
360 * Get prop value from either command line or boot environment.
361 * We always check kernel command line first, as this will keep the
362 * functionality and will allow user to override the values in environment.
365 find_boot_prop(const char *name
)
367 const char *value
= find_boot_line_prop(name
);
370 value
= find_boot_env_prop(name
);
374 #define MATCHES(p, pat) \
375 (strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
378 while (*(p) != 0 && *p != (c)) \
384 * find a tty mode property either from cmdline or from boot properties
387 get_mode_value(char *name
)
390 * when specified on boot line it looks like "name" "="....
392 if (boot_line
!= NULL
) {
393 return (find_boot_prop(name
));
400 * if we're running in the full kernel we check the bootenv.rc settings
403 static char propval
[20];
406 if (do_bsys_getproplen(NULL
, name
) <= 0)
408 (void) do_bsys_getprop(NULL
, name
, propval
);
415 * adjust serial port based on properties
416 * These come either from the cmdline or from boot properties.
419 serial_adjust_prop(void)
426 uchar_t mcr
= DTR
| RTS
;
428 (void) strcpy(propname
, "ttyX-mode");
429 propname
[3] = 'a' + tty_num
;
430 propval
= get_mode_value(propname
);
432 propval
= "9600,8,n,1,-";
435 console_mode_set
= 1;
438 /* property is of the form: "9600,8,n,1,-" */
440 if (MATCHES(p
, "110,"))
442 else if (MATCHES(p
, "150,"))
444 else if (MATCHES(p
, "300,"))
446 else if (MATCHES(p
, "600,"))
448 else if (MATCHES(p
, "1200,"))
450 else if (MATCHES(p
, "2400,"))
452 else if (MATCHES(p
, "4800,"))
454 else if (MATCHES(p
, "19200,"))
456 else if (MATCHES(p
, "38400,"))
458 else if (MATCHES(p
, "57600,"))
460 else if (MATCHES(p
, "115200,"))
466 outb(port
+ LCR
, DLAB
);
467 outb(port
+ DAT
+ DLL
, baud
& 0xff);
468 outb(port
+ DAT
+ DLH
, (baud
>> 8) & 0xff);
520 /* set parity bits */
521 outb(port
+ LCR
, lcr
);
523 (void) strcpy(propname
, "ttyX-rts-dtr-off");
524 propname
[3] = 'a' + tty_num
;
525 propval
= get_mode_value(propname
);
528 if (propval
[0] != 'f' && propval
[0] != 'F')
530 /* set modem control bits */
531 outb(port
+ MCR
, mcr
| OUT2
);
534 /* Obtain the console type */
536 boot_console_type(int *tnum
)
544 * A structure to map console names to values.
551 console_value_t console_devices
[] = {
552 { "ttya", CONS_TTY
}, /* 0 */
553 { "ttyb", CONS_TTY
}, /* 1 */
554 { "ttyc", CONS_TTY
}, /* 2 */
555 { "ttyd", CONS_TTY
}, /* 3 */
556 { "text", CONS_SCREEN_TEXT
},
557 { "graphics", CONS_SCREEN_GRAPHICS
},
559 { "usb-serial", CONS_USBSER
},
561 { NULL
, CONS_INVALID
}
565 bcons_init_env(struct xboot_info
*xbi
)
568 struct boot_modules
*modules
;
570 modules
= (struct boot_modules
*)(uintptr_t)xbi
->bi_modules
;
571 for (i
= 0; i
< xbi
->bi_module_cnt
; i
++) {
572 if (modules
[i
].bm_type
== BMT_ENV
)
575 if (i
== xbi
->bi_module_cnt
)
578 boot_env
.be_env
= (char *)(uintptr_t)modules
[i
].bm_addr
;
579 boot_env
.be_size
= modules
[i
].bm_size
;
583 bcons_init(struct xboot_info
*xbi
)
585 console_value_t
*consolep
;
586 size_t len
, cons_len
;
587 const char *cons_str
;
589 static char console_text
[] = "text";
590 extern int post_fastreboot
;
593 /* Set up data to fetch properties from commad line and boot env. */
594 boot_line
= (char *)(uintptr_t)xbi
->bi_cmdline
;
596 console
= CONS_INVALID
;
599 cons_str
= find_boot_prop("console");
600 if (cons_str
== NULL
)
601 cons_str
= find_boot_prop("output-device");
604 if (post_fastreboot
&& strcmp(cons_str
, "graphics") == 0)
605 cons_str
= console_text
;
609 * Go through the console_devices array trying to match the string
610 * we were given. The string on the command line must end with
611 * a comma or white space.
613 if (cons_str
!= NULL
) {
616 cons_len
= strlen(cons_str
);
617 for (n
= 0; console_devices
[n
].name
!= NULL
; n
++) {
618 consolep
= &console_devices
[n
];
619 len
= strlen(consolep
->name
);
620 if ((len
<= cons_len
) && ((cons_str
[len
] == '\0') ||
621 (cons_str
[len
] == ',') || (cons_str
[len
] == '\'') ||
622 (cons_str
[len
] == '"') || ISSPACE(cons_str
[len
])) &&
623 (strncmp(cons_str
, consolep
->name
, len
) == 0)) {
624 console
= consolep
->value
;
625 if (console
== CONS_TTY
)
634 * If no console device specified, default to text.
635 * Remember what was specified for second phase.
637 if (console
== CONS_INVALID
)
638 console
= CONS_SCREEN_TEXT
;
650 case CONS_HYPERVISOR
:
656 * We can't do anything with the usb serial
657 * until we have memory management.
661 case CONS_SCREEN_GRAPHICS
:
664 case CONS_SCREEN_TEXT
:
667 clear_screen(); /* clears the grub or xen screen */
676 * 2nd part of console initialization.
677 * In the kernel (ie. fakebop), this can be used only to switch to
678 * using a serial port instead of screen based on the contents
679 * of the bootenv.rc file.
683 bcons_init2(char *inputdev
, char *outputdev
, char *consoledev
)
685 int cons
= CONS_INVALID
;
687 char *devnames
[] = { consoledev
, outputdev
, inputdev
, NULL
};
688 console_value_t
*consolep
;
690 extern int post_fastreboot
;
692 if (post_fastreboot
&& console
== CONS_SCREEN_GRAPHICS
)
693 console
= CONS_SCREEN_TEXT
;
695 if (console
!= CONS_USBSER
&& console
!= CONS_SCREEN_GRAPHICS
) {
698 * If the console was set on the command line,
699 * but the ttyX-mode was not, we only need to
700 * check bootenv.rc for that setting.
702 if ((!console_mode_set
) && (console
== CONS_TTY
))
707 for (i
= 0; devnames
[i
] != NULL
; i
++) {
710 for (n
= 0; console_devices
[n
].name
!= NULL
; n
++) {
711 consolep
= &console_devices
[n
];
712 if (strcmp(devnames
[i
], consolep
->name
) == 0) {
713 cons
= consolep
->value
;
714 if (cons
== CONS_TTY
)
718 if (cons
!= CONS_INVALID
)
724 * if the hypervisor is using the currently selected console
725 * device then default to using the hypervisor as the console
728 if (cons
== console_hypervisor_device
) {
729 cons
= CONS_HYPERVISOR
;
730 console_hypervisor_redirect
= B_TRUE
;
734 if ((cons
== CONS_INVALID
) || (cons
== console
)) {
736 * we're sticking with whatever the current setting is
742 if (cons
== CONS_TTY
) {
749 * USB serial and GRAPHICS console
750 * we just collect data into a buffer
752 extern void *defcons_init(size_t);
753 defcons_buf
= defcons_cur
= defcons_init(MMU_PAGESIZE
);
759 bcons_hypervisor_redirect(void)
761 return (console_hypervisor_redirect
);
765 bcons_device_change(int new_console
)
767 if (new_console
< CONS_MIN
|| new_console
> CONS_MAX
)
771 * If we are asked to switch the console to the hypervisor, that
772 * really means to switch the console to whichever device the
773 * hypervisor is/was using.
775 if (new_console
== CONS_HYPERVISOR
)
776 new_console
= console_hypervisor_device
;
778 console
= new_console
;
780 if (new_console
== CONS_TTY
) {
781 tty_num
= console_hypervisor_tty_num
;
788 defcons_putchar(int c
)
790 if (defcons_buf
!= NULL
&&
791 defcons_cur
+ 1 - defcons_buf
< MMU_PAGESIZE
) {
799 serial_putchar(int c
)
803 while (((inb(port
+ LSR
) & XHRE
) == 0) && checks
--)
805 outb(port
+ DAT
, (char)c
);
813 while (serial_ischar() == 0)
816 lsr
= inb(port
+ LSR
);
817 if (lsr
& (SERIAL_BREAK
| SERIAL_FRAME
|
818 SERIAL_PARITY
| SERIAL_OVERRUN
)) {
819 if (lsr
& SERIAL_OVERRUN
) {
820 return (inb(port
+ DAT
));
822 /* Toss the garbage */
823 (void) inb(port
+ DAT
);
827 return (inb(port
+ DAT
));
833 return (inb(port
+ LSR
) & RCA
);
843 case CONS_SCREEN_TEXT
:
846 case CONS_SCREEN_GRAPHICS
:
858 static int bhcharpos
= 0;
864 } while (++bhcharpos
% 8);
866 } else if (c
== '\n' || c
== '\r') {
871 } else if (c
== '\b') {
883 * kernel character input functions
891 return (serial_getchar());
893 return (kb_getchar());
904 if (!DOMAIN_IS_INITDOMAIN(xen_info
) ||
905 console
== CONS_HYPERVISOR
)
906 return (bcons_ischar_xen());
911 return (serial_ischar());
913 return (kb_ischar());