blob: 759078d7eaba8c75d33898cc40bbe1b334959847 [file] [log] [blame]
/* Copyright (c) 2017-2021, 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 "VerifiedBoot.h"
#include "BootLinux.h"
#include "BootImage.h"
#include "KeymasterClient.h"
#include "libavb/libavb.h"
#include <FastbootLib/FastbootCmds.h>
#include <Library/MenuKeysDetection.h>
#include <Library/VerifiedBootMenu.h>
#include <Library/LEOEMCertificate.h>
STATIC CONST CHAR8 *VerityMode = " androidboot.veritymode=";
STATIC CONST CHAR8 *VerifiedState = " androidboot.verifiedbootstate=";
STATIC CONST CHAR8 *KeymasterLoadState = " androidboot.keymaster=1";
STATIC CONST CHAR8 *DmVerityCmd = " root=/dev/dm-0 dm=\"system none ro,0 1 "
"android-verity";
STATIC CONST CHAR8 *Space = " ";
#define MAX_NUM_REQ_PARTITION 8
#define MAX_PROPERTY_SIZE 10
static CHAR8 *avb_verify_partition_name[] = {
"boot",
"dtbo",
"vbmeta",
"recovery",
"vendor_boot"
};
STATIC struct verified_boot_verity_mode VbVm[] = {
{FALSE, "logging"},
{TRUE, "enforcing"},
};
STATIC struct verified_boot_state_name VbSn[] = {
{GREEN, "green"},
{ORANGE, "orange"},
{YELLOW, "yellow"},
{RED, "red"},
};
struct boolean_string {
BOOLEAN value;
CHAR8 *name;
};
STATIC struct boolean_string BooleanString[] = {
{FALSE, "false"},
{TRUE, "true"}
};
typedef struct {
AvbOps *Ops;
AvbSlotVerifyData *SlotData;
} VB2Data;
UINT32
GetAVBVersion ()
{
#if VERIFIED_BOOT_LE
return AVB_LE;
#elif VERIFIED_BOOT_2
return AVB_2;
#elif VERIFIED_BOOT
return AVB_1;
#else
return NO_AVB;
#endif
}
BOOLEAN
VerifiedBootEnbled ()
{
return (GetAVBVersion () > NO_AVB);
}
STATIC EFI_STATUS
AppendVBCmdLine (BootInfo *Info, CONST CHAR8 *Src)
{
EFI_STATUS Status = EFI_SUCCESS;
INT32 SrcLen = AsciiStrLen (Src);
CHAR8 *Dst = Info->VBCmdLine + Info->VBCmdLineFilledLen;
INT32 DstLen = Info->VBCmdLineLen - Info->VBCmdLineFilledLen;
GUARD (AsciiStrnCatS (Dst, DstLen, Src, SrcLen));
Info->VBCmdLineFilledLen += SrcLen;
return EFI_SUCCESS;
}
STATIC EFI_STATUS
AppendVBCommonCmdLine (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
if (Info->VbIntf->Revision >= QCOM_VERIFIEDBOOT_PROTOCOL_REVISION) {
GUARD (AppendVBCmdLine (Info, VerifiedState));
GUARD (AppendVBCmdLine (Info, VbSn[Info->BootState].name));
}
GUARD (AppendVBCmdLine (Info, KeymasterLoadState));
GUARD (AppendVBCmdLine (Info, Space));
return EFI_SUCCESS;
}
STATIC EFI_STATUS
NoAVBLoadReqImage (BootInfo *Info, VOID **DtboImage,
UINT32 *DtboSize, CHAR16 *Pname, CHAR16 *RequestedPartition)
{
EFI_STATUS Status = EFI_SUCCESS;
Slot CurrentSlot;
CHAR8 *AsciiPname = NULL;
UINT64 PartSize = 0;
AvbIOResult AvbStatus;
AvbOpsUserData *UserData = NULL;
AvbOps *Ops = NULL;
GUARD ( StrnCpyS (Pname,
(UINTN)MAX_GPT_NAME_SIZE,
(CONST CHAR16 *)RequestedPartition,
StrLen (RequestedPartition)));
if (Info->MultiSlotBoot) {
CurrentSlot = GetCurrentSlotSuffix ();
GUARD ( StrnCatS (Pname, MAX_GPT_NAME_SIZE,
CurrentSlot.Suffix, StrLen (CurrentSlot.Suffix)));
}
if (GetPartitionIndex (Pname) == INVALID_PTN) {
Status = EFI_NO_MEDIA;
goto out;
}
/* Get Partition size & compare with MAX supported size
* If Partition size > DTBO_MAX_SIZE_ALLOWED return
* Allocate Partition size memory otherwise
*/
UserData = avb_calloc (sizeof (AvbOpsUserData));
if (UserData == NULL) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AvbOpsUserData\n"));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
Ops = AvbOpsNew (UserData);
if (Ops == NULL) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AvbOps\n"));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
AsciiPname = avb_calloc (MAX_GPT_NAME_SIZE);
if (AsciiPname == NULL) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AsciiPname\n"));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
UnicodeStrToAsciiStr (Pname, AsciiPname);
AvbStatus = Ops->get_size_of_partition (Ops,
AsciiPname,
&PartSize);
if (AvbStatus != AVB_IO_RESULT_OK ||
PartSize == 0) {
DEBUG ((EFI_D_ERROR, "VB: Failed to get partition size "
"(or) DTBO size is too big: 0x%x\n", PartSize));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
if ((AsciiStrStr (AsciiPname, "dtbo")) &&
(PartSize > DTBO_MAX_SIZE_ALLOWED)) {
DEBUG ((EFI_D_ERROR, "DTBO size is too big: 0x%x\n", PartSize));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
DEBUG ((EFI_D_VERBOSE, "VB: Trying to allocate memory "
"for DTBO: 0x%x\n", PartSize));
*DtboSize = (UINT32) PartSize;
*DtboImage = AllocateZeroPool (PartSize);
if (*DtboImage == NULL) {
DEBUG ((EFI_D_ERROR, "VB: Unable to allocate memory for DTBO\n"));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
Status = LoadImageFromPartition (*DtboImage, DtboSize, Pname);
out:
if (Ops != NULL) {
AvbOpsFree (Ops);
}
if (UserData != NULL) {
avb_free (UserData);
}
if (AsciiPname != NULL) {
avb_free (AsciiPname);
}
return Status;
}
STATIC EFI_STATUS
VBCommonInit (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
Info->BootState = RED;
Status = gBS->LocateProtocol (&gEfiQcomVerifiedBootProtocolGuid, NULL,
(VOID **)&(Info->VbIntf));
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Unable to locate VB protocol: %r\n", Status));
return Status;
}
return Status;
}
/*
* Ensure Info->Pname is already updated before this function is called.
* If Command line already has "root=", return TRUE, else FALSE.
*/
STATIC EFI_STATUS
VBAllocateCmdLine (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
/* allocate VB command line*/
Info->VBCmdLine = AllocateZeroPool (DTB_PAD_SIZE);
if (Info->VBCmdLine == NULL) {
DEBUG ((EFI_D_ERROR, "VB CmdLine allocation failed!\n"));
Status = EFI_OUT_OF_RESOURCES;
return Status;
}
Info->VBCmdLineLen = DTB_PAD_SIZE;
Info->VBCmdLineFilledLen = 0;
Info->VBCmdLine[Info->VBCmdLineFilledLen] = '\0';
return Status;
}
STATIC
BOOLEAN
IsRootCmdLineUpdated (BootInfo *Info)
{
CHAR8* ImageCmdLine = NULL;
ImageCmdLine =
(CHAR8*) & (((boot_img_hdr*) (Info->Images[0].ImageBuffer))->cmdline[0]);
ImageCmdLine[BOOT_ARGS_SIZE - 1] = '\0';
if (AsciiStrStr (ImageCmdLine, "root=")) {
return TRUE;
} else {
return FALSE;
}
}
/**
Load Vendor Boot image if the boot image is v3
**/
STATIC EFI_STATUS
NoAVBLoadVendorBootImage (BootInfo *Info)
{
EFI_STATUS Status;
CHAR16 Pname[MAX_GPT_NAME_SIZE];
UINT8 ImgIdx = Info->NumLoadedImages;
Status = NoAVBLoadReqImage (Info,
(VOID **)&(Info->Images[ImgIdx].ImageBuffer),
(UINT32 *)&(Info->Images[ImgIdx].ImageSize),
Pname, (CHAR16 *)L"vendor_boot");
if (Status == EFI_NO_MEDIA) {
DEBUG ((EFI_D_INFO, "No vendor_boot partition is found, Skipping\n"));
if (Info->Images[ImgIdx].ImageBuffer != NULL) {
FreePool (Info->Images[ImgIdx].ImageBuffer);
}
return EFI_SUCCESS;
}
else if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"ERROR: Failed to load vendor_boot from partition: %r\n", Status));
if (Info->Images[ImgIdx].ImageBuffer != NULL) {
goto Err;
}
}
Info-> Images[ImgIdx].Name = AllocateZeroPool (StrLen (Pname) + 1);
if (!Info-> Images[ImgIdx].Name) {
Status = EFI_OUT_OF_RESOURCES;
goto Err;
}
UnicodeStrToAsciiStr (Pname, Info->Images[ImgIdx].Name);
Info-> NumLoadedImages++;
return EFI_SUCCESS;
Err:
FreePool (Info->Images[ImgIdx].ImageBuffer);
return Status;
}
STATIC EFI_STATUS
LoadVendorBootImageHeader (BootInfo *Info,
VOID **VendorImageHdrBuffer,
UINT32 *VendorImageHdrSize)
{
EFI_STATUS Status = EFI_SUCCESS;
CHAR16 Pname[MAX_GPT_NAME_SIZE] = {0};
StrnCpyS (Pname, ARRAY_SIZE (Pname),
(CHAR16 *)L"vendor_boot", StrLen ((CHAR16 *)L"vendor_boot"));
if (Info->MultiSlotBoot) {
GUARD (StrnCatS (Pname, ARRAY_SIZE (Pname),
GetCurrentSlotSuffix ().Suffix,
StrLen (GetCurrentSlotSuffix ().Suffix)));
}
return LoadImageHeader (Pname, VendorImageHdrBuffer, VendorImageHdrSize);
}
STATIC EFI_STATUS
LoadBootImageNoAuth (BootInfo *Info, UINT32 *PageSize, BOOLEAN *FastbootPath)
{
EFI_STATUS Status = EFI_SUCCESS;
VOID *ImageHdrBuffer = NULL;
UINT32 ImageHdrSize = 0;
UINT32 ImageSizeActual = 0;
VOID *VendorImageHdrBuffer = NULL;
UINT32 VendorImageHdrSize = 0;
BOOLEAN BootIntoRecovery = FALSE;
BOOLEAN BootImageLoaded;
/** The Images[0].ImageBuffer would have been loaded with the boot image
* already if we are coming from fastboot boot path. Ignore loading it
* again.
**/
BootImageLoaded = (Info->Images[0].ImageBuffer != NULL) &&
(Info->Images[0].ImageSize > 0);
*FastbootPath = BootImageLoaded;
if (BootImageLoaded) {
ImageHdrBuffer = Info->Images[0].ImageBuffer;
ImageHdrSize = BOOT_IMG_MAX_PAGE_SIZE;
} else {
Status = LoadImageHeader (Info->Pname, &ImageHdrBuffer, &ImageHdrSize);
if (Status != EFI_SUCCESS ||
ImageHdrBuffer == NULL) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to load image header: %r\n", Status));
return Status;
} else if (ImageHdrSize < sizeof (boot_img_hdr)) {
DEBUG ((EFI_D_ERROR,
"ERROR: Invalid image header size: %u\n", ImageHdrSize));
return EFI_BAD_BUFFER_SIZE;
}
BootIntoRecovery = Info->BootIntoRecovery;
}
Info->HeaderVersion = ((boot_img_hdr *)(ImageHdrBuffer))->header_version;
/* Additional vendor_boot image header needs be loaded for header
* versions than 3. Consider both the headers for validation.
*/
if (Info->HeaderVersion >= BOOT_HEADER_VERSION_THREE) {
Status = LoadVendorBootImageHeader (Info, &VendorImageHdrBuffer,
&VendorImageHdrSize);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"ERROR: Failed to load vendor_boot Image header: %r\n", Status));
goto ErrV3;
}
}
/* Add check for boot image header, kernel page size,
* and ensure kernel command line is terminate.
*/
Status = CheckImageHeader (ImageHdrBuffer, ImageHdrSize,
VendorImageHdrBuffer, VendorImageHdrSize,
&ImageSizeActual, PageSize, BootIntoRecovery);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Invalid boot image header:%r\n", Status));
goto Err;
}
if (!BootImageLoaded) {
Status = LoadImage (Info->Pname, (VOID **)&(Info->Images[0].ImageBuffer),
ImageSizeActual, *PageSize);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to load image from partition: %r\n",
Status));
goto Err;
}
Info->Images[0].Name = AllocateZeroPool (StrLen (Info->Pname) + 1);
if (!Info->Images[0].Name) {
Status = EFI_OUT_OF_RESOURCES;
goto ErrImg;
}
UnicodeStrToAsciiStr (Info->Pname, Info->Images[0].Name);
Info->NumLoadedImages = 1;
}
Info->Images[0].ImageSize = ImageSizeActual;
if (Info->HeaderVersion >= BOOT_HEADER_VERSION_THREE) {
Status = NoAVBLoadVendorBootImage (Info);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"ERROR: Failed to load vendor_boot Image : %r\n", Status));
goto ErrImgName;
}
}
return EFI_SUCCESS;
ErrImgName:
if (!BootImageLoaded &&
Info->Images[0].Name) {
FreePool (Info->Images[0].Name);
}
ErrImg:
if (!BootImageLoaded &&
Info->Images[0].ImageBuffer) {
UINT32 ImageSize =
ADD_OF (ROUND_TO_PAGE (ImageSizeActual, (*PageSize - 1)), *PageSize);
FreePages (Info->Images[0].ImageBuffer,
ALIGN_PAGES (ImageSize, ALIGNMENT_MASK_4KB));
}
Err:
if (VendorImageHdrBuffer) {
FreePages (VendorImageHdrBuffer,
ALIGN_PAGES (BOOT_IMG_MAX_PAGE_SIZE, ALIGNMENT_MASK_4KB));
}
ErrV3:
if (!BootImageLoaded &&
ImageHdrBuffer) {
FreePages (ImageHdrBuffer,
ALIGN_PAGES (BOOT_IMG_MAX_PAGE_SIZE, ALIGNMENT_MASK_4KB));
}
return Status;
}
STATIC EFI_STATUS
LoadImageNoAuth (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
CHAR16 Pname[MAX_GPT_NAME_SIZE];
UINTN *ImgIdx = &Info->NumLoadedImages;
UINT32 PageSize = 0;
BOOLEAN FastbootPath;
Status = LoadBootImageNoAuth (Info, &PageSize, &FastbootPath);
if (Status != EFI_SUCCESS) {
return Status;
}
/*load dt overlay when avb is disabled*/
Status = NoAVBLoadReqImage (Info,
(VOID **)&(Info->Images[*ImgIdx].ImageBuffer),
(UINT32 *)&(Info->Images[*ImgIdx].ImageSize),
Pname, (CHAR16 *)L"dtbo");
if (Status == EFI_NO_MEDIA) {
DEBUG ((EFI_D_INFO, "No dtbo partition is found, Skip dtbo\n"));
if (Info->Images[*ImgIdx].ImageBuffer != NULL) {
FreePool (Info->Images[*ImgIdx].ImageBuffer);
}
}
else if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"ERROR: Failed to load dtbo from partition: %r\n", Status));
Status = EFI_LOAD_ERROR;
goto Err;
} else { /* EFI_SUCCESS */
Info-> Images[*ImgIdx].Name = AllocateZeroPool (StrLen (Pname) + 1);
if (!Info->Images[*ImgIdx].Name) {
Status = EFI_OUT_OF_RESOURCES;
goto Err;
}
UnicodeStrToAsciiStr (Pname, Info->Images[*ImgIdx].Name);
++(*ImgIdx);
}
return EFI_SUCCESS;
Err:
/* Free all the Images' memory that was allocated */
for (--(*ImgIdx); *ImgIdx; --(*ImgIdx)) {
if (Info->Images[*ImgIdx].ImageBuffer != NULL) {
FreePool (Info->Images[*ImgIdx].ImageBuffer);
}
if (Info->Images[*ImgIdx].Name != NULL) {
FreePool (Info->Images[*ImgIdx].Name);
}
}
/* Images[0] needs to be freed in a special way as it was allocated
* using AllocPages(). Although, ignore if we are coming from a
* fastboot boot path.
*/
if (FastbootPath)
goto err_out;
if (Info->Images[0].ImageBuffer) {
UINT32 ImageSize =
ADD_OF (ROUND_TO_PAGE (Info->Images[0].ImageSize,
(PageSize - 1)), PageSize);
FreePages (Info->Images[0].ImageBuffer,
ALIGN_PAGES (ImageSize, ALIGNMENT_MASK_4KB));
}
if (Info->Images[0].Name) {
FreePool (Info->Images[0].Name);
}
err_out:
return Status;
}
STATIC EFI_STATUS
LoadImageNoAuthWrapper (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
CHAR8 *SystemPath = NULL;
UINT32 SystemPathLen = 0;
GUARD (VBAllocateCmdLine (Info));
GUARD (LoadImageNoAuth (Info));
if (!IsDynamicPartitionSupport () &&
!IsRootCmdLineUpdated (Info)) {
SystemPathLen = GetSystemPath (&SystemPath, Info);
if (SystemPathLen == 0 || SystemPath == NULL) {
DEBUG ((EFI_D_ERROR, "GetSystemPath failed!\n"));
return EFI_LOAD_ERROR;
}
GUARD (AppendVBCmdLine (Info, SystemPath));
}
return Status;
}
STATIC EFI_STATUS
LoadImageAndAuthVB1 (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
CHAR8 StrPnameAscii[MAX_GPT_NAME_SIZE]; /* partition name starting with
/ and no suffix */
CHAR8 PnameAscii[MAX_GPT_NAME_SIZE];
CHAR8 *SystemPath = NULL;
UINT32 SystemPathLen = 0;
CHAR8 *Temp = NULL;
GUARD (VBCommonInit (Info));
GUARD (VBAllocateCmdLine (Info));
GUARD (LoadImageNoAuth (Info));
device_info_vb_t DevInfo_vb;
DevInfo_vb.is_unlocked = IsUnlocked ();
DevInfo_vb.is_unlock_critical = IsUnlockCritical ();
Status = Info->VbIntf->VBDeviceInit (Info->VbIntf,
(device_info_vb_t *)&DevInfo_vb);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Error during VBDeviceInit: %r\n", Status));
return Status;
}
AsciiStrnCpyS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), "/",
AsciiStrLen ("/"));
UnicodeStrToAsciiStr (Info->Pname, PnameAscii);
if (Info->MultiSlotBoot) {
AsciiStrnCatS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), PnameAscii,
AsciiStrLen (PnameAscii) - (MAX_SLOT_SUFFIX_SZ - 1));
} else {
AsciiStrnCatS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), PnameAscii,
AsciiStrLen (PnameAscii));
}
Status =
Info->VbIntf->VBVerifyImage (Info->VbIntf, (UINT8 *)StrPnameAscii,
(UINT8 *)Info->Images[0].ImageBuffer,
Info->Images[0].ImageSize, &Info->BootState);
if (Status != EFI_SUCCESS || Info->BootState == BOOT_STATE_MAX) {
DEBUG ((EFI_D_ERROR, "VBVerifyImage failed with: %r\n", Status));
return Status;
}
Status = Info->VbIntf->VBSendRot (Info->VbIntf);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Error sending Rot : %r\n", Status));
return Status;
}
if (!IsRootCmdLineUpdated (Info)) {
SystemPathLen = GetSystemPath (&SystemPath, Info);
if (SystemPathLen == 0 || SystemPath == NULL) {
DEBUG ((EFI_D_ERROR, "GetSystemPath failed!\n"));
return EFI_LOAD_ERROR;
}
GUARD (AppendVBCmdLine (Info, DmVerityCmd));
/* Copy only the portion after "root=" in the SystemPath */
Temp = AsciiStrStr (SystemPath, "root=");
if (Temp) {
CopyMem (Temp, SystemPath + AsciiStrLen ("root=") + 1,
SystemPathLen - AsciiStrLen ("root=") - 1);
SystemPath[SystemPathLen - AsciiStrLen ("root=")] = '\0';
}
GUARD (AppendVBCmdLine (Info, SystemPath));
GUARD (AppendVBCmdLine (Info, "\""));
}
GUARD (AppendVBCommonCmdLine (Info));
GUARD (AppendVBCmdLine (Info, VerityMode));
GUARD (AppendVBCmdLine (Info, VbVm[IsEnforcing ()].name));
Info->VBData = NULL;
return Status;
}
STATIC BOOLEAN
ResultShouldContinue (AvbSlotVerifyResult Result)
{
switch (Result) {
case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
return FALSE;
case AVB_SLOT_VERIFY_RESULT_OK:
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
return TRUE;
}
return FALSE;
}
STATIC EFI_STATUS
LEGetImageHash (QcomAsn1x509Protocol *pEfiQcomASN1X509Protocol,
VB_HASH HashAlgorithm,
UINT8 *Img, UINTN ImgSize,
UINT8 *ImgHash, UINTN HashSize)
{
EFI_STATUS Status = EFI_FAILURE;
EFI_GUID *HashAlgorithmGuid;
UINTN DigestSize = 0;
EFI_HASH2_OUTPUT Hash2Output;
EFI_HASH2_PROTOCOL *pEfiHash2Protocol = NULL;
if (pEfiQcomASN1X509Protocol == NULL ||
Img == NULL ||
ImgHash == NULL) {
DEBUG ((EFI_D_ERROR,
"LEGetRSAPublicKeyInfoFromCertificate: Invalid pointer\n"));
return EFI_INVALID_PARAMETER;
}
switch (HashAlgorithm) {
case VB_SHA256:
HashAlgorithmGuid = &gEfiHashAlgorithmSha256Guid;
break;
default:
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: not supported algorithm:%d\n", HashAlgorithm));
Status = EFI_UNSUPPORTED;
goto exit;
}
Status = gBS->LocateProtocol (&gEfiHash2ProtocolGuid,
NULL, (VOID **)&pEfiHash2Protocol);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB:LEGetImageHash: LocateProtocol unsuccessful!Status: %r\n", Status));
goto exit;
}
Status = pEfiHash2Protocol->GetHashSize (pEfiHash2Protocol,
HashAlgorithmGuid, &DigestSize);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: GetHashSize unsuccessful! Status: %r\n", Status));
goto exit;
}
if (HashSize != DigestSize) {
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: Invalid size! HashSize: %d, DigestSize: %d\n"
, HashSize, DigestSize));
Status = EFI_FAILURE;
goto exit;
}
Status = pEfiHash2Protocol->HashInit (pEfiHash2Protocol, HashAlgorithmGuid);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: HashInit unsuccessful! Status: %r\n", Status));
goto exit;
}
Status = pEfiHash2Protocol->HashUpdate (pEfiHash2Protocol, Img, ImgSize);
if (EFI_SUCCESS != Status) {
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: HashUpdate unsuccessful(Img)!Status: %r\n"
, Status));
goto exit;
}
Status = pEfiHash2Protocol->HashFinal (pEfiHash2Protocol, &Hash2Output);
if (EFI_SUCCESS != Status) {
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: HashFinal unsuccessful! Status: %r\n", Status));
goto exit;
}
gBS->CopyMem ((VOID *)ImgHash, (VOID *)&Hash2Output, DigestSize);
Status = EFI_SUCCESS;
exit:
return Status;
}
STATIC EFI_STATUS LEGetRSAPublicKeyInfoFromCertificate (
QcomAsn1x509Protocol *pEfiQcomASN1X509Protocol,
CERTIFICATE *Certificate,
secasn1_data_type *Modulus,
secasn1_data_type *PublicExp,
UINT32 *PaddingType)
{
EFI_STATUS Status = EFI_FAILURE;
RSA RsaKey = {NULL};
if (pEfiQcomASN1X509Protocol == NULL ||
Certificate == NULL ||
Modulus == NULL ||
PublicExp == NULL ||
PaddingType == NULL) {
DEBUG ((EFI_D_ERROR,
"LEGetRSAPublicKeyInfoFromCertificate: Invalid pointer\n"));
return EFI_INVALID_PARAMETER;
}
Status = pEfiQcomASN1X509Protocol->ASN1X509GetRSAFromCert
(pEfiQcomASN1X509Protocol, Certificate, &RsaKey);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: ASN1X509GetRSAFromCert unsuccessful! Status : %r\n", Status));
goto exit;
}
Status = pEfiQcomASN1X509Protocol->ASN1X509GetKeymaterial
(pEfiQcomASN1X509Protocol, &RsaKey, Modulus, PublicExp);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: ASN1X509GetKeymaterial unsuccessful! Status: %r\n", Status));
goto exit;
}
*PaddingType = CE_RSA_PAD_PKCS1_V1_5_SIG;
exit:
return Status;
}
STATIC EFI_STATUS LEVerifyHashWithRSASignature (
UINT8 *ImgHash,
VB_HASH HashAlgorithm,
secasn1_data_type *Modulus,
secasn1_data_type *PublicExp,
UINT32 PaddingType,
CONST UINT8 *SignaturePtr,
UINT32 SignatureLen)
{
EFI_STATUS Status = EFI_FAILURE;
CE_RSA_KEY Key = {0};
BigInt ModulusBi;
BigInt PublicExpBi;
INT32 HashIdx;
INT32 HashLen;
VOID *PaddingInfo = NULL;
QcomSecRsaProtocol *pEfiQcomSecRSAProtocol = NULL;
SetMem (&Key, sizeof (CE_RSA_KEY), 0);
if (ImgHash == NULL ||
Modulus == NULL ||
PublicExp == NULL ||
SignaturePtr == NULL) {
DEBUG ((EFI_D_ERROR, "LEVerifyHashWithRSASignature:Invalid pointer\n"));
return EFI_INVALID_PARAMETER;
}
switch (HashAlgorithm) {
case VB_SHA256:
HashIdx = CE_HASH_IDX_SHA256;
HashLen = VB_SHA256_SIZE;
break;
default:
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: Hash algorithm not supported\n"));
Status = EFI_UNSUPPORTED;
goto exit;
}
Key.N = AllocateZeroPool (sizeof (S_BIGINT));
if (Key.N == NULL) {
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: mem allocation err for Key.N\n"));
goto exit;
}
Key.e = AllocateZeroPool (sizeof (S_BIGINT));
if (Key.e == NULL) {
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: mem allocation err for Key.e\n"));
goto exit;
}
Status = gBS->LocateProtocol (&gEfiQcomSecRSAProtocolGuid,
NULL, (VOID **) &pEfiQcomSecRSAProtocol);
if ( Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: LocateProtocol failed, Status: %r\n", Status));
goto exit;
}
Status = pEfiQcomSecRSAProtocol->SecRSABigIntReadBin (
pEfiQcomSecRSAProtocol, Modulus->data, Modulus->Len, &ModulusBi);
if ( Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: SecRSABigIntReadBin for Modulus failed!"
"Status: %r\n", Status));
goto exit;
}
Status = pEfiQcomSecRSAProtocol->SecRSABigIntReadBin (
pEfiQcomSecRSAProtocol, PublicExp->data, PublicExp->Len, &PublicExpBi);
if ( Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: LEVerifySignature: SecRSABigIntReadBin for"
" Modulus failed! Status: %r\n", Status));
goto exit;
}
Key.N->Bi = ModulusBi;
Key.e->Bi = PublicExpBi;
Key.e->Sign = S_BIGINT_POS;
Key.Type = CE_RSA_KEY_PUBLIC;
Status = pEfiQcomSecRSAProtocol->SecRSAVerifySig (pEfiQcomSecRSAProtocol,
&Key, PaddingType,
PaddingInfo, HashIdx,
ImgHash, HashLen, (UINT8*)SignaturePtr, SignatureLen);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: SecRSAVerifySig failed! Status: %r\n", Status));
goto exit;
}
DEBUG ((EFI_D_VERBOSE, "VB: LEVerifySignature: SecRSAVerifySig success!"
" Status: %r\n", Status));
Status = EFI_SUCCESS;
exit:
if (Key.N != NULL) {
FreePool (Key.N);
}
if (Key.e != NULL) {
FreePool (Key.e);
}
return Status;
}
STATIC EFI_STATUS LEVerifyHashWithSignature (
QcomAsn1x509Protocol *pEfiQcomASN1X509Protocol,
UINT8 *ImgHash, VB_HASH HashAlgorithm,
CERTIFICATE *Certificate,
CONST UINT8 *SignaturePtr,
UINT32 SignatureLen)
{
EFI_STATUS Status = EFI_FAILURE;
secasn1_data_type Modulus = {NULL};
secasn1_data_type PublicExp = {NULL};
UINT32 PaddingType = 0;
if (pEfiQcomASN1X509Protocol == NULL ||
ImgHash == NULL ||
Certificate == NULL ||
SignaturePtr == NULL) {
DEBUG ((EFI_D_ERROR, "LEVerifyHashWithSignature: Invalid pointer\n"));
return EFI_INVALID_PARAMETER;
}
/* TODO: get subject publick key info from certificate, implement new
algorithm in XBL*/
/* XBL implemented by default sha256 and rsaEncryption with
PKCS1_V1_5 padding*/
Status = LEGetRSAPublicKeyInfoFromCertificate (pEfiQcomASN1X509Protocol,
Certificate, &Modulus, &PublicExp, &PaddingType);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: LEGetRSAPublicKeyInfoFromCertificate "
"unsuccessful! Status: %r\n", Status));
goto exit;
}
Status = LEVerifyHashWithRSASignature (ImgHash, HashAlgorithm,
&Modulus, &PublicExp, PaddingType,
SignaturePtr, SignatureLen);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: LEVerifyHashWithSignature unsuccessful! "
"Status: %r\n", Status));
goto exit;
}
Status = EFI_SUCCESS;
exit:
return Status;
}
static BOOLEAN GetHeaderVersion (AvbSlotVerifyData *SlotData, CHAR8 *ImageName)
{
BOOLEAN HeaderVersion = 0;
UINTN LoadedIndex = 0;
for (LoadedIndex = 0; LoadedIndex < SlotData->num_loaded_partitions;
LoadedIndex++) {
if (avb_strcmp (SlotData->loaded_partitions[LoadedIndex].partition_name,
ImageName) == 0 )
return ( (boot_img_hdr *)
(SlotData->loaded_partitions[LoadedIndex].data))->header_version;
}
return HeaderVersion;
}
static VOID AddRequestedPartition (CHAR8 **RequestedPartititon, UINT32 Index)
{
UINTN PartIndex = 0;
for (PartIndex = 0; PartIndex < MAX_NUM_REQ_PARTITION; PartIndex++) {
if (RequestedPartititon[PartIndex] == NULL) {
RequestedPartititon[PartIndex] =
avb_verify_partition_name[Index];
break;
}
}
}
STATIC VOID
ComputeVbMetaDigest (AvbSlotVerifyData* SlotData, CHAR8* Digest) {
size_t Index;
AvbSHA256Ctx Ctx;
avb_sha256_init (&Ctx);
for (Index = 0; Index < SlotData->num_vbmeta_images; Index++) {
avb_sha256_update (&Ctx,
SlotData->vbmeta_images[Index].vbmeta_data,
SlotData->vbmeta_images[Index].vbmeta_size);
}
avb_memcpy (Digest, avb_sha256_final(&Ctx), AVB_SHA256_DIGEST_SIZE);
}
static UINT32 ParseBootSecurityLevel (CONST CHAR8 *BootSecurityLevel,
size_t BootSecurityLevelSize)
{
UINT32 PatchLevelDate = 0;
UINT32 PatchLevelMonth = 0;
UINT32 PatchLevelYear = 0;
UINT32 SeparatorCount = 0;
UINT32 Count = 0;
/*Parse the value of security patch as per YYYY-MM-DD format*/
while (Count < BootSecurityLevelSize) {
if (BootSecurityLevel[Count] == '-') {
SeparatorCount++;
}
else if (SeparatorCount == 2) {
PatchLevelDate *= 10;
PatchLevelDate += (BootSecurityLevel[Count] - '0');
}
else if (SeparatorCount == 1) {
PatchLevelMonth *= 10;
PatchLevelMonth += (BootSecurityLevel[Count] - '0');
}
else if (SeparatorCount == 0) {
PatchLevelYear *= 10;
PatchLevelYear += (BootSecurityLevel[Count] - '0');
}
else {
return -1;
}
Count++;
}
PatchLevelDate = PatchLevelDate << 11;
PatchLevelYear = (PatchLevelYear - 2000) << 4;
return (PatchLevelDate | PatchLevelYear | PatchLevelMonth);
}
STATIC BOOLEAN
IsValidPartition (Slot *Slot, CONST CHAR16 *Name)
{
CHAR16 PartiName[MAX_GPT_NAME_SIZE] = {0};
EFI_STATUS Status;
INT32 Index;
GUARD (StrnCpyS (PartiName, (UINTN)MAX_GPT_NAME_SIZE, Name, StrLen (Name)));
/* If *Slot is filled, it means that it's for multi-slot */
if (Slot) {
GUARD (StrnCatS (PartiName, MAX_GPT_NAME_SIZE,
Slot->Suffix, StrLen (Slot->Suffix)));
}
Index = GetPartitionIndex (PartiName);
return (Index == INVALID_PTN ||
Index >= MAX_NUM_PARTITIONS) ?
FALSE : TRUE;
}
STATIC EFI_STATUS
LoadImageAndAuthVB2 (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
AvbSlotVerifyResult Result;
AvbSlotVerifyData *SlotData = NULL;
VB2Data *VBData = NULL;
AvbOpsUserData *UserData = NULL;
AvbOps *Ops = NULL;
CHAR8 PnameAscii[MAX_GPT_NAME_SIZE] = {0};
CHAR8 *SlotSuffix = NULL;
BOOLEAN AllowVerificationError = IsUnlocked ();
CHAR8 *RequestedPartitionAll[MAX_NUM_REQ_PARTITION] = {NULL};
CHAR8 **RequestedPartition = NULL;
UINTN NumRequestedPartition = 0;
UINT32 ImageHdrSize = BOOT_IMG_MAX_PAGE_SIZE;
UINT32 PageSize = 0;
UINT32 ImageSizeActual = 0;
VOID *ImageBuffer = NULL;
UINTN ImageSize = 0;
VOID *VendorBootImageBuffer = NULL;
UINTN VendorBootImageSize = 0;
KMRotAndBootState Data = {0};
CONST CHAR8 *BootSecurityLevel = NULL;
size_t BootSecurityLevelSize = 0;
BOOLEAN DateSupport = FALSE;
CONST boot_img_hdr *BootImgHdr = NULL;
AvbSlotVerifyFlags VerifyFlags =
AllowVerificationError ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
: AVB_SLOT_VERIFY_FLAGS_NONE;
AvbHashtreeErrorMode VerityFlags =
AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE;
CHAR8 Digest[AVB_SHA256_DIGEST_SIZE];
BOOLEAN UpdateRollback = FALSE;
UINT32 OSVersion;
Info->BootState = RED;
GUARD (VBCommonInit (Info));
GUARD (VBAllocateCmdLine (Info));
UserData = avb_calloc (sizeof (AvbOpsUserData));
if (UserData == NULL) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AvbOpsUserData\n"));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
Ops = AvbOpsNew (UserData);
if (Ops == NULL) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AvbOps\n"));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
UserData->IsMultiSlot = Info->MultiSlotBoot;
if (Info->MultiSlotBoot) {
UnicodeStrToAsciiStr (Info->Pname, PnameAscii);
if ((MAX_SLOT_SUFFIX_SZ + 1) > AsciiStrLen (PnameAscii)) {
DEBUG ((EFI_D_ERROR, "ERROR: Can not determine slot suffix\n"));
Status = EFI_INVALID_PARAMETER;
goto out;
}
SlotSuffix = &PnameAscii[AsciiStrLen (PnameAscii) - MAX_SLOT_SUFFIX_SZ + 1];
} else {
SlotSuffix = "\0";
}
/* Check if slot is bootable and call into TZ to update rollback version.
* This is similar to the update done later for vbmeta bound images.
* However, here we call into TZ irrespective of version check and let
* the secure environment make the decision on updating rollback version
*/
UpdateRollback = avb_should_update_rollback (UserData->IsMultiSlot);
if (UpdateRollback) {
Status = UpdateRollbackSyscall ();
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "LoadImageAndAuthVB2: Error int TZ Rollback Version "
"syscall; ScmCall Status: (0x%x)\r\n", Status));
return Status;
}
}
DEBUG ((EFI_D_VERBOSE, "Slot: %a, allow verification error: %a\n", SlotSuffix,
BooleanString[AllowVerificationError].name));
if (FixedPcdGetBool (AllowEio)) {
VerityFlags = IsEnforcing () ? AVB_HASHTREE_ERROR_MODE_RESTART
: AVB_HASHTREE_ERROR_MODE_EIO;
} else {
VerityFlags = AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE;
}
RequestedPartition = RequestedPartitionAll;
if ( ( (!Info->MultiSlotBoot) ||
IsDynamicPartitionSupport ()) &&
(Info->BootIntoRecovery &&
!IsBuildUseRecoveryAsBoot ())) {
if (!Info->MultiSlotBoot)
VerifyFlags = VerifyFlags | AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION;
AddRequestedPartition (RequestedPartitionAll, IMG_RECOVERY);
NumRequestedPartition += 1;
Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition,
SlotSuffix, VerifyFlags, VerityFlags, &SlotData);
if (AllowVerificationError &&
ResultShouldContinue (Result)) {
DEBUG ((EFI_D_VERBOSE, "State: Unlocked, AvbSlotVerify returned "
"%a, continue boot\n",
avb_slot_verify_result_to_string (Result)));
} else if (Result != AVB_SLOT_VERIFY_RESULT_OK) {
DEBUG ((EFI_D_ERROR, "ERROR: Device State %a,AvbSlotVerify returned %a\n",
AllowVerificationError ? "Unlocked" : "Locked",
avb_slot_verify_result_to_string (Result)));
Status = EFI_LOAD_ERROR;
Info->BootState = RED;
goto out;
}
if (SlotData == NULL) {
Status = EFI_LOAD_ERROR;
Info->BootState = RED;
goto out;
}
BOOLEAN HeaderVersion = GetHeaderVersion (SlotData, "recovery");
DEBUG ( (EFI_D_VERBOSE, "Recovery HeaderVersion %d \n", HeaderVersion));
if (HeaderVersion == BOOT_HEADER_VERSION_ZERO ||
HeaderVersion >= BOOT_HEADER_VERSION_THREE) {
AddRequestedPartition (RequestedPartitionAll, IMG_DTBO);
NumRequestedPartition += 1;
if (SlotData != NULL) {
avb_slot_verify_data_free (SlotData);
}
Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition,
SlotSuffix, VerifyFlags, VerityFlags, &SlotData);
}
} else {
Slot CurrentSlot;
if (!Info->NumLoadedImages) {
AddRequestedPartition (RequestedPartitionAll, IMG_BOOT);
NumRequestedPartition += 1;
}
AddRequestedPartition (RequestedPartitionAll, IMG_DTBO);
NumRequestedPartition += 1;
Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition,
SlotSuffix, VerifyFlags, VerityFlags, &SlotData);
if (AllowVerificationError &&
ResultShouldContinue (Result)) {
DEBUG ((EFI_D_VERBOSE, "State: Unlocked, AvbSlotVerify returned "
"%a, continue boot\n",
avb_slot_verify_result_to_string (Result)));
} else if (Result != AVB_SLOT_VERIFY_RESULT_OK) {
DEBUG ((EFI_D_ERROR, "ERROR: Device State %a,AvbSlotVerify returned %a\n",
AllowVerificationError ? "Unlocked" : "Locked",
avb_slot_verify_result_to_string (Result)));
Status = EFI_LOAD_ERROR;
Info->BootState = RED;
goto out;
}
if (SlotData == NULL) {
Status = EFI_LOAD_ERROR;
Info->BootState = RED;
goto out;
}
BOOLEAN HeaderVersion = GetHeaderVersion (SlotData, "boot");
DEBUG ( (EFI_D_VERBOSE, "Boot HeaderVersion %d \n", HeaderVersion));
if (Info->MultiSlotBoot) {
CurrentSlot = GetCurrentSlotSuffix ();
}
/* Load vendor boot in following conditions
* 1. In Ram load case where Header version is 0 because
* boot image is not loaded in the flow& Valid partition.
* 2. In Case of header version 3 & valid partititon.
*/
if (IsValidPartition (&CurrentSlot, L"vendor_boot") &&
(HeaderVersion == BOOT_HEADER_VERSION_ZERO ||
HeaderVersion >= BOOT_HEADER_VERSION_THREE)) {
AddRequestedPartition (RequestedPartitionAll, IMG_VENDOR_BOOT);
NumRequestedPartition += 1;
if (SlotData != NULL) {
avb_slot_verify_data_free (SlotData);
}
Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition,
SlotSuffix, VerifyFlags, VerityFlags, &SlotData);
} else {
DEBUG ((EFI_D_ERROR, "Invalid vendor_boot partition. Skipping\n"));
}
}
if (SlotData == NULL) {
Status = EFI_LOAD_ERROR;
Info->BootState = RED;
goto out;
}
if (AllowVerificationError && ResultShouldContinue (Result)) {
DEBUG ((EFI_D_VERBOSE, "State: Unlocked, AvbSlotVerify returned "
"%a, continue boot\n",
avb_slot_verify_result_to_string (Result)));
} else if (Result != AVB_SLOT_VERIFY_RESULT_OK) {
DEBUG ((EFI_D_ERROR, "ERROR: Device State %a, AvbSlotVerify returned %a\n",
AllowVerificationError ? "Unlocked" : "Locked",
avb_slot_verify_result_to_string (Result)));
Status = EFI_LOAD_ERROR;
Info->BootState = RED;
goto out;
}
for (UINTN ReqIndex = 0; ReqIndex < NumRequestedPartition; ReqIndex++) {
DEBUG ((EFI_D_VERBOSE, "Requested Partition: %a\n",
RequestedPartition[ReqIndex]));
for (UINTN LoadedIndex = 0; LoadedIndex < SlotData->num_loaded_partitions;
LoadedIndex++) {
DEBUG ((EFI_D_VERBOSE, "Loaded Partition: %a\n",
SlotData->loaded_partitions[LoadedIndex].partition_name));
if (!AsciiStrnCmp (
RequestedPartition[ReqIndex],
SlotData->loaded_partitions[LoadedIndex].partition_name,
AsciiStrLen (
SlotData->loaded_partitions[LoadedIndex].partition_name))) {
if (Info->NumLoadedImages >= ARRAY_SIZE (Info->Images)) {
DEBUG ((EFI_D_ERROR, "NumLoadedPartition"
"(%d) too large "
"max images(%d)\n",
Info->NumLoadedImages, ARRAY_SIZE (Info->Images)));
Status = EFI_LOAD_ERROR;
Info->BootState = RED;
goto out;
}
Info->Images[Info->NumLoadedImages].Name =
SlotData->loaded_partitions[LoadedIndex].partition_name;
Info->Images[Info->NumLoadedImages].ImageBuffer =
SlotData->loaded_partitions[LoadedIndex].data;
Info->Images[Info->NumLoadedImages].ImageSize =
SlotData->loaded_partitions[LoadedIndex].data_size;
Info->NumLoadedImages++;
break;
}
}
}
if (Info->NumLoadedImages < NumRequestedPartition) {
DEBUG ((EFI_D_ERROR, "ERROR: AvbSlotVerify slot data error: num of "
"loaded partitions %d, requested %d\n",
Info->NumLoadedImages, NumRequestedPartition));
Status = EFI_LOAD_ERROR;
goto out;
}
DEBUG ((EFI_D_VERBOSE, "Total loaded partition %d\n", Info->NumLoadedImages));
VBData = (VB2Data *)avb_calloc (sizeof (VB2Data));
if (VBData == NULL) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate VB2Data\n"));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
VBData->Ops = Ops;
VBData->SlotData = SlotData;
Info->VBData = (VOID *)VBData;
GUARD_OUT (GetImage (Info, &ImageBuffer, &ImageSize,
( (!Info->MultiSlotBoot ||
IsDynamicPartitionSupport ()) &&
(Info->BootIntoRecovery &&
!IsBuildUseRecoveryAsBoot ())) ?
"recovery" : "boot"));
if (ImageSize < sizeof (boot_img_hdr)) {
DEBUG ((EFI_D_ERROR, "Invalid boot image header size: %u\n", ImageSize));
Status = EFI_BAD_BUFFER_SIZE;
goto out;
}
BootImgHdr = (boot_img_hdr *)ImageBuffer;
if (BootImgHdr->header_version >= BOOT_HEADER_VERSION_THREE) {
GUARD_OUT (GetImage (Info, &VendorBootImageBuffer,
&VendorBootImageSize, "vendor_boot"));
}
Status = CheckImageHeader (ImageBuffer, ImageHdrSize,
VendorBootImageBuffer, VendorBootImageSize,
&ImageSizeActual, &PageSize,
Info->BootIntoRecovery);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Invalid boot image header:%r\n", Status));
goto out;
}
if (AllowVerificationError) {
Info->BootState = ORANGE;
} else {
if (UserData->IsUserKey) {
Info->BootState = YELLOW;
} else {
Info->BootState = GREEN;
}
}
/* command line */
GUARD_OUT (AppendVBCommonCmdLine (Info));
GUARD_OUT (AppendVBCmdLine (Info, SlotData->cmdline));
/* Set Rot & Boot State*/
Data.Color = Info->BootState;
Data. IsUnlocked = AllowVerificationError;
Data.PublicKeyLength = UserData->PublicKeyLen;
Data.PublicKey = UserData->PublicKey;
GUARD_OUT (KeyMasterGetDateSupport (&DateSupport));
if (BootImgHdr->header_version >= BOOT_HEADER_VERSION_THREE) {
OSVersion = ((boot_img_hdr_v3 *)(ImageBuffer))->os_version;
} else {
OSVersion = BootImgHdr->os_version;
}
/* Send date value in security patch only when KM TA supports it and the
* property is available in vbmeta data, send the old value in other cases
*/
DEBUG ((EFI_D_VERBOSE, "DateSupport: %d\n", DateSupport));
if (DateSupport) {
BootSecurityLevel = avb_property_lookup (
SlotData->vbmeta_images[0].vbmeta_data,
SlotData->vbmeta_images[0].vbmeta_size,
"com.android.build.boot.security_patch",
0, &BootSecurityLevelSize);
if (BootSecurityLevel != NULL &&
BootSecurityLevelSize == MAX_PROPERTY_SIZE) {
Data.SystemSecurityLevel = ParseBootSecurityLevel (BootSecurityLevel,
BootSecurityLevelSize);
if (Data.SystemSecurityLevel < 0) {
DEBUG ((EFI_D_ERROR, "System security patch level format invalid\n"));
Status = EFI_INVALID_PARAMETER;
goto out;
}
}
else {
Data.SystemSecurityLevel = (OSVersion & 0x7FF);
}
}
else {
Data.SystemSecurityLevel = (OSVersion & 0x7FF);
}
Data.SystemVersion = (OSVersion & 0xFFFFF800) >> 11;
GUARD_OUT (KeyMasterSetRotAndBootState (&Data));
ComputeVbMetaDigest (SlotData, (CHAR8 *)&Digest);
GUARD_OUT (SetVerifiedBootHash ((CONST CHAR8 *)&Digest, sizeof(Digest)));
DEBUG ((EFI_D_INFO, "VB2: Authenticate complete! boot state is: %a\n",
VbSn[Info->BootState].name));
out:
if (Status != EFI_SUCCESS) {
if (SlotData != NULL) {
avb_slot_verify_data_free (SlotData);
}
if (Ops != NULL) {
AvbOpsFree (Ops);
}
if (UserData != NULL) {
avb_free (UserData);
}
if (VBData != NULL) {
avb_free (VBData);
}
Info->BootState = RED;
if (Info->MultiSlotBoot) {
HandleActiveSlotUnbootable ();
/* HandleActiveSlotUnbootable should have swapped slots and
* reboot the device. If no bootable slot found, enter fastboot
*/
DEBUG ((EFI_D_WARN, "No bootable slots found enter fastboot mode\n"));
} else {
DEBUG ((EFI_D_WARN,
"Non Multi-slot: Unbootable entering fastboot mode\n"));
}
}
DEBUG ((EFI_D_INFO, "VB2: boot state: %a(%d)\n",
VbSn[Info->BootState].name, Info->BootState));
return Status;
}
STATIC EFI_STATUS
DisplayVerifiedBootScreen (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
CHAR8 FfbmStr[FFBM_MODE_BUF_SIZE] = {'\0'};
if (GetAVBVersion () < AVB_1) {
return EFI_SUCCESS;
}
if (!StrnCmp (Info->Pname, L"boot", StrLen (L"boot"))) {
Status = GetFfbmCommand (FfbmStr, FFBM_MODE_BUF_SIZE);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_VERBOSE, "No Ffbm cookie found, ignore: %r\n", Status));
FfbmStr[0] = '\0';
}
}
DEBUG ((EFI_D_VERBOSE, "Boot State is : %d\n", Info->BootState));
switch (Info->BootState) {
case RED:
Status = DisplayVerifiedBootMenu (DISPLAY_MENU_RED);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_INFO,
"Your device is corrupt. It can't be trusted and will not boot."
"\nYour device will shutdown in 30s\n"));
}
MicroSecondDelay (30000000);
ShutdownDevice ();
break;
case YELLOW:
Status = DisplayVerifiedBootMenu (DISPLAY_MENU_YELLOW);
if (Status == EFI_SUCCESS) {
WaitForExitKeysDetection ();
} else {
DEBUG ((EFI_D_INFO, "Your device has loaded a different operating system."
"\nWait for 5 seconds before proceeding\n"));
MicroSecondDelay (5000000);
}
break;
case ORANGE:
if (FfbmStr[0] != '\0' && !TargetBuildVariantUser ()) {
DEBUG ((EFI_D_VERBOSE, "Device will boot into FFBM mode\n"));
} else {
Status = DisplayVerifiedBootMenu (DISPLAY_MENU_ORANGE);
if (Status == EFI_SUCCESS) {
WaitForExitKeysDetection ();
} else {
DEBUG (
(EFI_D_INFO, "Device is unlocked, Skipping boot verification\n"));
MicroSecondDelay (5000000);
}
}
break;
default:
break;
}
/* dm-verity warning */
if ((GetAVBVersion () != AVB_2) &&
!IsEnforcing () &&
!Info->BootIntoRecovery) {
Status = DisplayVerifiedBootMenu (DISPLAY_MENU_EIO);
if (Status == EFI_SUCCESS) {
WaitForExitKeysDetection ();
} else {
DEBUG ((EFI_D_INFO, "The dm-verity is not started in restart mode." \
"\nWait for 30 seconds before proceeding\n"));
MicroSecondDelay (30000000);
}
}
return EFI_SUCCESS;
}
STATIC EFI_STATUS LoadImageAndAuthForLE (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
QcomAsn1x509Protocol *QcomAsn1X509Protocal = NULL;
CONST UINT8 *OemCertFile = LeOemCertificate;
UINTN OemCertFileLen = sizeof (LeOemCertificate);
CERTIFICATE OemCert = {NULL};
UINTN HashSize;
UINT8 *ImgHash = NULL;
UINTN ImgSize;
VB_HASH HashAlgorithm;
UINT8 *SigAddr = NULL;
UINT32 SigSize = 0;
CHAR8 *SystemPath = NULL;
UINT32 SystemPathLen = 0;
BOOLEAN SecureDevice = FALSE;
/*Load image*/
GUARD (VBAllocateCmdLine (Info));
GUARD (VBCommonInit (Info));
GUARD (LoadImageNoAuth (Info));
Status = IsSecureDevice (&SecureDevice);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Failed read device state: %r\n", Status));
return Status;
}
if (!SecureDevice) {
if (!TargetBuildVariantUser () ) {
DEBUG ((EFI_D_INFO, "VB: verification skipped for debug builds\n"));
goto skip_verification;
}
}
/* Initialize Verified Boot*/
device_info_vb_t DevInfo_vb;
DevInfo_vb.is_unlocked = IsUnlocked ();
DevInfo_vb.is_unlock_critical = IsUnlockCritical ();
Status = Info->VbIntf->VBDeviceInit (Info->VbIntf,
(device_info_vb_t *)&DevInfo_vb);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Error during VBDeviceInit: %r\n", Status));
return Status;
}
/* Locate QcomAsn1x509Protocol*/
Status = gBS->LocateProtocol (&gEfiQcomASN1X509ProtocolGuid, NULL,
(VOID **)&QcomAsn1X509Protocal);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Error LocateProtocol "
"gEfiQcomASN1X509ProtocolGuid: %r\n", Status));
return Status;
}
/* Read OEM certificate from the embedded header file */
Status = QcomAsn1X509Protocal->ASN1X509VerifyOEMCertificate
(QcomAsn1X509Protocal, OemCertFile, OemCertFileLen, &OemCert);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Error during "
"ASN1X509VerifyOEMCertificate: %r\n", Status));
return Status;
}
/*Calculate kernel image hash, SHA256 is used by default*/
HashAlgorithm = VB_SHA256;
HashSize = VB_SHA256_SIZE;
ImgSize = Info->Images[0].ImageSize;
ImgHash = AllocateZeroPool (HashSize);
if (ImgHash == NULL) {
DEBUG ((EFI_D_ERROR, "kernel image hash buffer allocation failed!\n"));
Status = EFI_OUT_OF_RESOURCES;
return Status;
}
Status = LEGetImageHash (QcomAsn1X509Protocal, HashAlgorithm,
(UINT8 *)Info->Images[0].ImageBuffer,
ImgSize, ImgHash, HashSize);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Error during VBGetImageHash: %r\n", Status));
return Status;
}
SigAddr = (UINT8 *)Info->Images[0].ImageBuffer + ImgSize;
SigSize = LE_BOOTIMG_SIG_SIZE;
Status = LEVerifyHashWithSignature (QcomAsn1X509Protocal, ImgHash,
HashAlgorithm, &OemCert, SigAddr, SigSize);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Error during "
"LEVBVerifyHashWithSignature: %r\n", Status));
return Status;
}
DEBUG ((EFI_D_INFO, "VB: LoadImageAndAuthForLE complete!\n"));
skip_verification:
if (!IsRootCmdLineUpdated (Info)) {
SystemPathLen = GetSystemPath (&SystemPath, Info);
if (SystemPathLen == 0 ||
SystemPath == NULL) {
return EFI_LOAD_ERROR;
}
GUARD (AppendVBCmdLine (Info, SystemPath));
}
return Status;
}
EFI_STATUS
LoadImageAndAuth (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
BOOLEAN MdtpActive = FALSE;
QCOM_MDTP_PROTOCOL *MdtpProtocol;
UINT32 AVBVersion = NO_AVB;
WaitForFlashFinished ();
if (Info == NULL) {
DEBUG ((EFI_D_ERROR, "Invalid parameter Info\n"));
return EFI_INVALID_PARAMETER;
}
/* Get Partition Name*/
if (!Info->MultiSlotBoot) {
if (Info->BootIntoRecovery) {
DEBUG ((EFI_D_INFO, "Booting Into Recovery Mode\n"));
StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"recovery",
StrLen (L"recovery"));
} else {
DEBUG ((EFI_D_INFO, "Booting Into Mission Mode\n"));
StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"boot",
StrLen (L"boot"));
}
} else {
Slot CurrentSlot = {{0}};
GUARD (FindBootableSlot (&CurrentSlot));
if (IsSuffixEmpty (&CurrentSlot)) {
DEBUG ((EFI_D_ERROR, "No bootable slot\n"));
return EFI_LOAD_ERROR;
}
if (IsDynamicPartitionSupport () &&
Info->BootIntoRecovery) {
DEBUG ((EFI_D_INFO, "Booting Into Recovery Mode\n"));
StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"recovery",
StrLen (L"recovery"));
} else {
DEBUG ((EFI_D_INFO, "Booting Into Mission Mode\n"));
GUARD (StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"boot",
StrLen (L"boot")));
}
GUARD (StrnCatS (Info->Pname, ARRAY_SIZE (Info->Pname), CurrentSlot.Suffix,
StrLen (CurrentSlot.Suffix)));
}
DEBUG ((EFI_D_VERBOSE, "MultiSlot %a, partition name %s\n",
BooleanString[Info->MultiSlotBoot].name, Info->Pname));
if (FixedPcdGetBool (EnableMdtpSupport)) {
Status = IsMdtpActive (&MdtpActive);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to get activation state for MDTP, "
"Status=%r."
" Considering MDTP as active and continuing \n",
Status));
if (Status != EFI_NOT_FOUND)
MdtpActive = TRUE;
}
}
AVBVersion = GetAVBVersion ();
DEBUG ((EFI_D_VERBOSE, "AVB version %d\n", AVBVersion));
/* Load and Authenticate */
switch (AVBVersion) {
case NO_AVB:
return LoadImageNoAuthWrapper (Info);
break;
case AVB_1:
Status = LoadImageAndAuthVB1 (Info);
break;
case AVB_2:
Status = LoadImageAndAuthVB2 (Info);
break;
case AVB_LE:
Status = LoadImageAndAuthForLE (Info);
break;
default:
DEBUG ((EFI_D_ERROR, "Unsupported AVB version %d\n", AVBVersion));
Status = EFI_UNSUPPORTED;
}
// if MDTP is active Display Recovery UI
if (Status != EFI_SUCCESS && MdtpActive) {
Status = gBS->LocateProtocol (&gQcomMdtpProtocolGuid, NULL,
(VOID **)&MdtpProtocol);
if (EFI_ERROR (Status)) {
DEBUG (
(EFI_D_ERROR, "Failed to locate MDTP protocol, Status=%r\n", Status));
return Status;
}
/* Perform Local Deactivation of MDTP */
Status = MdtpProtocol->MdtpDeactivate (MdtpProtocol, FALSE);
}
if (IsUnlocked () && Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "LoadImageAndAuth failed %r\n", Status));
return Status;
}
if (AVBVersion != AVB_LE) {
DisplayVerifiedBootScreen (Info);
DEBUG ((EFI_D_VERBOSE, "Sending Milestone Call\n"));
Status = Info->VbIntf->VBSendMilestone (Info->VbIntf);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Error sending milestone call to TZ\n"));
return Status;
}
}
return Status;
}
VOID
FreeVerifiedBootResource (BootInfo *Info)
{
DEBUG ((EFI_D_VERBOSE, "FreeVerifiedBootResource\n"));
if (Info == NULL) {
return;
}
VB2Data *VBData = Info->VBData;
if (VBData != NULL) {
AvbOps *Ops = VBData->Ops;
if (Ops != NULL) {
if (Ops->user_data != NULL) {
avb_free (Ops->user_data);
}
AvbOpsFree (Ops);
}
AvbSlotVerifyData *SlotData = VBData->SlotData;
if (SlotData != NULL) {
avb_slot_verify_data_free (SlotData);
}
avb_free (VBData);
}
if (Info->VBCmdLine != NULL) {
FreePool (Info->VBCmdLine);
}
return;
}
EFI_STATUS
GetCertFingerPrint (UINT8 *FingerPrint,
UINTN FingerPrintLen,
UINTN *FingerPrintLenOut)
{
EFI_STATUS Status = EFI_SUCCESS;
if (FingerPrint == NULL || FingerPrintLenOut == NULL ||
FingerPrintLen < AVB_SHA256_DIGEST_SIZE) {
DEBUG ((EFI_D_ERROR, "GetCertFingerPrint: Invalid parameters\n"));
return EFI_INVALID_PARAMETER;
}
if (GetAVBVersion () == AVB_1) {
QCOM_VERIFIEDBOOT_PROTOCOL *VbIntf = NULL;
Status = gBS->LocateProtocol (&gEfiQcomVerifiedBootProtocolGuid, NULL,
(VOID **)&VbIntf);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Unable to locate VerifiedBoot Protocol\n"));
return Status;
}
if (VbIntf->Revision < QCOM_VERIFIEDBOOT_PROTOCOL_REVISION) {
DEBUG ((EFI_D_ERROR, "GetCertFingerPrint: VB1: not "
"supported for this revision\n"));
return EFI_UNSUPPORTED;
}
Status = VbIntf->VBGetCertFingerPrint (VbIntf, FingerPrint, FingerPrintLen,
FingerPrintLenOut);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Failed Reading CERT FingerPrint\n"));
return Status;
}
} else if (GetAVBVersion () == AVB_2) {
CHAR8 *UserKeyBuffer = NULL;
UINT32 UserKeyLength = 0;
AvbSHA256Ctx UserKeyCtx = {{0}};
CHAR8 *UserKeyDigest = NULL;
GUARD (GetUserKey (&UserKeyBuffer, &UserKeyLength));
avb_sha256_init (&UserKeyCtx);
avb_sha256_update (&UserKeyCtx, (UINT8 *)UserKeyBuffer, UserKeyLength);
UserKeyDigest = (CHAR8 *)avb_sha256_final (&UserKeyCtx);
CopyMem (FingerPrint, UserKeyDigest, AVB_SHA256_DIGEST_SIZE);
*FingerPrintLenOut = AVB_SHA256_DIGEST_SIZE;
} else {
DEBUG ((EFI_D_ERROR, "GetCertFingerPrint: not supported\n"));
return EFI_UNSUPPORTED;
}
return Status;
}