reactos/boot/environ/lib/io/display/efi/textcons.c

625 lines
17 KiB
C

/*
* COPYRIGHT: See COPYING.ARM in the top level directory
* PROJECT: ReactOS UEFI Boot Library
* FILE: boot/environ/lib/io/display/efi/textcons.c
* PURPOSE: Boot Library EFI Text Console Routines
* PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include "bl.h"
/* DATA VARIABLES ************************************************************/
/* FUNCTIONS *****************************************************************/
BL_COLOR
ConsoleEfiTextGetColorForeground (
_In_ UINT32 Attributes
)
{
/* Read the foreground color attribute and convert to CGA color index */
switch (Attributes & 0x0F)
{
case EFI_BLACK:
return Black;
case EFI_BLUE:
return Blue;
case EFI_GREEN:
return Green;
case EFI_RED:
return Red;
case EFI_CYAN:
return Cyan;
case EFI_MAGENTA:
return Magenta;
case EFI_BROWN:
return Brown;
case EFI_LIGHTGRAY:
return LtGray;
case EFI_DARKGRAY:
return Gray;
case EFI_LIGHTBLUE:
return LtBlue;
case EFI_LIGHTGREEN:
return LtGreen;
case EFI_LIGHTCYAN:
return LtCyan;
case EFI_LIGHTRED:
return LtRed;
case EFI_LIGHTMAGENTA:
return LtMagenta;
case EFI_YELLOW:
return Yellow;
case EFI_WHITE:
default:
return White;
}
}
BL_COLOR
ConsoleEfiTextGetColorBackground (
_In_ UINT32 Attributes
)
{
/* Read the background color attribute and convert to CGA color index */
switch (Attributes & 0xF0)
{
case EFI_BACKGROUND_MAGENTA:
return Magenta;
case EFI_BACKGROUND_BROWN:
return Brown;
case EFI_BACKGROUND_LIGHTGRAY:
return White;
case EFI_BACKGROUND_BLACK:
default:
return Black;
case EFI_BACKGROUND_RED:
return Red;
case EFI_BACKGROUND_GREEN:
return Green;
case EFI_BACKGROUND_CYAN:
return Cyan;
case EFI_BACKGROUND_BLUE:
return Blue;
}
}
ULONG
ConsoleEfiTextGetEfiColorBackground (
_In_ BL_COLOR Color
)
{
/* Convert the CGA color index into an EFI background attribute */
switch (Color)
{
case Blue:
case LtBlue:
return EFI_BACKGROUND_BLUE;
case Green:
case LtGreen:
return EFI_BACKGROUND_GREEN;
case Cyan:
case LtCyan:
return EFI_BACKGROUND_CYAN;
case Red:
case LtRed:
return EFI_BACKGROUND_RED;
case Magenta:
case LtMagenta:
return EFI_BACKGROUND_MAGENTA;
case Brown:
case Yellow:
return EFI_BACKGROUND_BROWN;
case LtGray:
case White:
return EFI_BACKGROUND_LIGHTGRAY;
case Black:
case Gray:
default:
return EFI_BACKGROUND_BLACK;
}
}
ULONG
ConsoleEfiTextGetEfiColorForeground (
_In_ BL_COLOR Color
)
{
/* Convert the CGA color index into an EFI foreground attribute */
switch (Color)
{
case Black:
return EFI_BLACK;
case Blue:
return EFI_BLUE;
case Green:
return EFI_GREEN;
case Cyan:
return EFI_CYAN;
case Red:
return EFI_RED;
case Magenta:
return EFI_MAGENTA;
case Brown:
return EFI_BROWN;
case LtGray:
return EFI_LIGHTGRAY;
case Gray:
return EFI_DARKGRAY;
case LtBlue:
return EFI_LIGHTBLUE;
case LtGreen:
return EFI_LIGHTGREEN;
case LtCyan:
return EFI_LIGHTCYAN;
case LtRed:
return EFI_LIGHTRED;
case LtMagenta:
return EFI_LIGHTMAGENTA;
case Yellow:
return EFI_YELLOW;
case White:
default:
return EFI_WHITE;
}
}
ULONG
ConsoleEfiTextGetAttribute (
BL_COLOR BgColor,
BL_COLOR FgColor
)
{
/* Convert each part and OR into a single attribute */
return ConsoleEfiTextGetEfiColorBackground(BgColor) |
ConsoleEfiTextGetEfiColorForeground(FgColor);
}
VOID
ConsoleEfiTextGetStateFromMode (
_In_ EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode,
_Out_ PBL_DISPLAY_STATE State
)
{
ULONG TextWidth, TextHeight;
/* Get all the EFI data and convert it into our own structure */
BlDisplayGetTextCellResolution(&TextWidth, &TextHeight);
State->FgColor = ConsoleEfiTextGetColorForeground(Mode->Attribute);
State->BgColor = ConsoleEfiTextGetColorBackground(Mode->Attribute);
State->XPos = Mode->CursorColumn * TextWidth;
State->YPos = Mode->CursorRow * TextHeight;
State->CursorVisible = Mode->CursorVisible != FALSE;
}
NTSTATUS
ConsoleFirmwareTextSetState (
_In_ PBL_TEXT_CONSOLE TextConsole,
_In_ UCHAR Mask,
_In_ PBL_DISPLAY_STATE State
)
{
NTSTATUS Status;
ULONG FgColor, BgColor, Attribute, XPos, YPos, TextHeight, TextWidth;
BOOLEAN Visible;
/* Check if foreground state is being set */
if (Mask & 1)
{
/* Check if there's a difference from current */
FgColor = State->FgColor;
if (TextConsole->State.FgColor != FgColor)
{
/* Ignore invalid color */
if (FgColor > White)
{
return STATUS_INVALID_PARAMETER;
}
/* Convert from NT/CGA format to EFI, and then set the attribute */
Attribute = ConsoleEfiTextGetAttribute(TextConsole->State.BgColor,
FgColor);
Status = EfiConOutSetAttribute(TextConsole->Protocol, Attribute);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Update cached state */
TextConsole->State.FgColor = FgColor;
}
}
/* Check if background state is being set */
if (Mask & 2)
{
/* Check if there's a difference from current */
BgColor = State->BgColor;
if (TextConsole->State.BgColor != BgColor)
{
/* Ignore invalid color */
if (BgColor > White)
{
return STATUS_INVALID_PARAMETER;
}
/* Convert from NT/CGA format to EFI, and then set the attribute */
Attribute = ConsoleEfiTextGetAttribute(BgColor,
TextConsole->State.FgColor);
Status = EfiConOutSetAttribute(TextConsole->Protocol, Attribute);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Update cached state */
TextConsole->State.BgColor = BgColor;
}
}
/* Check if position state is being set */
if (Mask & 4)
{
/* Check if there's a difference from current */
XPos = State->XPos;
YPos = State->YPos;
if ((TextConsole->State.XPos != XPos) ||
(TextConsole->State.YPos != YPos))
{
/* Set the new cursor position */
BlDisplayGetTextCellResolution(&TextWidth, &TextHeight);
Status = EfiConOutSetCursorPosition(TextConsole->Protocol,
XPos/ TextWidth,
YPos / TextHeight);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Update cached state */
TextConsole->State.XPos = XPos;
TextConsole->State.YPos = YPos;
}
}
/* Check if cursor state is being set */
if (Mask & 8)
{
/* Check if there's a difference from current */
Visible = State->CursorVisible;
if (TextConsole->State.CursorVisible != Visible)
{
/* Ignore invalid state */
if (Visible >= 3)
{
return STATUS_INVALID_PARAMETER;
}
/* Set the new cursor state */
Status = EfiConOutEnableCursor(TextConsole->Protocol, Visible);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Update cached status */
TextConsole->State.CursorVisible = Visible;
}
}
/* Return success */
return STATUS_SUCCESS;
}
NTSTATUS
ConsoleEfiTextFindModeFromAllowed (
_In_ SIMPLE_TEXT_OUTPUT_INTERFACE *TextProtocol,
_In_ PBL_DISPLAY_MODE SupportedModes,
_In_ ULONG MaxIndex,
_Out_ PULONG SupportedMode
)
{
EFI_SIMPLE_TEXT_OUTPUT_MODE ModeInfo;
ULONG MaxMode, MaxQueriedMode, Mode, i, MatchingMode;
UINTN HRes, VRes;
ULONGLONG ModeListSize;
PBL_DISPLAY_MODE ModeEntry, ModeList, SupportedModeEntry;
NTSTATUS Status;
/* Read information on the current mode */
EfiConOutReadCurrentMode(TextProtocol, &ModeInfo);
/* Figure out the max mode, and how many modes we'll have to read */
MaxMode = ModeInfo.MaxMode;
ModeListSize = sizeof(*ModeEntry) * ModeInfo.MaxMode;
if (ModeListSize > MAXULONG)
{
return STATUS_INTEGER_OVERFLOW;
}
/* Allocate a list for all the supported EFI modes */
ModeList = BlMmAllocateHeap(ModeListSize);
if (!ModeList)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Scan all the EFI modes */
EfiPrintf(L"Scanning through %d modes\r\n", MaxMode);
for (MaxQueriedMode = 0, Mode = 0; Mode < MaxMode; Mode++)
{
/* Query information on this mode */
ModeEntry = &ModeList[MaxQueriedMode];
if (NT_SUCCESS(EfiConOutQueryMode(TextProtocol,
Mode,
&HRes,
&VRes)))
{
/* This mode was successfully queried. Save the data */
EfiPrintf(L"EFI Firmware Supported Mode %d is H: %d V: %d\r\n", Mode, HRes, VRes);
ModeEntry->HRes = HRes;
ModeEntry->VRes = VRes;
ModeEntry->HRes2 = HRes;
MaxQueriedMode = Mode + 1;
}
}
/* Loop all the supported mode entries */
for (i = 0; i < MaxIndex; i++)
{
/* Loop all the UEFI queried modes */
SupportedModeEntry = &SupportedModes[i];
for (MatchingMode = 0; MatchingMode < MaxQueriedMode; MatchingMode++)
{
/* Check if the UEFI mode is compatible with our supported mode */
ModeEntry = &ModeList[MatchingMode];
EfiPrintf(L"H1: %d V1: %d - H2: %d - V2: %d\r\n", ModeEntry->HRes, ModeEntry->VRes, SupportedModeEntry->HRes, SupportedModeEntry->VRes);
if ((ModeEntry->HRes == SupportedModeEntry->HRes) &&
(ModeEntry->VRes == SupportedModeEntry->VRes))
{
/* Yep -- free the mode list and return this mode */
BlMmFreeHeap(ModeList);
*SupportedMode = MatchingMode;
return STATUS_SUCCESS;
}
}
}
/* We can't do anything -- there are no matching modes */
Status = STATUS_UNSUCCESSFUL;
BlMmFreeHeap(ModeList);
return Status;
}
VOID
ConsoleFirmwareTextClose (
_In_ PBL_TEXT_CONSOLE TextConsole
)
{
ULONG Mode;
BL_DISPLAY_STATE DisplayState;
/* Read the original mode, and see if it's different than the one now */
Mode = TextConsole->OldMode.Mode;
if (Mode != TextConsole->Mode)
{
/* Restore to the original mode */
EfiConOutSetMode(TextConsole->Protocol, Mode);
}
/* Read the EFI settings for the original mode */
ConsoleEfiTextGetStateFromMode(&TextConsole->OldMode, &DisplayState);
/* Set the original settings */
ConsoleFirmwareTextSetState(TextConsole, 0xF, &DisplayState);
}
NTSTATUS
ConsoleFirmwareTextOpen (
_In_ PBL_TEXT_CONSOLE TextConsole
)
{
BL_DISPLAY_MODE DisplayMode;
EFI_SIMPLE_TEXT_OUTPUT_MODE CurrentMode, NewMode;
UINTN HRes, VRes;
ULONG Mode;
NTSTATUS Status;
/* Read the current mode and its settings */
EfiConOutReadCurrentMode(EfiConOut, &CurrentMode);
Status = EfiConOutQueryMode(EfiConOut, CurrentMode.Mode, &HRes, &VRes);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Save the current mode and its settings */
NewMode = CurrentMode;
DisplayMode.VRes = VRes;
DisplayMode.HRes = HRes;
DisplayMode.HRes2 = HRes;
/* Check if the current mode is compatible with one of our modes */
if (!ConsolepFindResolution(&DisplayMode, ConsoleTextResolutionList, 1))
{
/* It isn't -- find a matching EFI mode for what we need */
EfiPrintf(L"In incorrect mode, scanning for right one\r\n");
Status = ConsoleEfiTextFindModeFromAllowed(EfiConOut,
ConsoleTextResolutionList,
1,
&Mode);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"Failed to find mode: %lx\r\n", Status);
return Status;
}
/* Set the new EFI mode */
EfiPrintf(L"Setting new mode: %d\r\n", Mode);
Status = EfiConOutSetMode(EfiConOut, Mode);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Read the current mode and its settings */
EfiConOutReadCurrentMode(EfiConOut, &NewMode);
Status = EfiConOutQueryMode(EfiConOut, Mode, &HRes, &VRes);
if (!NT_SUCCESS(Status))
{
EfiConOutSetMode(EfiConOut, CurrentMode.Mode);
return Status;
}
/* Save the current mode and its settings */
DisplayMode.HRes = HRes;
DisplayMode.VRes = VRes;
DisplayMode.HRes2 = HRes;
}
/* Capture all the current settings */
ConsoleEfiTextGetStateFromMode(&NewMode, &TextConsole->State);
TextConsole->Mode = NewMode.Mode;
TextConsole->DisplayMode = DisplayMode;
TextConsole->Protocol = EfiConOut;
TextConsole->OldMode = CurrentMode;
return STATUS_SUCCESS;
}
NTSTATUS
ConsoleInputBaseEraseBuffer (
_In_ PBL_INPUT_CONSOLE Console,
_In_opt_ PULONG FillValue
)
{
ULONG ValueToFill;
PULONG i;
/* Check if we should fill with a particular value */
if (FillValue)
{
/* Use it */
ValueToFill = *FillValue;
}
else
{
/* Otherwise, use default */
ValueToFill = 0x10020;
}
/* Set the input buffer to its last location */
Console->DataStart = Console->DataEnd;
/* Fill the buffer with the value */
for (i = Console->Buffer; i < Console->EndBuffer; i++)
{
*i = ValueToFill;
}
/* All done */
return STATUS_SUCCESS;
}
NTSTATUS
ConsoleInputLocalEraseBuffer (
_In_ PBL_INPUT_CONSOLE Console,
_In_opt_ PULONG FillValue
)
{
NTSTATUS Status, EfiStatus;
/* Erase the software buffer */
Status = ConsoleInputBaseEraseBuffer(Console, FillValue);
/* Reset the hardware console */
EfiStatus = EfiConInEx ? EfiConInExReset() : EfiConInReset();
if (!NT_SUCCESS(EfiStatus))
{
/* Normalize the failure code */
EfiStatus = STATUS_UNSUCCESSFUL;
}
/* Check if software reset worked */
if (NT_SUCCESS(Status))
{
/* Then return the firmware code */
Status = EfiStatus;
}
/* All done */
return Status;
}
NTSTATUS
ConsoleFirmwareTextClear (
_In_ PBL_TEXT_CONSOLE Console,
_In_ BOOLEAN LineOnly
)
{
BL_ARCH_MODE OldMode;
EFI_STATUS EfiStatus;
NTSTATUS Status;
ULONG i, Column, Row, TextWidth, TextHeight;
/* Get the text resolution */
BlDisplayGetTextCellResolution(&TextWidth, &TextHeight);
/* Are we just clearing a line? */
if (LineOnly)
{
/* Get the current column and row */
Column = Console->State.XPos / TextWidth;
Row = Console->State.YPos / TextHeight;
/* Loop over every remaining character */
for (i = 0; i < Console->DisplayMode.HRes - Column - 1; i++)
{
/* Write a space on top of it */
Status = EfiConOutOutputString(Console->Protocol, L" ");
if (!NT_SUCCESS(Status))
{
break;
}
}
/* And reset the cursor back at the initial position */
Status = EfiConOutSetCursorPosition(Console->Protocol,
Column,
Row);
}
else
{
/* Are we in protected mode? */
OldMode = CurrentExecutionContext->Mode;
if (OldMode != BlRealMode)
{
/* FIXME: Not yet implemented */
return STATUS_NOT_IMPLEMENTED;
}
/* Clear the screen */
EfiStatus = Console->Protocol->ClearScreen(Console->Protocol);
/* Switch back to protected mode if we came from there */
if (OldMode != BlRealMode)
{
BlpArchSwitchContext(OldMode);
}
/* Conver to NT status -- did that work? */
Status = EfiGetNtStatusCode(EfiStatus);
if (NT_SUCCESS(Status))
{
/* Reset current positions */
Console->State.XPos = 0;
Console->State.YPos = 0;
}
}
/* All done */
return Status;
}