audk/MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenu.c

1082 lines
36 KiB
C

/** @file
The application to show the Boot Manager Menu.
Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#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
)
{
gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row);
return Print (L"%s", String);
}
/**
Prints a chracter 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
)
{
gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row);
return Print (L"%c", Character);
}
/**
Count the storage space of a Unicode string which uses current lanaguag 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 proccessed.
@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;
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 funciton 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 optin.
@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 funciton 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 proccessed
@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;
}
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;
}
//
// Print want to select item
//
FirstItem = BootMenuData->ScrollBarControl.FirstItem;
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);
//
// if Want Select and selected item isn't the same and doesn't re-draw selectable
// items, clear select item
//
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);
}
gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
BootMenuData->SelectItem = WantSelectItem;
return EFI_SUCCESS;
}
/**
This funciton 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 Height;
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;
Height = BootMenuData->MenuScreen.Height;
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 funciton 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 requried 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 requried 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 requried 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;
}