/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012 Gary Mills
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/archsystm.h>
#include <sys/boot_console.h>
#if defined(__xpv)
#include <sys/hypervisor.h>
#endif /* __xpv */
#include "boot_serial.h"
#include "boot_vga.h"
#if defined(_BOOT)
#include <dboot/dboot_asm.h>
#include <dboot/dboot_xboot.h>
#else /* _BOOT */
#include <sys/bootconf.h>
#if defined(__xpv)
#include <sys/evtchn_impl.h>
#endif /* __xpv */
static char *defcons_buf;
static char *defcons_cur;
#endif /* _BOOT */
#if defined(__xpv)
extern void bcons_init_xen(char *);
extern void bcons_putchar_xen(int);
extern int bcons_getchar_xen(void);
extern int bcons_ischar_xen(void);
#endif /* __xpv */
static int tty_num = 0;
#if defined(__xpv)
static int console_hypervisor_tty_num = 0;
/* Obtain the hypervisor console type */
int
{
return (console_hypervisor_device);
}
#endif /* __xpv */
static int serial_ischar(void);
static int serial_getchar(void);
static void serial_putchar(int);
static void serial_adjust_prop(void);
#if !defined(_BOOT)
/* Set if the console or mode are expressed in the boot line */
#endif
/* Clear the screen and initialize VIDEO, XPOS and YPOS. */
void
clear_screen(void)
{
/*
* XXX should set vga mode so we don't depend on the
* state left by the boot loader. Note that we have to
* enable the cursor before clearing the screen since
* the cursor position is dependant upon the cursor
* skew, which is initialized by vga_cursor_display()
*/
vga_setpos(0, 0);
}
/* Put the character C on the screen. */
static void
screen_putchar(int c)
{
switch (c) {
case '\t':
if (col == VGA_TEXT_COLS)
col = 79;
break;
case '\r':
vga_setpos(row, 0);
break;
case '\b':
if (col > 0)
break;
case '\n':
else
break;
default:
vga_drawc(c, cons_color);
else {
vga_setpos(row, 0);
}
break;
}
}
static int port;
static void
serial_init(void)
{
/*
* 82510 chip is present
*/
} else {
/*
* set the UART in FIFO mode if it has FIFO buffers.
* use 16550 fifo reset sequence specified in NS
* application note. disable fifos until chip is
* initialized.
*/
/*
* no fifo buffers so disable fifos.
* this is true for 8250's
*/
}
}
/* disable interrupts */
#if !defined(_BOOT)
if (IN_XPV_PANIC())
return;
#endif
/* adjust setting based on tty properties */
#if defined(_BOOT)
/*
* Do a full reset to match console behavior.
* 0x1B + c - reset everything
*/
serial_putchar(0x1B);
serial_putchar('c');
#endif
}
/* Advance str pointer past white space */
str++; \
}
/*
* boot_line is set when we call here. Search it for the argument name,
* and if found, return a pointer to it.
*/
static char *
{
char *ptr;
char end_char;
return (NULL);
/*
* We have two nested loops here: the outer loop discards all options
* except -B, and the inner loop parses the -B options looking for
* the one we're interested in.
*/
if (*ptr == '-') {
ptr++;
ptr++;
if (*ptr == '\0')
goto out;
else if (*ptr != 'B')
continue;
} else {
ptr++;
if (*ptr == '\0')
goto out;
continue;
}
do {
ptr++;
ptr++;
} else {
end_char = ',';
}
goto consume_property;
}
/*
* We have a property, and it's not the one we're
* interested in. Skip the property name. A name
* can end with '=', a comma, or white space.
*/
ptr++;
/*
* We only want to go through the rest of the inner
* loop if we have a comma. If we have a property
* name without a value, either continue or break.
*/
if (*ptr == '\0')
goto out;
else if (*ptr == ',')
continue;
break;
ptr++;
/*
* Is the property quoted?
*/
ptr++;
} else {
/*
* Not quoted, so the string ends at a comma
* or at white space. Deal with white space
* later.
*/
end_char = ',';
}
/*
* Now, we can ignore any characters until we find
* end_char.
*/
break;
}
ptr++;
} while (*ptr == ',');
}
out:
return (ret);
}
#define SKIP(p, c) \
while (*(p) != 0 && *p != (c)) \
++(p); \
if (*(p) == (c)) \
++(p);
/*
* find a tty mode property either from cmdline or from boot properties
*/
static char *
{
/*
* when specified on boot line it looks like "name" "="....
*/
return (find_boot_line_prop(name));
}
#if defined(_BOOT)
return (NULL);
#else
/*
* if we're running in the full kernel we check the bootenv.rc settings
*/
{
static char propval[20];
propval[0] = 0;
return (NULL);
return (propval);
}
#endif
}
/*
* adjust serial port based on properties
* These come either from the cmdline or from boot properties.
*/
static void
serial_adjust_prop(void)
{
char *propval;
char *p;
propval = "9600,8,n,1,-";
#if !defined(_BOOT)
else
console_mode_set = 1;
#endif
/* property is of the form: "9600,8,n,1,-" */
p = propval;
if (MATCHES(p, "110,"))
else if (MATCHES(p, "150,"))
else if (MATCHES(p, "300,"))
else if (MATCHES(p, "600,"))
else if (MATCHES(p, "1200,"))
else if (MATCHES(p, "2400,"))
else if (MATCHES(p, "4800,"))
else if (MATCHES(p, "19200,"))
else if (MATCHES(p, "38400,"))
else if (MATCHES(p, "57600,"))
else if (MATCHES(p, "115200,"))
else {
SKIP(p, ',');
}
switch (*p) {
case '5':
++p;
break;
case '6':
++p;
break;
case '7':
++p;
break;
case '8':
++p;
default:
break;
}
SKIP(p, ',');
switch (*p) {
case 'n':
lcr |= PARITY_NONE;
++p;
break;
case 'o':
lcr |= PARITY_ODD;
++p;
break;
case 'e':
++p;
default:
lcr |= PARITY_EVEN;
break;
}
SKIP(p, ',');
switch (*p) {
case '1':
/* STOP1 is 0 */
++p;
break;
default:
break;
}
/* set parity bits */
propval = "false";
mcr = 0;
/* set modem control bits */
}
/* Obtain the console type */
int
{
return (console);
}
/*
* A structure to map console names to values.
*/
typedef struct {
char *name;
int value;
{ "text", CONS_SCREEN_TEXT },
{ "graphics", CONS_SCREEN_GRAPHICS },
#if defined(__xpv)
{ "hypervisor", CONS_HYPERVISOR },
#endif
#if !defined(_BOOT)
{ "usb-serial", CONS_USBSER },
#endif
{ NULL, CONS_INVALID }
};
void
{
char *cons_str;
#if !defined(_BOOT)
extern int post_fastreboot;
#endif
#if defined(__xpv)
#endif /* __xpv */
#if !defined(_BOOT)
#endif
/*
* Go through the console_devices array trying to match the string
* we were given. The string on the command line must end with
* a comma or white space.
*/
int n;
consolep = &console_devices[n];
tty_num = n;
break;
}
}
}
#if defined(__xpv)
/*
* domU's always use the hypervisor regardless of what
* the console variable may be set to.
*/
if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
}
#endif /* __xpv */
/*
* If no console device specified, default to text.
* Remember what was specified for second phase.
*/
if (console == CONS_INVALID)
#if !defined(_BOOT)
else
console_set = 1;
#endif
#if defined(__xpv)
if (DOMAIN_IS_INITDOMAIN(xen_info)) {
case XEN_CONSOLE_COM1:
case XEN_CONSOLE_COM2:
break;
case XEN_CONSOLE_VGA:
/*
* Currently xen doesn't really support
* What this setting means is that
* "vga=keep" has been enabled, which is
* more of a xen debugging tool that a
* true console mode. Hence, we're going
* to ignore this xen "console" setting.
*/
/*FALLTHROUGH*/
default:
}
}
/*
* if the hypervisor is using the currently selected serial
* port then default to using the hypervisor as the console
* device.
*/
if (console == console_hypervisor_device) {
}
#endif /* __xpv */
switch (console) {
case CONS_TTY:
serial_init();
break;
case CONS_HYPERVISOR:
break;
#if !defined(_BOOT)
case CONS_USBSER:
/*
* We can't do anything with the usb serial
* until we have memory management.
*/
break;
#endif
case CONS_SCREEN_GRAPHICS:
kb_init();
break;
case CONS_SCREEN_TEXT:
default:
#if defined(_BOOT)
clear_screen(); /* clears the grub or xen screen */
#endif /* _BOOT */
kb_init();
break;
}
}
#if !defined(_BOOT)
/*
* 2nd part of console initialization.
* In the kernel (ie. fakebop), this can be used only to switch to
* using a serial port instead of screen based on the contents
* of the bootenv.rc file.
*/
/*ARGSUSED*/
void
{
int ttyn;
int i;
extern int post_fastreboot;
if (console_set) {
/*
* If the console was set on the command line,
* but the ttyX-mode was not, we only need to
* check bootenv.rc for that setting.
*/
serial_init();
return;
}
int n;
consolep = &console_devices[n];
ttyn = n;
}
}
if (cons != CONS_INVALID)
break;
}
#if defined(__xpv)
/*
* if the hypervisor is using the currently selected console
* device then default to using the hypervisor as the console
* device.
*/
if (cons == console_hypervisor_device) {
}
#endif /* __xpv */
/*
* we're sticking with whatever the current setting is
*/
return;
}
serial_init();
return;
}
} else {
/*
* USB serial and GRAPHICS console
* we just collect data into a buffer
*/
extern void *defcons_init(size_t);
}
}
#if defined(__xpv)
{
return (console_hypervisor_redirect);
}
void
{
return;
/*
* If we are asked to switch the console to the hypervisor, that
* really means to switch the console to whichever device the
*/
if (new_console == CONS_HYPERVISOR)
if (new_console == CONS_TTY) {
serial_init();
}
}
#endif /* __xpv */
static void
defcons_putchar(int c)
{
if (defcons_buf != NULL &&
*defcons_cur++ = c;
*defcons_cur = 0;
}
}
#endif /* _BOOT */
static void
serial_putchar(int c)
{
;
}
static int
serial_getchar(void)
{
while (serial_ischar() == 0)
;
SERIAL_PARITY | SERIAL_OVERRUN)) {
if (lsr & SERIAL_OVERRUN) {
} else {
/* Toss the garbage */
return (0);
}
}
}
static int
serial_ischar(void)
{
}
static void
_doputchar(int c)
{
switch (console) {
case CONS_TTY:
serial_putchar(c);
return;
case CONS_SCREEN_TEXT:
screen_putchar(c);
return;
case CONS_SCREEN_GRAPHICS:
#if !defined(_BOOT)
case CONS_USBSER:
defcons_putchar(c);
#endif /* _BOOT */
return;
}
}
void
bcons_putchar(int c)
{
static int bhcharpos = 0;
#if defined(__xpv)
if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
console == CONS_HYPERVISOR) {
return;
}
#endif /* __xpv */
if (c == '\t') {
do {
_doputchar(' ');
} while (++bhcharpos % 8);
return;
} else if (c == '\n' || c == '\r') {
bhcharpos = 0;
_doputchar('\r');
_doputchar(c);
return;
} else if (c == '\b') {
if (bhcharpos)
bhcharpos--;
_doputchar(c);
return;
}
bhcharpos++;
_doputchar(c);
}
/*
* kernel character input functions
*/
int
bcons_getchar(void)
{
#if defined(__xpv)
if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
return (bcons_getchar_xen());
#endif /* __xpv */
switch (console) {
case CONS_TTY:
return (serial_getchar());
default:
return (kb_getchar());
}
}
#if !defined(_BOOT)
int
bcons_ischar(void)
{
#if defined(__xpv)
if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
return (bcons_ischar_xen());
#endif /* __xpv */
switch (console) {
case CONS_TTY:
return (serial_ischar());
default:
return (kb_ischar());
}
}
#endif /* _BOOT */