mirror of
https://github.com/reactos/reactos.git
synced 2024-11-09 16:20:37 +00:00
625 lines
17 KiB
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;
|
|
}
|
|
|