blob: ef1931961432da8ee1481adfd1b38004de0dd64b [file] [log] [blame]
/** @file
The application to show the Boot Manager Menu.
Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "BootManagerMenu.h"
EFI_HII_HANDLE gStringPackHandle;
BOOLEAN mModeInitialized = FALSE;
//
// Boot video resolution and text mode.
//
UINT32 mBootHorizontalResolution = 0;
UINT32 mBootVerticalResolution = 0;
UINT32 mBootTextModeColumn = 0;
UINT32 mBootTextModeRow = 0;
//
// BIOS setup video resolution and text mode.
//
UINT32 mSetupTextModeColumn = 0;
UINT32 mSetupTextModeRow = 0;
UINT32 mSetupHorizontalResolution = 0;
UINT32 mSetupVerticalResolution = 0;
/**
Prints a unicode string to the default console, at
the supplied cursor position, using L"%s" format.
@param Column The cursor position to print the string at.
@param Row The cursor position to print the string at
@param String String pointer.
@return Length of string printed to the console
**/
UINTN
PrintStringAt (
IN UINTN Column,
IN UINTN Row,
IN CHAR16 *String
)
{
UINTN ScreenWidth;
UINTN ScreenRows;
CHAR16 *TurncateString;
EFI_STATUS Status;
UINTN ShowingLength;
gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row);
gST->ConOut->QueryMode (
gST->ConOut,
gST->ConOut->Mode->Mode,
&ScreenWidth,
&ScreenRows
);
if ((Column > (ScreenWidth - 1)) || (Row > (ScreenRows - 1))) {
return 0;
}
if ((StrLen (String) + Column) > (ScreenWidth - 1)) {
//
// | - ScreenWidth - |
// ...Column.....................
// TurncateString length should leave one character for draw box and
// require one character for string end.
//
ShowingLength = ScreenWidth - Column - 1;
TurncateString = AllocatePool ((ShowingLength + 1) * sizeof (CHAR16));
if (TurncateString == NULL) {
return 0;
}
Status = StrnCpyS (TurncateString, ShowingLength + 1, String, ShowingLength - 3);
if (EFI_ERROR (Status)) {
FreePool (TurncateString);
return 0;
}
*(TurncateString + ShowingLength - 3) = L'.';
*(TurncateString + ShowingLength - 2) = L'.';
*(TurncateString + ShowingLength - 1) = L'.';
*(TurncateString + ShowingLength) = L'\0';
ShowingLength = Print (L"%s", TurncateString);
FreePool (TurncateString);
return ShowingLength;
} else {
return Print (L"%s", String);
}
}
/**
Prints a character to the default console, at
the supplied cursor position, using L"%c" format.
@param Column The cursor position to print the string at.
@param Row The cursor position to print the string at.
@param Character Character to print.
@return Length of string printed to the console.
**/
UINTN
PrintCharAt (
IN UINTN Column,
IN UINTN Row,
CHAR16 Character
)
{
UINTN ScreenWidth;
UINTN ScreenRows;
gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row);
gST->ConOut->QueryMode (
gST->ConOut,
gST->ConOut->Mode->Mode,
&ScreenWidth,
&ScreenRows
);
if ((Column > (ScreenWidth - 1)) || (Row > (ScreenRows - 1))) {
return 0;
}
return Print (L"%c", Character);
}
/**
Count the storage space of a Unicode string which uses current language to get
from input string ID.
@param StringId The input string to be counted.
@return Storage space for the input string.
**/
UINTN
GetLineWidth (
IN EFI_STRING_ID StringId
)
{
UINTN Index;
UINTN IncrementValue;
EFI_STRING String;
UINTN LineWidth;
LineWidth = 0;
String = HiiGetString (gStringPackHandle, StringId, NULL);
if (String != NULL) {
Index = 0;
IncrementValue = 1;
do {
//
// Advance to the null-terminator or to the first width directive
//
for ( ;
(String[Index] != NARROW_CHAR) && (String[Index] != WIDE_CHAR) && (String[Index] != 0);
Index++, LineWidth = LineWidth + IncrementValue
)
{
}
//
// We hit the null-terminator, we now have a count
//
if (String[Index] == 0) {
break;
}
//
// We encountered a narrow directive - strip it from the size calculation since it doesn't get printed
// and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2)
//
if (String[Index] == NARROW_CHAR) {
//
// Skip to the next character
//
Index++;
IncrementValue = 1;
} else {
//
// Skip to the next character
//
Index++;
IncrementValue = 2;
}
} while (String[Index] != 0);
FreePool (String);
}
return LineWidth;
}
/**
This function uses calculate the boot menu location, size and scroll bar information.
@param BootMenuData The boot menu data to be processed.
@return EFI_SUCCESS calculate boot menu information successful.
@retval EFI_INVALID_PARAMETER Input parameter is invalid
**/
EFI_STATUS
InitializeBootMenuScreen (
IN OUT BOOT_MENU_POPUP_DATA *BootMenuData
)
{
UINTN MaxStrWidth;
UINTN StrWidth;
UINTN Index;
UINTN Column;
UINTN Row;
UINTN MaxPrintRows;
UINTN UnSelectableItmes;
if (BootMenuData == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Get maximum string width
//
MaxStrWidth = 0;
for (Index = 0; Index < TITLE_TOKEN_COUNT; Index++) {
StrWidth = GetLineWidth (BootMenuData->TitleToken[Index]);
MaxStrWidth = MaxStrWidth > StrWidth ? MaxStrWidth : StrWidth;
}
for (Index = 0; Index < BootMenuData->ItemCount; Index++) {
StrWidth = GetLineWidth (BootMenuData->PtrTokens[Index]);
MaxStrWidth = MaxStrWidth > StrWidth ? MaxStrWidth : StrWidth;
}
for (Index = 0; Index < HELP_TOKEN_COUNT; Index++) {
StrWidth = GetLineWidth (BootMenuData->HelpToken[Index]);
MaxStrWidth = MaxStrWidth > StrWidth ? MaxStrWidth : StrWidth;
}
//
// query current row and column to calculate boot menu location
//
gST->ConOut->QueryMode (
gST->ConOut,
gST->ConOut->Mode->Mode,
&Column,
&Row
);
MaxPrintRows = Row - 6;
UnSelectableItmes = TITLE_TOKEN_COUNT + 2 + HELP_TOKEN_COUNT + 2;
if (MaxStrWidth + 8 > Column) {
BootMenuData->MenuScreen.Width = Column;
} else {
BootMenuData->MenuScreen.Width = MaxStrWidth + 8;
}
if (BootMenuData->ItemCount + UnSelectableItmes > MaxPrintRows) {
BootMenuData->MenuScreen.Height = MaxPrintRows;
BootMenuData->ScrollBarControl.HasScrollBar = TRUE;
BootMenuData->ScrollBarControl.ItemCountPerScreen = MaxPrintRows - UnSelectableItmes;
BootMenuData->ScrollBarControl.FirstItem = 0;
BootMenuData->ScrollBarControl.LastItem = MaxPrintRows - UnSelectableItmes - 1;
} else {
BootMenuData->MenuScreen.Height = BootMenuData->ItemCount + UnSelectableItmes;
BootMenuData->ScrollBarControl.HasScrollBar = FALSE;
BootMenuData->ScrollBarControl.ItemCountPerScreen = BootMenuData->ItemCount;
BootMenuData->ScrollBarControl.FirstItem = 0;
BootMenuData->ScrollBarControl.LastItem = BootMenuData->ItemCount - 1;
}
BootMenuData->MenuScreen.StartCol = (Column - BootMenuData->MenuScreen.Width) / 2;
BootMenuData->MenuScreen.StartRow = (Row - BootMenuData->MenuScreen.Height) / 2;
return EFI_SUCCESS;
}
/**
This function uses check boot option is wheher setup application or no
@param BootOption Pointer to EFI_BOOT_MANAGER_LOAD_OPTION array.
@retval TRUE This boot option is setup application.
@retval FALSE This boot options isn't setup application
**/
BOOLEAN
IsBootManagerMenu (
IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
)
{
EFI_STATUS Status;
EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;
Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);
if (!EFI_ERROR (Status)) {
EfiBootManagerFreeLoadOption (&BootManagerMenu);
}
return (BOOLEAN)(!EFI_ERROR (Status) && (BootOption->OptionNumber == BootManagerMenu.OptionNumber));
}
/**
Return whether to ignore the boot option.
@param BootOption Pointer to EFI_BOOT_MANAGER_LOAD_OPTION to check.
@retval TRUE Ignore the boot option.
@retval FALSE Do not ignore the boot option.
**/
BOOLEAN
IgnoreBootOption (
IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
//
// Ignore myself.
//
Status = gBS->HandleProtocol (gImageHandle, &gEfiLoadedImageDevicePathProtocolGuid, (VOID **)&ImageDevicePath);
ASSERT_EFI_ERROR (Status);
if (CompareMem (BootOption->FilePath, ImageDevicePath, GetDevicePathSize (ImageDevicePath)) == 0) {
return TRUE;
}
//
// Do not ignore Boot Manager Menu.
//
if (IsBootManagerMenu (BootOption)) {
return FALSE;
}
//
// Ignore the hidden/inactive boot option.
//
if (((BootOption->Attributes & LOAD_OPTION_HIDDEN) != 0) || ((BootOption->Attributes & LOAD_OPTION_ACTIVE) == 0)) {
return TRUE;
}
return FALSE;
}
/**
This function uses to initialize boot menu data
@param BootOption Pointer to EFI_BOOT_MANAGER_LOAD_OPTION array.
@param BootOptionCount Number of boot option.
@param BootMenuData The Input BootMenuData to be initialized.
@retval EFI_SUCCESS Initialize boot menu data successful.
@retval EFI_INVALID_PARAMETER Input parameter is invalid.
**/
EFI_STATUS
InitializeBootMenuData (
IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption,
IN UINTN BootOptionCount,
OUT BOOT_MENU_POPUP_DATA *BootMenuData
)
{
UINTN Index;
UINTN StrIndex;
if ((BootOption == NULL) || (BootMenuData == NULL)) {
return EFI_INVALID_PARAMETER;
}
BootMenuData->TitleToken[0] = STRING_TOKEN (STR_BOOT_POPUP_MENU_TITLE_STRING);
BootMenuData->PtrTokens = AllocateZeroPool (BootOptionCount * sizeof (EFI_STRING_ID));
ASSERT (BootMenuData->PtrTokens != NULL);
//
// Skip boot option which created by BootNext Variable
//
for (StrIndex = 0, Index = 0; Index < BootOptionCount; Index++) {
if (IgnoreBootOption (&BootOption[Index])) {
continue;
}
ASSERT (BootOption[Index].Description != NULL);
BootMenuData->PtrTokens[StrIndex++] = HiiSetString (
gStringPackHandle,
0,
BootOption[Index].Description,
NULL
);
}
BootMenuData->ItemCount = StrIndex;
BootMenuData->HelpToken[0] = STRING_TOKEN (STR_BOOT_POPUP_MENU_HELP1_STRING);
BootMenuData->HelpToken[1] = STRING_TOKEN (STR_BOOT_POPUP_MENU_HELP2_STRING);
BootMenuData->HelpToken[2] = STRING_TOKEN (STR_BOOT_POPUP_MENU_HELP3_STRING);
InitializeBootMenuScreen (BootMenuData);
BootMenuData->SelectItem = 0;
return EFI_SUCCESS;
}
/**
This function uses input select item to highlight selected item
and set current selected item in BootMenuData
@param WantSelectItem The user wants to select item.
@param BootMenuData The boot menu data to be processed
@return EFI_SUCCESS Highlight selected item and update current selected
item successful
@retval EFI_INVALID_PARAMETER Input parameter is invalid
**/
EFI_STATUS
BootMenuSelectItem (
IN UINTN WantSelectItem,
IN OUT BOOT_MENU_POPUP_DATA *BootMenuData
)
{
INT32 SavedAttribute;
EFI_STRING String;
UINTN StartCol;
UINTN StartRow;
UINTN PrintCol;
UINTN PrintRow;
UINTN TopShadeNum;
UINTN LowShadeNum;
UINTN FirstItem;
UINTN LastItem;
UINTN ItemCountPerScreen;
UINTN Index;
BOOLEAN RePaintItems;
if ((BootMenuData == NULL) || (WantSelectItem >= BootMenuData->ItemCount)) {
return EFI_INVALID_PARAMETER;
}
ASSERT (BootMenuData->ItemCount != 0);
SavedAttribute = gST->ConOut->Mode->Attribute;
RePaintItems = FALSE;
StartCol = BootMenuData->MenuScreen.StartCol;
StartRow = BootMenuData->MenuScreen.StartRow;
//
// print selectable items again and adjust scroll bar if need
//
if (BootMenuData->ScrollBarControl.HasScrollBar &&
((WantSelectItem < BootMenuData->ScrollBarControl.FirstItem) ||
(WantSelectItem > BootMenuData->ScrollBarControl.LastItem) ||
(WantSelectItem == BootMenuData->SelectItem)))
{
ItemCountPerScreen = BootMenuData->ScrollBarControl.ItemCountPerScreen;
//
// Set first item and last item
//
if (WantSelectItem < BootMenuData->ScrollBarControl.FirstItem) {
BootMenuData->ScrollBarControl.FirstItem = WantSelectItem;
BootMenuData->ScrollBarControl.LastItem = WantSelectItem + ItemCountPerScreen - 1;
} else if (WantSelectItem > BootMenuData->ScrollBarControl.LastItem) {
BootMenuData->ScrollBarControl.FirstItem = WantSelectItem - ItemCountPerScreen + 1;
BootMenuData->ScrollBarControl.LastItem = WantSelectItem;
}
gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLUE);
FirstItem = BootMenuData->ScrollBarControl.FirstItem;
LastItem = BootMenuData->ScrollBarControl.LastItem;
TopShadeNum = 0;
if (FirstItem != 0) {
TopShadeNum = (FirstItem * ItemCountPerScreen) / BootMenuData->ItemCount;
if ((FirstItem * ItemCountPerScreen) % BootMenuData->ItemCount != 0) {
TopShadeNum++;
}
PrintCol = StartCol + BootMenuData->MenuScreen.Width - 2;
PrintRow = StartRow + TITLE_TOKEN_COUNT + 2;
for (Index = 0; Index < TopShadeNum; Index++, PrintRow++) {
PrintCharAt (PrintCol, PrintRow, BLOCKELEMENT_LIGHT_SHADE);
}
}
LowShadeNum = 0;
if (LastItem != BootMenuData->ItemCount - 1) {
LowShadeNum = ((BootMenuData->ItemCount - 1 - LastItem) * ItemCountPerScreen) / BootMenuData->ItemCount;
if (((BootMenuData->ItemCount - 1 - LastItem) * ItemCountPerScreen) % BootMenuData->ItemCount != 0) {
LowShadeNum++;
}
PrintCol = StartCol + BootMenuData->MenuScreen.Width - 2;
PrintRow = StartRow + TITLE_TOKEN_COUNT + 2 + ItemCountPerScreen - LowShadeNum;
for (Index = 0; Index < LowShadeNum; Index++, PrintRow++) {
PrintCharAt (PrintCol, PrintRow, BLOCKELEMENT_LIGHT_SHADE);
}
}
PrintCol = StartCol + BootMenuData->MenuScreen.Width - 2;
PrintRow = StartRow + TITLE_TOKEN_COUNT + 2 + TopShadeNum;
for (Index = TopShadeNum; Index < ItemCountPerScreen - LowShadeNum; Index++, PrintRow++) {
PrintCharAt (PrintCol, PrintRow, BLOCKELEMENT_FULL_BLOCK);
}
//
// Clear selectable items first
//
PrintCol = StartCol + 1;
PrintRow = StartRow + TITLE_TOKEN_COUNT + 2;
String = AllocateZeroPool ((BootMenuData->MenuScreen.Width - 2) * sizeof (CHAR16));
ASSERT (String != NULL);
for (Index = 0; Index < BootMenuData->MenuScreen.Width - 3; Index++) {
String[Index] = 0x20;
}
for (Index = 0; Index < ItemCountPerScreen; Index++) {
PrintStringAt (PrintCol, PrintRow + Index, String);
}
FreePool (String);
//
// print selectable items
//
for (Index = 0; Index < ItemCountPerScreen; Index++, PrintRow++) {
String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[Index + FirstItem], NULL);
PrintStringAt (PrintCol, PrintRow, String);
FreePool (String);
}
RePaintItems = TRUE;
}
//
// if Want Select and selected item isn't the same and doesn't re-draw selectable
// items, clear select item
//
FirstItem = BootMenuData->ScrollBarControl.FirstItem;
if ((WantSelectItem != BootMenuData->SelectItem) && !RePaintItems) {
gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLUE);
String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[BootMenuData->SelectItem], NULL);
PrintCol = StartCol + 1;
PrintRow = StartRow + 3 + BootMenuData->SelectItem - FirstItem;
PrintStringAt (PrintCol, PrintRow, String);
FreePool (String);
}
//
// Print want to select item
//
gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLACK);
String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[WantSelectItem], NULL);
PrintCol = StartCol + 1;
PrintRow = StartRow + TITLE_TOKEN_COUNT + 2 + WantSelectItem - FirstItem;
PrintStringAt (PrintCol, PrintRow, String);
FreePool (String);
gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
BootMenuData->SelectItem = WantSelectItem;
return EFI_SUCCESS;
}
/**
This function uses to draw boot popup menu
@param BootMenuData The Input BootMenuData to be processed.
@retval EFI_SUCCESS Draw boot popup menu successful.
**/
EFI_STATUS
DrawBootPopupMenu (
IN BOOT_MENU_POPUP_DATA *BootMenuData
)
{
EFI_STRING String;
UINTN Index;
UINTN Width;
UINTN StartCol;
UINTN StartRow;
UINTN PrintRow;
UINTN PrintCol;
UINTN LineWidth;
INT32 SavedAttribute;
UINTN ItemCountPerScreen;
gST->ConOut->ClearScreen (gST->ConOut);
SavedAttribute = gST->ConOut->Mode->Attribute;
gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLUE);
Width = BootMenuData->MenuScreen.Width;
StartCol = BootMenuData->MenuScreen.StartCol;
StartRow = BootMenuData->MenuScreen.StartRow;
ItemCountPerScreen = BootMenuData->ScrollBarControl.ItemCountPerScreen;
PrintRow = StartRow;
gST->ConOut->EnableCursor (gST->ConOut, FALSE);
//
// Draw Boot popup menu screen
//
PrintCharAt (StartCol, PrintRow, BOXDRAW_DOWN_RIGHT);
for (Index = 1; Index < Width - 1; Index++) {
PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL);
}
PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_DOWN_LEFT);
//
// Draw the screen for title
//
String = AllocateZeroPool ((Width - 1) * sizeof (CHAR16));
ASSERT (String != NULL);
for (Index = 0; Index < Width - 2; Index++) {
String[Index] = 0x20;
}
for (Index = 0; Index < TITLE_TOKEN_COUNT; Index++) {
PrintRow++;
PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL);
PrintStringAt (StartCol + 1, PrintRow, String);
PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL);
}
PrintRow++;
PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL_RIGHT);
for (Index = 1; Index < Width - 1; Index++) {
PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL);
}
PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL_LEFT);
//
// Draw screen for selectable items
//
for (Index = 0; Index < ItemCountPerScreen; Index++) {
PrintRow++;
PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL);
PrintStringAt (StartCol + 1, PrintRow, String);
PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL);
}
PrintRow++;
PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL_RIGHT);
for (Index = 1; Index < Width - 1; Index++) {
PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL);
}
PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL_LEFT);
//
// Draw screen for Help
//
for (Index = 0; Index < HELP_TOKEN_COUNT; Index++) {
PrintRow++;
PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL);
PrintStringAt (StartCol + 1, PrintRow, String);
PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL);
}
FreePool (String);
PrintRow++;
PrintCharAt (StartCol, PrintRow, BOXDRAW_UP_RIGHT);
for (Index = 1; Index < Width - 1; Index++) {
PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL);
}
PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_UP_LEFT);
//
// print title strings
//
PrintRow = StartRow + 1;
for (Index = 0; Index < TITLE_TOKEN_COUNT; Index++, PrintRow++) {
String = HiiGetString (gStringPackHandle, BootMenuData->TitleToken[Index], NULL);
LineWidth = GetLineWidth (BootMenuData->TitleToken[Index]);
PrintCol = StartCol + (Width - LineWidth) / 2;
PrintStringAt (PrintCol, PrintRow, String);
FreePool (String);
}
//
// print selectable items
//
PrintCol = StartCol + 1;
PrintRow = StartRow + TITLE_TOKEN_COUNT + 2;
for (Index = 0; Index < ItemCountPerScreen; Index++, PrintRow++) {
String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[Index], NULL);
PrintStringAt (PrintCol, PrintRow, String);
FreePool (String);
}
//
// Print Help strings
//
PrintRow++;
for (Index = 0; Index < HELP_TOKEN_COUNT; Index++, PrintRow++) {
String = HiiGetString (gStringPackHandle, BootMenuData->HelpToken[Index], NULL);
LineWidth = GetLineWidth (BootMenuData->HelpToken[Index]);
PrintCol = StartCol + (Width - LineWidth) / 2;
PrintStringAt (PrintCol, PrintRow, String);
FreePool (String);
}
//
// Print scroll bar if has scroll bar
//
if (BootMenuData->ScrollBarControl.HasScrollBar) {
PrintCol = StartCol + Width - 2;
PrintRow = StartRow + 2;
PrintCharAt (PrintCol, PrintRow, GEOMETRICSHAPE_UP_TRIANGLE);
PrintCharAt (PrintCol + 1, PrintRow, BOXDRAW_VERTICAL);
PrintRow += (ItemCountPerScreen + 1);
PrintCharAt (PrintCol, PrintRow, GEOMETRICSHAPE_DOWN_TRIANGLE);
PrintCharAt (PrintCol + 1, PrintRow, BOXDRAW_VERTICAL);
}
gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
//
// Print Selected item
//
BootMenuSelectItem (BootMenuData->SelectItem, BootMenuData);
return EFI_SUCCESS;
}
/**
This function uses to boot from selected item
@param BootOptions Pointer to EFI_BOOT_MANAGER_LOAD_OPTION array.
@param BootOptionCount Number of boot option.
@param SelectItem Current selected item.
**/
VOID
BootFromSelectOption (
IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
IN UINTN BootOptionCount,
IN UINTN SelectItem
)
{
UINTN ItemNum;
UINTN Index;
ASSERT (BootOptions != NULL);
for (ItemNum = 0, Index = 0; Index < BootOptionCount; Index++) {
if (IgnoreBootOption (&BootOptions[Index])) {
continue;
}
if (ItemNum++ == SelectItem) {
EfiBootManagerBoot (&BootOptions[Index]);
break;
}
}
}
/**
This function will change video resolution and text mode
according to defined setup mode or defined boot mode
@param IsSetupMode Indicate mode is changed to setup mode or boot mode.
@retval EFI_SUCCESS Mode is changed successfully.
@retval Others Mode failed to be changed.
**/
EFI_STATUS
EFIAPI
BdsSetConsoleMode (
BOOLEAN IsSetupMode
)
{
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut;
UINTN SizeOfInfo;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
UINT32 MaxGopMode;
UINT32 MaxTextMode;
UINT32 ModeNumber;
UINT32 NewHorizontalResolution;
UINT32 NewVerticalResolution;
UINT32 NewColumns;
UINT32 NewRows;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
EFI_STATUS Status;
UINTN Index;
UINTN CurrentColumn;
UINTN CurrentRow;
MaxGopMode = 0;
MaxTextMode = 0;
//
// Get current video resolution and text mode
//
Status = gBS->HandleProtocol (
gST->ConsoleOutHandle,
&gEfiGraphicsOutputProtocolGuid,
(VOID **)&GraphicsOutput
);
if (EFI_ERROR (Status)) {
GraphicsOutput = NULL;
}
Status = gBS->HandleProtocol (
gST->ConsoleOutHandle,
&gEfiSimpleTextOutProtocolGuid,
(VOID **)&SimpleTextOut
);
if (EFI_ERROR (Status)) {
SimpleTextOut = NULL;
}
if ((GraphicsOutput == NULL) || (SimpleTextOut == NULL)) {
return EFI_UNSUPPORTED;
}
if (IsSetupMode) {
//
// The required resolution and text mode is setup mode.
//
NewHorizontalResolution = mSetupHorizontalResolution;
NewVerticalResolution = mSetupVerticalResolution;
NewColumns = mSetupTextModeColumn;
NewRows = mSetupTextModeRow;
} else {
//
// The required resolution and text mode is boot mode.
//
NewHorizontalResolution = mBootHorizontalResolution;
NewVerticalResolution = mBootVerticalResolution;
NewColumns = mBootTextModeColumn;
NewRows = mBootTextModeRow;
}
if (GraphicsOutput != NULL) {
MaxGopMode = GraphicsOutput->Mode->MaxMode;
}
if (SimpleTextOut != NULL) {
MaxTextMode = SimpleTextOut->Mode->MaxMode;
}
//
// 1. If current video resolution is same with required video resolution,
// video resolution need not be changed.
// 1.1. If current text mode is same with required text mode, text mode need not be changed.
// 1.2. If current text mode is different from required text mode, text mode need be changed.
// 2. If current video resolution is different from required video resolution, we need restart whole console drivers.
//
for (ModeNumber = 0; ModeNumber < MaxGopMode; ModeNumber++) {
Status = GraphicsOutput->QueryMode (
GraphicsOutput,
ModeNumber,
&SizeOfInfo,
&Info
);
if (!EFI_ERROR (Status)) {
if ((Info->HorizontalResolution == NewHorizontalResolution) &&
(Info->VerticalResolution == NewVerticalResolution))
{
if ((GraphicsOutput->Mode->Info->HorizontalResolution == NewHorizontalResolution) &&
(GraphicsOutput->Mode->Info->VerticalResolution == NewVerticalResolution))
{
//
// Current resolution is same with required resolution, check if text mode need be set
//
Status = SimpleTextOut->QueryMode (SimpleTextOut, SimpleTextOut->Mode->Mode, &CurrentColumn, &CurrentRow);
ASSERT_EFI_ERROR (Status);
if ((CurrentColumn == NewColumns) && (CurrentRow == NewRows)) {
//
// If current text mode is same with required text mode. Do nothing
//
FreePool (Info);
return EFI_SUCCESS;
} else {
//
// If current text mode is different from required text mode. Set new video mode
//
for (Index = 0; Index < MaxTextMode; Index++) {
Status = SimpleTextOut->QueryMode (SimpleTextOut, Index, &CurrentColumn, &CurrentRow);
if (!EFI_ERROR (Status)) {
if ((CurrentColumn == NewColumns) && (CurrentRow == NewRows)) {
//
// Required text mode is supported, set it.
//
Status = SimpleTextOut->SetMode (SimpleTextOut, Index);
ASSERT_EFI_ERROR (Status);
//
// Update text mode PCD.
//
Status = PcdSet32S (PcdConOutColumn, mSetupTextModeColumn);
ASSERT_EFI_ERROR (Status);
Status = PcdSet32S (PcdConOutRow, mSetupTextModeRow);
ASSERT_EFI_ERROR (Status);
FreePool (Info);
return EFI_SUCCESS;
}
}
}
if (Index == MaxTextMode) {
//
// If required text mode is not supported, return error.
//
FreePool (Info);
return EFI_UNSUPPORTED;
}
}
} else {
//
// If current video resolution is not same with the new one, set new video resolution.
// In this case, the driver which produces simple text out need be restarted.
//
Status = GraphicsOutput->SetMode (GraphicsOutput, ModeNumber);
if (!EFI_ERROR (Status)) {
FreePool (Info);
break;
}
}
}
FreePool (Info);
}
}
if (ModeNumber == MaxGopMode) {
//
// If the resolution is not supported, return error.
//
return EFI_UNSUPPORTED;
}
//
// Set PCD to Inform GraphicsConsole to change video resolution.
// Set PCD to Inform Consplitter to change text mode.
//
Status = PcdSet32S (PcdVideoHorizontalResolution, NewHorizontalResolution);
ASSERT_EFI_ERROR (Status);
Status = PcdSet32S (PcdVideoVerticalResolution, NewVerticalResolution);
ASSERT_EFI_ERROR (Status);
Status = PcdSet32S (PcdConOutColumn, NewColumns);
ASSERT_EFI_ERROR (Status);
Status = PcdSet32S (PcdConOutRow, NewRows);
ASSERT_EFI_ERROR (Status);
//
// Video mode is changed, so restart graphics console driver and higher level driver.
// Reconnect graphics console driver and higher level driver.
// Locate all the handles with GOP protocol and reconnect it.
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleTextOutProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (!EFI_ERROR (Status)) {
for (Index = 0; Index < HandleCount; Index++) {
gBS->DisconnectController (HandleBuffer[Index], NULL, NULL);
}
for (Index = 0; Index < HandleCount; Index++) {
gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
}
if (HandleBuffer != NULL) {
FreePool (HandleBuffer);
}
}
return EFI_SUCCESS;
}
/**
Display the boot popup menu and allow user select boot item.
@param ImageHandle The image handle.
@param SystemTable The system table.
@retval EFI_SUCCESS Boot from selected boot option, and return success from boot option
@retval EFI_NOT_FOUND User select to enter setup or can not find boot option
**/
EFI_STATUS
EFIAPI
BootManagerMenuEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_BOOT_MANAGER_LOAD_OPTION *BootOption;
UINTN BootOptionCount;
EFI_STATUS Status;
BOOT_MENU_POPUP_DATA BootMenuData;
UINTN Index;
EFI_INPUT_KEY Key;
BOOLEAN ExitApplication;
UINTN SelectItem;
EFI_BOOT_LOGO_PROTOCOL *BootLogo;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut;
UINTN BootTextColumn;
UINTN BootTextRow;
//
// Set Logo status invalid when boot manager menu is launched
//
BootLogo = NULL;
Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **)&BootLogo);
if (!EFI_ERROR (Status) && (BootLogo != NULL)) {
Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);
ASSERT_EFI_ERROR (Status);
}
gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
gStringPackHandle = HiiAddPackages (
&gEfiCallerIdGuid,
gImageHandle,
BootManagerMenuAppStrings,
NULL
);
ASSERT (gStringPackHandle != NULL);
//
// Connect all prior to entering the platform setup menu.
//
EfiBootManagerConnectAll ();
EfiBootManagerRefreshAllBootOption ();
BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
if (!mModeInitialized) {
//
// After the console is ready, get current video resolution
// and text mode before launching setup at first time.
//
Status = gBS->HandleProtocol (
gST->ConsoleOutHandle,
&gEfiGraphicsOutputProtocolGuid,
(VOID **)&GraphicsOutput
);
if (EFI_ERROR (Status)) {
GraphicsOutput = NULL;
}
Status = gBS->HandleProtocol (
gST->ConsoleOutHandle,
&gEfiSimpleTextOutProtocolGuid,
(VOID **)&SimpleTextOut
);
if (EFI_ERROR (Status)) {
SimpleTextOut = NULL;
}
if (GraphicsOutput != NULL) {
//
// Get current video resolution and text mode.
//
mBootHorizontalResolution = GraphicsOutput->Mode->Info->HorizontalResolution;
mBootVerticalResolution = GraphicsOutput->Mode->Info->VerticalResolution;
}
if (SimpleTextOut != NULL) {
Status = SimpleTextOut->QueryMode (
SimpleTextOut,
SimpleTextOut->Mode->Mode,
&BootTextColumn,
&BootTextRow
);
mBootTextModeColumn = (UINT32)BootTextColumn;
mBootTextModeRow = (UINT32)BootTextRow;
}
//
// Get user defined text mode for setup.
//
mSetupHorizontalResolution = PcdGet32 (PcdSetupVideoHorizontalResolution);
mSetupVerticalResolution = PcdGet32 (PcdSetupVideoVerticalResolution);
mSetupTextModeColumn = PcdGet32 (PcdSetupConOutColumn);
mSetupTextModeRow = PcdGet32 (PcdSetupConOutRow);
mModeInitialized = TRUE;
}
//
// Set back to conventional setup resolution
//
BdsSetConsoleMode (TRUE);
//
// Initialize Boot menu data
//
Status = InitializeBootMenuData (BootOption, BootOptionCount, &BootMenuData);
//
// According to boot menu data to draw boot popup menu
//
DrawBootPopupMenu (&BootMenuData);
//
// check user input to determine want to re-draw or boot from user selected item
//
ExitApplication = FALSE;
while (!ExitApplication) {
gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
if (!EFI_ERROR (Status)) {
switch (Key.UnicodeChar) {
case CHAR_NULL:
switch (Key.ScanCode) {
case SCAN_UP:
SelectItem = BootMenuData.SelectItem == 0 ? BootMenuData.ItemCount - 1 : BootMenuData.SelectItem - 1;
BootMenuSelectItem (SelectItem, &BootMenuData);
break;
case SCAN_DOWN:
SelectItem = BootMenuData.SelectItem == BootMenuData.ItemCount - 1 ? 0 : BootMenuData.SelectItem + 1;
BootMenuSelectItem (SelectItem, &BootMenuData);
break;
case SCAN_ESC:
gST->ConOut->ClearScreen (gST->ConOut);
ExitApplication = TRUE;
//
// Set boot resolution for normal boot
//
BdsSetConsoleMode (FALSE);
break;
default:
break;
}
break;
case CHAR_CARRIAGE_RETURN:
gST->ConOut->ClearScreen (gST->ConOut);
//
// Set boot resolution for normal boot
//
BdsSetConsoleMode (FALSE);
BootFromSelectOption (BootOption, BootOptionCount, BootMenuData.SelectItem);
//
// Back to boot manager menu again, set back to setup resolution
//
BdsSetConsoleMode (TRUE);
DrawBootPopupMenu (&BootMenuData);
break;
default:
break;
}
}
}
EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount);
FreePool (BootMenuData.PtrTokens);
HiiRemovePackages (gStringPackHandle);
return Status;
}