/** @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; }