mirror of https://github.com/acidanthera/audk.git
1141 lines
37 KiB
C
1141 lines
37 KiB
C
/** @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;
|
|
|
|
}
|