/** @file | |
This library is only intended to be used by PlatformBootManagerLib | |
to show progress bar and LOGO. | |
Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> | |
Copyright (c) 2016, Microsoft Corporation<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Uefi.h> | |
#include <Protocol/GraphicsOutput.h> | |
#include <Protocol/SimpleTextOut.h> | |
#include <Protocol/PlatformLogo.h> | |
#include <Protocol/UgaDraw.h> | |
#include <Protocol/BootLogo.h> | |
#include <Protocol/BootLogo2.h> | |
#include <Library/BaseLib.h> | |
#include <Library/UefiLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/PcdLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/DebugLib.h> | |
/** | |
Show LOGO returned from Edkii Platform Logo protocol on all consoles. | |
@retval EFI_SUCCESS Logo was displayed. | |
@retval EFI_UNSUPPORTED Logo was not found or cannot be displayed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
BootLogoEnableLogo ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
EDKII_PLATFORM_LOGO_PROTOCOL *PlatformLogo; | |
EDKII_PLATFORM_LOGO_DISPLAY_ATTRIBUTE Attribute; | |
INTN OffsetX; | |
INTN OffsetY; | |
UINT32 SizeOfX; | |
UINT32 SizeOfY; | |
INTN DestX; | |
INTN DestY; | |
UINT32 Instance; | |
EFI_IMAGE_INPUT Image; | |
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; | |
EFI_UGA_DRAW_PROTOCOL *UgaDraw; | |
UINT32 ColorDepth; | |
UINT32 RefreshRate; | |
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; | |
EFI_BOOT_LOGO_PROTOCOL *BootLogo; | |
EDKII_BOOT_LOGO2_PROTOCOL *BootLogo2; | |
UINTN NumberOfLogos; | |
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *LogoBlt; | |
UINTN LogoDestX; | |
UINTN LogoDestY; | |
UINTN LogoHeight; | |
UINTN LogoWidth; | |
UINTN NewDestX; | |
UINTN NewDestY; | |
UINTN BufferSize; | |
Status = gBS->LocateProtocol (&gEdkiiPlatformLogoProtocolGuid, NULL, (VOID **)&PlatformLogo); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
UgaDraw = NULL; | |
// | |
// Try to open GOP first | |
// | |
Status = gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput); | |
if (EFI_ERROR (Status) && FeaturePcdGet (PcdUgaConsumeSupport)) { | |
GraphicsOutput = NULL; | |
// | |
// Open GOP failed, try to open UGA | |
// | |
Status = gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiUgaDrawProtocolGuid, (VOID **)&UgaDraw); | |
if (EFI_ERROR (Status)) { | |
UgaDraw = NULL; | |
} | |
} | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Try to open Boot Logo Protocol. | |
// | |
Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **)&BootLogo); | |
if (EFI_ERROR (Status)) { | |
BootLogo = NULL; | |
} | |
// | |
// Try to open Boot Logo 2 Protocol. | |
// | |
Status = gBS->LocateProtocol (&gEdkiiBootLogo2ProtocolGuid, NULL, (VOID **)&BootLogo2); | |
if (EFI_ERROR (Status)) { | |
BootLogo2 = NULL; | |
} | |
// | |
// Erase Cursor from screen | |
// | |
gST->ConOut->EnableCursor (gST->ConOut, FALSE); | |
if (GraphicsOutput != NULL) { | |
SizeOfX = GraphicsOutput->Mode->Info->HorizontalResolution; | |
SizeOfY = GraphicsOutput->Mode->Info->VerticalResolution; | |
} else { | |
ASSERT (UgaDraw != NULL); | |
Status = UgaDraw->GetMode (UgaDraw, &SizeOfX, &SizeOfY, &ColorDepth, &RefreshRate); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
} | |
Blt = NULL; | |
NumberOfLogos = 0; | |
LogoDestX = 0; | |
LogoDestY = 0; | |
LogoHeight = 0; | |
LogoWidth = 0; | |
NewDestX = 0; | |
NewDestY = 0; | |
Instance = 0; | |
DestX = 0; | |
DestY = 0; | |
while (TRUE) { | |
// | |
// Get image from PlatformLogo protocol. | |
// | |
Status = PlatformLogo->GetImage ( | |
PlatformLogo, | |
&Instance, | |
&Image, | |
&Attribute, | |
&OffsetX, | |
&OffsetY | |
); | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
if (Blt != NULL) { | |
FreePool (Blt); | |
} | |
Blt = Image.Bitmap; | |
// | |
// Calculate the display position according to Attribute. | |
// | |
switch (Attribute) { | |
case EdkiiPlatformLogoDisplayAttributeLeftTop: | |
DestX = 0; | |
DestY = 0; | |
break; | |
case EdkiiPlatformLogoDisplayAttributeCenterTop: | |
DestX = (SizeOfX - Image.Width) / 2; | |
DestY = 0; | |
break; | |
case EdkiiPlatformLogoDisplayAttributeRightTop: | |
DestX = SizeOfX - Image.Width; | |
DestY = 0; | |
break; | |
case EdkiiPlatformLogoDisplayAttributeCenterLeft: | |
DestX = 0; | |
DestY = (SizeOfY - Image.Height) / 2; | |
break; | |
case EdkiiPlatformLogoDisplayAttributeCenter: | |
DestX = (SizeOfX - Image.Width) / 2; | |
DestY = (SizeOfY - Image.Height) / 2; | |
break; | |
case EdkiiPlatformLogoDisplayAttributeCenterRight: | |
DestX = SizeOfX - Image.Width; | |
DestY = (SizeOfY - Image.Height) / 2; | |
break; | |
case EdkiiPlatformLogoDisplayAttributeLeftBottom: | |
DestX = 0; | |
DestY = SizeOfY - Image.Height; | |
break; | |
case EdkiiPlatformLogoDisplayAttributeCenterBottom: | |
DestX = (SizeOfX - Image.Width) / 2; | |
DestY = SizeOfY - Image.Height; | |
break; | |
case EdkiiPlatformLogoDisplayAttributeRightBottom: | |
DestX = SizeOfX - Image.Width; | |
DestY = SizeOfY - Image.Height; | |
break; | |
default: | |
ASSERT (FALSE); | |
continue; | |
break; | |
} | |
DestX += OffsetX; | |
DestY += OffsetY; | |
if ((DestX >= 0) && (DestY >= 0)) { | |
if (GraphicsOutput != NULL) { | |
Status = GraphicsOutput->Blt ( | |
GraphicsOutput, | |
Blt, | |
EfiBltBufferToVideo, | |
0, | |
0, | |
(UINTN)DestX, | |
(UINTN)DestY, | |
Image.Width, | |
Image.Height, | |
Image.Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) | |
); | |
} else { | |
ASSERT (UgaDraw != NULL); | |
Status = UgaDraw->Blt ( | |
UgaDraw, | |
(EFI_UGA_PIXEL *)Blt, | |
EfiUgaBltBufferToVideo, | |
0, | |
0, | |
(UINTN)DestX, | |
(UINTN)DestY, | |
Image.Width, | |
Image.Height, | |
Image.Width * sizeof (EFI_UGA_PIXEL) | |
); | |
} | |
// | |
// Report displayed Logo information. | |
// | |
if (!EFI_ERROR (Status)) { | |
NumberOfLogos++; | |
if (NumberOfLogos == 1) { | |
// | |
// The first Logo. | |
// | |
LogoDestX = (UINTN)DestX; | |
LogoDestY = (UINTN)DestY; | |
LogoWidth = Image.Width; | |
LogoHeight = Image.Height; | |
} else { | |
// | |
// Merge new logo with old one. | |
// | |
NewDestX = MIN ((UINTN)DestX, LogoDestX); | |
NewDestY = MIN ((UINTN)DestY, LogoDestY); | |
LogoWidth = MAX ((UINTN)DestX + Image.Width, LogoDestX + LogoWidth) - NewDestX; | |
LogoHeight = MAX ((UINTN)DestY + Image.Height, LogoDestY + LogoHeight) - NewDestY; | |
LogoDestX = NewDestX; | |
LogoDestY = NewDestY; | |
} | |
} | |
} | |
} | |
if (((BootLogo == NULL) && (BootLogo2 == NULL)) || (NumberOfLogos == 0)) { | |
// | |
// No logo displayed. | |
// | |
if (Blt != NULL) { | |
FreePool (Blt); | |
} | |
return Status; | |
} | |
// | |
// Advertise displayed Logo information. | |
// | |
if (NumberOfLogos == 1) { | |
// | |
// Only one logo displayed, use its Blt buffer directly for BootLogo protocol. | |
// | |
LogoBlt = Blt; | |
Status = EFI_SUCCESS; | |
} else { | |
// | |
// More than one Logo displayed, get merged BltBuffer using VideoToBuffer operation. | |
// | |
if (Blt != NULL) { | |
FreePool (Blt); | |
} | |
// | |
// Ensure the LogoHeight * LogoWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) doesn't overflow | |
// | |
if (LogoHeight > MAX_UINTN / LogoWidth / sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) { | |
return EFI_UNSUPPORTED; | |
} | |
BufferSize = LogoWidth * LogoHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); | |
LogoBlt = AllocatePool (BufferSize); | |
if (LogoBlt == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
if (GraphicsOutput != NULL) { | |
Status = GraphicsOutput->Blt ( | |
GraphicsOutput, | |
LogoBlt, | |
EfiBltVideoToBltBuffer, | |
LogoDestX, | |
LogoDestY, | |
0, | |
0, | |
LogoWidth, | |
LogoHeight, | |
LogoWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) | |
); | |
} else { | |
Status = UgaDraw->Blt ( | |
UgaDraw, | |
(EFI_UGA_PIXEL *)LogoBlt, | |
EfiUgaVideoToBltBuffer, | |
LogoDestX, | |
LogoDestY, | |
0, | |
0, | |
LogoWidth, | |
LogoHeight, | |
LogoWidth * sizeof (EFI_UGA_PIXEL) | |
); | |
} | |
} | |
if (!EFI_ERROR (Status)) { | |
// | |
// Attempt to register logo with Boot Logo 2 Protocol first | |
// | |
if (BootLogo2 != NULL) { | |
Status = BootLogo2->SetBootLogo (BootLogo2, LogoBlt, LogoDestX, LogoDestY, LogoWidth, LogoHeight); | |
} | |
// | |
// If Boot Logo 2 Protocol is not available or registration with Boot Logo 2 | |
// Protocol failed, then attempt to register logo with Boot Logo Protocol | |
// | |
if (EFI_ERROR (Status) && (BootLogo != NULL)) { | |
Status = BootLogo->SetBootLogo (BootLogo, LogoBlt, LogoDestX, LogoDestY, LogoWidth, LogoHeight); | |
} | |
// | |
// Status of this function is EFI_SUCCESS even if registration with Boot | |
// Logo 2 Protocol or Boot Logo Protocol fails. | |
// | |
Status = EFI_SUCCESS; | |
} | |
FreePool (LogoBlt); | |
return Status; | |
} | |
/** | |
Use SystemTable Conout to turn on video based Simple Text Out consoles. The | |
Simple Text Out screens will now be synced up with all non video output devices | |
@retval EFI_SUCCESS UGA devices are back in text mode and synced up. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
BootLogoDisableLogo ( | |
VOID | |
) | |
{ | |
// | |
// Enable Cursor on Screen | |
// | |
gST->ConOut->EnableCursor (gST->ConOut, TRUE); | |
return EFI_SUCCESS; | |
} | |
/** | |
Update progress bar with title above it. It only works in Graphics mode. | |
@param TitleForeground Foreground color for Title. | |
@param TitleBackground Background color for Title. | |
@param Title Title above progress bar. | |
@param ProgressColor Progress bar color. | |
@param Progress Progress (0-100) | |
@param PreviousValue The previous value of the progress. | |
@retval EFI_STATUS Success update the progress bar | |
**/ | |
EFI_STATUS | |
EFIAPI | |
BootLogoUpdateProgress ( | |
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL TitleForeground, | |
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL TitleBackground, | |
IN CHAR16 *Title, | |
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL ProgressColor, | |
IN UINTN Progress, | |
IN UINTN PreviousValue | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; | |
EFI_UGA_DRAW_PROTOCOL *UgaDraw; | |
UINT32 SizeOfX; | |
UINT32 SizeOfY; | |
UINT32 ColorDepth; | |
UINT32 RefreshRate; | |
EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color; | |
UINTN BlockHeight; | |
UINTN BlockWidth; | |
UINTN BlockNum; | |
UINTN PosX; | |
UINTN PosY; | |
UINTN Index; | |
if (Progress > 100) { | |
return EFI_INVALID_PARAMETER; | |
} | |
UgaDraw = NULL; | |
Status = gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput); | |
if (EFI_ERROR (Status) && FeaturePcdGet (PcdUgaConsumeSupport)) { | |
GraphicsOutput = NULL; | |
Status = gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiUgaDrawProtocolGuid, (VOID **)&UgaDraw); | |
if (EFI_ERROR (Status)) { | |
UgaDraw = NULL; | |
} | |
} | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
SizeOfX = 0; | |
SizeOfY = 0; | |
if (GraphicsOutput != NULL) { | |
SizeOfX = GraphicsOutput->Mode->Info->HorizontalResolution; | |
SizeOfY = GraphicsOutput->Mode->Info->VerticalResolution; | |
} else if (UgaDraw != NULL) { | |
Status = UgaDraw->GetMode ( | |
UgaDraw, | |
&SizeOfX, | |
&SizeOfY, | |
&ColorDepth, | |
&RefreshRate | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
} else { | |
return EFI_UNSUPPORTED; | |
} | |
BlockWidth = SizeOfX / 100; | |
BlockHeight = SizeOfY / 50; | |
BlockNum = Progress; | |
PosX = 0; | |
PosY = SizeOfY * 48 / 50; | |
if (BlockNum == 0) { | |
// | |
// Clear progress area | |
// | |
SetMem (&Color, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0x0); | |
if (GraphicsOutput != NULL) { | |
Status = GraphicsOutput->Blt ( | |
GraphicsOutput, | |
&Color, | |
EfiBltVideoFill, | |
0, | |
0, | |
0, | |
PosY - EFI_GLYPH_HEIGHT - 1, | |
SizeOfX, | |
SizeOfY - (PosY - EFI_GLYPH_HEIGHT - 1), | |
SizeOfX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) | |
); | |
} else if (FeaturePcdGet (PcdUgaConsumeSupport)) { | |
Status = UgaDraw->Blt ( | |
UgaDraw, | |
(EFI_UGA_PIXEL *)&Color, | |
EfiUgaVideoFill, | |
0, | |
0, | |
0, | |
PosY - EFI_GLYPH_HEIGHT - 1, | |
SizeOfX, | |
SizeOfY - (PosY - EFI_GLYPH_HEIGHT - 1), | |
SizeOfX * sizeof (EFI_UGA_PIXEL) | |
); | |
} else { | |
return EFI_UNSUPPORTED; | |
} | |
} | |
// | |
// Show progress by drawing blocks | |
// | |
for (Index = PreviousValue; Index < BlockNum; Index++) { | |
PosX = Index * BlockWidth; | |
if (GraphicsOutput != NULL) { | |
Status = GraphicsOutput->Blt ( | |
GraphicsOutput, | |
&ProgressColor, | |
EfiBltVideoFill, | |
0, | |
0, | |
PosX, | |
PosY, | |
BlockWidth - 1, | |
BlockHeight, | |
(BlockWidth) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) | |
); | |
} else if (FeaturePcdGet (PcdUgaConsumeSupport)) { | |
Status = UgaDraw->Blt ( | |
UgaDraw, | |
(EFI_UGA_PIXEL *)&ProgressColor, | |
EfiUgaVideoFill, | |
0, | |
0, | |
PosX, | |
PosY, | |
BlockWidth - 1, | |
BlockHeight, | |
(BlockWidth) * sizeof (EFI_UGA_PIXEL) | |
); | |
} else { | |
return EFI_UNSUPPORTED; | |
} | |
} | |
PrintXY ( | |
(SizeOfX - StrLen (Title) * EFI_GLYPH_WIDTH) / 2, | |
PosY - EFI_GLYPH_HEIGHT - 1, | |
&TitleForeground, | |
&TitleBackground, | |
Title | |
); | |
return EFI_SUCCESS; | |
} |