blob: eb7f8b9e3ed6facc8fa8147959fea7a782f68af2 [file] [log] [blame]
/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/DeviceInfo.h>
#include <Library/DrawUI.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/MenuKeysDetection.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UnlockMenu.h>
#include <Library/UpdateDeviceTree.h>
#include <Library/VerifiedBoot.h>
#include <Library/VerifiedBootMenu.h>
#include <Library/BootLinux.h>
#include <Protocol/EFIVerifiedBoot.h>
#include <Uefi.h>
#if VERIFIED_BOOT || VERIFIED_BOOT_2
#define FINGERPRINT_LINE_LEN 16
#define FINGERPRINT_FORMATED_LINE_LEN FINGERPRINT_LINE_LEN + 5
#define VERIFIED_BOOT_OPTION_NUM 5
STATIC OPTION_MENU_INFO gMenuInfo;
typedef struct {
MENU_MSG_INFO WarningTitle;
MENU_MSG_INFO WarningMsg;
MENU_MSG_INFO UrlMsg;
MENU_MSG_INFO NoticeMsg;
MENU_MSG_INFO Fingerprint;
} WARNING_MENU_MSG_INFO;
STATIC WARNING_MENU_MSG_INFO mMenuMsgInfo[] = {
[DISPLAY_MENU_YELLOW] =
{{{"<!>"},
BIG_FACTOR,
BGR_YELLOW,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"\n\nYour device has loaded a different operating system\n\n"
"Visit this link on another device:\n"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"g.co/ABH\n\n\n"},
COMMON_FACTOR,
BGR_YELLOW,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"PRESS POWER KEY TO PAUSE BOOT\n\n\n\n"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
ALIGN_LEFT,
0,
NOACTION},
{{""},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
COMMON,
0,
NOACTION}},
[DISPLAY_MENU_ORANGE] =
{{{"<!>"},
BIG_FACTOR,
BGR_ORANGE,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"\n\nThe boot loader is unlocked and software integrity cannot "
"be guaranteed. Any data stored on the device may be available "
"to attackers. "
"Do not store any sensitive data on the device.\n\n"
"Visit this link on another device:\n"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"g.co/ABH\n\n\n"},
COMMON_FACTOR,
BGR_ORANGE,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"PRESS POWER KEY TO PAUSE BOOT\n\n\n\n"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
ALIGN_LEFT,
0,
NOACTION},
{{""},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
COMMON,
0,
NOACTION}},
[DISPLAY_MENU_RED] =
{{{"<!>"},
BIG_FACTOR,
BGR_RED,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"\n\nYour device is corrupt. It can't be trusted and will not "
"boot\n\n"
"Visit this link on another device:\n"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"g.co/ABH\n\n\n"},
COMMON_FACTOR,
BGR_RED,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{""},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
COMMON,
0,
NOACTION}},
[DISPLAY_MENU_EIO] =
{{{"<!>"},
BIG_FACTOR,
BGR_RED,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"\n\nYour device is corrupt. It can't be trusted and may "
"not work properly\n\n"
"Visit this link on another device:\n"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"g.co/ABH\n\n\n"},
COMMON_FACTOR,
BGR_RED,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"PRESS POWER KEY TO CONTINUE\n\n\n\n"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
ALIGN_LEFT,
0,
NOACTION},
{{""},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
COMMON,
0,
NOACTION}},
};
STATIC MENU_MSG_INFO mOptionMenuMsgInfo[] = {
{{"Options menu:\n"},
BIG_FACTOR,
BGR_WHITE,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"\nPress volume key to select, and press power key to select\n\n"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
COMMON,
0,
NOACTION},
{{"____________________"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
LINEATION,
0,
NOACTION},
{{"Power off"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
OPTION_ITEM,
0,
POWEROFF},
{{"____________________"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
LINEATION,
0,
NOACTION},
{{"Restart"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
OPTION_ITEM,
0,
RESTART},
{{"____________________"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
LINEATION,
0,
NOACTION},
{{"Recovery"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
OPTION_ITEM,
0,
RECOVER},
{{"____________________"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
LINEATION,
0,
NOACTION},
{{"Fastboot"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
OPTION_ITEM,
0,
FASTBOOT},
{{"____________________"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
LINEATION,
0,
NOACTION},
{{"Back to previous page"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
OPTION_ITEM,
0,
BACK},
{{"____________________"},
COMMON_FACTOR,
BGR_WHITE,
BGR_BLACK,
LINEATION,
0,
NOACTION},
};
/**
Convert UINT8 array to a CHAR8 hex array.
The caller of the function should allocate memory properly.
Size of target should be at least twice than the source size (len).
@param[out] *target Pointer to the output array
@param[in] *source Pointer to the source array
@param[in] len Size of the source array
**/
STATIC VOID
GetHexString (CHAR8 *Target, UINT8 *Source, UINTN Len)
{
UINTN TargetIndex = 0;
UINTN SourceIndex = 0;
UINTN TargetLen = Len * 2;
CHAR8 HexBuf[3];
for (TargetIndex = 0; TargetIndex < TargetLen;
TargetIndex = TargetIndex + 2) {
AsciiSPrint (HexBuf, sizeof (HexBuf), "%02x", Source[SourceIndex]);
gBS->CopyMem (Target + TargetIndex, HexBuf, 2);
SourceIndex++;
}
}
/**
Construct display output array.
The caller should allocate the buffer properly before invoking this function.
The assumption is display output and finger print have larger size than 22 and
16.
The target array starts with string "ID: " following with the fingerprint.
Each 16 charcaters of fingerprint are shown in one line.
If fingerprint size is too big, it only copies the number of characters
that matches the output size.
Eaxmple output:
ID: 3F957EBAD2EE02F2
CD23ED905C51913D
4E9AAA2C5A4A1AE8
0F9D6BF727593F14
@param[out] *target Pointer to the output array
@param[in] target_len Size of output
@param[in] *source Pointer to the input array
@param[in] source_len Size of input
**/
STATIC VOID
GetDisplayOutPut (CHAR8 *Target,
UINTN TargetLen,
CHAR8 *Source,
UINTN SourceLen)
{
UINTN LastLineCharsCount = 0;
UINTN TargetIndex = 0;
UINTN SourceIndex = 0;
UINTN LineNum = 0;
UINTN FinalLen = 0;
/* First line starts with 4 characters of "ID: ",
other lines start with 4 spaces to make the length of each line 21
*/
CONST CHAR8 Id[] = "ID: ";
CONST CHAR8 StartlineSpace[] = " ";
/* Each line contains 21 characters (4 spaces in the beginning),
16 characters from fingerprint, one character for endline */
UINTN NumberOfLines = SourceLen / FINGERPRINT_LINE_LEN +
((SourceLen % FINGERPRINT_LINE_LEN == 0 ? 0 : 1));
/* each line contains 4 spaces at the beginning and one endline
character at the end */
FinalLen = (AsciiStrLen (Id) + 1) * (NumberOfLines) + SourceLen;
/* if final size is bigger that display output size,
reduce the numbe of lines,
one character is needed for NULL character */
while (FinalLen > TargetLen - 1) {
NumberOfLines--;
FinalLen = FinalLen - FINGERPRINT_FORMATED_LINE_LEN;
SourceLen = SourceLen - FINGERPRINT_LINE_LEN;
}
for (LineNum = 0; LineNum < NumberOfLines; LineNum++) {
if (LineNum == 0) {
gBS->CopyMem (Target + TargetIndex, (VOID *)Id, AsciiStrLen (Id));
} else {
gBS->CopyMem (Target + TargetIndex, (VOID *)StartlineSpace,
AsciiStrLen (StartlineSpace));
}
TargetIndex = TargetIndex + AsciiStrLen (Id);
if ((SourceLen - SourceIndex) >= FINGERPRINT_LINE_LEN) {
gBS->CopyMem (Target + TargetIndex, Source + SourceIndex,
FINGERPRINT_LINE_LEN);
TargetIndex = TargetIndex + FINGERPRINT_LINE_LEN;
SourceIndex = SourceIndex + FINGERPRINT_LINE_LEN;
} else {
LastLineCharsCount = SourceLen % FINGERPRINT_LINE_LEN;
gBS->CopyMem (Target + TargetIndex, Source + SourceIndex,
LastLineCharsCount);
TargetIndex = TargetIndex + LastLineCharsCount;
SourceIndex = SourceIndex + LastLineCharsCount;
}
Target[TargetIndex] = '\n';
TargetIndex = TargetIndex + 1;
}
/* NULL terminat the target array */
Target[TargetIndex] = '\0';
}
/**
Draw the verified boot option menu
@param[out] OptionMenuInfo The option info
@retval EFI_SUCCESS The entry point is executed successfully.
@retval other Some error occurs when executing this entry point.
**/
EFI_STATUS
VerifiedBootOptionMenuShowScreen (OPTION_MENU_INFO *OptionMenuInfo)
{
EFI_STATUS Status = EFI_SUCCESS;
UINT32 Location = 0;
UINT32 Height = 0;
UINT32 i = 0;
UINT32 j = 0;
/* Clear the screen before launch the verified boot option menu */
gST->ConOut->ClearScreen (gST->ConOut);
ZeroMem (&OptionMenuInfo->Info, sizeof (MENU_OPTION_ITEM_INFO));
OptionMenuInfo->Info.MsgInfo = mOptionMenuMsgInfo;
for (i = 0; i < ARRAY_SIZE (mOptionMenuMsgInfo); i++) {
if (OptionMenuInfo->Info.MsgInfo[i].Attribute == OPTION_ITEM) {
if (j < VERIFIED_BOOT_OPTION_NUM) {
OptionMenuInfo->Info.OptionItems[j] = i;
j++;
}
}
OptionMenuInfo->Info.MsgInfo[i].Location = Location;
Status = DrawMenu (&OptionMenuInfo->Info.MsgInfo[i], &Height);
if (Status != EFI_SUCCESS)
return Status;
Location += Height;
}
OptionMenuInfo->Info.MenuType = DISPLAY_MENU_MORE_OPTION;
OptionMenuInfo->Info.OptionNum = VERIFIED_BOOT_OPTION_NUM;
/* Initialize the option index */
OptionMenuInfo->Info.OptionIndex = VERIFIED_BOOT_OPTION_NUM;
return Status;
}
/**
Update the verified boot menu
@param[out] OptionMenuInfo The option info
@retval EFI_SUCCESS The entry point is executed successfully.
@retval other Some error occurs when executing this entry point.
**/
EFI_STATUS
VerifiedBootMenuUpdateShowScreen (OPTION_MENU_INFO *OptionMenuInfo)
{
EFI_STATUS Status = EFI_SUCCESS;
UINT32 Location = OptionMenuInfo->Info.MsgInfo->Location;
UINT32 Height = 0;
MENU_MSG_INFO *MsgStrInfo = NULL;
MsgStrInfo = AllocateZeroPool (sizeof (MENU_MSG_INFO));
if (MsgStrInfo == NULL) {
DEBUG ((EFI_D_ERROR, "Failed to allocate zero pool.\n"));
Status = EFI_OUT_OF_RESOURCES;
goto Exit;
}
SetMenuMsgInfo (MsgStrInfo, "PRESS POWER KEY TO CONTINUE", COMMON_FACTOR,
BGR_WHITE, BGR_BLACK, ALIGN_LEFT, Location, 0);
Status = DrawMenu (MsgStrInfo, &Height);
if (Status != EFI_SUCCESS)
goto Exit;
OptionMenuInfo->Info.TimeoutTime = 60;
OptionMenuInfo->Info.MsgInfo->Action = POWEROFF;
Exit:
if (MsgStrInfo) {
FreePool (MsgStrInfo);
MsgStrInfo = NULL;
}
return Status;
}
/**
Draw the verified boot menu
@param[in] Type The warning menu type
@param[out] OptionMenuInfo The option info
@retval EFI_SUCCESS The entry point is executed successfully.
@retval other Some error occurs when executing this entry point.
**/
EFI_STATUS
VerifiedBootMenuShowScreen (OPTION_MENU_INFO *OptionMenuInfo,
DISPLAY_MENU_TYPE Type)
{
EFI_STATUS Status = EFI_SUCCESS;
UINT32 Location = 0;
UINT32 Height = 0;
ZeroMem (&OptionMenuInfo->Info, sizeof (MENU_OPTION_ITEM_INFO));
mMenuMsgInfo[Type].WarningTitle.Location = Location;
Status = DrawMenu (&mMenuMsgInfo[Type].WarningTitle, &Height);
if (Status != EFI_SUCCESS)
return Status;
Location += Height;
mMenuMsgInfo[Type].WarningMsg.Location = Location;
Status = DrawMenu (&mMenuMsgInfo[Type].WarningMsg, &Height);
if (Status != EFI_SUCCESS)
return Status;
Location += Height;
mMenuMsgInfo[Type].UrlMsg.Location = Location;
Status = DrawMenu (&mMenuMsgInfo[Type].UrlMsg, &Height);
if (Status != EFI_SUCCESS)
return Status;
Location += Height;
OptionMenuInfo->Info.MsgInfo = &mMenuMsgInfo[Type].NoticeMsg;
OptionMenuInfo->Info.MsgInfo->Location = Location;
Status = DrawMenu (OptionMenuInfo->Info.MsgInfo, &Height);
if (Status != EFI_SUCCESS)
return Status;
Location += Height;
if (Type == DISPLAY_MENU_YELLOW) {
UINT8 FingerPrint[MAX_MSG_SIZE];
UINTN FingerPrintLen = 0;
mMenuMsgInfo[Type].Fingerprint.Location = Location;
Status = GetCertFingerPrint (FingerPrint, ARRAY_SIZE (FingerPrint),
&FingerPrintLen);
if (Status == EFI_SUCCESS) {
UINTN DisplayStrLen = 0;
CHAR8 *DisplayStr = NULL;
/* Each bytes needs two characters to be shown on display */
DisplayStrLen = FingerPrintLen * 2;
DisplayStr = AllocateZeroPool (DisplayStrLen);
if (DisplayStr == NULL) {
return EFI_OUT_OF_RESOURCES;
}
/* Convert the fingerprint to a charcater string */
GetHexString (DisplayStr, FingerPrint, FingerPrintLen);
/* Save fingerprint in a formated string for display */
GetDisplayOutPut (mMenuMsgInfo[Type].Fingerprint.Msg, MAX_MSG_SIZE,
DisplayStr, DisplayStrLen);
FreePool (DisplayStr);
DisplayStr = NULL;
} else {
AsciiSPrint (mMenuMsgInfo[Type].Fingerprint.Msg, MAX_MSG_SIZE, "ID: %a\n",
"unsupported");
}
Status = DrawMenu (&mMenuMsgInfo[Type].Fingerprint, &Height);
if (Status != EFI_SUCCESS)
return Status;
}
OptionMenuInfo->Info.MenuType = Type;
/* Initialize the time out time */
if (Type == DISPLAY_MENU_RED || Type == DISPLAY_MENU_EIO)
OptionMenuInfo->Info.TimeoutTime = 30;
else
OptionMenuInfo->Info.TimeoutTime = 10;
return Status;
}
/**
Draw the verified boot menu and start to detect the key's status
@param[in] Type The type of the warning menu:
[DISPLAY_MENU_YELLOW] ----- Yellow warning menu
[DISPLAY_MENU_ORANGE] ----- Orange warning menu
[DISPLAY_MENU_RED] ----- Red warning menu
[DISPLAY_MENU_EIO] ----- dm-verity EIO warning menu
@retval EFI_SUCCESS The entry point is executed successfully.
@retval other Some error occurs when executing this entry point.
**/
EFI_STATUS
DisplayVerifiedBootMenu (DISPLAY_MENU_TYPE Type)
{
EFI_STATUS Status = EFI_SUCCESS;
OPTION_MENU_INFO *OptionMenuInfo;
if (IsEnableDisplayMenuFlagSupported ()) {
OptionMenuInfo = &gMenuInfo;
DrawMenuInit ();
/* Initialize the last_msg_type */
OptionMenuInfo->LastMenuType = OptionMenuInfo->Info.MenuType;
Status = VerifiedBootMenuShowScreen (OptionMenuInfo, Type);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Unable to show verified menu on screen: %r\n",
Status));
return Status;
}
if (Type != DISPLAY_MENU_RED) {
MenuKeysDetectionInit (OptionMenuInfo);
}
} else {
DEBUG ((EFI_D_INFO, "Display menu is not enabled!\n"));
Status = EFI_NOT_STARTED;
}
return Status;
}
#endif