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

944 lines
29 KiB
C

/** @file
;******************************************************************************
;* Copyright (c) 2012 - 2016, 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 "SpecificVariableServiceSmm.h"
EFI_SMM_CPU_PROTOCOL *mSmmCpu = NULL;
EFI_PHYSICAL_ADDRESS *mLvarBuffer = NULL;
EFI_STATUS
LenovoVariableSyncDataAndBufferWithRom (
IN OUT LENOVO_VARIABLE_PRIVATE_DATA *PrivateData
)
{
LENOVO_VARIABLE_RIGION_HEADER *FlashRegion1Address;
LENOVO_VARIABLE_RIGION_HEADER *FlashRegion2Address;
UINT32 L05VariableRegion1Size;
UINT32 L05VariableRegion2Size;
L05VariableRegion1Size = (UINT32) FdmGetNAtSize (&gL05H2OFlashMapRegionVaribale1Guid, 1);
L05VariableRegion2Size = (UINT32) FdmGetNAtSize (&gL05H2OFlashMapRegionVaribale2Guid, 1);
FlashRegion1Address = (LENOVO_VARIABLE_RIGION_HEADER *) (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionVaribale1Guid, 1);
FlashRegion2Address = (LENOVO_VARIABLE_RIGION_HEADER *) (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionVaribale2Guid, 1);
if (FlashRegion1Address->PriorityFlag == 0 && FlashRegion2Address->PriorityFlag == 0) {
//Nothing in region
PrivateData->Region = LENOVO_VARIABLE_NO_DATA;
SetMem (PrivateData->VariableBuffer, L05VariableRegion1Size, 0);
CopyMem (PrivateData->VariableBuffer, FlashRegion1Address, sizeof (LENOVO_VARIABLE_RIGION_HEADER));
//Generate RandomNumber
{
UINT64 TimeStamp;
TimeStamp = AsmReadTsc ();
while ((UINT8) TimeStamp == 0) {
TimeStamp = AsmReadTsc ();
}
((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->RandomNumber = (UINT8) TimeStamp;
}
} else if (FlashRegion1Address->PriorityFlag > FlashRegion2Address->PriorityFlag) {
//Use sub region 1
if (CompareMem (PrivateData->VariableBuffer, FlashRegion1Address, sizeof (LENOVO_VARIABLE_RIGION_HEADER)) == 0 && PrivateData->Region == LENOVO_VARIABLE_USING_REGION_1) {
return EFI_SUCCESS;
} else {
//if not the same, read from ROM to sync
PrivateData->Region = LENOVO_VARIABLE_USING_REGION_1;
CopyMem (PrivateData->VariableBuffer, FlashRegion1Address, L05VariableRegion1Size);
//Verify sub region 1 checksum
{
UINTN Index;
UINT8 *BufferPointer;
UINT16 Checksum;
Checksum = 0;
BufferPointer = PrivateData->VariableBuffer + sizeof (LENOVO_VARIABLE_RIGION_HEADER);
for (Index = 0; Index < L05VariableRegion1Size - sizeof (LENOVO_VARIABLE_RIGION_HEADER); Index++) {
Checksum += *(BufferPointer + Index);
}
if (Checksum != FlashRegion1Address->Checksum) {
PrivateData->Region = LENOVO_VARIABLE_USING_REGION_2;
CopyMem (PrivateData->VariableBuffer, FlashRegion2Address, L05VariableRegion2Size);
}
}
}
} else {
//Use sub region 2
if (CompareMem (PrivateData->VariableBuffer, FlashRegion2Address, sizeof (LENOVO_VARIABLE_RIGION_HEADER)) == 0 && PrivateData->Region == LENOVO_VARIABLE_USING_REGION_2) {
return EFI_SUCCESS;
} else {
PrivateData->Region = LENOVO_VARIABLE_USING_REGION_2;
CopyMem (PrivateData->VariableBuffer, FlashRegion2Address, L05VariableRegion2Size);
//Verify sub region 2 checksum
{
UINTN Index;
UINT8 *BufferPointer;
UINT16 Checksum;
BufferPointer = PrivateData->VariableBuffer + sizeof (LENOVO_VARIABLE_RIGION_HEADER);
Checksum = 0;
for (Index = 0; Index < L05VariableRegion2Size - sizeof (LENOVO_VARIABLE_RIGION_HEADER); Index++) {
Checksum += *(BufferPointer + Index);
}
if (Checksum != FlashRegion2Address->Checksum) {
PrivateData->Region = LENOVO_VARIABLE_USING_REGION_1;
CopyMem (PrivateData->VariableBuffer, FlashRegion1Address, L05VariableRegion1Size);
}
}
}
}
//Decode
if (PrivateData->Region != LENOVO_VARIABLE_NO_DATA) {
UINT8 *BufferPointer;
UINTN Index;
BufferPointer = PrivateData->VariableBuffer + sizeof (LENOVO_VARIABLE_RIGION_HEADER);
for (Index = 0; Index < L05VariableRegion1Size - sizeof (LENOVO_VARIABLE_RIGION_HEADER); Index++) {
*(BufferPointer + Index) = *(BufferPointer + Index) ^ ((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->RandomNumber;
}
}
//Update LastByte field
{
UINT8 *BufferPointer;
UINTN Index;
BufferPointer = PrivateData->VariableBuffer + sizeof (LENOVO_VARIABLE_RIGION_HEADER);
for (Index = 0; Index < ((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->VariableCount; Index++) {
BufferPointer = BufferPointer + ((LENOVO_VARIABLE_VARIABLE *) BufferPointer)->VariableDataSize;
BufferPointer = BufferPointer + sizeof (LENOVO_VARIABLE_VARIABLE) - 1;
}
BufferPointer--;
PrivateData->LastByte = BufferPointer;
}
return EFI_SUCCESS;
}
/*
Search VariableGuidName from PrivateData->VariableBuffer
If found, VariableSizeAddress point to the variable address
If not found, VariableSizeAddress = NULL
*/
VOID
LenovoVariableSearchVar (
IN LENOVO_VARIABLE_PRIVATE_DATA *PrivateData,
IN EFI_GUID *VariableGuidName,
OUT UINT8 **VariableAddress
)
{
UINT8 *BufferPointer;
UINTN Index;
BufferPointer = PrivateData->VariableBuffer + sizeof (LENOVO_VARIABLE_RIGION_HEADER);
for (Index = 0; Index < ((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->VariableCount; Index++) {
if (CompareGuid (VariableGuidName, &(((LENOVO_VARIABLE_VARIABLE *) BufferPointer)->VariableGuidName))) {
*VariableAddress = BufferPointer;
return;
} else {
BufferPointer = BufferPointer + ((LENOVO_VARIABLE_VARIABLE *) BufferPointer)->VariableDataSize;
BufferPointer = BufferPointer + sizeof (LENOVO_VARIABLE_VARIABLE) - 1;
}
}
*VariableAddress = NULL;
return;
}
EFI_STATUS
LenovoVariableWriteBufferToFlash (
IN LENOVO_VARIABLE_PRIVATE_DATA *PrivateData
)
{
EFI_STATUS Status;
UINTN WriteFlashAddress;
UINTN WriteFlashSize;
WriteFlashAddress = 0;
WriteFlashSize = (UINTN) FdmGetNAtSize (&gL05H2OFlashMapRegionVaribale1Guid, 1);
switch (PrivateData->Region) {
case LENOVO_VARIABLE_NO_DATA:
WriteFlashAddress = (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionVaribale1Guid, 1);
break;
case LENOVO_VARIABLE_USING_REGION_1:
WriteFlashAddress = (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionVaribale2Guid, 1);
break;
case LENOVO_VARIABLE_USING_REGION_2:
WriteFlashAddress = (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionVaribale1Guid, 1);
break;
default:
break;
}
((LENOVO_VARIABLE_RIGION_HEADER *) (PrivateData->VariableBuffer))->PriorityFlag++;
//Encode
{
UINT8 *BufferPointer;
UINTN Index;
BufferPointer = PrivateData->VariableBuffer + sizeof (LENOVO_VARIABLE_RIGION_HEADER);
for (Index = 0; Index < WriteFlashSize - sizeof (LENOVO_VARIABLE_RIGION_HEADER); Index++) {
*(BufferPointer + Index) = *(BufferPointer + Index) ^ ((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->RandomNumber;
}
}
//Update checksum
{
UINT8 *BufferPointer;
UINTN Index;
UINT16 Checksum;
BufferPointer = PrivateData->VariableBuffer + sizeof (LENOVO_VARIABLE_RIGION_HEADER);
Checksum = 0;
for (Index = 0; Index < (UINTN) FdmGetNAtSize (&gL05H2OFlashMapRegionVaribale1Guid, 1) - sizeof (LENOVO_VARIABLE_RIGION_HEADER); Index++) {
Checksum += *(BufferPointer + Index);
}
((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->Checksum = Checksum;
}
//Write buffer to flash
Status = FlashErase (WriteFlashAddress, WriteFlashSize);
if (!EFI_ERROR (Status)) {
Status = FlashProgram (
(UINT8 *) WriteFlashAddress,
PrivateData->VariableBuffer,
&WriteFlashSize,
WriteFlashAddress
);
}
//Update region
if (!EFI_ERROR (Status)) {
PrivateData->Region = (PrivateData->Region == LENOVO_VARIABLE_USING_REGION_1) ? LENOVO_VARIABLE_USING_REGION_2 : LENOVO_VARIABLE_USING_REGION_1;
}
//Decode
{
UINT8 *BufferPointer;
UINTN Index;
BufferPointer = PrivateData->VariableBuffer + sizeof (LENOVO_VARIABLE_RIGION_HEADER);
for (Index = 0; Index < WriteFlashSize - sizeof (LENOVO_VARIABLE_RIGION_HEADER); Index++) {
*(BufferPointer + Index) = *(BufferPointer + Index) ^ ((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->RandomNumber;
}
}
return Status;
}
EFI_STATUS
LenovoDeleteVariable (
IN EFI_L05_VARIABLE_PROTOCOL *This,
IN EFI_GUID *VariableGuidName
)
{
EFI_STATUS Status;
LENOVO_VARIABLE_PRIVATE_DATA *PrivateData;
UINT8 *VariableAddress;
UINT8 *ShiftBegin;
UINTN ShiftCount;
UINTN Index;
PrivateData = INSTANCE_FROM_LENV_THIS (This);
if (VariableGuidName == NULL) {
return EFI_INVALID_PARAMETER;
}
LenovoVariableSyncDataAndBufferWithRom (PrivateData);
if (((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->LockFlag == LENOVO_VARIABLE_REGION_LOCKED) {
return EFI_ACCESS_DENIED;
}
LenovoVariableSearchVar (PrivateData, VariableGuidName, &VariableAddress);
if (VariableAddress == NULL) {
return EFI_NOT_FOUND;
}
if ((((LENOVO_VARIABLE_VARIABLE *) VariableAddress)->VariableAttribute & LENOVO_VARIABLE_ATTRIBUTE_LOCKED) == LENOVO_VARIABLE_ATTRIBUTE_LOCKED) {
return EFI_WRITE_PROTECTED;
}
ShiftBegin = VariableAddress + ((LENOVO_VARIABLE_VARIABLE *) VariableAddress)->VariableDataSize;
ShiftBegin = ShiftBegin + sizeof (LENOVO_VARIABLE_VARIABLE) - 1;
ShiftCount = 0 + ((LENOVO_VARIABLE_VARIABLE *) VariableAddress)->VariableDataSize;
ShiftCount = ShiftCount + sizeof (LENOVO_VARIABLE_VARIABLE) - 1;
//Begin shift
while (ShiftBegin <= PrivateData->LastByte) {
*(ShiftBegin - ShiftCount) = *ShiftBegin;
ShiftBegin++;
}
for (Index = 0; Index < ShiftCount; Index++) {
*(ShiftBegin - ShiftCount + Index) = 0;
}
((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->VariableCount--;
PrivateData->LastByte -= ShiftCount;
Status = LenovoVariableWriteBufferToFlash (PrivateData);
return Status;
}
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
)
{
LENOVO_VARIABLE_PRIVATE_DATA *PrivateData;
UINT32 VariableSize;
UINT8 *VariableAddress;
PrivateData = INSTANCE_FROM_LENV_THIS (This);
if (VariableGuidName == NULL || DataSize == NULL) {
return EFI_INVALID_PARAMETER;
}
LenovoVariableSyncDataAndBufferWithRom (PrivateData);
if (PrivateData->Region == LENOVO_VARIABLE_NO_DATA || ((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->VariableCount == 0) {
return EFI_NOT_FOUND;
}
LenovoVariableSearchVar (PrivateData, VariableGuidName, &VariableAddress);
if (VariableAddress != NULL) {
VariableSize = ((LENOVO_VARIABLE_VARIABLE *) VariableAddress)->VariableDataSize;
if (*DataSize < VariableSize) {
*DataSize = VariableSize;
return EFI_BUFFER_TOO_SMALL;
}
if (Data == NULL) {
return EFI_INVALID_PARAMETER;
}
*DataSize = VariableSize;
CopyMem (Data, ((LENOVO_VARIABLE_VARIABLE *) VariableAddress)->VariableData, VariableSize);
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}
/*++
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
)
{
EFI_STATUS Status;
LENOVO_VARIABLE_PRIVATE_DATA *PrivateData;
LENOVO_VARIABLE_RIGION_HEADER *FlashRegion1Address;
LENOVO_VARIABLE_RIGION_HEADER *FlashRegion2Address;
UINTN L05VariableRegion1Size;
UINTN L05VariableRegion2Size;
L05VariableRegion1Size = (UINTN) FdmGetNAtSize (&gL05H2OFlashMapRegionVaribale1Guid, 1);
L05VariableRegion2Size = (UINTN) FdmGetNAtSize (&gL05H2OFlashMapRegionVaribale2Guid, 1);
FlashRegion1Address = (LENOVO_VARIABLE_RIGION_HEADER *) (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionVaribale1Guid, 1);
FlashRegion2Address = (LENOVO_VARIABLE_RIGION_HEADER *) (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionVaribale2Guid, 1);
PrivateData = INSTANCE_FROM_LENV_THIS (This);
if (VariableGuidName == NULL) {
return EFI_INVALID_PARAMETER;
}
LenovoVariableSyncDataAndBufferWithRom (PrivateData);
if (((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->LockFlag == LENOVO_VARIABLE_REGION_LOCKED) {
return EFI_ACCESS_DENIED;
}
//If DataSize = 0, then delete variable, sync with EFI variable
if (DataSize == 0) {
return LenovoDeleteVariable (This, VariableGuidName);
}
if (Data == NULL) {
return EFI_INVALID_PARAMETER;
}
//If variable exist, need delete it first
Status = LenovoDeleteVariable (This, VariableGuidName);
if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) {
return Status;
}
//Make sure we have enough space
if (PrivateData->Region == LENOVO_VARIABLE_USING_REGION_2) {
//Is using region2
if ((UINTN) (PrivateData->LastByte) + sizeof (LENOVO_VARIABLE_VARIABLE) - 1 + DataSize > \
(UINTN) FlashRegion2Address + L05VariableRegion2Size - 1) {
return EFI_OUT_OF_RESOURCES;
}
} else {
if ((UINTN) (PrivateData->LastByte) + sizeof (LENOVO_VARIABLE_VARIABLE) - 1 + DataSize > \
(UINTN) FlashRegion1Address + L05VariableRegion1Size - 1) {
return EFI_OUT_OF_RESOURCES;
}
}
//Add a variable
{
LENOVO_VARIABLE_VARIABLE Variable;
SetMem (&Variable, sizeof (Variable), 0x0);
CopyMem (&(Variable.VariableGuidName), VariableGuidName, sizeof (*VariableGuidName));
Variable.VariableDataSize = DataSize;
Variable.VariableAttribute = 0;
CopyMem ((PrivateData->LastByte) + 1, &Variable, sizeof (Variable) - 1);
CopyMem ((PrivateData->LastByte) + 1 + sizeof (Variable) - 1, Data, DataSize);
PrivateData->LastByte = PrivateData->LastByte + sizeof (Variable) - 1 + DataSize;
}
((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->VariableCount++;
Status = LenovoVariableWriteBufferToFlash (PrivateData);
return Status;
}
/*++
Routine Description:
Not implemented.
--*/
EFI_STATUS
L05SmmLockVariable (
IN EFI_L05_VARIABLE_PROTOCOL *This,
IN EFI_GUID *VariableGuidName
)
{
LENOVO_VARIABLE_PRIVATE_DATA *PrivateData;
UINT8 *VariableAddress;
PrivateData = INSTANCE_FROM_LENV_THIS (This);
LenovoVariableSyncDataAndBufferWithRom (PrivateData);
if (((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->LockFlag == LENOVO_VARIABLE_REGION_LOCKED) {
return EFI_ACCESS_DENIED;
}
if (PrivateData->Region == LENOVO_VARIABLE_NO_DATA || ((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->VariableCount == 0) {
return EFI_NOT_FOUND;
}
LenovoVariableSearchVar (PrivateData, VariableGuidName, &VariableAddress);
if (VariableAddress != NULL) {
((LENOVO_VARIABLE_VARIABLE *) VariableAddress)->VariableAttribute |= LENOVO_VARIABLE_ATTRIBUTE_LOCKED;
return LenovoVariableWriteBufferToFlash (PrivateData);
} else {
return EFI_NOT_FOUND;
}
}
/*++
Routine Description:
Not implemented.
--*/
EFI_STATUS
L05SmmUnlockVariable (
IN EFI_L05_VARIABLE_PROTOCOL *This,
IN EFI_GUID *VariableGuidName,
IN CHAR16 *Password
)
{
LENOVO_VARIABLE_PRIVATE_DATA *PrivateData;
UINT8 *VariableAddress;
CHAR16 *LenovoVariablePassword = LENOVO_VARIABLE_UNLOCK_PASSWORD;
PrivateData = INSTANCE_FROM_LENV_THIS (This);
LenovoVariableSyncDataAndBufferWithRom (PrivateData);
if (((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->LockFlag == LENOVO_VARIABLE_REGION_LOCKED) {
return EFI_ACCESS_DENIED;
}
if (PrivateData->Region == LENOVO_VARIABLE_NO_DATA || ((LENOVO_VARIABLE_RIGION_HEADER *) PrivateData->VariableBuffer)->VariableCount == 0) {
return EFI_NOT_FOUND;
}
LenovoVariableSearchVar (PrivateData, VariableGuidName, &VariableAddress);
if (VariableAddress != NULL) {
//Verify Password
if (StrCmp (Password, LenovoVariablePassword) != 0) {
return EFI_SECURITY_VIOLATION;
}
((LENOVO_VARIABLE_VARIABLE *) VariableAddress)->VariableAttribute &= ~LENOVO_VARIABLE_ATTRIBUTE_LOCKED;
return LenovoVariableWriteBufferToFlash (PrivateData);
} else {
return EFI_NOT_FOUND;
}
}
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 Edi;
UINTN CombineData;
EFI_L05_VARIABLE_PROTOCOL *LenovoVariableInstance;
UINT32 LvarBufferAddressH;
UINT32 LvarBufferAddressL;
FunctionCode = 0;
Eax = 0;
Ecx = 0;
Edi = 0;
CombineData = 0;
LvarBufferAddressH = 0;
LvarBufferAddressL = 0;
Status = L05FoundTriggerCpu (&CpuNum);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
Status = gSmst->SmmLocateProtocol (
&gEfiL05VariableProtocolGuid,
NULL,
&LenovoVariableInstance
);
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);
if (PcdGetBool (PcdDxeIplSwitchToLongMode)) {
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RDI, sizeof (UINT32), CpuNum, &Edi);
//
// According to Spec(05_Lenovo variable(lvar) specification v1.05)
// ECX - Lower 32 bit of physical memory address (base address of SMAPI_DATA
// structure)
// EDI - Higher 32 bit of physical memory address (base address of SMAPI_DATA
// structure), 0 if base address is 32 bit
//
CombineData = (UINTN) InternalMathLShiftU64 ((UINT64) Edi, 32);
CombineData = (UINTN) (((UINT64) CombineData) | Ecx);
SmapiData = (L05_SMAPI_DATA *) CombineData;
} else {
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 (
LenovoVariableInstance,
(EFI_GUID *)(UINTN) &(SmapiData->Guid),
(UINT32 *)(UINTN) &(SmapiData->DataLength),
SmapiData->Data
);
break;
case WRITE_NVRAM:
Status = L05SmmSetVariable (
LenovoVariableInstance,
(EFI_GUID *)(UINTN) &(SmapiData->Guid),
SmapiData->DataLength,
SmapiData->Data
);
break;
case DELETE_NVRAM:
Status = L05SmmSetVariable (
LenovoVariableInstance,
(EFI_GUID *)(UINTN) &(SmapiData->Guid),
0,
NULL
);
break;
case LOCK_NVRAM:
Status = L05SmmLockVariable (
LenovoVariableInstance,
(EFI_GUID *)(UINTN) &(SmapiData->Guid)
);
break;
case UNLOCK_NVRAM:
Status = L05SmmUnlockVariable (
LenovoVariableInstance,
(EFI_GUID *)(UINTN) &(SmapiData->Guid),
(CHAR16 *) SmapiData->Data
);
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;
Eax |= LVAR_FUNCTION_NUMBER_WRONG;
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
return EFI_UNSUPPORTED;
}
//
// According to the Status of L05SmmGetVariable/L05SmmSetVariable/L05SmmLockVariable/L05SmmUnlockVariable
// function, it will write error code 0xB1~0xB8
//
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
Eax &= EFI_L05_VARIABLE_EAX_MASK;
if (!EFI_ERROR (Status)) {
Eax |= EFI_L05_VARIABLE_NO_ERROR;
} else if (Status == EFI_NOT_FOUND) {
Eax |= LVAR_NOT_FOUND;
} else if (Status == EFI_BUFFER_TOO_SMALL) {
Eax |= LVAR_BUFFER_TOO_SMALL;
} else if (Status == EFI_ACCESS_DENIED) {
Eax |= LVAR_REGIONE_LOCKED;
} else if (Status == EFI_WRITE_PROTECTED) {
Eax |= LVAR_VARIABLE_LOCKED;
} else if (Status == EFI_SECURITY_VIOLATION) {
Eax |= LVAR_PASSWORD_WRONG;
} else {
Eax |= LVAR_OTHER_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;
LENOVO_VARIABLE_PRIVATE_DATA *PrivateData;
UINT8 *BufferAddress;
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->SmmAllocatePool (
EfiRuntimeServicesData,
sizeof (LENOVO_VARIABLE_PRIVATE_DATA),
&PrivateData
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gSmst->SmmAllocatePool (
EfiRuntimeServicesData,
(UINTN) FdmGetNAtSize (&gL05H2OFlashMapRegionVaribale1Guid, 1),
&BufferAddress
);
if (EFI_ERROR (Status)) {
return Status;
}
PrivateData->Signature = LENV_SIGNATURE;
PrivateData->VariableBuffer = BufferAddress;
LenovoVariableSyncDataAndBufferWithRom (PrivateData);
PrivateData->LenovoVariableInstance.GetVariable = L05SmmGetVariable;
PrivateData->LenovoVariableInstance.SetVariable = L05SmmSetVariable;
PrivateData->LenovoVariableInstance.LockVariable = L05SmmLockVariable;
PrivateData->LenovoVariableInstance.UnlockVariable = L05SmmUnlockVariable;
SwHandle = NULL;
Status = gSmst->SmmInstallProtocolInterface (
&SwHandle,
&gEfiL05VariableProtocolGuid,
EFI_NATIVE_INTERFACE,
&(PrivateData->LenovoVariableInstance)
);
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,
(VOID **) &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;
}