alder_lake_bios/Oem/L05/FeatureCommon/InsydeL05ModulePkg/ModernPreloadSmm/ModernPreloadSmm.c

626 lines
18 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"
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;
#ifdef L05_SPECIFIC_VARIABLE_SERVICE_ENABLE
EFI_L05_VARIABLE_PROTOCOL *L05VariablePtr;
UINT32 DataLength;
UINT8 TpmLock;
#else
EFI_L05_EEPROM_MAP_120 EepromBuffer;
UINTN EepromBase;
UINTN EepromSize;
#endif
Status = EFI_SUCCESS;
#ifdef L05_SPECIFIC_VARIABLE_SERVICE_ENABLE
L05VariablePtr = NULL;
Status = gSmst->SmmLocateProtocol (&gEfiL05VariableProtocolGuid, NULL, &L05VariablePtr);
if (EFI_ERROR (Status)) {
return Status;
}
TpmLock = 0;
DataLength = sizeof (TpmLock);
//
// Get TPM Lock data
//
Status = L05VariablePtr->GetVariable (
L05VariablePtr,
&gL05TpmLockGuid,
&DataLength,
&TpmLock
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get TPM Lock State
//
L05TpmLockRegister->Byte = TpmLock;
#else
ZeroMem (&EepromBuffer, sizeof (EepromBuffer));
EepromBase = (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionEepromGuid, 1);
EepromSize = (UINTN) FdmGetNAtSize (&gL05H2OFlashMapRegionEepromGuid, 1);
if ((EepromBase == 0) || (EepromSize == 0)) {
return EFI_UNSUPPORTED;
}
//
// Get EEPROM data
//
CopyMem ((VOID *) &EepromBuffer, (VOID *) EepromBase, sizeof (EepromBuffer));
//
// Get TPM Lock State
//
L05TpmLockRegister->Byte = EepromBuffer.TpmLock[0];
#endif
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;
#ifdef L05_SPECIFIC_VARIABLE_SERVICE_ENABLE
EFI_L05_VARIABLE_PROTOCOL *L05VariablePtr;
UINT32 DataLength;
UINT8 TpmLock;
#else
EFI_SMM_FW_BLOCK_SERVICE_PROTOCOL *SmmFwBlockService;
UINT8 *EepromBuffer;
EFI_L05_EEPROM_MAP_120 *EepromPtr;
UINTN EepromBase;
UINTN EepromSize;
#endif
Status = EFI_SUCCESS;
#ifdef L05_SPECIFIC_VARIABLE_SERVICE_ENABLE
L05VariablePtr = NULL;
Status = gSmst->SmmLocateProtocol (&gEfiL05VariableProtocolGuid, NULL, &L05VariablePtr);
if (EFI_ERROR (Status)) {
return Status;
}
TpmLock = 0;
DataLength = sizeof (TpmLock);
//
// Get TPM Lock data
//
Status = L05VariablePtr->GetVariable (
L05VariablePtr,
&gL05TpmLockGuid,
&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 = L05VariablePtr->SetVariable(
L05VariablePtr,
&gL05TpmLockGuid,
DataLength,
&TpmLock
);
#else
SmmFwBlockService = NULL;
EepromBase = (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionEepromGuid, 1);
EepromSize = (UINTN) FdmGetNAtSize (&gL05H2OFlashMapRegionEepromGuid, 1);
if ((EepromBase == 0) || (EepromSize == 0)) {
return EFI_UNSUPPORTED;
}
Status = gSmst->SmmLocateProtocol (&gEfiSmmFwBlockServiceProtocolGuid, NULL, &SmmFwBlockService);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get EEPROM data
//
EepromBuffer = AllocatePages (EFI_SIZE_TO_PAGES (EepromSize));
if (EepromBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
ZeroMem (EepromBuffer, (EFI_SIZE_TO_PAGES (EepromSize) * EFI_PAGE_SIZE));
CopyMem ((VOID *) EepromBuffer, (VOID *) EepromBase, EepromSize);
EepromPtr = (EFI_L05_EEPROM_MAP_120 *) EepromBuffer;
if (L05TpmLockRegister->Byte == EepromPtr->TpmLock[0]) {
return EFI_SUCCESS;
}
Status = SmmFwBlockService->EraseBlocks (SmmFwBlockService, EepromBase, &EepromSize);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Set TPM Lock State
//
EepromPtr->TpmLock[0] = L05TpmLockRegister->Byte;
Status = SmmFwBlockService->Write (SmmFwBlockService, EepromBase, &EepromSize, (VOID *) EepromBuffer);
FreePages (EepromBuffer, EFI_SIZE_TO_PAGES (EepromSize));
#endif
return Status;
}
/**
Set TPM Lock Done.
@param None.
@retval L05_MODERN_PRELOAD_SUCCESS Function successful.
@retval L05_MODERN_PRELOAD_SET_TPMLOCKD_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_SET_TPMLOCKD_FAILED;
}
L05TpmLockRegister.Bits.State = L05_TPMLOCKD_DONE;
Status = SetTpmLockState (&L05TpmLockRegister);
if (EFI_ERROR (Status)) {
return L05_MODERN_PRELOAD_SET_TPMLOCKD_FAILED;
}
return L05_MODERN_PRELOAD_SUCCESS;
}
/**
Check TPM Lock Done.
@param None.
@retval L05_MODERN_PRELOAD_TPMLOCK_DONE TPMLOCK Done.
@retval L05_MODERN_PRELOAD_GET_TPMLOCKD_FAILED Get TPMLOCK status failed.
@retval L05_MODERN_PRELOAD_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_GET_TPMLOCKD_FAILED;
}
if (L05TpmLockRegister.Bits.State == L05_TPMLOCKD_DONE) {
return L05_MODERN_PRELOAD_TPMLOCK_DONE;
}
return L05_MODERN_PRELOAD_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
)
{
EFI_STATUS Status;
UINT32 ErrorStatus;
UINT32 Eax;
UINT32 Ebx;
Status = EFI_SUCCESS;
ErrorStatus = L05_MODERN_PRELOAD_GET_TPMLOCKD_FAILED;
CpuNum = 0;
Eax = 0;
Ebx = 0;
//
// Find out which CPU triggered the S/W SMI and check function signature is valid or not
//
for (CpuNum = 0; CpuNum < gSmst->NumberOfCpus; CpuNum++) {
Status = ReadCpuRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
if (!EFI_ERROR (Status) && (Eax == L05_MODERN_PRELOAD_SUPPORT_FUNCTION)) {
break;
}
}
if (CpuNum == gSmst->NumberOfCpus) {
//
// Error out due to CPU not found
//
return EFI_NOT_FOUND;
}
//
// 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_SUCCESS) {
ErrorStatus = CheckTpmLockDone ();
if (ErrorStatus != L05_MODERN_PRELOAD_TPMLOCK_DONE) {
ErrorStatus = L05_MODERN_PRELOAD_SET_TPMLOCKD_FAILED;
}
}
break;
case L05_MODERN_PRELOAD_CHECK_TPMLOCKD:
ErrorStatus = CheckTpmLockDone ();
break;
default:
ErrorStatus = L05_MODERN_PRELOAD_GET_TPMLOCKD_FAILED;
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) {
PcdSet32S (PcdDxeTpmPolicy, (PcdGet32 (PcdDxeTpmPolicy) | (SKIP_TPM_REVOKE_TRUST)));
}
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;
}