/** @file IHISI Variable Services Implementation ;****************************************************************************** ;* Copyright (c) 2014 - 2018, 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 "IhisiVats.h" // // Factory default Support Function // STATIC IHISI_REGISTER_TABLE VATS_REGISTER_TABLE[] = { // // AH=00h // { VATSRead, "S00Kn_VatsRead00000", KernelVatsRead}, \ // // AH=01h // { VATSWrite, "S01Kn_VatsWrite0000", KernelVatsWrite}, \ // // AH=05h // { VATSNext, "S05Kn_VatsGetNext00", KernelVatsNext} }; /** Determine if two buffers overlap in memory. @param[in] Buff1 Pointer to first buffer @param[in] Size1 Size of Buff1 @param[in] Buff2 Pointer to second buffer @param[in] Size2 Size of Buff2 @retval TRUE Buffers overlap in memory. @retval FALSE Buffer doesn't overlap. **/ STATIC BOOLEAN IsBufferOverlapped ( IN UINT8 *Buff1, IN UINTN Size1, IN UINT8 *Buff2, IN UINTN Size2 ) { // // If buff1's end is less than the start of buff2, then it's ok. // Also, if buff1's start is beyond buff2's end, then it's ok. // if (((Buff1 + Size1) <= Buff2) || (Buff1 >= (Buff2 + Size2))) { return FALSE; } return TRUE; } /** Get IHISI status.translated from EFI status @param[in] Status EFI_STATUS @return UINT32 IHISI status **/ UINT32 GetVatsIhisiStatus ( 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_NOT_FOUND: return (UINT32) IHISI_VATS_VARIABLE_NOT_FOUND; break; case EFI_INVALID_PARAMETER: return (UINT32) IHISI_VATS_WRONG_OB_FORMAT; break; default: return (UINT32) IHISI_VATS_VARIABLE_ACCESS_ERROR; break; } } /** IHISI checksum calulation. @param[in] Ptr A pointer to to a buffer. @param[in] Size The buffer size. @param[in] Checksum An element in IHISI output table. @retval TURE Ckecksum error. @return FALSE Ckecksum correct. **/ BOOLEAN IhisiChecksumError ( IN VOID *Ptr, IN UINTN Size, IN UINT16 Checksum ) { UINTN Index; UINT8 *TempPtr; UINT32 Sum; TempPtr = Ptr; Sum = 0; for (Index = 0 ; Index < Size ; Index++) { Sum = Sum + TempPtr[Index]; } Sum = (UINT32) ((Checksum + Sum) & 0x0000ffff); return (BOOLEAN) Sum; } /** Vats security check. @return UINT32 IHISI status **/ STATIC EFI_STATUS VatsSecurityCheckError ( VOID ) { IHISI_VATS_OUTPUT_BUFFER *OutBuffer; OutBuffer = (IHISI_VATS_OUTPUT_BUFFER *)(UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RDI); if (!IhisiProtBufferInCmdBuffer ((VOID *) OutBuffer, sizeof (IHISI_VATS_OUTPUT_BUFFER))) { return IHISI_BUFFER_RANGE_ERROR; } if ((OutBuffer->TableRev) != IHISI_TABLE_REVISION) { return IHISI_VATS_OB_TABLE_REV_UNSUPPORTED; } if (((OutBuffer->TableId1) != IHISI_H2O_SIGNATURE) || ((OutBuffer->TableId2) != IHISI_VAR_SIGNATURE) || ((OutBuffer->TableId3) != IHISI_TBL_SIGNATURE)) { return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; } /** Internal function to check Whole variable name is located in command buffer @retval TRUE Whole variable name is located in coomand buffer. @return FALSE Any byte of the variable name in not located in command buffer. **/ STATIC BOOLEAN VarNameInCmdBuffer ( IN CHAR16 *VarName ) { UINTN Index; for (Index = 0; IhisiProtBufferInCmdBuffer ((VOID *) (VarName + Index), sizeof (CHAR16)); Index++) { if (VarName[Index] == 0) { return TRUE; } } return FALSE; } /** AH=00h, Read the specific variable into the specified buffer. @retval EFI_SUCCESS AL 00h = Function succeeded. @return Others AL Returned error code. **/ EFI_STATUS KernelVatsRead ( VOID ) { IHISI_VATS_INPUT_BUFFER *InBuffer; IHISI_VATS_OUTPUT_BUFFER *OutBuffer; UINT8 *VarData; CHAR16 *VarName; EFI_STATUS Status; UINTN VarLength; UINTN InBufferLen; UINTN OutBufferLen; // // Check the valid signature "H2O$Var$Tbl" in obTableId filed. // Status = VatsSecurityCheckError (); if (EFI_ERROR (Status)) { return Status; } // // DS:ESI Pointer to the beginning of the VATS input buffer. // DS:EDI Pointer to the beginning of the VATS output buffer. // InBuffer = (IHISI_VATS_INPUT_BUFFER *)(UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RSI); OutBuffer = (IHISI_VATS_OUTPUT_BUFFER *)(UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RDI); if (!IhisiProtBufferInCmdBuffer ((VOID *) InBuffer, sizeof (IHISI_VATS_INPUT_BUFFER)) || !IhisiProtBufferInCmdBuffer ((VOID *) OutBuffer, sizeof (IHISI_VATS_OUTPUT_BUFFER))) { return IHISI_BUFFER_RANGE_ERROR; } VarName = (CHAR16 *) ((UINTN) InBuffer + (sizeof (IHISI_VATS_INPUT_BUFFER) / sizeof (UINT8))); VarData = (UINT8 *) ((UINTN) OutBuffer + (sizeof (IHISI_VATS_OUTPUT_BUFFER) / sizeof (UINT8))); VarLength = (UINTN) OutBuffer->VarLength; if (!VarNameInCmdBuffer (VarName) || (VarLength != 0 && !IhisiProtBufferInCmdBuffer ((VOID *) VarData, VarLength))) { return IHISI_BUFFER_RANGE_ERROR; } InBufferLen = sizeof (IHISI_VATS_INPUT_BUFFER) + StrSize (VarName); OutBufferLen = sizeof (IHISI_VATS_OUTPUT_BUFFER) + VarLength; if (IsBufferOverlapped ((UINT8 *) InBuffer, InBufferLen, (UINT8 *) OutBuffer, OutBufferLen)) { return IHISI_BUFFER_RANGE_ERROR; } Status = EFI_UNSUPPORTED; if (mSmmVariable != NULL) { Status = mSmmVariable->SmmGetVariable ( VarName, &InBuffer->VarGuid, &OutBuffer->Attribute, &VarLength, VarData ); } if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) { // // Status is EFI_BUFFER_TOO_SMALL also need return required data size to application // OutBuffer->VarLength = (UINT32) VarLength; } return Status; } /** AH=01h, Write the specified buffer to the specific variable. @retval EFI_SUCCESS AL 00h = Function succeeded. @return Others AL Returned error code. **/ EFI_STATUS KernelVatsWrite ( VOID ) { IHISI_VATS_INPUT_BUFFER *InBuffer; IHISI_VATS_OUTPUT_BUFFER *OutBuffer; UINT8 *VarData; CHAR16 *VarName; EFI_STATUS Status; UINTN InBufferLen; UINTN OutBufferLen; // // Check the valid signature "H2O$Var$Tbl" in obTableId filed. // Status = VatsSecurityCheckError (); if (EFI_ERROR (Status)) { return Status; } // // DS:ESI Pointer to the beginning of the VATS input buffer. // DS:EDI Pointer to the beginning of the VATS output buffer. // InBuffer = (IHISI_VATS_INPUT_BUFFER *)(UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RSI); OutBuffer = (IHISI_VATS_OUTPUT_BUFFER *)(UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RDI); if (!IhisiProtBufferInCmdBuffer ((VOID *) InBuffer, sizeof (IHISI_VATS_INPUT_BUFFER)) || !IhisiProtBufferInCmdBuffer ((VOID *) OutBuffer, sizeof (IHISI_VATS_OUTPUT_BUFFER))) { return IHISI_BUFFER_RANGE_ERROR; } VarName = (CHAR16 *) ((UINTN) InBuffer + (sizeof (IHISI_VATS_INPUT_BUFFER) / sizeof (UINT8))); VarData = (UINT8 *) ((UINTN) OutBuffer + (sizeof (IHISI_VATS_OUTPUT_BUFFER) / sizeof (UINT8))); if (!VarNameInCmdBuffer (VarName) || (OutBuffer->VarLength != 0 && !IhisiProtBufferInCmdBuffer ((VOID *) VarData, OutBuffer->VarLength))) { return IHISI_BUFFER_RANGE_ERROR; } InBufferLen = sizeof (IHISI_VATS_INPUT_BUFFER) + StrSize (VarName); OutBufferLen = sizeof (IHISI_VATS_OUTPUT_BUFFER) + OutBuffer->VarLength; if (IsBufferOverlapped ((UINT8 *) InBuffer, InBufferLen, (UINT8 *) OutBuffer, OutBufferLen)) { return IHISI_BUFFER_RANGE_ERROR; } if (IhisiChecksumError (VarData, OutBuffer->VarLength, OutBuffer->VarChecksum)) { return IHISI_VATS_OB_CHECKSUM_FAILED; } if (OutBuffer->Attribute == 0) { OutBuffer->Attribute = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; } Status = IHISI_VATS_VARIABLE_ACCESS_ERROR; if (mSmmVariable != NULL) { Status = mSmmVariable->SmmSetVariable ( VarName, &InBuffer->VarGuid, OutBuffer->Attribute, (UINTN) OutBuffer->VarLength, VarData ); if (EFI_ERROR (Status)) { Status = IHISI_VATS_VARIABLE_ACCESS_ERROR; } } return Status; } /** AH=05h, Get next specified buffer to the specific variable. @retval EFI_SUCCESS AL 00h = Function succeeded. @return Others AL Returned error code. **/ EFI_STATUS KernelVatsNext ( VOID ) { IHISI_VATS_INPUT_BUFFER *InBuffer; CHAR16 *VarName; UINTN NameSize; EFI_STATUS Status; // // DS:ESI Pointer to the beginning of the VATS input buffer. // DS:ECX Next Variable Name buffer size // InBuffer = (IHISI_VATS_INPUT_BUFFER *)(UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RSI); if (!IhisiProtBufferInCmdBuffer ((VOID *) InBuffer, sizeof (IHISI_VATS_INPUT_BUFFER))) { return IHISI_BUFFER_RANGE_ERROR; } NameSize = (UINT32) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX); VarName = (CHAR16 *) ((UINTN) InBuffer + (sizeof (IHISI_VATS_INPUT_BUFFER) / sizeof (UINT8))); if (!IhisiProtBufferInCmdBuffer ((VOID *) VarName, NameSize)) { return IHISI_BUFFER_RANGE_ERROR; } Status = EFI_UNSUPPORTED; if (mSmmVariable != NULL) { Status = mSmmVariable->SmmGetNextVariableName( &NameSize, VarName, &InBuffer->VarGuid ); if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) { IhisiProtWriteCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX, (UINT32)NameSize); } } return Status; } EFI_STATUS InstallVatsServices ( VOID ) { EFI_STATUS Status; IHISI_REGISTER_TABLE *SubFuncTable; UINT16 TableCount; SubFuncTable = VATS_REGISTER_TABLE; TableCount = sizeof(VATS_REGISTER_TABLE)/sizeof(VATS_REGISTER_TABLE[0]); Status = RegisterIhisiSubFunction (SubFuncTable, TableCount); if (EFI_ERROR(Status)) { ASSERT_EFI_ERROR (Status); } return EFI_SUCCESS; }