1525 lines
45 KiB
C
1525 lines
45 KiB
C
/** @file
|
|
Runtime DXE driver implementation for the OemSvc Registration
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2014 - 2021, 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 "IhisiRegistration.h"
|
|
#include "IhisiServicesSmm.h"
|
|
#include "IhisiFbts.h"
|
|
#include "IhisiVats.h"
|
|
#include "SecureFlash.h"
|
|
#include <SmiTable.h>
|
|
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/SeamlessRecoveryLib.h>
|
|
#include <Library/LockBoxLib.h>
|
|
|
|
#include <Protocol/SmmAccess2.h>
|
|
#include <Protocol/SmmSwDispatch2.h>
|
|
#include <Protocol/SmmExitBootServices.h>
|
|
#include <Protocol/SmmReadyToBoot.h>
|
|
#include <Protocol/SmmEndOfDxe.h>
|
|
#include <Protocol/VariableLock.h>
|
|
|
|
STATIC UINTN mSmramRangeCount;
|
|
EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable;
|
|
STATIC EFI_SMRAM_DESCRIPTOR *mSmramRanges;
|
|
IHISI_CONTEXT *mIhisiContext = NULL;
|
|
EFI_SMM_FW_BLOCK_SERVICE_PROTOCOL *mSmmFwBlockService;
|
|
EFI_HANDLE mIhisiHandle = NULL;
|
|
BOOLEAN mCapsuleUpdate = FALSE;
|
|
BOOLEAN mRuntimeDisableIhisi = FALSE;
|
|
EFI_PHYSICAL_ADDRESS mIhisiCmdBuffer;
|
|
H2O_IHISI_PARAMS *mIhisiParaBuffer;
|
|
UINTN mIhisiCmdBufferSize;
|
|
EFI_PHYSICAL_ADDRESS mIhisiImageBuffer;
|
|
UINTN mIhisiImageBufferSize;
|
|
extern AP_COMMUNICATION_DATA_TABLE mApCommDataBuffer;
|
|
STATIC CONST BOOLEAN mPcdIhisiRegisterTableValid = (BOOLEAN)(FixedPcdGetPtrSize (PcdIhisiRegisterTable) % sizeof (PCD_IHISI_REGISTER_TABLE) == 0);
|
|
STATIC IHISI_SMI mForbiddenCmdInAuthNone [] = {
|
|
DATSWrite,
|
|
DATSErase,
|
|
FBTSWrite,
|
|
FBTSComplete,
|
|
FBTSSkipMcCheckAndBinaryTrans,
|
|
FETSWrite,
|
|
FMTSWrite,
|
|
OEMSFOEMExDataWrite,
|
|
FBTSCommonWrite,
|
|
FBTSWriteToSPIRom
|
|
};
|
|
BOOLEAN mPurifyVariable = FALSE;
|
|
|
|
REG_TO_PARAM_OFFSET mRegToParamOffset[] = {
|
|
{EFI_SMM_SAVE_STATE_REGISTER_RAX, OFFSET_OF(H2O_IHISI_PARAMS, Param1)},
|
|
{EFI_SMM_SAVE_STATE_REGISTER_RBX, OFFSET_OF(H2O_IHISI_PARAMS, Param2)},
|
|
{EFI_SMM_SAVE_STATE_REGISTER_RCX, OFFSET_OF(H2O_IHISI_PARAMS, Param3)},
|
|
{EFI_SMM_SAVE_STATE_REGISTER_RDX, OFFSET_OF(H2O_IHISI_PARAMS, Param4)},
|
|
{EFI_SMM_SAVE_STATE_REGISTER_RSI, OFFSET_OF(H2O_IHISI_PARAMS, Param5)},
|
|
{EFI_SMM_SAVE_STATE_REGISTER_RDI, OFFSET_OF(H2O_IHISI_PARAMS, Param6)},
|
|
{EFI_SMM_SAVE_STATE_REGISTER_FS, OFFSET_OF(H2O_IHISI_PARAMS, Param7)},
|
|
{EFI_SMM_SAVE_STATE_REGISTER_GS, OFFSET_OF(H2O_IHISI_PARAMS, Param8)},
|
|
};
|
|
|
|
|
|
/**
|
|
Allocate IHISI memory buffer to communicate information with tools.
|
|
|
|
@retval EFI_SUCCESS IHISI memory buffer is allocated.
|
|
@retval Others Allocate IHISI memory buffer failed.
|
|
**/
|
|
EFI_STATUS
|
|
AllocateIhisiBuffer (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN CmdBufferSize;
|
|
UINTN CmdBufferPaddingSize;
|
|
UINTN ImageBufferSize;
|
|
UINTN ImageBufferPaddingSize;
|
|
EFI_PHYSICAL_ADDRESS IhisiParaBufferAddress;
|
|
EFI_STATUS Status;
|
|
H2O_IHISI_PARAM_BUFFER_PROTOCOL *IhisParamBufferProtocol;
|
|
EFI_HANDLE Handle;
|
|
EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
|
|
|
|
IhisParamBufferProtocol = NULL;
|
|
IhisiParaBufferAddress = 0;
|
|
Status = gBS->AllocatePool (
|
|
EfiReservedMemoryType,
|
|
sizeof (H2O_IHISI_PARAMS),
|
|
&mIhisiParaBuffer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
mIhisiParaBuffer->Size = sizeof (H2O_IHISI_PARAMS);
|
|
mIhisiParaBuffer->Reserved = 0;
|
|
IhisiParaBufferAddress = (UINTN)mIhisiParaBuffer;
|
|
Status = CommonSetVariable (
|
|
IHISI_PARAM_BUFFER_VARIABLE_NAME,
|
|
&gH2OIhisParamBufferGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
sizeof (UINT64),
|
|
&IhisiParaBufferAddress
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = VariableLock->RequestToLock (VariableLock, IHISI_PARAM_BUFFER_VARIABLE_NAME, &gH2OIhisParamBufferGuid);
|
|
}
|
|
Status = gBS->AllocatePool (
|
|
EfiBootServicesData,
|
|
sizeof (H2O_IHISI_PARAM_BUFFER_PROTOCOL),
|
|
&IhisParamBufferProtocol
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
IhisParamBufferProtocol->ParamBuffer = IhisiParaBufferAddress;
|
|
Handle = NULL;
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Handle,
|
|
&gH2OIhisiParamBufferProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
(VOID*) IhisParamBufferProtocol
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
if (FeaturePcdGet(PcdH2OIhisiCmdBufferSupported)) {
|
|
CmdBufferSize = PcdGet32 (PcdH2OIhisiCmdBufferSize);
|
|
if (CmdBufferSize != 0) {
|
|
CmdBufferPaddingSize = 0;
|
|
if (FeaturePcdGet (PcdH2OIhisiCmdBufferPaddingSupported)) {
|
|
CmdBufferPaddingSize = EFI_PAGE_SIZE;
|
|
}
|
|
Status = gBS->AllocatePool (
|
|
EfiReservedMemoryType,
|
|
CmdBufferSize + 2 * CmdBufferPaddingSize,
|
|
(VOID **) (UINTN) (&mIhisiCmdBuffer)
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
if (CmdBufferPaddingSize != 0) {
|
|
SetMem32 ((VOID *)(UINTN)mIhisiCmdBuffer, CmdBufferPaddingSize, IHISI_EBX_SIGNATURE);
|
|
mIhisiCmdBuffer += CmdBufferPaddingSize;
|
|
}
|
|
mIhisiCmdBufferSize = CmdBufferSize;
|
|
if (CmdBufferPaddingSize != 0) {
|
|
SetMem32 ((VOID *)(UINTN)(mIhisiCmdBuffer + CmdBufferSize), CmdBufferPaddingSize, IHISI_EBX_SIGNATURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FeaturePcdGet(PcdH2OIhisiImageBufferSupported)) {
|
|
ImageBufferSize = PcdGet32 (PcdH2OIhisiImageBufferSize);
|
|
if (ImageBufferSize == 0) {
|
|
//
|
|
// TODO: Need add code to auto detect image buffer size because PcdH2OIhisiImageBufferSize
|
|
// is 0 indicate the image buffer should be auto detected at runtime.
|
|
//
|
|
}
|
|
ImageBufferPaddingSize = 0;
|
|
if (FeaturePcdGet (PcdH2OIhisiImageBufferPaddingSupported)) {
|
|
ImageBufferPaddingSize = EFI_PAGE_SIZE;
|
|
}
|
|
Status = gBS->AllocatePool (
|
|
EfiReservedMemoryType,
|
|
ImageBufferSize + 2 * ImageBufferPaddingSize,
|
|
(VOID **) (UINTN) (&mIhisiImageBuffer)
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
if (ImageBufferPaddingSize != 0) {
|
|
SetMem32 ((VOID *)(UINTN)mIhisiImageBuffer, ImageBufferPaddingSize, IHISI_EBX_SIGNATURE);
|
|
mIhisiImageBuffer += ImageBufferPaddingSize;
|
|
}
|
|
mIhisiImageBufferSize = ImageBufferSize;
|
|
if (ImageBufferPaddingSize != 0) {
|
|
SetMem32 ((VOID *)(UINTN)(mIhisiImageBuffer + ImageBufferSize), ImageBufferPaddingSize, IHISI_EBX_SIGNATURE);
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
if (mIhisiParaBuffer != NULL) {
|
|
gBS->FreePool (mIhisiParaBuffer);
|
|
}
|
|
if (IhisParamBufferProtocol != NULL) {
|
|
gBS->FreePool ((VOID *)(UINTN)IhisParamBufferProtocol);
|
|
}
|
|
if (mIhisiCmdBuffer != 0) {
|
|
gBS->FreePool ((VOID *)(UINTN)mIhisiCmdBuffer);
|
|
}
|
|
if (mIhisiImageBuffer != 0) {
|
|
gBS->FreePool ((VOID *)(UINTN)mIhisiImageBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
/**
|
|
The notification of gEfiSmmFwBlockServiceProtocolGuid protocol is installed
|
|
|
|
@param[in] Protocol Points to the protocol's unique identifier.
|
|
@param[in] Interface Points to the interface instance.
|
|
@param[in] Handle The handle on which the interface was installed.
|
|
|
|
@retval EFI_SUCCESS Locate gEfiSmmVariableProtocolGuid protocol successful.
|
|
@retval EFI_NOT_FOUND Cannot find gEfiSmmVariableProtocolGuid instance.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
SmmFwBlockNotify (
|
|
IN CONST EFI_GUID *Protocol,
|
|
IN VOID *Interface,
|
|
IN EFI_HANDLE Handle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmFwBlockServiceProtocolGuid,
|
|
NULL,
|
|
(VOID **) &mSmmFwBlockService
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
The notification of gEfiSmmVariableProtocolGuid protocol is installed
|
|
|
|
@param[in] Protocol Points to the protocol's unique identifier.
|
|
@param[in] Interface Points to the interface instance.
|
|
@param[in] Handle The handle on which the interface was installed.
|
|
|
|
@retval EFI_SUCCESS Locate gEfiSmmVariableProtocolGuid protocol successful.
|
|
@retval EFI_NOT_FOUND Cannot find gEfiSmmVariableProtocolGuid instance.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
SmmVariableNotify (
|
|
IN CONST EFI_GUID *Protocol,
|
|
IN VOID *Interface,
|
|
IN EFI_HANDLE Handle
|
|
)
|
|
{
|
|
return gSmst->SmmLocateProtocol (
|
|
&gEfiSmmVariableProtocolGuid,
|
|
NULL,
|
|
(VOID **) &mSmmVariable
|
|
);
|
|
}
|
|
|
|
UINT32
|
|
GetOtherIhisiStatus (
|
|
IN EFI_STATUS Status
|
|
)
|
|
{
|
|
switch (Status) {
|
|
case EFI_SUCCESS:
|
|
return (UINT32) IHISI_SUCCESS;
|
|
break;
|
|
|
|
case EFI_BUFFER_TOO_SMALL:
|
|
return (UINT32) IHISI_OB_LEN_TOO_SMALL;
|
|
break;
|
|
|
|
case EFI_INVALID_PARAMETER:
|
|
return (UINT32) IHISI_INVALID_PARAMETER;
|
|
break;
|
|
|
|
case EFI_UNSUPPORTED:
|
|
case EFI_NOT_FOUND:
|
|
return (UINT32) IHISI_UNSUPPORTED_FUNCTION;
|
|
break;
|
|
|
|
case EFI_OUT_OF_RESOURCES:
|
|
return (UINT32) IHISI_OUT_OF_RESOURCES;
|
|
break;
|
|
|
|
default:
|
|
return (UINT32) IHISI_ACCESS_PROHIBITED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Get IHISI status.translated from EFI status
|
|
|
|
@param[in] Status EFI_STATUS
|
|
|
|
@return UINT32 IHISI status
|
|
**/
|
|
UINT32
|
|
EfiStatusToIhisiStatus (
|
|
UINT32 FuncType,
|
|
EFI_STATUS Status
|
|
)
|
|
{
|
|
UINT32 IhisiStatus;
|
|
|
|
IhisiStatus = (UINT32)Status;
|
|
|
|
switch (FuncType) {
|
|
case VATSRead:
|
|
case VATSWrite:
|
|
case VATSNext:
|
|
IhisiStatus = GetVatsIhisiStatus(Status);
|
|
break;
|
|
|
|
case FBTSRead:
|
|
if (Status == EFI_ACCESS_DENIED) {
|
|
IhisiStatus = IHISI_FBTS_ME_LOCK_READ_FAILED;
|
|
break;
|
|
}
|
|
|
|
case FBTSWrite:
|
|
if (Status == EFI_WRITE_PROTECTED) {
|
|
IhisiStatus = IHISI_FBTS_SKIP_THIS_WRITE_BLOCK;
|
|
break;
|
|
}
|
|
case FBTSGetSupportVersion:
|
|
case FBTSGetPlatformInfo:
|
|
case FBTSGetPlatformRomMap:
|
|
case FBTSGetFlashPartInfo:
|
|
case FBTSComplete:
|
|
case FBTSGetRomFileAndPlatformTable:
|
|
case FBTSOemCustomization1:
|
|
case FBTSOemCustomization2:
|
|
case FBTSSkipMcCheckAndBinaryTrans:
|
|
case FBTSGetATpInformation:
|
|
case FBTSPassPlatforminiSettings:
|
|
case FBTSGetWholeBiosRomMap:
|
|
case FBTSApHookPoint:
|
|
case FBTSOEMCapsuleSecureFlash:
|
|
case FBTSPassImageFromTool:
|
|
case FBTSGetRuntimeBuffer:
|
|
case FBTSPassImagetoBios:
|
|
case FBTSWriteToSPIRom:
|
|
case FBTSCommonCommunication:
|
|
case FBTSCommonWrite:
|
|
case FBTSCommonRead:
|
|
IhisiStatus = GetFbtsIhisiStatus(Status);
|
|
break;
|
|
|
|
case FETSWrite:
|
|
case FETSGetEcPartInfo:
|
|
case FETSRead:
|
|
IhisiStatus = GetFbtsIhisiStatus(Status);
|
|
break;
|
|
|
|
default :
|
|
IhisiStatus = GetOtherIhisiStatus(Status);
|
|
break;
|
|
}
|
|
return IhisiStatus;
|
|
}
|
|
|
|
/**
|
|
Returned error code in AL.
|
|
|
|
@param[in] IhisiStatus Returned error code in AL.
|
|
**/
|
|
VOID
|
|
IhisiErrorCodeHandler (
|
|
IN UINT32 IhisiStatus
|
|
)
|
|
{
|
|
UINT32 Eax;
|
|
|
|
Eax = IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RAX);
|
|
Eax = (UINT32) ((Eax & 0xffffff00) | IhisiStatus);
|
|
IhisiProtWriteCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RAX, Eax);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
UpdateApRequestFlag (
|
|
UINT8 ApRequest
|
|
)
|
|
{
|
|
UINT32 Ecx;
|
|
|
|
Ecx = (IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX) & 0xffffff00) | ApRequest;
|
|
IhisiProtWriteCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX, Ecx);
|
|
}
|
|
|
|
BOOLEAN
|
|
IsSecureFlashFlagEnabled (
|
|
VOID
|
|
)
|
|
{
|
|
UINT8 SecureFlashFlag;
|
|
UINTN Size;
|
|
EFI_STATUS Status;
|
|
|
|
SecureFlashFlag = FALSE;
|
|
Size = sizeof (SecureFlashFlag);
|
|
Status = RestoreLockBox (&gSecureFlashInfoGuid, &SecureFlashFlag, &Size);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
}
|
|
return (BOOLEAN) (SecureFlashFlag == FLASH_COMPLETE_IN_RECOVERY);
|
|
}
|
|
|
|
/**
|
|
Update the purify variable flag according to gSecureFlashInfoGuid lock box data.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
UpdatePurifyVariableFlag (
|
|
VOID
|
|
)
|
|
{
|
|
UINT8 SecureFlashFlag;
|
|
UINTN Size;
|
|
EFI_STATUS Status;
|
|
|
|
mPurifyVariable = TRUE;
|
|
SecureFlashFlag = 0;
|
|
Size = sizeof (SecureFlashFlag);
|
|
Status = RestoreLockBox (&gSecureFlashInfoGuid, &SecureFlashFlag, &Size);
|
|
if (!EFI_ERROR (Status) && (BOOLEAN) (SecureFlashFlag == RESET_IN_RECOVERY)) {
|
|
mPurifyVariable = FALSE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RecoveryUpdateCheck (
|
|
VOID
|
|
)
|
|
{
|
|
UINT8 RequestAction;
|
|
|
|
if (IsSecureFlashFlagEnabled ()) {
|
|
RequestAction = (UINT8) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX);
|
|
UpdateLockBox (&gSecureFlashInfoGuid, 0, &RequestAction, sizeof (RequestAction));
|
|
UpdateApRequestFlag (0x00);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CapsuleUpdateCheck (
|
|
VOID
|
|
)
|
|
{
|
|
|
|
if (mCapsuleUpdate) {
|
|
//
|
|
// Force override CL register as 0x00 to avoid system do reset behavior during Windows Capsule Update
|
|
// It will cause ESRT of LastAttemptStatus field not be updated completely.
|
|
//
|
|
UpdateApRequestFlag (0x00);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SpecialCaseFunHook(
|
|
UINT32 Cmd
|
|
)
|
|
{
|
|
if (!IsSecureFlashFlagEnabled () && IsFirmwareFailureRecovery ()) {
|
|
//
|
|
// Check the PcdSeamlessRecoverySignature GUID have been existed on FtwSpareBase during CapsuleUpdate
|
|
//
|
|
mCapsuleUpdate = TRUE;
|
|
}
|
|
|
|
switch (Cmd) {
|
|
|
|
case FBTSComplete:
|
|
UpdatePurifyVariableFlag ();
|
|
if (PcdGetBool (PcdSecureFlashSupported)) {
|
|
//
|
|
// If the firmware update is initiated by capsule update, IHISI FBTS Complete should not
|
|
// reset or shutdown the system, otherwise the ESRT status won't get updated
|
|
//
|
|
CapsuleUpdateCheck ();
|
|
|
|
//
|
|
// In Crisis recovery process, IHISI FBTS complete should not reset or shutdown the system
|
|
//
|
|
RecoveryUpdateCheck ();
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
IHISI_COMMAND_ENTRY *
|
|
IhisiFindCommandEntry (
|
|
UINT32 CmdNumber
|
|
)
|
|
{
|
|
IHISI_COMMAND_ENTRY *Node;
|
|
LIST_ENTRY *Link;
|
|
|
|
Link = GetFirstNode (&mIhisiContext->CommandList);
|
|
while (!IsNull (&mIhisiContext->CommandList, Link)) {
|
|
Node = IHISI_COMMAND_ENTRY_FROM_LINK (Link);
|
|
if (Node->CmdNumber == CmdNumber) {
|
|
return Node;
|
|
}
|
|
Link = GetNextNode (&mIhisiContext->CommandList, Link);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Internal function to chekc if platform supports input IHISI command.
|
|
|
|
@param[in] CmdNumber Command code of the IHISI function
|
|
|
|
@retval TRUE Platform supports this IHISI command
|
|
@retval FALSE Platform doens't support this IHISI command
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IhsiCommandSupported (
|
|
IN UINT32 CmdNumber
|
|
)
|
|
{
|
|
if (!FeaturePcdGet (PcdH2OIhisiVatsSupported)) {
|
|
if (CmdNumber == VATSRead || CmdNumber == VATSWrite || CmdNumber == VATSNext) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!FeaturePcdGet (PcdH2OIhisiFbtsSupported)) {
|
|
if (CmdNumber >= FBTSGetSupportVersion && CmdNumber <= FBTSApHookPoint) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (!FeaturePcdGet (PcdH2OIhisiFbts2Supported)) {
|
|
if (CmdNumber >= FBTSOEMCapsuleSecureFlash && CmdNumber <= FBTSWriteToSPIRom) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!FeaturePcdGet (PcdH2OIhisiFetsSupported)) {
|
|
if (CmdNumber == FETSWrite || CmdNumber == FETSGetEcPartInfo || CmdNumber == FETSRead) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (!FeaturePcdGet (PcdH2OIhisiFmtsSupported)) {
|
|
if (CmdNumber == FMTSGetMEPartInfo || CmdNumber == FMTSWrite) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!FeaturePcdGet (PcdH2OIhisiDatsSupported)) {
|
|
if (CmdNumber == DATSWrite || CmdNumber == DATSErase || CmdNumber == DATSQuery) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!FeaturePcdGet (PcdH2OIhisiAuthSupported)) {
|
|
if (CmdNumber == IhisiAuthStatus || CmdNumber == IhisiAuthLock || CmdNumber == IhisiAuthUnlock || CmdNumber == IhisiAuth) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IhisiProtRegisterCommand (
|
|
IN UINT32 CmdNumber,
|
|
IN IHISI_FUNCTION IhisiFunction,
|
|
IN UINT8 Priority
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
IHISI_COMMAND_ENTRY *CmdNode;
|
|
IHISI_FUNCTION_ENTRY *FunctionNode;
|
|
IHISI_FUNCTION_ENTRY *FunctionEntry;
|
|
|
|
if (!IhsiCommandSupported (CmdNumber)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
CmdNode = IhisiFindCommandEntry (CmdNumber);
|
|
if (CmdNode == NULL) {
|
|
CmdNode = AllocatePool (sizeof (IHISI_COMMAND_ENTRY));
|
|
if (CmdNode == NULL) {
|
|
ASSERT (CmdNode != NULL);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
CmdNode->Signature = IHISI_SIGNATURE;
|
|
CmdNode->CmdNumber = CmdNumber;
|
|
InitializeListHead (&CmdNode->FunctionChain);
|
|
InsertTailList (&mIhisiContext->CommandList, &CmdNode->Link);
|
|
}
|
|
|
|
FunctionEntry = AllocatePool (sizeof(IHISI_FUNCTION_ENTRY));
|
|
if (FunctionEntry == NULL) {
|
|
ASSERT (FunctionEntry != NULL);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
FunctionEntry->Signature = IHISI_SIGNATURE;
|
|
FunctionEntry->Function = IhisiFunction;
|
|
FunctionEntry->Priority = Priority;
|
|
|
|
Link = GetFirstNode (&CmdNode->FunctionChain);
|
|
while (!IsNull (&CmdNode->FunctionChain, Link)) {
|
|
FunctionNode = IHISI_FUNCTION_ENTRY_FROM_LINK (Link);
|
|
if (FunctionNode->Priority == Priority) {
|
|
FreePool (FunctionEntry);
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
if (FunctionNode->Priority < Priority) {
|
|
InsertTailList (Link, &FunctionEntry->Link);
|
|
return EFI_SUCCESS;
|
|
}
|
|
Link = GetNextNode (&CmdNode->FunctionChain, Link);
|
|
}
|
|
InsertTailList (&CmdNode->FunctionChain, &FunctionEntry->Link);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IhisiProtRemoveFunctions (
|
|
UINT32 CmdNumber,
|
|
UINT8 FromPriority,
|
|
UINT8 ToPriority
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
LIST_ENTRY *NextLink;
|
|
IHISI_COMMAND_ENTRY *CmdNode;
|
|
IHISI_FUNCTION_ENTRY *FunctionNode;
|
|
|
|
CmdNode = IhisiFindCommandEntry (CmdNumber);
|
|
if (CmdNode == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
Link = GetFirstNode (&CmdNode->FunctionChain);
|
|
while (!IsNull (&CmdNode->FunctionChain, Link)) {
|
|
NextLink = GetNextNode (&CmdNode->FunctionChain, Link);
|
|
FunctionNode = IHISI_FUNCTION_ENTRY_FROM_LINK (Link);
|
|
if ( (FunctionNode->Priority >= FromPriority) &&
|
|
(FunctionNode->Priority <= ToPriority)) {
|
|
RemoveEntryList (&FunctionNode->Link);
|
|
FreePool (FunctionNode);
|
|
}
|
|
Link = NextLink;
|
|
}
|
|
if (IsListEmpty (&CmdNode->FunctionChain)) {
|
|
RemoveEntryList (&CmdNode->Link);
|
|
FreePool (CmdNode);
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IhisiProtExecuteCommand (
|
|
UINT32 CmdNumber
|
|
)
|
|
{
|
|
return IhisiProtExecuteCommandByPriority (CmdNumber, 0x00, 0xFF);
|
|
}
|
|
|
|
|
|
UINT32
|
|
EFIAPI
|
|
IhisiProtReadCpuReg32 (
|
|
IN EFI_SMM_SAVE_STATE_REGISTER RegisterNum
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN Count;
|
|
|
|
Count = sizeof (mRegToParamOffset) / sizeof (REG_TO_PARAM_OFFSET);
|
|
for (Index = 0; Index < Count; Index++) {
|
|
if (RegisterNum != mRegToParamOffset[Index].Reg) {
|
|
continue;
|
|
}
|
|
|
|
return (UINT32)(*(UINT64 *)((UINTN)mIhisiParaBuffer + mRegToParamOffset[Index].ParmOffset));
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IhisiProtWriteCpuReg32 (
|
|
IN EFI_SMM_SAVE_STATE_REGISTER RegisterNum,
|
|
IN UINT32 Value
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN Count;
|
|
|
|
Count = sizeof (mRegToParamOffset) / sizeof (REG_TO_PARAM_OFFSET);
|
|
for (Index = 0; Index < Count; Index++) {
|
|
if (RegisterNum != mRegToParamOffset[Index].Reg) {
|
|
continue;
|
|
}
|
|
*(UINT64 *)((UINTN)mIhisiParaBuffer + mRegToParamOffset[Index].ParmOffset) = Value;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
Check the input memory buffer is whether overlap the SMRAM ranges.
|
|
|
|
@param[in] Buffer The pointer to the buffer to be checked.
|
|
@param[in] BufferSize The size in bytes of the input buffer
|
|
|
|
@retval TURE The buffer overlaps SMRAM ranges.
|
|
@retval FALSE The buffer doesn't overlap SMRAM ranges.
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
IhisiProtBufferOverlapSmram (
|
|
IN VOID *Buffer,
|
|
IN UINTN BufferSize
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_PHYSICAL_ADDRESS BufferEnd;
|
|
EFI_PHYSICAL_ADDRESS BufferStart;
|
|
|
|
if (Buffer == NULL || BufferSize == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
BufferStart = (EFI_PHYSICAL_ADDRESS) (UINTN) Buffer;
|
|
BufferEnd = (EFI_PHYSICAL_ADDRESS) ((UINTN) Buffer + BufferSize - 1);
|
|
|
|
//
|
|
// Integer overflow check
|
|
//
|
|
if (BufferEnd < BufferStart) {
|
|
return TRUE;
|
|
}
|
|
|
|
for (Index = 0; Index < mSmramRangeCount; Index ++) {
|
|
//
|
|
// The condition for two ranges doesn't overlap is:
|
|
// Buffer End is smaller than the range start or Buffer start is larger than the range end.
|
|
// so the overlap condition is above condition isn't satisfied.
|
|
//
|
|
if (!(BufferEnd < mSmramRanges[Index].CpuStart ||
|
|
BufferStart >= (mSmramRanges[Index].CpuStart + mSmramRanges[Index].PhysicalSize))) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Check the input memory buffer whether lies within the command buffer..
|
|
|
|
@param[in] Buffer Pointer to the first byte in a buffer.
|
|
@param[in] BufferSize Unsigned integer that specifies the size of the Buffer, in bytes.
|
|
|
|
@retval TURE Buffer lies within the command buffer.
|
|
@retval FALSE Buffer lies outside the command buffer.
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
IhisiProtBufferInCmdBuffer (
|
|
IN VOID *Buffer,
|
|
IN UINTN BufferSize
|
|
)
|
|
{
|
|
EFI_PHYSICAL_ADDRESS BufferEnd;
|
|
EFI_PHYSICAL_ADDRESS BufferStart;
|
|
|
|
if (!FeaturePcdGet(PcdH2OIhisiCmdBufferSupported)) {
|
|
return IhisiProtBufferOverlapSmram (Buffer, BufferSize) ? FALSE : TRUE;
|
|
}
|
|
|
|
if (Buffer == NULL || BufferSize == 0) {
|
|
return FALSE;
|
|
}
|
|
BufferStart = (EFI_PHYSICAL_ADDRESS) (UINTN) Buffer;
|
|
BufferEnd = (EFI_PHYSICAL_ADDRESS) ((UINTN) Buffer + BufferSize - 1);
|
|
|
|
//
|
|
// Integer overflow check
|
|
//
|
|
if (BufferEnd < BufferStart) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (BufferStart >= mIhisiCmdBuffer &&
|
|
BufferEnd < mIhisiCmdBuffer + mIhisiCmdBufferSize) {
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/**
|
|
This function returns the current possible security levels and the current security level (if any).
|
|
|
|
@param[out] SecurityLevelsSupported Pointer to a bitmask that indicates the security levels supported by
|
|
IHISI on this platform.
|
|
@param[out] CurrentSecurityLevel Pointer to a bitmask that indicates which security level was used when the
|
|
IHISI session was opened.
|
|
|
|
@retval EFI_SUCCESS This function completed successfully.
|
|
@return EFI_INVALID_PARAMETER SecurityLevelsSupported or CurrentSecurityLevel are NULL.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IhisiProtGetAuthStatus (
|
|
OUT UINT32 *SecurityLevelsSupported,
|
|
OUT UINT32 *CurrentSecurityLevel
|
|
)
|
|
{
|
|
if (SecurityLevelsSupported == NULL || CurrentSecurityLevel == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
//
|
|
// TODO: Need add code to determine the current security level in different time. also need
|
|
// report what security level is supported in different situation.
|
|
//
|
|
*SecurityLevelsSupported = GetSupportedIhisiSecurityLevels ();
|
|
*CurrentSecurityLevel = GetCurrentIhisiSecurityLevel ();
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This function gets the command buffer and size in bytes of the command buffer.
|
|
|
|
@param[out] Buffer The address to return a pointer to the command buffer.
|
|
@param[out] BufferSize The size in bytes of the return command buffer.
|
|
|
|
@retval EFI_SUCCESS This function completed successfully.
|
|
@return EFI_INVALID_PARAMETER Buffer or BufferSize is NULL.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IhisiProtGetCmdBuffer (
|
|
OUT VOID **Buffer,
|
|
OUT UINTN *BufferSize
|
|
)
|
|
{
|
|
if (Buffer == NULL || BufferSize == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*Buffer = (VOID *) (UINTN) mIhisiCmdBuffer;
|
|
*BufferSize = mIhisiCmdBufferSize;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This function gets the image buffer and size in bytes of the image buffer.
|
|
|
|
@param[out] Buffer The address to return a pointer to the image buffer.
|
|
@param[out] BufferSize The size in bytes of the return image buffer.
|
|
|
|
@retval EFI_SUCCESS This function completed successfully.
|
|
@return EFI_INVALID_PARAMETER Buffer or BufferSize is NULL.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IhisiProtGetImageBuffer (
|
|
OUT VOID **Buffer,
|
|
OUT UINTN *BufferSize
|
|
)
|
|
{
|
|
if (Buffer == NULL || BufferSize == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*Buffer = (VOID *) (UINTN) mIhisiImageBuffer;
|
|
*BufferSize = mIhisiImageBufferSize;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This function returns whether the bytes from Buffer through Buffer + BufferSize - 1
|
|
lie entirely within the image buffer (TRUE) or whether one or more bytes lie outside the
|
|
image buffer (FALSE).
|
|
If PcdH2OIhisiImageBufferSupported is set to FALSE, then this function will always
|
|
return TRUE.
|
|
|
|
@param[in] Buffer Pointer to the first byte in a buffer.
|
|
@param[in] BufferSize Unsigned integer that specifies the size of the Buffer, in bytes.
|
|
|
|
@retval TRUE Whole buffer lies within the image buffer.
|
|
@return FALSE Any of byte in buffer lies outside the image buffer.
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
IhisiProtBufferInImageBuffer (
|
|
IN VOID *Buffer,
|
|
IN UINTN BufferSize
|
|
)
|
|
{
|
|
EFI_PHYSICAL_ADDRESS BufferEnd;
|
|
EFI_PHYSICAL_ADDRESS BufferStart;
|
|
|
|
if (!FeaturePcdGet(PcdH2OIhisiImageBufferSupported)) {
|
|
return IhisiProtBufferOverlapSmram (Buffer, BufferSize) ? FALSE : TRUE;
|
|
}
|
|
|
|
if (Buffer == NULL || BufferSize == 0) {
|
|
return FALSE;
|
|
}
|
|
BufferStart = (EFI_PHYSICAL_ADDRESS) (UINTN) Buffer;
|
|
BufferEnd = (EFI_PHYSICAL_ADDRESS) ((UINTN) Buffer + BufferSize - 1);
|
|
|
|
//
|
|
// Integer overflow check
|
|
//
|
|
if (BufferEnd < BufferStart) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (BufferStart >= mIhisiImageBuffer &&
|
|
BufferEnd < mIhisiImageBuffer + mIhisiImageBufferSize) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Handle all registered handlers for the specified IHISI command with priorities greater than
|
|
or equal to FromPriority and less than or equal to ToPriority, or until one of the handlers
|
|
returns IHISI_END_FUNCTION_CHAIN.
|
|
|
|
Functions ranges from FromPriority to ToPriority (inclusive) in the IHISI command function
|
|
chain will be executed.
|
|
|
|
@param[in] CmdCode Pointer to the first byte in a buffer.
|
|
@param[in] FromPriority Vlaue of From Priority
|
|
@param[in] ToPriority Vlaue of To Priority
|
|
|
|
@retval TRUE Whole buffer lies within the image buffer.
|
|
@return FALSE Any of byte in buffer lies outside the image buffer.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IhisiProtExecuteCommandByPriority (
|
|
IN UINT32 CmdCode,
|
|
IN UINT8 FromPriority,
|
|
IN UINT8 ToPriority
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LIST_ENTRY *Link;
|
|
IHISI_COMMAND_ENTRY *CmdNode;
|
|
IHISI_FUNCTION_ENTRY *FunctionNode;
|
|
|
|
CmdNode = IhisiFindCommandEntry (CmdCode);
|
|
if (CmdNode == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
;
|
|
for (Link = GetFirstNode (&CmdNode->FunctionChain);
|
|
!IsNull (&CmdNode->FunctionChain, Link);
|
|
Link = GetNextNode (&CmdNode->FunctionChain, Link)) {
|
|
FunctionNode = IHISI_FUNCTION_ENTRY_FROM_LINK (Link);
|
|
if (FunctionNode->Priority > ToPriority || FunctionNode->Priority < FromPriority) {
|
|
continue;
|
|
}
|
|
Status = FunctionNode->Function();
|
|
if (Status == IHISI_END_FUNCTION_CHAIN) {
|
|
return EFI_SUCCESS;
|
|
} else if (Status != EFI_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
if (CmdCode == OEMSFOEMExCommunication) {
|
|
CopyMem(
|
|
(AP_COMMUNICATION_DATA_TABLE*) (UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX),
|
|
&mApCommDataBuffer,
|
|
sizeof (AP_COMMUNICATION_DATA_TABLE)
|
|
);
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This function gets the parameter buffer and size in bytes of the parameter buffer.
|
|
|
|
@param[out] ParamBuffer points to the address of the first byte of the parameter buffer.
|
|
@param[out] ParamBufferSize points to the unsigned integer that indicates the size of the
|
|
parameter buffer.
|
|
|
|
@retval EFI_SUCCESS Function completed successfully.
|
|
@return EFI_NOT_FOUND Parameter Buffer not found.
|
|
@return EFI_INVALID_PARAMETER ParamBuffer is NULL or ParamBufferSize is NULL.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IhisiProtGetParamBuffer (
|
|
OUT H2O_IHISI_PARAMS **ParamBuffer,
|
|
OUT UINT32 *ParamBufferSize
|
|
)
|
|
{
|
|
if (ParamBuffer == NULL || ParamBufferSize == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (mIhisiParaBuffer == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*ParamBuffer = mIhisiParaBuffer;
|
|
*ParamBufferSize = sizeof (H2O_IHISI_PARAMS);
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
/**
|
|
Internal function to check the input IHISI command is allowed to execute in current security level.
|
|
|
|
@param[in] Cmd IHISI command number.
|
|
|
|
@retval TRUE This IHISI command is allowed to execute in current security level.
|
|
@retval FALSE This IHISI command is NOT allowed to execute in current security level.
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
CheckCommandBySecurityLevel (
|
|
IHISI_SMI Cmd
|
|
)
|
|
{
|
|
UINTN CmdCount;
|
|
UINTN Index;
|
|
|
|
if (((UINTN)GetCurrentIhisiSecurityLevel () & ~((UINTN)IHISI_AUTH_NONE)) != 0) {
|
|
return TRUE;
|
|
}
|
|
CmdCount = sizeof (mForbiddenCmdInAuthNone) / sizeof (IHISI_SMI);
|
|
for (Index = 0; Index < CmdCount; Index++) {
|
|
if (mForbiddenCmdInAuthNone[Index] == Cmd) {
|
|
//
|
|
// TODO: Some function may partial forbidden in IHISI_AUTH_NONE and we may need handle this case.
|
|
// Ex: Function 0x16 only need forbidden in IHISI_AUTH_NONE if CompleteStatus is NormalFlash.
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
IHISI Services entry function
|
|
|
|
@param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
|
|
@param[in] Context Points to an optional handler context which was specified when the
|
|
handler was registered.
|
|
@param[in,out] CommBuffer A pointer to a collection of data in memory that will
|
|
be conveyed from a non-SMM environment into an SMM environment.
|
|
@param[in,out] CommBufferSize The size of the CommBuffer.
|
|
|
|
@retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
|
|
should still be called.
|
|
@retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
|
|
still be called.
|
|
@retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
|
|
be called.
|
|
@retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
IhisiServicesCallback (
|
|
IN EFI_HANDLE DispatchHandle,
|
|
IN CONST VOID *Context OPTIONAL,
|
|
IN OUT VOID *CommBuffer OPTIONAL,
|
|
IN OUT UINTN *CommBufferSize OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Cmd;
|
|
UINT32 IhisiStatus;
|
|
UINT32 RegisterValue;
|
|
IHISI_COMMAND_ENTRY *CommandEntry;
|
|
UINT32 SessionToken;
|
|
UINT32 RandomNum;
|
|
H2O_IHISI_PARAMS *OrgIhisiParaBuffer;
|
|
|
|
if (mSmmFwBlockService == NULL) {
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmFwBlockServiceProtocolGuid,
|
|
NULL,
|
|
(VOID **) &mSmmFwBlockService
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
IhisiErrorCodeHandler ((UINT32)IHISI_ACCESS_PROHIBITED);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (mSmmVariable == NULL) {
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmVariableProtocolGuid,
|
|
NULL,
|
|
(VOID **) &mSmmVariable
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
IhisiErrorCodeHandler ((UINT32)IHISI_ACCESS_PROHIBITED);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
//
|
|
// Copy H2O_IHISI_PARAMS to SMRAM to prevent from TOCTOU attack.
|
|
//
|
|
OrgIhisiParaBuffer = mIhisiParaBuffer;
|
|
mIhisiParaBuffer = AllocateCopyPool (sizeof (H2O_IHISI_PARAMS), OrgIhisiParaBuffer);
|
|
if (mIhisiParaBuffer == NULL) {
|
|
mIhisiParaBuffer = OrgIhisiParaBuffer;
|
|
return EFI_SUCCESS;
|
|
}
|
|
//
|
|
// Find out which CPU triggered the S/W SMI
|
|
//
|
|
SessionToken = 0;
|
|
RegisterValue = (UINT32)mIhisiParaBuffer->Param2;
|
|
if (RegisterValue != IHISI_EBX_SIGNATURE && (mIhisiLock && RegisterValue != mIhisiSessionToken)) {
|
|
IhisiErrorCodeHandler ((UINT32)IHISI_ACCESS_PROHIBITED);
|
|
Status = EFI_NOT_FOUND;
|
|
goto Done;
|
|
}
|
|
SessionToken = RegisterValue;
|
|
if (mRuntimeDisableIhisi == TRUE) {
|
|
IhisiErrorCodeHandler ((UINT32)IHISI_ACCESS_PROHIBITED);
|
|
Status = EFI_NOT_FOUND;
|
|
goto Done;
|
|
}
|
|
|
|
RegisterValue = (UINT32)mIhisiParaBuffer->Param1;
|
|
Cmd = (UINTN)((RegisterValue >> 8) & 0xFF);
|
|
if (FeaturePcdGet (PcdH2OIhisiAuthSupported)) {
|
|
if (Cmd != IhisiAuthStatus && Cmd != IhisiAuthLock && Cmd != IhisiAuthUnlock) {
|
|
if (!mIhisiLock) {
|
|
IhisiErrorCodeHandler (IHISI_INTERFACE_CLOSED);
|
|
Status = EFI_SUCCESS;
|
|
goto Done;
|
|
} else if (SessionToken != mIhisiSessionToken) {
|
|
IhisiErrorCodeHandler (IHISI_ACCESS_PROHIBITED);
|
|
Status = EFI_SUCCESS;
|
|
goto Done;
|
|
}
|
|
} else if (Cmd == IhisiAuthStatus && mIhisiLock && SessionToken != mIhisiSessionToken) {
|
|
IhisiErrorCodeHandler (IHISI_ACCESS_PROHIBITED);
|
|
Status = EFI_SUCCESS;
|
|
goto Done;
|
|
}
|
|
|
|
if (!CheckCommandBySecurityLevel ((IHISI_SMI)Cmd)) {
|
|
IhisiErrorCodeHandler (IHISI_ACCESS_PROHIBITED);
|
|
Status = EFI_SUCCESS;
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
RandomNum = 0;
|
|
GetRandomNumber32 (&RandomNum);
|
|
CommandEntry = IhisiFindCommandEntry (Cmd);
|
|
if (CommandEntry == NULL) {
|
|
if (FeaturePcdGet (PcdH2OIhisiAuthSupported)) {
|
|
mIhisiSessionToken = RandomNum;
|
|
IhisiProtWriteCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RBX, RandomNum);
|
|
}
|
|
IhisiErrorCodeHandler ((UINT32)IHISI_UNSUPPORTED_FUNCTION);
|
|
Status = EFI_NOT_FOUND;
|
|
goto Done;
|
|
}
|
|
|
|
SpecialCaseFunHook(Cmd);
|
|
Status = IhisiProtExecuteCommand (Cmd);
|
|
if ((Status & IHISI_STATUS_BIT) == 00) {
|
|
IhisiStatus = EfiStatusToIhisiStatus (Cmd, Status);
|
|
} else {
|
|
IhisiStatus = (UINT32)Status;
|
|
}
|
|
|
|
IhisiErrorCodeHandler (IhisiStatus);
|
|
if (IhisiStatus == IHISI_BUFFER_RANGE_ERROR) {
|
|
mIhisiLock = FALSE;
|
|
mIhisiSessionToken = IHISI_EBX_SIGNATURE;
|
|
//
|
|
// needn't update session token in below cases:
|
|
// 1. IhisiAuthStatus (0x80). This is because the session token is already controlled by this function.
|
|
// 2. IhisiAuthLock (0x81). This is because the session token is already controlled by this function.
|
|
// 3. IhisiAuthUnlock (0x82). After doing unlock, will unlock the system and set session token to default ($H2O).
|
|
//
|
|
} else if (FeaturePcdGet (PcdH2OIhisiAuthSupported) && Cmd != IhisiAuthLock && Cmd != IhisiAuthUnlock && Cmd != IhisiAuthStatus) {
|
|
mIhisiSessionToken = RandomNum;
|
|
IhisiProtWriteCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RBX, RandomNum);
|
|
}
|
|
Status = EFI_SUCCESS;
|
|
Done:
|
|
//
|
|
// Restore data from SMRAM to IHISI parameter buffer.
|
|
//
|
|
CopyMem (OrgIhisiParaBuffer, mIhisiParaBuffer, sizeof (H2O_IHISI_PARAMS));
|
|
FreePool (mIhisiParaBuffer);
|
|
mIhisiParaBuffer = OrgIhisiParaBuffer;
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
IHISI Protocol installation routine
|
|
|
|
@retval EFI_SUCCESS: IHISI Protocol is successfully installed
|
|
@retval Others Failed to install IHISI Protocol
|
|
**/
|
|
EFI_STATUS
|
|
InstallIhisiProtocol(
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE Handle;
|
|
EFI_SMM_SW_REGISTER_CONTEXT SwContext;
|
|
EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
|
|
|
|
//
|
|
// Software SMI for IHISI services callback function
|
|
//
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmSwDispatch2ProtocolGuid,
|
|
NULL,
|
|
(VOID **) &SwDispatch
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
SwContext.SwSmiInputValue = IHISI_SW_SMI;
|
|
Status = SwDispatch->Register (
|
|
SwDispatch,
|
|
IhisiServicesCallback,
|
|
&SwContext,
|
|
&mIhisiHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
Handle = NULL;
|
|
Status = gSmst->SmmInstallProtocolInterface (
|
|
&Handle,
|
|
&gH2OIhisiProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&mIhisiContext->Ihisi
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (mIhisiContext);
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InitIhisi (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Size;
|
|
EFI_SMM_ACCESS2_PROTOCOL *SmmAccess;
|
|
VOID *FwBlockRegistration;
|
|
VOID *VariableRegistration;
|
|
|
|
//
|
|
// Locate SMM FwBlock Protocol
|
|
//
|
|
mSmmFwBlockService = NULL;
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmFwBlockServiceProtocolGuid,
|
|
NULL,
|
|
(VOID **) &mSmmFwBlockService
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Status = gSmst->SmmRegisterProtocolNotify (
|
|
&gEfiSmmFwBlockServiceProtocolGuid,
|
|
SmmFwBlockNotify,
|
|
&FwBlockRegistration
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
//
|
|
// Locate SMM Variable Protocol
|
|
//
|
|
mSmmVariable = NULL;
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmVariableProtocolGuid,
|
|
NULL,
|
|
(VOID **) &mSmmVariable
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Status = gSmst->SmmRegisterProtocolNotify (
|
|
&gEfiSmmVariableProtocolGuid,
|
|
SmmVariableNotify,
|
|
&VariableRegistration
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
//
|
|
// Locate SMM Access2 Protocol
|
|
//
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiSmmAccess2ProtocolGuid,
|
|
NULL,
|
|
(VOID **)&SmmAccess
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get SMRAM range information
|
|
//
|
|
Size = 0;
|
|
Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
|
|
ASSERT (Status == EFI_BUFFER_TOO_SMALL);
|
|
mSmramRanges = NULL;
|
|
mSmramRanges = AllocatePool (Size);
|
|
ASSERT (mSmramRanges != NULL);
|
|
if (mSmramRanges == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmramRanges);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
mSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
|
|
|
|
//
|
|
//Initialize mIhisiContext
|
|
//
|
|
mIhisiContext = AllocateZeroPool (sizeof (IHISI_CONTEXT));
|
|
if (mIhisiContext == NULL) {
|
|
ASSERT (mIhisiContext != NULL);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
InitializeListHead (&mIhisiContext->CommandList);
|
|
mIhisiContext->Signature = IHISI_SIGNATURE;
|
|
mIhisiContext->Ihisi.Size = sizeof (H2O_IHISI_PROTOCOL);
|
|
mIhisiContext->Ihisi.RegisterCommand = IhisiProtRegisterCommand;
|
|
mIhisiContext->Ihisi.RemoveFunctions = IhisiProtRemoveFunctions;
|
|
mIhisiContext->Ihisi.ExecuteCommand = IhisiProtExecuteCommand;
|
|
mIhisiContext->Ihisi.ReadCpuReg32 = IhisiProtReadCpuReg32;
|
|
mIhisiContext->Ihisi.WriteCpuReg32 = IhisiProtWriteCpuReg32;
|
|
mIhisiContext->Ihisi.BufferOverlapSmram = IhisiProtBufferOverlapSmram;
|
|
mIhisiContext->Ihisi.BufferInCmdBuffer = IhisiProtBufferInCmdBuffer;
|
|
mIhisiContext->Ihisi.GetAuthStatus = IhisiProtGetAuthStatus;
|
|
mIhisiContext->Ihisi.GetCmdBuffer = IhisiProtGetCmdBuffer;
|
|
mIhisiContext->Ihisi.GetImageBuffer = IhisiProtGetImageBuffer;
|
|
mIhisiContext->Ihisi.BufferInImageBuffer = IhisiProtBufferInImageBuffer;
|
|
mIhisiContext->Ihisi.ExecuteCommandByPriority = IhisiProtExecuteCommandByPriority;
|
|
mIhisiContext->Ihisi.GetParamBuffer = IhisiProtGetParamBuffer;
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Unregister the SMI which used by IHISI interface.
|
|
|
|
@retval EFI_SUCCESS: Locate EFI_SMM_SW_DISPATCH2_PROTOCOL success
|
|
@retval Others EFI_SMM_SW_DISPATCH2_PROTOCOL not installed
|
|
**/
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
RemoveIhisiServicesCallback (
|
|
IN CONST EFI_GUID *Protocol,
|
|
IN VOID *Interface,
|
|
IN EFI_HANDLE Handle
|
|
)
|
|
{
|
|
if (mIhisiHandle != NULL) {
|
|
mRuntimeDisableIhisi = TRUE;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IhisiRuntimeProtect (
|
|
VOID
|
|
)
|
|
{
|
|
VOID *Registration;
|
|
|
|
mRuntimeDisableIhisi = FALSE;
|
|
gSmst->SmmRegisterProtocolNotify (
|
|
&gEdkiiSmmExitBootServicesProtocolGuid,
|
|
RemoveIhisiServicesCallback,
|
|
&Registration
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Register IHISI sub function if SubFuncTable CmdNumber/AsciiFuncGuid define in PcdIhisiRegisterTable list.
|
|
|
|
@param[out] SubFuncTable Pointer to ihisi register table.
|
|
@param[out] TableCount SubFuncTable count
|
|
|
|
@retval EFI_SUCCESS Function succeeded.
|
|
@return Other Error occurred in this function.
|
|
**/
|
|
EFI_STATUS
|
|
RegisterIhisiSubFunction (
|
|
IHISI_REGISTER_TABLE *SubFuncTable,
|
|
UINT16 TableCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *PcdTable;
|
|
UINT8 PcdPriority;
|
|
UINT8 EndChar;
|
|
UINTN Index;
|
|
UINTN PcdCount;
|
|
UINTN PcdMaxCount;
|
|
UINTN SignatureSize;
|
|
BOOLEAN PcdFoundRegistered;
|
|
|
|
Status = EFI_SUCCESS;
|
|
EndChar = 0;
|
|
PcdFoundRegistered = FALSE;
|
|
|
|
PcdTable = (UINT8 *)PcdGetPtr (PcdIhisiRegisterTable);
|
|
PcdMaxCount = FixedPcdGetPtrSize (PcdIhisiRegisterTable) / sizeof (UINT8);
|
|
if (!mPcdIhisiRegisterTableValid) {
|
|
DEBUG ((EFI_D_ERROR, "PcdIhisiRegisterTable of description not follow PCD_IHISI_REGISTER_TABLE definition, \
|
|
it may cause some of IHISI function register fail \n"));
|
|
}
|
|
|
|
for (Index = 0; Index < TableCount; Index ++) {
|
|
PcdCount = 0;
|
|
PcdPriority = 0x80;
|
|
PcdFoundRegistered = FALSE;
|
|
SignatureSize = AsciiStrLen (SubFuncTable[Index].FuncSignature);
|
|
|
|
//
|
|
// Calculate PCD of address to find 1. CmdNumber 2. FuncSignature 3. Priority
|
|
//
|
|
do {
|
|
if (SubFuncTable[Index].CmdNumber == *(PcdTable + PcdCount)) {
|
|
PcdCount++;
|
|
if (AsciiStrnCmp (SubFuncTable[Index].FuncSignature, (CHAR8 *)(PcdTable + PcdCount), SignatureSize) == 0) {
|
|
if (EndChar == *(PcdTable + PcdCount + SignatureSize)) {
|
|
PcdPriority = *(PcdTable + PcdCount + SignatureSize + 1);
|
|
PcdFoundRegistered = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
PcdCount++;
|
|
} while (PcdCount < PcdMaxCount);
|
|
|
|
if (PcdFoundRegistered && IhsiCommandSupported (SubFuncTable[Index].CmdNumber)) {
|
|
Status = IhisiProtRegisterCommand (SubFuncTable[Index].CmdNumber, SubFuncTable[Index].IhisiFunction, PcdPriority);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((EFI_D_ERROR, "IHISI command :0x%X, priority : 0x%X, that already has a registered function\n", \
|
|
SubFuncTable[Index].CmdNumber, PcdPriority));
|
|
ASSERT (FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|