alder_lake_bios/Insyde/InsydeModulePkg/Universal/IhisiServicesSmm/IhisiVats.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;
}