556 lines
16 KiB
C
556 lines
16 KiB
C
/** @file
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2019, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
#include "ModernPreloadSmm.h"
|
|
#include <Library/LfcSwSmiCpuRegisterLib.h>
|
|
#include <Protocol/LenovoVariable.h>
|
|
EFI_SMM_CPU_PROTOCOL *mSmmCpu;
|
|
EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable;
|
|
|
|
/**
|
|
Read information from the CPU save state.
|
|
|
|
@param Register Specifies the CPU register to read form the save state.
|
|
@param Width The number of bytes to read from the CPU save state.
|
|
@param CpuNum Specifies the zero-based index of the CPU save state.
|
|
@param RegisterData Upon return, this holds the CPU register value read from the save state.
|
|
|
|
@retval EFI_SUCCESS The register was read from Save State
|
|
@retval EFI_NOT_FOUND The register is not defined for the Save State of Processor
|
|
@retval EFI_INVALID_PARAMTER This or Buffer is NULL.
|
|
**/
|
|
EFI_STATUS
|
|
ReadCpuRegister (
|
|
IN EFI_SMM_SAVE_STATE_REGISTER RegisterNum,
|
|
IN UINTN Width,
|
|
IN UINTN CpuNum,
|
|
OUT VOID *RegisterData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = mSmmCpu->ReadSaveState (
|
|
mSmmCpu,
|
|
Width,
|
|
RegisterNum,
|
|
CpuNum,
|
|
RegisterData
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Write value to a CPU Save State register on the target processor.
|
|
|
|
This function abstracts the differences that whether the CPU Save State register is in the
|
|
IA32 CPU Save State Map or X64 CPU Save State Map.
|
|
|
|
This function supports writing a CPU Save State register in SMBase relocation handler.
|
|
|
|
@param RegisterNum Specifies the CPU register to write to the save state.
|
|
@param Width The number of bytes to read from the CPU save state.
|
|
@param CpuNum Specifies the zero-based index of the CPU save state.
|
|
@param RegisterData Upon entry, this holds the new CPU register value.
|
|
|
|
@retval EFI_SUCCESS The register was written to Save State.
|
|
@retval EFI_NOT_FOUND The register is not defined for the Save State of Processor.
|
|
@retval EFI_INVALID_PARAMTER ProcessorIndex or Width is not correct.
|
|
**/
|
|
EFI_STATUS
|
|
WriteCpuRegister (
|
|
IN EFI_SMM_SAVE_STATE_REGISTER RegisterNum,
|
|
IN UINTN Width,
|
|
IN UINTN CpuNum,
|
|
IN VOID *RegisterData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = mSmmCpu->WriteSaveState (
|
|
mSmmCpu,
|
|
Width,
|
|
RegisterNum,
|
|
CpuNum,
|
|
RegisterData
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Get TPM Lock State.
|
|
|
|
@param L05TpmLockRegister Specifies the TPM lock register.
|
|
|
|
@retval EFI_SUCCESS The register was get from save state.
|
|
@retval EFI_UNSUPPORTED The register is not defined for the Save State.
|
|
**/
|
|
EFI_STATUS
|
|
GetTpmLockState (
|
|
IN OUT EFI_L05_TPM_LOCK_REGISTER *L05TpmLockRegister
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
Status = EFI_SUCCESS;
|
|
LENOVO_VARIABLE_PROTOCOL *mLenovoVariable;
|
|
UINT32 DataLength;
|
|
UINT8 TpmLock;
|
|
EFI_GUID LvarTpmLockFlagGuid = LVAR_TPM_LOCK_FLAG_GUID;
|
|
|
|
mLenovoVariable = NULL;
|
|
Status = gSmst->SmmLocateProtocol (&gLenovoVariableProtocolGuid, NULL, &mLenovoVariable);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
TpmLock = 0;
|
|
DataLength = sizeof (TpmLock);
|
|
|
|
//
|
|
// Get TPM Lock data
|
|
//
|
|
Status = mLenovoVariable->GetVariable (
|
|
mLenovoVariable,
|
|
&LvarTpmLockFlagGuid,
|
|
&DataLength,
|
|
&TpmLock);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get TPM Lock State
|
|
//
|
|
L05TpmLockRegister->Byte = TpmLock;
|
|
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Set TPM Lock State.
|
|
|
|
@param L05TpmLockRegister Specifies the TPM lock register.
|
|
|
|
@retval EFI_SUCCESS The register was written from save state.
|
|
@retval EFI_UNSUPPORTED The register is not defined for the Save State.
|
|
**/
|
|
EFI_STATUS
|
|
SetTpmLockState (
|
|
IN EFI_L05_TPM_LOCK_REGISTER *L05TpmLockRegister
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LENOVO_VARIABLE_PROTOCOL *mLenovoVariable;
|
|
UINT32 DataLength;
|
|
UINT8 TpmLock;
|
|
EFI_GUID LvarTpmLockFlagGuid = LVAR_TPM_LOCK_FLAG_GUID;
|
|
Status = EFI_SUCCESS;
|
|
mLenovoVariable = NULL;
|
|
|
|
Status = gSmst->SmmLocateProtocol (&gLenovoVariableProtocolGuid, NULL, &mLenovoVariable);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
TpmLock = 0;
|
|
DataLength = sizeof (TpmLock);
|
|
|
|
//
|
|
// Get TPM Lock data
|
|
//
|
|
Status = mLenovoVariable->GetVariable (
|
|
mLenovoVariable,
|
|
&LvarTpmLockFlagGuid,
|
|
&DataLength,
|
|
&TpmLock
|
|
);
|
|
|
|
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
|
|
return Status;
|
|
}
|
|
|
|
if (L05TpmLockRegister->Byte == TpmLock) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Set TPM Lock State
|
|
//
|
|
TpmLock = L05TpmLockRegister->Byte;
|
|
DataLength = sizeof (TpmLock);
|
|
|
|
Status = mLenovoVariable->SetVariable(
|
|
mLenovoVariable,
|
|
&LvarTpmLockFlagGuid,
|
|
DataLength,
|
|
&TpmLock
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Set TPM Lock Done.
|
|
|
|
@param None.
|
|
|
|
@retval L05_MODERN_PRELOAD_ERROR_SUCCESS Function successful.
|
|
@retval L05_MODERN_PRELOAD_ERROR_SET_FAILED Function failed.
|
|
**/
|
|
UINT32
|
|
SetTpmLockDone (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_L05_TPM_LOCK_REGISTER L05TpmLockRegister;
|
|
|
|
ZeroMem (&L05TpmLockRegister, sizeof (EFI_L05_TPM_LOCK_REGISTER));
|
|
|
|
Status = GetTpmLockState (&L05TpmLockRegister);
|
|
if (EFI_ERROR (Status)) {
|
|
return L05_MODERN_PRELOAD_ERROR_SET_FAILED;
|
|
}
|
|
|
|
L05TpmLockRegister.Bits.State = L05_TPMLOCKD_DONE;
|
|
|
|
Status = SetTpmLockState (&L05TpmLockRegister);
|
|
if (EFI_ERROR (Status)) {
|
|
return L05_MODERN_PRELOAD_ERROR_SET_FAILED;
|
|
}
|
|
|
|
return L05_MODERN_PRELOAD_ERROR_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Check TPM Lock Done.
|
|
|
|
@param None.
|
|
|
|
@retval L05_MODERN_PRELOAD_ERROR_SUCCESS TPMLOCK Done.
|
|
@retval L05_MODERN_PRELOAD_ERROR_CHECK_ACCESS Get TPMLOCK status failed.
|
|
@retval L05_MODERN_PRELOAD_ERROR_TPMLOCK_NOT_DONE TPMLOCK not Doney.
|
|
**/
|
|
|
|
UINT32
|
|
CheckTpmLockDone (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_L05_TPM_LOCK_REGISTER L05TpmLockRegister;
|
|
|
|
ZeroMem (&L05TpmLockRegister, sizeof (EFI_L05_TPM_LOCK_REGISTER));
|
|
|
|
Status = GetTpmLockState (&L05TpmLockRegister);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return L05_MODERN_PRELOAD_ERROR_CHECK_ACCESS;
|
|
}
|
|
|
|
if (L05TpmLockRegister.Bits.State == L05_TPMLOCKD_DONE) {
|
|
return L05_MODERN_PRELOAD_ERROR_SUCCESS;
|
|
}
|
|
|
|
return L05_MODERN_PRELOAD_ERROR_TPMLOCK_NOT_DONE;
|
|
}
|
|
|
|
/**
|
|
A callback function for SMM Modern Preload.
|
|
|
|
@param CpuNum CPU number
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval Others An unexpected error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
L05SmmModernPreloadCallback (
|
|
IN UINTN CpuNum
|
|
)
|
|
{
|
|
UINT32 ErrorStatus;
|
|
UINT32 Eax;
|
|
UINT32 Ebx;
|
|
EFI_STATUS Status;
|
|
ErrorStatus = L05_MODERN_PRELOAD_ERROR_CHECK_ACCESS;
|
|
CpuNum = 0;
|
|
Eax = 0;
|
|
Ebx = 0;
|
|
|
|
//
|
|
// Check function signature is valid or not
|
|
//
|
|
Status = IdentifyCpuIndexByEax (L05_MODERN_PRELOAD_SUPPORT_FUNCTION, 0xffffffff, &CpuNum);
|
|
if (EFI_ERROR (Status)) {
|
|
//[-start-211014-BAIN000051-modify]//
|
|
//#ifdef LCFC_SUPPORT
|
|
//#if defined(C970_BSH_SUPPORT)
|
|
//[-start-21119-TAMT000032-modify]//
|
|
//[-start-211203-QINGLIN0125-modify]//
|
|
//[-start-211206-OWENWU0029-modify]//
|
|
//[-start-211214-Ching000017-modify]//
|
|
#if defined(C970_BSH_SUPPORT) || defined(C770_BSH_SUPPORT) || defined(S77014_BSH_SUPPORT) || defined(S370_BSH_SUPPORT) || defined(S570_BSH_SUPPORT) || defined(S77013_BSH_SUPPORT) || defined(S77014IAH_BSH_SUPPORT)
|
|
return EFI_NOT_FOUND;
|
|
#else
|
|
return EFI_SUCCESS;
|
|
#endif
|
|
//[-end-211214-Ching000017-modify]//
|
|
//[-end-211206-OWENWU0029-modify]//
|
|
//[-end-211203-QINGLIN0125-modify]//
|
|
//[-end-21119-TAMT000032-modify]//
|
|
//[-end-211014-BAIN000051-modify]//
|
|
}
|
|
ReadCpuRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
|
|
if (Eax != L05_MODERN_PRELOAD_SUPPORT_FUNCTION) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Get SMI interface
|
|
//
|
|
ReadCpuRegister (EFI_SMM_SAVE_STATE_REGISTER_RBX, sizeof (UINT32), CpuNum, &Ebx);
|
|
|
|
switch (Ebx) {
|
|
|
|
case L05_MODERN_PRELOAD_SET_TPMLOCKD:
|
|
ErrorStatus = SetTpmLockDone ();
|
|
|
|
if (ErrorStatus == L05_MODERN_PRELOAD_ERROR_SUCCESS) {
|
|
ErrorStatus = CheckTpmLockDone ();
|
|
|
|
if (ErrorStatus != L05_MODERN_PRELOAD_ERROR_SUCCESS) {
|
|
ErrorStatus = L05_MODERN_PRELOAD_ERROR_SET_FAILED;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case L05_MODERN_PRELOAD_CHECK_TPMLOCKD:
|
|
ErrorStatus = CheckTpmLockDone ();
|
|
break;
|
|
|
|
default:
|
|
ErrorStatus = L05_MODERN_PRELOAD_ERROR_CHECK_ACCESS;
|
|
break;
|
|
}
|
|
|
|
WriteCpuRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &ErrorStatus);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Init TPM Lock.
|
|
|
|
BIOS should initially set this value to "Not Done" state. Only MFG utilities can change the value.
|
|
The initial value about Bit1 and Bit0 should be 11b.
|
|
|
|
@param None.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval Others An unexpected error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
InitTpmLock (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_L05_TPM_LOCK_REGISTER L05TpmLockRegister;
|
|
|
|
ZeroMem (&L05TpmLockRegister, sizeof (EFI_L05_TPM_LOCK_REGISTER));
|
|
|
|
Status = GetTpmLockState (&L05TpmLockRegister);
|
|
|
|
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// [BIOS and Tool Requirements for Modern Preload Support, Version 1.00 Draft]
|
|
// 1.2 Structures definition
|
|
// BIOS should initially set this value to "Not Done" state. Only MFG utilities can change the value.
|
|
// The initial value about Bit1 and Bit0 should be 11b.
|
|
//
|
|
if ((L05TpmLockRegister.Bits.State != L05_TPMLOCKD_DONE) &&
|
|
(L05TpmLockRegister.Bits.State != L05_TPMLOCKD_NOT_DONE)) {
|
|
L05TpmLockRegister.Byte = 0xFF;
|
|
Status = SetTpmLockState (&L05TpmLockRegister);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Check TPM Lock.
|
|
|
|
BIOS are required to send different TPM commands before and after TPMLOCK Done.
|
|
If TPMLOCK is Not Done, then TPM PlatformAuth will be NULL.
|
|
If TPMLOCK is Done, then TPM PlatformAuth will be RANDOM.
|
|
|
|
@param None.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval Others An unexpected error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
CheckTpmLock (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_L05_TPM_LOCK_REGISTER L05TpmLockRegister;
|
|
|
|
ZeroMem (&L05TpmLockRegister, sizeof (EFI_L05_TPM_LOCK_REGISTER));
|
|
|
|
Status = GetTpmLockState (&L05TpmLockRegister);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// [BIOS and Tool Requirements for Modern Preload Support, Version 1.00 Draft]
|
|
// 1.1 BIOS FLOW Chart
|
|
// Check TPMLOCK -> Not Done -> Continue POST
|
|
// |
|
|
// ---> Done -> Randomize PlatformAuth -> Continue POST
|
|
//
|
|
// 1.2 Structures definition
|
|
// BIOS Requirement Before and After TPMLOCK Done
|
|
// BIOS are required to send different TPM commands before and after TPMLOCK Done,
|
|
// make TPM behavior as below:
|
|
// | TPMLOCK = Not Done | TPMLOCK = Done
|
|
// ---------------------------------------------------------
|
|
// TPM PlatformAuth | NULL | RANDOM
|
|
//
|
|
if (L05TpmLockRegister.Bits.State != L05_TPMLOCKD_DONE) {
|
|
//[-start-210618-YUNLEI0103-modify]//
|
|
#ifdef LCFC_SUPPORT
|
|
PcdSet32S (PcdDxeTpmPolicy, (PcdGet32 (PcdDxeTpmPolicy) | (SKIP_TPM_REVOKE_TRUST)));
|
|
#else
|
|
PcdSet32 (PcdDxeTpmPolicy, (PcdGet32 (PcdDxeTpmPolicy) | (SKIP_TPM_REVOKE_TRUST)));
|
|
#endif
|
|
//[-end-210618-YUNLEI0103-modify]//
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Modern Preload SMM entry.
|
|
|
|
Lenovo Notebook to support Modern Preload (aka Cloud Preload) Platform Key generation in MFG.
|
|
The main reason is Modern Preload Platform Key generation need TPM PlatformAuth is NULL.
|
|
|
|
@param ImageHandle The firmware allocated handle for the UEFI image.
|
|
@param SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval Others An unexpected error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
L05ModernPreloadSmmEntry (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_L05_SMM_SW_SMI_INTERFACE_PROTOCOL *L05SwSmiPtr;
|
|
|
|
mSmmCpu = NULL;
|
|
mSmmVariable = NULL;
|
|
L05SwSmiPtr = NULL;
|
|
|
|
//
|
|
// Locate SMM CPU protocol for CPU save state manipulation
|
|
//
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmCpuProtocolGuid,
|
|
NULL,
|
|
&mSmmCpu
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Locate SMM Variable protocol
|
|
//
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmVariableProtocolGuid,
|
|
NULL,
|
|
&mSmmVariable
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// [BIOS and Tool Requirements for Modern Preload Support, Version 1.00 Draft]
|
|
// 1.2 Structures definition
|
|
// BIOS should initially set this value to "Not Done" state. Only MFG utilities can change the value.
|
|
// The initial value about Bit1 and Bit0 should be 11b.
|
|
//
|
|
InitTpmLock ();
|
|
|
|
//
|
|
// [BIOS and Tool Requirements for Modern Preload Support, Version 1.00 Draft]
|
|
// 1.1 BIOS FLOW Chart
|
|
// Check TPMLOCK -> Not Done -> Continue POST
|
|
// |
|
|
// ---> Done -> Randomize PlatformAuth -> Continue POST
|
|
//
|
|
// 1.2 Structures definition
|
|
// BIOS Requirement Before and After TPMLOCK Done
|
|
// BIOS are required to send different TPM commands before and after TPMLOCK Done,
|
|
// make TPM behavior as below:
|
|
// | TPMLOCK = Not Done | TPMLOCK = Done
|
|
// ---------------------------------------------------------
|
|
// TPM PlatformAuth | NULL | RANDOM
|
|
//
|
|
CheckTpmLock ();
|
|
|
|
//
|
|
// Register Software SMI function with L05SwSmi Driver
|
|
//
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiL05SmmSwSmiInterfaceProtocolGuid,
|
|
NULL,
|
|
&L05SwSmiPtr
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = L05SwSmiPtr->RegisterCallbackFunction (
|
|
L05SwSmiPtr,
|
|
L05_SECURITY_SW_SMI,
|
|
FeatureCallbackType,
|
|
L05SmmModernPreloadCallback
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|