/** @file
Simple Console that sits on a SerialLib.
Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
/*
Symbols used in table below
===========================
ESC = 0x1B
CSI = 0x9B
DEL = 0x7f
^ = CTRL
+=========+======+===========+==========+==========+
| | EFI | UEFI 2.0 | | |
| | Scan | | VT100+ | |
| KEY | Code | PC ANSI | VTUTF8 | VT100 |
+=========+======+===========+==========+==========+
| NULL | 0x00 | | | |
| UP | 0x01 | ESC [ A | ESC [ A | ESC [ A |
| DOWN | 0x02 | ESC [ B | ESC [ B | ESC [ B |
| RIGHT | 0x03 | ESC [ C | ESC [ C | ESC [ C |
| LEFT | 0x04 | ESC [ D | ESC [ D | ESC [ D |
| HOME | 0x05 | ESC [ H | ESC h | ESC [ H |
| END | 0x06 | ESC [ F | ESC k | ESC [ K |
| INSERT | 0x07 | ESC [ @ | ESC + | ESC [ @ |
| | | ESC [ L | | ESC [ L |
| DELETE | 0x08 | ESC [ X | ESC - | ESC [ P |
| PG UP | 0x09 | ESC [ I | ESC ? | ESC [ V |
| | | | | ESC [ ? |
| PG DOWN | 0x0A | ESC [ G | ESC / | ESC [ U |
| | | | | ESC [ / |
| F1 | 0x0B | ESC [ M | ESC 1 | ESC O P |
| F2 | 0x0C | ESC [ N | ESC 2 | ESC O Q |
| F3 | 0x0D | ESC [ O | ESC 3 | ESC O w |
| F4 | 0x0E | ESC [ P | ESC 4 | ESC O x |
| F5 | 0x0F | ESC [ Q | ESC 5 | ESC O t |
| F6 | 0x10 | ESC [ R | ESC 6 | ESC O u |
| F7 | 0x11 | ESC [ S | ESC 7 | ESC O q |
| F8 | 0x12 | ESC [ T | ESC 8 | ESC O r |
| F9 | 0x13 | ESC [ U | ESC 9 | ESC O p |
| F10 | 0x14 | ESC [ V | ESC 0 | ESC O M |
| Escape | 0x17 | ESC | ESC | ESC |
| F11 | 0x15 | | ESC ! | |
| F12 | 0x16 | | ESC @ | |
+=========+======+===========+==========+==========+
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MODE0_COLUMN_COUNT 80
#define MODE0_ROW_COUNT 25
EFI_STATUS
EFIAPI
TextInReset(
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
);
EFI_STATUS
EFIAPI
ReadKeyStroke(
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
OUT EFI_INPUT_KEY *Key
);
EFI_STATUS
EFIAPI
TextOutReset(
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
);
CHAR8 *
EFIAPI
SafeUnicodeStrToAsciiStr (
IN CONST CHAR16 *Source,
OUT CHAR8 *Destination
);
EFI_STATUS
EFIAPI
OutputString (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN CHAR16 *String
);
EFI_STATUS
EFIAPI
TestString (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN CHAR16 *String
);
EFI_STATUS
EFIAPI
QueryMode (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN ModeNumber,
OUT UINTN *Columns,
OUT UINTN *Rows
);
EFI_STATUS
EFIAPI
SetMode(
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN ModeNumber
);
EFI_STATUS
EFIAPI
SetAttribute(
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN Attribute
);
EFI_STATUS
EFIAPI
ClearScreen (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
);
EFI_STATUS
EFIAPI
SetCursorPosition (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN Column,
IN UINTN Row
);
EFI_STATUS
EFIAPI
EnableCursor (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN BOOLEAN Enable
);
EFI_SIMPLE_TEXT_INPUT_PROTOCOL mSimpleTextIn = {
TextInReset,
ReadKeyStroke,
NULL
};
EFI_SIMPLE_TEXT_OUTPUT_MODE mSimpleTextOutMode = {
1,
0,
EFI_TEXT_ATTR( EFI_LIGHTGRAY, EFI_BLACK ),
0,
0,
TRUE
};
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL mSimpleTextOut = {
TextOutReset,
OutputString,
TestString,
QueryMode,
SetMode,
SetAttribute,
ClearScreen,
SetCursorPosition,
EnableCursor,
&mSimpleTextOutMode
};
EFI_HANDLE mInstallHandle = NULL;
typedef struct {
VENDOR_DEVICE_PATH Guid;
UART_DEVICE_PATH Uart;
EFI_DEVICE_PATH_PROTOCOL End;
} SIMPLE_TEXT_OUT_DEVICE_PATH;
SIMPLE_TEXT_OUT_DEVICE_PATH mDevicePath = {
{
{ HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0} },
EFI_CALLER_ID_GUID
},
{
{ MESSAGING_DEVICE_PATH, MSG_UART_DP, { sizeof (UART_DEVICE_PATH), 0} },
0, // Reserved
FixedPcdGet64 (PcdUartDefaultBaudRate), // BaudRate
FixedPcdGet8 (PcdUartDefaultDataBits), // DataBits
FixedPcdGet8 (PcdUartDefaultParity), // Parity (N)
FixedPcdGet8 (PcdUartDefaultStopBits) // StopBits
},
{ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0} }
};
BOOLEAN
TextOutIsValidAscii (
IN CHAR16 Ascii
)
{
//
// valid ASCII code lies in the extent of 0x20 - 0x7F
//
if ((Ascii >= 0x20) && (Ascii <= 0x7F)) {
return TRUE;
}
return FALSE;
}
BOOLEAN
TextOutIsValidEfiCntlChar (
IN CHAR16 Char
)
{
//
// only support four control characters.
//
if (Char == CHAR_NULL ||
Char == CHAR_BACKSPACE ||
Char == CHAR_LINEFEED ||
Char == CHAR_CARRIAGE_RETURN ||
Char == CHAR_TAB ) {
return TRUE;
}
return FALSE;
}
VOID
EFIAPI
WaitForKeyEvent (
IN EFI_EVENT Event,
IN VOID *Context
)
{
if (SerialPortPoll ()) {
gBS->SignalEvent (Event);
}
}
EFI_STATUS
EFIAPI
TextInReset (
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
ReadKeyStroke (
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
OUT EFI_INPUT_KEY *Key
)
{
CHAR8 Char;
if (!SerialPortPoll ()) {
return EFI_NOT_READY;
}
SerialPortRead ((UINT8 *)&Char, 1);
//
// Check for ESC sequence. This code is not technically correct VT100 code.
// An illegal ESC sequence represents an ESC and the characters that follow.
// This code will eat one or two chars after an escape. This is done to
// prevent some complex FIFOing of the data. It is good enough to get
// the arrow and delete keys working
//
Key->UnicodeChar = 0;
Key->ScanCode = SCAN_NULL;
if (Char == 0x1b) {
SerialPortRead ((UINT8 *)&Char, 1);
if (Char == '[') {
SerialPortRead ((UINT8 *)&Char, 1);
switch (Char) {
case 'A':
Key->ScanCode = SCAN_UP;
break;
case 'B':
Key->ScanCode = SCAN_DOWN;
break;
case 'C':
Key->ScanCode = SCAN_RIGHT;
break;
case 'D':
Key->ScanCode = SCAN_LEFT;
break;
case 'H':
Key->ScanCode = SCAN_HOME;
break;
case 'K':
case 'F': // PC ANSI
Key->ScanCode = SCAN_END;
break;
case '@':
case 'L':
Key->ScanCode = SCAN_INSERT;
break;
case 'P':
case 'X': // PC ANSI
Key->ScanCode = SCAN_DELETE;
break;
case 'U':
case '/':
case 'G': // PC ANSI
Key->ScanCode = SCAN_PAGE_DOWN;
break;
case 'V':
case '?':
case 'I': // PC ANSI
Key->ScanCode = SCAN_PAGE_UP;
break;
// PCANSI that does not conflict with VT100
case 'M':
Key->ScanCode = SCAN_F1;
break;
case 'N':
Key->ScanCode = SCAN_F2;
break;
case 'O':
Key->ScanCode = SCAN_F3;
break;
case 'Q':
Key->ScanCode = SCAN_F5;
break;
case 'R':
Key->ScanCode = SCAN_F6;
break;
case 'S':
Key->ScanCode = SCAN_F7;
break;
case 'T':
Key->ScanCode = SCAN_F8;
break;
default:
Key->UnicodeChar = Char;
break;
}
} else if (Char == '0') {
SerialPortRead ((UINT8 *)&Char, 1);
switch (Char) {
case 'P':
Key->ScanCode = SCAN_F1;
break;
case 'Q':
Key->ScanCode = SCAN_F2;
break;
case 'w':
Key->ScanCode = SCAN_F3;
break;
case 'x':
Key->ScanCode = SCAN_F4;
break;
case 't':
Key->ScanCode = SCAN_F5;
break;
case 'u':
Key->ScanCode = SCAN_F6;
break;
case 'q':
Key->ScanCode = SCAN_F7;
break;
case 'r':
Key->ScanCode = SCAN_F8;
break;
case 'p':
Key->ScanCode = SCAN_F9;
break;
case 'm':
Key->ScanCode = SCAN_F10;
break;
default :
break;
}
}
} else if (Char < ' ') {
if ((Char == CHAR_BACKSPACE) ||
(Char == CHAR_TAB) ||
(Char == CHAR_LINEFEED) ||
(Char == CHAR_CARRIAGE_RETURN)) {
// Only let through EFI required control characters
Key->UnicodeChar = (CHAR16)Char;
}
} else if (Char == 0x7f) {
Key->ScanCode = SCAN_DELETE;
} else {
Key->UnicodeChar = (CHAR16)Char;
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
TextOutReset (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
EFI_STATUS Status;
This->SetAttribute(
This,
EFI_TEXT_ATTR(This->Mode->Attribute & 0x0F, EFI_BACKGROUND_BLACK)
);
Status = This->SetMode (This, 0);
return Status;
}
CHAR8 *
EFIAPI
SafeUnicodeStrToAsciiStr (
IN CONST CHAR16 *Source,
OUT CHAR8 *Destination
)
{
CHAR8 *ReturnValue;
ASSERT (Destination != NULL);
//
// ASSERT if Source is long than PcdMaximumUnicodeStringLength.
// Length tests are performed inside StrLen().
//
ASSERT (StrSize (Source) != 0);
//
// Source and Destination should not overlap
//
ASSERT ((UINTN) ((CHAR16 *) Destination - Source) > StrLen (Source));
ASSERT ((UINTN) ((CHAR8 *) Source - Destination) > StrLen (Source));
ReturnValue = Destination;
while (*Source != '\0') {
//
// If any non-ascii characters in Source then replace it with '?'.
//
if (*Source < 0x80) {
*Destination = (CHAR8) *Source;
} else {
*Destination = '?';
//Surrogate pair check.
if ((*Source >= 0xD800) && (*Source <= 0xDFFF)) {
Source++;
}
}
Destination++;
Source++;
}
*Destination = '\0';
//
// ASSERT Original Destination is less long than PcdMaximumAsciiStringLength.
// Length tests are performed inside AsciiStrLen().
//
ASSERT (AsciiStrSize (ReturnValue) != 0);
return ReturnValue;
}
EFI_STATUS
EFIAPI
OutputString (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN CHAR16 *String
)
{
UINTN Size;
CHAR8* OutputString;
EFI_STATUS Status;
EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode;
UINTN MaxColumn;
UINTN MaxRow;
Size = StrLen(String) + 1;
OutputString = AllocatePool(Size);
//If there is any non-ascii characters in String buffer then replace it with '?'
//Eventually, UnicodeStrToAsciiStr API should be fixed.
SafeUnicodeStrToAsciiStr(String, OutputString);
SerialPortWrite ((UINT8 *)OutputString, Size - 1);
//
// Parse each character of the string to output
// to update the cursor position information
//
Mode = This->Mode;
Status = This->QueryMode (
This,
Mode->Mode,
&MaxColumn,
&MaxRow
);
if (EFI_ERROR (Status)) {
return Status;
}
for (; *String != CHAR_NULL; String++) {
switch (*String) {
case CHAR_BACKSPACE:
if (Mode->CursorColumn > 0) {
Mode->CursorColumn--;
}
break;
case CHAR_LINEFEED:
if (Mode->CursorRow < (INT32) (MaxRow - 1)) {
Mode->CursorRow++;
}
break;
case CHAR_CARRIAGE_RETURN:
Mode->CursorColumn = 0;
break;
default:
if (Mode->CursorColumn >= (INT32) (MaxColumn - 1)) {
// Move the cursor as if we print CHAR_CARRIAGE_RETURN & CHAR_LINE_FEED
// CHAR_LINEFEED
if (Mode->CursorRow < (INT32) (MaxRow - 1)) {
Mode->CursorRow++;
}
// CHAR_CARIAGE_RETURN
Mode->CursorColumn = 0;
} else {
Mode->CursorColumn++;
}
break;
}
}
FreePool(OutputString);
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
TestString (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN CHAR16 *String
)
{
CHAR8 Character;
for ( ; *String != CHAR_NULL; String++) {
Character = (CHAR8)*String;
if (!(TextOutIsValidAscii (Character) || TextOutIsValidEfiCntlChar (Character))) {
return EFI_UNSUPPORTED;
}
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
QueryMode (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN ModeNumber,
OUT UINTN *Columns,
OUT UINTN *Rows
)
{
if (This->Mode->MaxMode > 1) {
return EFI_DEVICE_ERROR;
}
if (ModeNumber == 0) {
*Columns = MODE0_COLUMN_COUNT;
*Rows = MODE0_ROW_COUNT;
return EFI_SUCCESS;
}
return EFI_UNSUPPORTED;
}
EFI_STATUS
EFIAPI
SetMode (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN ModeNumber
)
{
if (ModeNumber != 0) {
return EFI_UNSUPPORTED;
}
This->Mode->Mode = 0;
This->ClearScreen (This);
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
SetAttribute(
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN Attribute
)
{
This->Mode->Attribute = (INT32)Attribute;
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
ClearScreen (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
)
{
EFI_STATUS Status;
Status = This->SetCursorPosition (This, 0, 0);
return Status;
}
EFI_STATUS
EFIAPI
SetCursorPosition (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN Column,
IN UINTN Row
)
{
EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode;
EFI_STATUS Status;
UINTN MaxColumn;
UINTN MaxRow;
Mode = This->Mode;
Status = This->QueryMode(
This,
Mode->Mode,
&MaxColumn,
&MaxRow
);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
if ((Column >= MaxColumn) || (Row >= MaxRow)) {
return EFI_UNSUPPORTED;
}
Mode->CursorColumn = (INT32)Column;
Mode->CursorRow = (INT32)Row;
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
EnableCursor (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN BOOLEAN Enable
)
{
if (!Enable) {
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
SimpleTextInOutEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = gBS->CreateEvent (
EVT_NOTIFY_WAIT,
TPL_NOTIFY,
WaitForKeyEvent,
NULL,
&mSimpleTextIn.WaitForKey
);
ASSERT_EFI_ERROR (Status);
Status = gBS->InstallMultipleProtocolInterfaces(
&mInstallHandle,
&gEfiSimpleTextInProtocolGuid, &mSimpleTextIn,
&gEfiSimpleTextOutProtocolGuid, &mSimpleTextOut,
&gEfiDevicePathProtocolGuid, &mDevicePath,
NULL
);
if (!EFI_ERROR (Status)) {
gST->ConOut = &mSimpleTextOut;
gST->ConIn = &mSimpleTextIn;
}
return Status;
}