461 lines
13 KiB
C
461 lines
13 KiB
C
/** @file
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012 - 2013, 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 "VariableServiceSmm.h"
|
|
|
|
EFI_L05_VARIABLE_PROTOCOL *mEfiL05VariablePtr = NULL;
|
|
EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable = NULL;
|
|
EFI_SMM_CPU_PROTOCOL *mSmmCpu = NULL;
|
|
EFI_PHYSICAL_ADDRESS *mLvarBuffer = NULL;
|
|
|
|
EFI_STATUS
|
|
L05FoundTriggerCpu (
|
|
UINT8 *CpuNum
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 Index;
|
|
UINT16 SmiPort;
|
|
UINT32 Eax;
|
|
UINT32 Edx;
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
SmiPort = PcdGet16 (PcdSoftwareSmiPort);
|
|
Eax = 0;
|
|
Edx = 0;
|
|
|
|
//
|
|
// Find out which CPU triggered the S/W SMI
|
|
//
|
|
for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
|
|
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT16), Index, &Eax);
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RDX, sizeof (UINT16), Index, &Edx);
|
|
|
|
if (((Eax & 0xff) == EFI_L05_VARIABLE_CALLBACK) && ((Edx & 0xffff) == SmiPort)) {
|
|
//
|
|
// CPU found!
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
*CpuNum = Index;
|
|
|
|
if (*CpuNum < gSmst->NumberOfCpus) {
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Using runtime service GetVariable(DXE) or SMM variable protocol(SMM) to get variable.
|
|
Variable name always be the same, we only use GUID to distingue variable.
|
|
Simply return get variable function.
|
|
|
|
--*/
|
|
EFI_STATUS
|
|
L05SmmGetVariable (
|
|
IN EFI_L05_VARIABLE_PROTOCOL *This,
|
|
IN EFI_GUID *VariableGuidName,
|
|
IN OUT UINT32 *DataSize,
|
|
OUT VOID *Data
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN DataSizeUintn;
|
|
|
|
DataSizeUintn = *DataSize;
|
|
|
|
Status = mSmmVariable->SmmGetVariable (
|
|
EFI_L05_VARIABLE_NAME,
|
|
VariableGuidName,
|
|
NULL,
|
|
&DataSizeUintn,
|
|
Data
|
|
);
|
|
*DataSize = (UINT32) DataSizeUintn;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Using runtime service SetVariable(DXE) or SMM variable protocol(SMM) to set variable.
|
|
Variable name always be the same, we only use GUID to distingue variable.
|
|
Simply return set variable function.
|
|
|
|
--*/
|
|
EFI_STATUS
|
|
L05SmmSetVariable (
|
|
IN EFI_L05_VARIABLE_PROTOCOL *This,
|
|
IN EFI_GUID *VariableGuidName,
|
|
IN UINT32 DataSize,
|
|
IN VOID *Data
|
|
)
|
|
{
|
|
UINT32 Attribute;
|
|
|
|
Attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE;
|
|
|
|
return mSmmVariable->SmmSetVariable (
|
|
EFI_L05_VARIABLE_NAME,
|
|
VariableGuidName,
|
|
Attribute,
|
|
(UINTN) DataSize,
|
|
Data
|
|
);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Not implemented.
|
|
|
|
--*/
|
|
EFI_STATUS
|
|
L05SmmLockVariable (
|
|
IN EFI_L05_VARIABLE_PROTOCOL *This,
|
|
IN EFI_GUID *VariableGuidName
|
|
)
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Not implemented.
|
|
|
|
--*/
|
|
EFI_STATUS
|
|
L05SmmUnlockVariable (
|
|
IN EFI_L05_VARIABLE_PROTOCOL *This,
|
|
IN EFI_GUID *VariableGuidName,
|
|
IN CHAR16 *Password
|
|
)
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
L05SmmVariableCallback (
|
|
IN EFI_HANDLE DispatchHandle,
|
|
IN CONST VOID *DispatchContext,
|
|
IN OUT VOID *CommBuffer,
|
|
IN OUT UINTN *CommBufferSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT16 FunctionCode;
|
|
L05_SMAPI_DATA *SmapiData;
|
|
UINT32 Eax;
|
|
UINT32 Ecx;
|
|
UINT8 CpuNum;
|
|
UINT32 LvarBufferAddressH;
|
|
UINT32 LvarBufferAddressL;
|
|
|
|
FunctionCode = 0;
|
|
Eax = 0;
|
|
Ecx = 0;
|
|
LvarBufferAddressH = 0;
|
|
LvarBufferAddressL = 0;
|
|
|
|
Status = L05FoundTriggerCpu (&CpuNum);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT16), CpuNum, &Eax);
|
|
|
|
if (Eax != EFI_L05_VARIABLE_FUNCTION) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RBX, sizeof (UINT16), CpuNum, &FunctionCode);
|
|
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RCX, sizeof (UINT32), CpuNum, &Ecx);
|
|
SmapiData = (L05_SMAPI_DATA *) (UINTN) Ecx;
|
|
|
|
//
|
|
// Check SmapiData pointer is not NULL.
|
|
//
|
|
if (SmapiData == NULL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Check SmapiData pointer is in mLvarBuffer range.
|
|
//
|
|
if (((UINTN) SmapiData < (UINTN) mLvarBuffer) || ((UINTN) SmapiData > ((UINTN) mLvarBuffer + LVAR_RESERVED_MEMORY_SIZE - sizeof (L05_SMAPI_DATA)))) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Check Data of SmapiData is in mLvarBuffer range.
|
|
//
|
|
if ((UINTN) SmapiData + sizeof (L05_SMAPI_DATA) + SmapiData->DataLength - sizeof (UINT8) > (UINTN) mLvarBuffer + LVAR_RESERVED_MEMORY_SIZE) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
switch (FunctionCode) {
|
|
|
|
case READ_NVRAM:
|
|
Status = L05SmmGetVariable (
|
|
mEfiL05VariablePtr,
|
|
(EFI_GUID *)(UINTN) &(SmapiData->Guid),
|
|
(UINT32 *)(UINTN) &(SmapiData->DataLength),
|
|
SmapiData->Data
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
Eax &= EFI_L05_VARIABLE_EAX_MASK;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
Eax |= EFI_L05_VARIABLE_ERROR;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
break;
|
|
|
|
case WRITE_NVRAM:
|
|
Status = L05SmmSetVariable (
|
|
mEfiL05VariablePtr,
|
|
(EFI_GUID *)(UINTN) &(SmapiData->Guid),
|
|
SmapiData->DataLength,
|
|
SmapiData->Data
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
Eax &= EFI_L05_VARIABLE_EAX_MASK;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
Eax |= EFI_L05_VARIABLE_ERROR;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
break;
|
|
|
|
case DELETE_NVRAM:
|
|
Status = L05SmmSetVariable (
|
|
mEfiL05VariablePtr,
|
|
(EFI_GUID *)(UINTN) &(SmapiData->Guid),
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
Eax &= EFI_L05_VARIABLE_EAX_MASK;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
Eax |= EFI_L05_VARIABLE_ERROR;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
break;
|
|
|
|
case GET_BUFFER_ADDRESS:
|
|
LvarBufferAddressL = (UINT32)(UINTN) mLvarBuffer;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RCX, sizeof (UINT32), CpuNum, &LvarBufferAddressL);
|
|
|
|
LvarBufferAddressH = (UINT32)((UINTN) mLvarBuffer >> 32);
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RDI, sizeof (UINT32), CpuNum, &LvarBufferAddressH);
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
break;
|
|
|
|
default:
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
Eax &= EFI_L05_VARIABLE_EAX_MASK;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
Eax |= EFI_L05_VARIABLE_ERROR;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
Eax &= EFI_L05_VARIABLE_EAX_MASK;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
|
|
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
Eax |= EFI_L05_VARIABLE_NO_ERROR;
|
|
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
|
|
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
EFI_STATUS
|
|
L05VariableServiceSmmEntry (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE SwHandle;
|
|
EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
|
|
EFI_SMM_SW_REGISTER_CONTEXT SwContext;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
//
|
|
// Locate Smm Cpu protocol for Cpu save state manipulation
|
|
//
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmCpuProtocolGuid,
|
|
NULL,
|
|
&mSmmCpu
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gSmst->SmmLocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **) &mSmmVariable);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gSmst->SmmAllocatePool (
|
|
EfiRuntimeServicesData,
|
|
sizeof (EFI_L05_VARIABLE_PROTOCOL),
|
|
&mEfiL05VariablePtr
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
ZeroMem ((VOID *) mEfiL05VariablePtr, sizeof (EFI_L05_VARIABLE_PROTOCOL));
|
|
|
|
mEfiL05VariablePtr->GetVariable = L05SmmGetVariable;
|
|
mEfiL05VariablePtr->SetVariable = L05SmmSetVariable;
|
|
mEfiL05VariablePtr->LockVariable = L05SmmLockVariable;
|
|
mEfiL05VariablePtr->UnlockVariable = L05SmmUnlockVariable;
|
|
|
|
SwHandle = NULL;
|
|
Status = gSmst->SmmInstallProtocolInterface (
|
|
&SwHandle,
|
|
&gEfiL05VariableProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
mEfiL05VariablePtr
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// According to Lenovo's requirements,
|
|
// BIOS needs to reserve 8*4K memory buffer for LVAR tool and memory type should be EfiReservedMemoryType
|
|
//
|
|
Status = gBS->AllocatePool (
|
|
EfiReservedMemoryType,
|
|
LVAR_RESERVED_MEMORY_SIZE,
|
|
(VOID **) &mLvarBuffer
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get the Sw dispatch protocol
|
|
//
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmSwDispatch2ProtocolGuid,
|
|
NULL,
|
|
&SwDispatch
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Register Software SMI function
|
|
//
|
|
SwContext.SwSmiInputValue = EFI_L05_VARIABLE_CALLBACK;
|
|
Status = SwDispatch->Register (
|
|
SwDispatch,
|
|
L05SmmVariableCallback,
|
|
&SwContext,
|
|
&SwHandle
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
ReadDwordRegister (
|
|
IN EFI_SMM_SAVE_STATE_REGISTER RegisterNum,
|
|
IN UINTN Width,
|
|
IN UINT8 CpuNum,
|
|
OUT VOID *RegisterData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = mSmmCpu->ReadSaveState (
|
|
mSmmCpu,
|
|
Width,
|
|
RegisterNum,
|
|
CpuNum,
|
|
RegisterData
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
WriteDwordRegister (
|
|
IN EFI_SMM_SAVE_STATE_REGISTER RegisterNum,
|
|
IN UINTN Width,
|
|
IN UINT8 CpuNum,
|
|
OUT VOID *RegisterData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = mSmmCpu->WriteSaveState (
|
|
mSmmCpu,
|
|
Width,
|
|
RegisterNum,
|
|
CpuNum,
|
|
RegisterData
|
|
);
|
|
|
|
return Status;
|
|
}
|