406 lines
11 KiB
C
406 lines
11 KiB
C
/** @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;
|
|
}
|
|
|