loader: fall back to term_emu on efi console with serial backend
In case of efi console having serial backend (video + serial or only serial), we need to stick with old emulator till we can draw console. Eventually we would need to get console terminal emulator to be removed from serial console because the serial link already has the terminal. However, we need to implement comconsole on all efi platforms first, then we need the ability to draw console, so we do not have to use SimpleTextOutput protocol (which will write both on video and serial in case of multiplexed ComOut). Differential Revision: https://reviews.freebsd.org/D22161
This commit is contained in:
parent
a4f7035c09
commit
615ed873f7
@ -106,6 +106,7 @@ EFI_STATUS errno_to_efi_status(int errno);
|
||||
void efi_time_init(void);
|
||||
void efi_time_fini(void);
|
||||
|
||||
int parse_uefi_con_out(void);
|
||||
bool efi_cons_update_mode(void);
|
||||
EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab);
|
||||
|
||||
|
@ -61,4 +61,9 @@ CFLAGS+= -I${LDRSRC}
|
||||
# Handle FreeBSD specific %b and %D printf format specifiers
|
||||
CFLAGS+= ${FORMAT_EXTENSIONS}
|
||||
|
||||
# Do not use TERM_EMU on arm and arm64 as it doesn't behave well with serial console
|
||||
.if ${MACHINE_CPUARCH} != "arm" && ${MACHINE_CPUARCH} != "aarch64"
|
||||
CFLAGS+= -DTERM_EMU
|
||||
.endif
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
#include <teken.h>
|
||||
#include <sys/reboot.h>
|
||||
|
||||
#include "bootstrap.h"
|
||||
|
||||
@ -38,6 +39,26 @@ static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
|
||||
static SIMPLE_INPUT_INTERFACE *conin;
|
||||
static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
|
||||
|
||||
static int mode; /* Does ConOut have serial console? */
|
||||
|
||||
static uint32_t utf8_left;
|
||||
static uint32_t utf8_partial;
|
||||
#ifdef TERM_EMU
|
||||
#define DEFAULT_FGCOLOR EFI_LIGHTGRAY
|
||||
#define DEFAULT_BGCOLOR EFI_BLACK
|
||||
|
||||
#define MAXARGS 8
|
||||
static int args[MAXARGS], argc;
|
||||
static int fg_c, bg_c, curx, cury;
|
||||
static int esc;
|
||||
|
||||
void get_pos(int *x, int *y);
|
||||
void curs_move(int *_x, int *_y, int x, int y);
|
||||
static void CL(int);
|
||||
void HO(void);
|
||||
void end_term(void);
|
||||
#endif
|
||||
|
||||
static tf_bell_t efi_cons_bell;
|
||||
static tf_cursor_t efi_text_cursor;
|
||||
static tf_putchar_t efi_text_putchar;
|
||||
@ -447,6 +468,340 @@ efi_set_colors(struct env_var *ev, int flags, const void *value)
|
||||
return (CMD_OK);
|
||||
}
|
||||
|
||||
#ifdef TERM_EMU
|
||||
/* Get cursor position. */
|
||||
void
|
||||
get_pos(int *x, int *y)
|
||||
{
|
||||
*x = conout->Mode->CursorColumn;
|
||||
*y = conout->Mode->CursorRow;
|
||||
}
|
||||
|
||||
/* Move cursor to x rows and y cols (0-based). */
|
||||
void
|
||||
curs_move(int *_x, int *_y, int x, int y)
|
||||
{
|
||||
conout->SetCursorPosition(conout, x, y);
|
||||
if (_x != NULL)
|
||||
*_x = conout->Mode->CursorColumn;
|
||||
if (_y != NULL)
|
||||
*_y = conout->Mode->CursorRow;
|
||||
}
|
||||
|
||||
/* Clear internal state of the terminal emulation code. */
|
||||
void
|
||||
end_term(void)
|
||||
{
|
||||
esc = 0;
|
||||
argc = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
efi_cons_rawputchar(int c)
|
||||
{
|
||||
int i;
|
||||
UINTN x, y;
|
||||
conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
|
||||
|
||||
if (c == '\t') {
|
||||
int n;
|
||||
|
||||
n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
|
||||
for (i = 0; i < n; i++)
|
||||
efi_cons_rawputchar(' ');
|
||||
} else {
|
||||
#ifndef TERM_EMU
|
||||
if (c == '\n')
|
||||
efi_cons_efiputchar('\r');
|
||||
efi_cons_efiputchar(c);
|
||||
#else
|
||||
switch (c) {
|
||||
case '\r':
|
||||
curx = 0;
|
||||
efi_cons_efiputchar('\r');
|
||||
return;
|
||||
case '\n':
|
||||
efi_cons_efiputchar('\n');
|
||||
efi_cons_efiputchar('\r');
|
||||
cury++;
|
||||
if (cury >= y)
|
||||
cury--;
|
||||
curx = 0;
|
||||
return;
|
||||
case '\b':
|
||||
if (curx > 0) {
|
||||
efi_cons_efiputchar('\b');
|
||||
curx--;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
efi_cons_efiputchar(c);
|
||||
curx++;
|
||||
if (curx > x-1) {
|
||||
curx = 0;
|
||||
cury++;
|
||||
}
|
||||
if (cury > y-1) {
|
||||
curx = 0;
|
||||
cury--;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
conout->EnableCursor(conout, TRUE);
|
||||
}
|
||||
|
||||
#ifdef TERM_EMU
|
||||
/* Gracefully exit ESC-sequence processing in case of misunderstanding. */
|
||||
static void
|
||||
bail_out(int c)
|
||||
{
|
||||
char buf[16], *ch;
|
||||
int i;
|
||||
|
||||
if (esc) {
|
||||
efi_cons_rawputchar('\033');
|
||||
if (esc != '\033')
|
||||
efi_cons_rawputchar(esc);
|
||||
for (i = 0; i <= argc; ++i) {
|
||||
sprintf(buf, "%d", args[i]);
|
||||
ch = buf;
|
||||
while (*ch)
|
||||
efi_cons_rawputchar(*ch++);
|
||||
}
|
||||
}
|
||||
efi_cons_rawputchar(c);
|
||||
end_term();
|
||||
}
|
||||
|
||||
/* Clear display from current position to end of screen. */
|
||||
static void
|
||||
CD(void)
|
||||
{
|
||||
int i;
|
||||
UINTN x, y;
|
||||
|
||||
get_pos(&curx, &cury);
|
||||
if (curx == 0 && cury == 0) {
|
||||
conout->ClearScreen(conout);
|
||||
end_term();
|
||||
return;
|
||||
}
|
||||
|
||||
conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
|
||||
CL(0); /* clear current line from cursor to end */
|
||||
for (i = cury + 1; i < y-1; i++) {
|
||||
curs_move(NULL, NULL, 0, i);
|
||||
CL(0);
|
||||
}
|
||||
curs_move(NULL, NULL, curx, cury);
|
||||
end_term();
|
||||
}
|
||||
|
||||
/*
|
||||
* Absolute cursor move to args[0] rows and args[1] columns
|
||||
* (the coordinates are 1-based).
|
||||
*/
|
||||
static void
|
||||
CM(void)
|
||||
{
|
||||
if (args[0] > 0)
|
||||
args[0]--;
|
||||
if (args[1] > 0)
|
||||
args[1]--;
|
||||
curs_move(&curx, &cury, args[1], args[0]);
|
||||
end_term();
|
||||
}
|
||||
|
||||
/* Home cursor (left top corner), also called from mode command. */
|
||||
void
|
||||
HO(void)
|
||||
{
|
||||
argc = 1;
|
||||
args[0] = args[1] = 1;
|
||||
CM();
|
||||
}
|
||||
|
||||
/* Clear line from current position to end of line */
|
||||
static void
|
||||
CL(int direction)
|
||||
{
|
||||
int i, len;
|
||||
UINTN x, y;
|
||||
CHAR16 *line;
|
||||
|
||||
conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
|
||||
switch (direction) {
|
||||
case 0: /* from cursor to end */
|
||||
len = x - curx + 1;
|
||||
break;
|
||||
case 1: /* from beginning to cursor */
|
||||
len = curx;
|
||||
break;
|
||||
case 2: /* entire line */
|
||||
len = x;
|
||||
break;
|
||||
default: /* NOTREACHED */
|
||||
__unreachable();
|
||||
}
|
||||
|
||||
if (cury == y - 1)
|
||||
len--;
|
||||
|
||||
line = malloc(len * sizeof (CHAR16));
|
||||
if (line == NULL) {
|
||||
printf("out of memory\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < len; i++)
|
||||
line[i] = ' ';
|
||||
line[len-1] = 0;
|
||||
|
||||
if (direction != 0)
|
||||
curs_move(NULL, NULL, 0, cury);
|
||||
|
||||
conout->OutputString(conout, line);
|
||||
/* restore cursor position */
|
||||
curs_move(NULL, NULL, curx, cury);
|
||||
free(line);
|
||||
end_term();
|
||||
}
|
||||
|
||||
static void
|
||||
get_arg(int c)
|
||||
{
|
||||
if (argc < 0)
|
||||
argc = 0;
|
||||
args[argc] *= 10;
|
||||
args[argc] += c - '0';
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Emulate basic capabilities of cons25 terminal */
|
||||
static void
|
||||
efi_term_emu(int c)
|
||||
{
|
||||
#ifdef TERM_EMU
|
||||
static int ansi_col[] = {
|
||||
0, 4, 2, 6, 1, 5, 3, 7
|
||||
};
|
||||
int t, i;
|
||||
EFI_STATUS status;
|
||||
|
||||
switch (esc) {
|
||||
case 0:
|
||||
switch (c) {
|
||||
case '\033':
|
||||
esc = c;
|
||||
break;
|
||||
default:
|
||||
efi_cons_rawputchar(c);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '\033':
|
||||
switch (c) {
|
||||
case '[':
|
||||
esc = c;
|
||||
args[0] = 0;
|
||||
argc = -1;
|
||||
break;
|
||||
default:
|
||||
bail_out(c);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
switch (c) {
|
||||
case ';':
|
||||
if (argc < 0)
|
||||
argc = 0;
|
||||
else if (argc + 1 >= MAXARGS)
|
||||
bail_out(c);
|
||||
else
|
||||
args[++argc] = 0;
|
||||
break;
|
||||
case 'H': /* ho = \E[H */
|
||||
if (argc < 0)
|
||||
HO();
|
||||
else if (argc == 1)
|
||||
CM();
|
||||
else
|
||||
bail_out(c);
|
||||
break;
|
||||
case 'J': /* cd = \E[J */
|
||||
if (argc < 0)
|
||||
CD();
|
||||
else
|
||||
bail_out(c);
|
||||
break;
|
||||
case 'm':
|
||||
if (argc < 0) {
|
||||
fg_c = DEFAULT_FGCOLOR;
|
||||
bg_c = DEFAULT_BGCOLOR;
|
||||
}
|
||||
for (i = 0; i <= argc; ++i) {
|
||||
switch (args[i]) {
|
||||
case 0: /* back to normal */
|
||||
fg_c = DEFAULT_FGCOLOR;
|
||||
bg_c = DEFAULT_BGCOLOR;
|
||||
break;
|
||||
case 1: /* bold */
|
||||
fg_c |= 0x8;
|
||||
break;
|
||||
case 4: /* underline */
|
||||
case 5: /* blink */
|
||||
bg_c |= 0x8;
|
||||
break;
|
||||
case 7: /* reverse */
|
||||
t = fg_c;
|
||||
fg_c = bg_c;
|
||||
bg_c = t;
|
||||
break;
|
||||
case 22: /* normal intensity */
|
||||
fg_c &= ~0x8;
|
||||
break;
|
||||
case 24: /* not underline */
|
||||
case 25: /* not blinking */
|
||||
bg_c &= ~0x8;
|
||||
break;
|
||||
case 30: case 31: case 32: case 33:
|
||||
case 34: case 35: case 36: case 37:
|
||||
fg_c = ansi_col[args[i] - 30];
|
||||
break;
|
||||
case 39: /* normal */
|
||||
fg_c = DEFAULT_FGCOLOR;
|
||||
break;
|
||||
case 40: case 41: case 42: case 43:
|
||||
case 44: case 45: case 46: case 47:
|
||||
bg_c = ansi_col[args[i] - 40];
|
||||
break;
|
||||
case 49: /* normal */
|
||||
bg_c = DEFAULT_BGCOLOR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
|
||||
end_term();
|
||||
break;
|
||||
default:
|
||||
if (isdigit(c))
|
||||
get_arg(c);
|
||||
else
|
||||
bail_out(c);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bail_out(c);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
efi_cons_rawputchar(c);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
efi_cons_update_mode(void)
|
||||
{
|
||||
@ -461,36 +816,59 @@ efi_cons_update_mode(void)
|
||||
rows = 24;
|
||||
}
|
||||
|
||||
if (buffer != NULL) {
|
||||
if (tp.tp_row == rows && tp.tp_col == cols)
|
||||
return (true);
|
||||
free(buffer);
|
||||
} else {
|
||||
teken_init(&teken, &tf, NULL);
|
||||
}
|
||||
|
||||
tp.tp_row = rows;
|
||||
tp.tp_col = cols;
|
||||
buffer = malloc(rows * cols * sizeof(*buffer));
|
||||
if (buffer == NULL)
|
||||
return (false);
|
||||
|
||||
teken_set_winsize(&teken, &tp);
|
||||
a = teken_get_defattr(&teken);
|
||||
|
||||
snprintf(env, sizeof(env), "%d", a->ta_fgcolor);
|
||||
env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors,
|
||||
env_nounset);
|
||||
snprintf(env, sizeof(env), "%d", a->ta_bgcolor);
|
||||
env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors,
|
||||
env_nounset);
|
||||
|
||||
for (int row = 0; row < rows; row++)
|
||||
for (int col = 0; col < cols; col++) {
|
||||
buffer[col + row * tp.tp_col].c = ' ';
|
||||
buffer[col + row * tp.tp_col].a = *a;
|
||||
/*
|
||||
* When we have serial port listed in ConOut, use pre-teken emulator,
|
||||
* if built with.
|
||||
* The problem is, we can not output text on efi and comconsole when
|
||||
* efi also has comconsole bound. But then again, we need to have
|
||||
* terminal emulator for efi text mode to support the menu.
|
||||
* While teken is too expensive to be used on serial console, the
|
||||
* pre-teken emulator is light enough to be used on serial console.
|
||||
*/
|
||||
mode = parse_uefi_con_out();
|
||||
if ((mode & RB_SERIAL) == 0) {
|
||||
if (buffer != NULL) {
|
||||
if (tp.tp_row == rows && tp.tp_col == cols)
|
||||
return (true);
|
||||
free(buffer);
|
||||
} else {
|
||||
teken_init(&teken, &tf, NULL);
|
||||
}
|
||||
|
||||
tp.tp_row = rows;
|
||||
tp.tp_col = cols;
|
||||
buffer = malloc(rows * cols * sizeof(*buffer));
|
||||
if (buffer == NULL)
|
||||
return (false);
|
||||
|
||||
teken_set_winsize(&teken, &tp);
|
||||
a = teken_get_defattr(&teken);
|
||||
|
||||
snprintf(env, sizeof(env), "%d", a->ta_fgcolor);
|
||||
env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors,
|
||||
env_nounset);
|
||||
snprintf(env, sizeof(env), "%d", a->ta_bgcolor);
|
||||
env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors,
|
||||
env_nounset);
|
||||
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols; col++) {
|
||||
buffer[col + row * tp.tp_col].c = ' ';
|
||||
buffer[col + row * tp.tp_col].a = *a;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#ifdef TERM_EMU
|
||||
conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
|
||||
DEFAULT_BGCOLOR));
|
||||
end_term();
|
||||
get_pos(&curx, &cury);
|
||||
curs_move(&curx, &cury, curx, cury);
|
||||
fg_c = DEFAULT_FGCOLOR;
|
||||
bg_c = DEFAULT_BGCOLOR;
|
||||
#endif
|
||||
}
|
||||
|
||||
snprintf(env, sizeof (env), "%u", (unsigned)rows);
|
||||
setenv("LINES", env, 1);
|
||||
snprintf(env, sizeof (env), "%u", (unsigned)cols);
|
||||
@ -522,11 +900,109 @@ efi_cons_init(int arg)
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
input_partial(void)
|
||||
{
|
||||
unsigned i;
|
||||
uint32_t c;
|
||||
|
||||
if (utf8_left == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < sizeof(utf8_partial); i++) {
|
||||
c = (utf8_partial >> (24 - (i << 3))) & 0xff;
|
||||
if (c != 0)
|
||||
efi_term_emu(c);
|
||||
}
|
||||
utf8_left = 0;
|
||||
utf8_partial = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
input_byte(uint8_t c)
|
||||
{
|
||||
if ((c & 0x80) == 0x00) {
|
||||
/* One-byte sequence. */
|
||||
input_partial();
|
||||
efi_term_emu(c);
|
||||
return;
|
||||
}
|
||||
if ((c & 0xe0) == 0xc0) {
|
||||
/* Two-byte sequence. */
|
||||
input_partial();
|
||||
utf8_left = 1;
|
||||
utf8_partial = c;
|
||||
return;
|
||||
}
|
||||
if ((c & 0xf0) == 0xe0) {
|
||||
/* Three-byte sequence. */
|
||||
input_partial();
|
||||
utf8_left = 2;
|
||||
utf8_partial = c;
|
||||
return;
|
||||
}
|
||||
if ((c & 0xf8) == 0xf0) {
|
||||
/* Four-byte sequence. */
|
||||
input_partial();
|
||||
utf8_left = 3;
|
||||
utf8_partial = c;
|
||||
return;
|
||||
}
|
||||
if ((c & 0xc0) == 0x80) {
|
||||
/* Invalid state? */
|
||||
if (utf8_left == 0) {
|
||||
efi_term_emu(c);
|
||||
return;
|
||||
}
|
||||
utf8_left--;
|
||||
utf8_partial = (utf8_partial << 8) | c;
|
||||
if (utf8_left == 0) {
|
||||
uint32_t v, u;
|
||||
uint8_t b;
|
||||
|
||||
v = 0;
|
||||
u = utf8_partial;
|
||||
b = (u >> 24) & 0xff;
|
||||
if (b != 0) { /* Four-byte sequence */
|
||||
v = b & 0x07;
|
||||
b = (u >> 16) & 0xff;
|
||||
v = (v << 6) | (b & 0x3f);
|
||||
b = (u >> 8) & 0xff;
|
||||
v = (v << 6) | (b & 0x3f);
|
||||
b = u & 0xff;
|
||||
v = (v << 6) | (b & 0x3f);
|
||||
} else if ((b = (u >> 16) & 0xff) != 0) {
|
||||
v = b & 0x0f; /* Three-byte sequence */
|
||||
b = (u >> 8) & 0xff;
|
||||
v = (v << 6) | (b & 0x3f);
|
||||
b = u & 0xff;
|
||||
v = (v << 6) | (b & 0x3f);
|
||||
} else if ((b = (u >> 8) & 0xff) != 0) {
|
||||
v = b & 0x1f; /* Two-byte sequence */
|
||||
b = u & 0xff;
|
||||
v = (v << 6) | (b & 0x3f);
|
||||
}
|
||||
/* Send unicode char directly to console. */
|
||||
efi_cons_efiputchar(v);
|
||||
utf8_partial = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* Anything left is illegal in UTF-8 sequence. */
|
||||
input_partial();
|
||||
efi_term_emu(c);
|
||||
}
|
||||
|
||||
void
|
||||
efi_cons_putchar(int c)
|
||||
{
|
||||
unsigned char ch = c;
|
||||
|
||||
if ((mode & RB_SERIAL) != 0) {
|
||||
input_byte(ch);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer != NULL)
|
||||
teken_input(&teken, &ch, sizeof (ch));
|
||||
else
|
||||
|
@ -688,7 +688,7 @@ setenv_int(const char *key, int val)
|
||||
* ACPI name space to map the UID for the serial port to a port. The
|
||||
* latter is especially hard.
|
||||
*/
|
||||
static int
|
||||
int
|
||||
parse_uefi_con_out(void)
|
||||
{
|
||||
int how, rv;
|
||||
|
Loading…
Reference in New Issue
Block a user