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