/** @file
  SMI VMCALL handler

  Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php.

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "StmRuntime.h"
#include "PeStm.h"
#include "PeLoadVm.h"
#include "StmInit.h"

extern PE_VM_DATA PeVmData[4];
extern VOID EptDumpPageTable (IN EPT_POINTER *EptPointer );

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallInitializeProtectionHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  DEBUG ((EFI_D_INFO, "STM_API_INITIALIZE_PROTECTION:\n"));
  return ERROR_STM_ALREADY_STARTED;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/

extern VOID CpuReadySync(UINT32 Index);

STM_STATUS
SmiVmcallStartHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  //
  // Let STM enable SMI for SMM guest
  //

   GUEST_INTERRUPTIBILITY_STATE                 GuestInterruptibilityState;

  DEBUG ((EFI_D_INFO, "%ld SmiVmcallStartHandler - STM_API_START:\n", Index));
  if (!mGuestContextCommonSmm[SMI_HANDLER].GuestContextPerCpu[Index].Actived) {
    mGuestContextCommonSmm[SMI_HANDLER].GuestContextPerCpu[Index].Actived = TRUE;
    SmmSetup (Index);

    if(Index == 0)
    {
		//EptDumpPageTable (&mGuestContextCommonSmm[0].EptPointer);  // **DEBUG** Dump the SMI Handler EPT tables
		// sync the BSP CPU once the API is ready
        CpuReadySync(Index);

		// turn on SMI for the BSP - the base code allows SMIs before this point
		// this mod prvents SMIs until the STM is ready to process them

		GuestInterruptibilityState.Uint32 = VmRead32 (VMCS_32_GUEST_INTERRUPTIBILITY_STATE_INDEX);
        GuestInterruptibilityState.Bits.BlockingBySmi = 0;
		VmWrite32 (VMCS_32_GUEST_INTERRUPTIBILITY_STATE_INDEX, GuestInterruptibilityState.Uint32);
    }
#if 0
	DumpVmcsAllField();
#endif
    return STM_SUCCESS;
  } else {
	  DEBUG((EFI_D_ERROR, "%d STM_API_START -- Error STM already started\n", (UINTN) Index));
    return ERROR_STM_ALREADY_STARTED;
  }
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallStopHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  X86_REGISTER                       *Reg;

  Reg = &mGuestContextCommonSmi.GuestContextPerCpu[Index].Register;

  mHostContextCommon.StmShutdown = 1; // let the VM/PE know to quit
  //
  // Launch SMM Teardown handler.
  //
  DEBUG ((EFI_D_INFO, "STM_API_STOP:\n"));
  SmmTeardown (Index);
  WriteUnaligned32 ((UINT32 *)&Reg->Rax, STM_SUCCESS);
  VmWriteN (VMCS_N_GUEST_RFLAGS_INDEX, VmReadN(VMCS_N_GUEST_RFLAGS_INDEX) & ~RFLAGS_CF);
  StmTeardown (Index);
  DEBUG((EFI_D_INFO, "CpuDeadLoop\n"));
  CpuDeadLoop ();

  return STM_SUCCESS;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallProtectResourceHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_RSC                            *StmResource;
  STM_RSC                            *BiosResource;
  STM_STATUS                         Status;
  STM_RSC                            *LocalBuffer;

  // ECX:EBX - STM_RESOURCE_LIST
  AcquireSpinLock (&mHostContextCommon.SmiVmcallLock);
  DEBUG ((EFI_D_INFO, "%ld STM_API_PROTECT_RESOURCE:\n", Index));

  // BiosHwResourceRequirementsPtr to local BiosResource, delay it to first ProtectResource VMCALL, because BIOS may change resource at runtime.
  if (mGuestContextCommonSmm[SMI_HANDLER].BiosHwResourceRequirementsPtr == 0) {
		if (!IsResourceListValid ((STM_RSC *)(UINTN)mHostContextCommon.HostContextPerCpu[0].TxtProcessorSmmDescriptor->BiosHwResourceRequirementsPtr, FALSE)) {
			ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
			DEBUG ((EFI_D_ERROR, "%ld SmiVmcallProtectResourceHandler - ValidateBiosResourceList fail!\n",
				Index));
			return ERROR_STM_MALFORMED_RESOURCE_LIST;
	  }
   mGuestContextCommonSmm[SMI_HANDLER].BiosHwResourceRequirementsPtr =
	(UINT64)(UINTN)DuplicateResource ((STM_RSC *)(UINTN)mHostContextCommon.HostContextPerCpu[0].TxtProcessorSmmDescriptor->BiosHwResourceRequirementsPtr);
   RegisterBiosResource ((STM_RSC *)(UINTN)mGuestContextCommonSmm[SMI_HANDLER].BiosHwResourceRequirementsPtr);
  }

  //
  // Copy data to local, to prevent time of check VS time of use attack
  //
  LocalBuffer = RawDuplicateResource ((STM_RSC *)(UINTN)AddressParameter);
  if (LocalBuffer == NULL) {
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_MALFORMED_RESOURCE_LIST;
  }

  StmResource = (STM_RSC *)(UINTN)LocalBuffer;

  //DumpStmResource (StmResource);

  if (!IsResourceListValid (StmResource, TRUE)) {
    DEBUG ((EFI_D_ERROR, "IsResourceListValid fail!\n"));
    RawFreeResource (LocalBuffer);
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_MALFORMED_RESOURCE_LIST;
  }
  DEBUG ((EFI_D_INFO, "IsResourceListValid pass!\n"));

  BiosResource = (STM_RSC *)(UINTN)mGuestContextCommonSmm[SMI_HANDLER].BiosHwResourceRequirementsPtr;
  if (IsResourceListOverlap (StmResource, BiosResource)) {
    DEBUG ((EFI_D_ERROR, "IsResourceListOverlap fail!\n"));
    RawFreeResource (LocalBuffer);
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_UNPROTECTABLE_RESOURCE;
  }
  DEBUG ((EFI_D_INFO, "IsResourceListOverlap pass!\n"));

  Status = AddProtectedResource (&mHostContextCommon.MleProtectedResource, StmResource);
  if (Status != STM_SUCCESS) {
    DEBUG ((EFI_D_ERROR, "AddProtectedResource fail!\n"));
    RawFreeResource (LocalBuffer);
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return Status;
  }
  AddProtectedResourceWithType (&mHostContextCommon.MleProtectedTrappedIoResource, StmResource, TRAPPED_IO_RANGE);

  RegisterProtectedResource (StmResource);

  RawFreeResource (LocalBuffer);
  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);

  return STM_SUCCESS;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallUnprotectResourceHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_RSC                            *StmResource;
  STM_RSC                            *LocalBuffer;

  // ECX:EBX - STM_RESOURCE_LIST
  AcquireSpinLock (&mHostContextCommon.SmiVmcallLock);
  DEBUG ((EFI_D_INFO, "STM_API_UNPROTECT_RESOURCE:\n"));

  //
  // Copy data to local, to prevent time of check VS time of use attack
  //
  LocalBuffer = RawDuplicateResource ((STM_RSC *)(UINTN)AddressParameter);
  if (LocalBuffer == NULL) {
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_MALFORMED_RESOURCE_LIST;
  }

  StmResource = (STM_RSC *)(UINTN)LocalBuffer;

  if (!IsResourceListValid (StmResource, TRUE)) {
    DEBUG ((EFI_D_ERROR, "IsResourceListValid fail!\n"));
    RawFreeResource (LocalBuffer);
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_MALFORMED_RESOURCE_LIST;
  }
  DEBUG ((EFI_D_INFO, "IsResourceListValid pass!\n"));

  //DumpStmResource (StmResource);

  DeleteProtectedResource (&mHostContextCommon.MleProtectedResource, StmResource);

  UnRegisterProtectedResource (StmResource);

  RawFreeResource (LocalBuffer);
  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);

  return STM_SUCCESS;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallGetBiosResourcesHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_RSC                            *BiosResource;
  UINTN                              BiosResourceSize;
  UINT32                             PageNum;
  X86_REGISTER                       *Reg;
  Reg = &mGuestContextCommonSmi.GuestContextPerCpu[Index].Register;

  // ECX:EBX - STM_RESOURCE_LIST
  // EDX: PageCount
  AcquireSpinLock (&mHostContextCommon.SmiVmcallLock);
  DEBUG ((EFI_D_INFO, "%ld STM_API_GET_BIOS_RESOURCES:\n", Index));

  // BiosHwResourceRequirementsPtr to local BiosResource, delay it to first ProtectResource VMCALL, because BIOS may change resource at runtime.
  if (mGuestContextCommonSmm[SMI_HANDLER].BiosHwResourceRequirementsPtr == 0) {
	  if (!IsResourceListValid ((STM_RSC *)(UINTN)mHostContextCommon.HostContextPerCpu[0].TxtProcessorSmmDescriptor->BiosHwResourceRequirementsPtr, FALSE)) {
		  DEBUG ((EFI_D_ERROR, "%ld SmiVmcallGetBiosResourcesHandler - ValidateBiosResourceList fail!\n", Index));
		  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
		return ERROR_STM_MALFORMED_RESOURCE_LIST;
    }
	mGuestContextCommonSmm[SMI_HANDLER].BiosHwResourceRequirementsPtr = (UINT64)(UINTN)DuplicateResource ((STM_RSC *)(UINTN)mHostContextCommon.HostContextPerCpu[0].TxtProcessorSmmDescriptor->BiosHwResourceRequirementsPtr);
    RegisterBiosResource ((STM_RSC *)(UINTN)mGuestContextCommonSmm[SMI_HANDLER].BiosHwResourceRequirementsPtr);
  }

  PageNum = (UINT32)Reg->Rdx;

  if (!IsGuestAddressValid ((UINTN)AddressParameter, STM_PAGES_TO_SIZE(PageNum + 1), TRUE)) {
    DEBUG ((EFI_D_ERROR, "%ld Security Violation!\n", Index));
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_SECURITY_VIOLATION;
  }

  BiosResource = (STM_RSC *)(UINTN)mGuestContextCommonSmm[SMI_HANDLER].BiosHwResourceRequirementsPtr;
  BiosResourceSize = GetSizeFromResource (BiosResource);
  if (BiosResourceSize == 0) {
    ReleaseSpinLock(&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_SECURITY_VIOLATION;
  }

  DEBUG ((EFI_D_INFO, "%ld BiosResource - %016lx(%08x), PageCount - %d\n", (UINTN)Index, (UINT64)(UINTN)BiosResource, (UINTN)BiosResourceSize, (UINTN)PageNum));
//  DumpStmResource (BiosResource);

  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);

  if (PageNum >= STM_SIZE_TO_PAGES (BiosResourceSize)) {
    WriteUnaligned32 ((UINT32 *)&Reg->Rdx, 0);
	DEBUG((EFI_D_INFO, "%ld SmiVmcallGetBiosResourcesHandler - ERROR_STM_PAGE_NOT_FOUND - writing 0 to 0x%x\n", Index, &Reg->Rdx));
    return ERROR_STM_PAGE_NOT_FOUND;
  }
  // Write data
  CopyMem (
    (VOID *)(UINTN)AddressParameter,
    (VOID *)((UINTN)BiosResource + SIZE_4KB * PageNum),
    SIZE_4KB
    );
  PageNum++;
  if (PageNum >= STM_SIZE_TO_PAGES (BiosResourceSize)) {
    WriteUnaligned32 ((UINT32 *)&Reg->Rdx, 0);
  } else {
    WriteUnaligned32 ((UINT32 *)&Reg->Rdx, PageNum);
  }

  return STM_SUCCESS;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallManageVmcsDatabaseHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_VMCS_DATABASE_REQUEST          *VmcsDatabaseRequest;
  STM_STATUS                         Status;
  STM_VMCS_DATABASE_REQUEST          LocalBuffer;

  // ECX:EBX - STM_VMCS_DATABASE_REQUEST
  AcquireSpinLock (&mHostContextCommon.SmiVmcallLock);
  DEBUG ((EFI_D_INFO, "STM_API_MANAGE_VMCS_DATABASE:\n"));

  if (!IsGuestAddressValid ((UINTN)AddressParameter, sizeof(STM_VMCS_DATABASE_REQUEST), TRUE)) {
    DEBUG ((EFI_D_ERROR, "Security Violation!\n"));
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_SECURITY_VIOLATION;
  }

  //
  // Copy data to local, to prevent time of check VS time of use attack
  //
  CopyMem (&LocalBuffer, (VOID *)(UINTN)AddressParameter, sizeof(LocalBuffer));

  VmcsDatabaseRequest = (STM_VMCS_DATABASE_REQUEST *)&LocalBuffer;

  Status = RequestVmcsDatabaseEntry (
             VmcsDatabaseRequest,
             (VMCS_RECORD_STRUCTURE *)(UINTN)mHostContextCommon.VmcsDatabase
             );

  DEBUG ((EFI_D_INFO, "VMCS Database (%d) - %016lx\n", (UINTN)Index, mHostContextCommon.VmcsDatabase));

  DumpVmcsRecord (mHostContextCommon.VmcsDatabase);
  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);

  return Status;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallEventNewLogHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  UINT32                             PageIndex;
  STM_EVENT_LOG_MANAGEMENT_REQUEST   *EventLogRequest;
  UINTN                              PageCount;
  UINT8                              LocalBuffer[SIZE_4KB];

  //
  // SmiVmcallManageEventLogHandler already checked [AddressParameter, sizeof(STM_EVENT_LOG_MANAGEMENT_REQUEST)]
  //
  // Use PageCount only, because other data is still in Non-SMRAM and under attack.
  //

  // ECX:EBX - STM_EVENT_LOG_MANAGEMENT_REQUEST
  EventLogRequest = (STM_EVENT_LOG_MANAGEMENT_REQUEST *)(UINTN)AddressParameter;

  PageCount = (UINTN)EventLogRequest->Data.LogBuffer.PageCount;
  if (PageCount == 0) {
    return ERROR_STM_INVALID_PAGECOUNT;
  }
  if (PageCount > ((SIZE_4KB - sizeof(STM_EVENT_LOG_MANAGEMENT_REQUEST)) / sizeof(UINT64))) {
    return ERROR_STM_INVALID_PAGECOUNT;
  }
  if (!IsGuestAddressValid((UINTN)AddressParameter, sizeof(STM_EVENT_LOG_MANAGEMENT_REQUEST) + (PageCount - 1) * sizeof(UINT64), TRUE)) {
    DEBUG((EFI_D_ERROR, "Security Violation!\n"));
    return ERROR_STM_SECURITY_VIOLATION;
  }

  //
  // Copy data to local, to prevent time of check VS time of use attack
  //
  CopyMem (LocalBuffer, (VOID *)(UINTN)AddressParameter, sizeof(STM_EVENT_LOG_MANAGEMENT_REQUEST) + (PageCount - 1) * sizeof(UINT64));

  // ECX:EBX - STM_EVENT_LOG_MANAGEMENT_REQUEST
  EventLogRequest = (STM_EVENT_LOG_MANAGEMENT_REQUEST *)LocalBuffer;
  //
  // Check if local copy matches previous PageCount
  //
  if (PageCount != (UINTN)EventLogRequest->Data.LogBuffer.PageCount) {
    DEBUG ((EFI_D_ERROR, "Security Violation!\n"));
    return ERROR_STM_SECURITY_VIOLATION;
  }

  DEBUG ((EFI_D_INFO, "NEW_LOG: PageCount - %x\n", PageCount));
  for (PageIndex = 0; PageIndex < PageCount; PageIndex++) {
    DEBUG ((EFI_D_INFO, "Page(%x) - %08x\n", (UINTN)PageIndex, (UINTN)EventLogRequest->Data.LogBuffer.Pages[PageIndex]));
    if (!IsGuestAddressValid ((UINTN)EventLogRequest->Data.LogBuffer.Pages[PageIndex], SIZE_4KB, TRUE)) {
      DEBUG ((EFI_D_ERROR, "Security Violation!\n"));
      return ERROR_STM_SECURITY_VIOLATION;
    }
  }
  DEBUG ((EFI_D_INFO, "\n"));

  if (mHostContextCommon.EventLog.State != EvtInvalid) {
    return ERROR_STM_LOG_ALLOCATED;
  }

  mHostContextCommon.EventLog.PageCount = EventLogRequest->Data.LogBuffer.PageCount;
  // Check page in MLE memory
  CopyMem (mHostContextCommon.EventLog.Pages, EventLogRequest->Data.LogBuffer.Pages, EventLogRequest->Data.LogBuffer.PageCount * sizeof(UINT64));

  mHostContextCommon.EventLog.State = EvtLogStopped;

  return STM_SUCCESS;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallEventConfigureLogHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_EVENT_LOG_MANAGEMENT_REQUEST   *EventLogRequest;
  STM_EVENT_LOG_MANAGEMENT_REQUEST   LocalBuffer;

  //
  // Copy data to local, to prevent time of check VS time of use attack
  //
  CopyMem (&LocalBuffer, (VOID *)(UINTN)AddressParameter, sizeof(LocalBuffer));

  // ECX:EBX - STM_EVENT_LOG_MANAGEMENT_REQUEST
  EventLogRequest = (STM_EVENT_LOG_MANAGEMENT_REQUEST *)&LocalBuffer;

  DEBUG ((EFI_D_INFO, "CONFIGURE_LOG: EventEnables - (%x)\n", (UINTN)EventLogRequest->Data.EventEnableBitmap));
  if (mHostContextCommon.EventLog.State == EvtInvalid) {
    return ERROR_STM_LOG_NOT_ALLOCATED;
  }
  if (mHostContextCommon.EventLog.State == EvtLogStarted) {
    return ERROR_STM_LOG_NOT_STOPPED;
  }
  if (EventLogRequest->Data.EventEnableBitmap >= (1 << EvtMleMax)) {
    return ERROR_STM_RESERVED_BIT_SET;
  }
  mHostContextCommon.EventLog.EventEnableBitmap = EventLogRequest->Data.EventEnableBitmap;

  return STM_SUCCESS;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallEventStartLogHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  LOG_ENTRY_DATA                     LogEntryData;

  DEBUG ((EFI_D_INFO, "START_LOG:\n"));
  if (mHostContextCommon.EventLog.State == EvtInvalid) {
    return ERROR_STM_LOG_NOT_ALLOCATED;
  }
  if (mHostContextCommon.EventLog.State == EvtLogStarted) {
    return ERROR_STM_LOG_NOT_STOPPED;
  }
  if (mHostContextCommon.EventLog.EventEnableBitmap == 0) {
    return ERROR_STM_NO_EVENTS_ENABLED;
  }
  mHostContextCommon.EventLog.State = EvtLogStarted;
  LogEntryData.Started.Reserved = 0;
  AddEventLog (EvtLogStarted, &LogEntryData, sizeof(LogEntryData.Started), &mHostContextCommon.EventLog);

  return STM_SUCCESS;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallEventStopLogHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  LOG_ENTRY_DATA                     LogEntryData;

  DEBUG ((EFI_D_INFO, "STOP_LOG:\n"));
  if (mHostContextCommon.EventLog.State == EvtInvalid) {
    return ERROR_STM_LOG_NOT_ALLOCATED;
  }
  if (mHostContextCommon.EventLog.State == EvtLogStopped) {
    return ERROR_STM_LOG_NOT_STARTED;
  }
  LogEntryData.Stopped.Reserved = 0;
  AddEventLog (EvtLogStopped, &LogEntryData, sizeof(LogEntryData.Stopped), &mHostContextCommon.EventLog);
  mHostContextCommon.EventLog.State = EvtLogStopped;

  return STM_SUCCESS;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallEventClearLogHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  DEBUG ((EFI_D_INFO, "CLEAR_LOG:\n"));
  if (mHostContextCommon.EventLog.State == EvtInvalid) {
    return ERROR_STM_LOG_NOT_ALLOCATED;
  }
  if (mHostContextCommon.EventLog.State == EvtLogStarted) {
    return ERROR_STM_LOG_NOT_STOPPED;
  }
  ClearEventLog (&mHostContextCommon.EventLog);

  return STM_SUCCESS;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallEventDeleteLogHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  DEBUG ((EFI_D_INFO, "DELETE_LOG:\n"));
  if (mHostContextCommon.EventLog.State == EvtLogStarted) {
    return ERROR_STM_LOG_NOT_STOPPED;
  }
  mHostContextCommon.EventLog.State = (UINT32)EvtInvalid;
  mHostContextCommon.EventLog.EventSerialNumber = 0;
  mHostContextCommon.EventLog.EventEnableBitmap = 0;
  mHostContextCommon.EventLog.PageCount = 0;
  ZeroMem (mHostContextCommon.EventLog.Pages, STM_PAGES_TO_SIZE(1));

  return STM_SUCCESS;
}

STM_VMCALL_HANDLER_STRUCT  mSmiVmcallEventLogHandler[] = {
  {NEW_LOG,                 SmiVmcallEventNewLogHandler},
  {CONFIGURE_LOG,           SmiVmcallEventConfigureLogHandler},
  {START_LOG,               SmiVmcallEventStartLogHandler},
  {STOP_LOG,                SmiVmcallEventStopLogHandler},
  {CLEAR_LOG,               SmiVmcallEventClearLogHandler},
  {DELETE_LOG,              SmiVmcallEventDeleteLogHandler},
};

/**

  This function returns SMI EventLog VMCALL handler by FuncIndex.

  @param FuncIndex         VmCall function index

  @return VMCALL Handler

**/
STM_VMCALL_HANDLER
GetSmiVmcallEventLogHandlerByIndex (
  IN UINT32  FuncIndex
  )
{
  UINTN  Index;
  for (Index = 0; Index < sizeof(mSmiVmcallEventLogHandler)/sizeof(mSmiVmcallEventLogHandler[0]); Index++) {
    if (mSmiVmcallEventLogHandler[Index].FuncIndex == FuncIndex) {
      return mSmiVmcallEventLogHandler[Index].StmVmcallHandler;
    }
  }
  return NULL;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallManageEventLogHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_EVENT_LOG_MANAGEMENT_REQUEST   *EventLogRequest;
  STM_STATUS                         Status;
  STM_VMCALL_HANDLER                 StmVmcallHandler;

  // ECX:EBX - STM_EVENT_LOG_MANAGEMENT_REQUEST
  AcquireSpinLock (&mHostContextCommon.SmiVmcallLock);
  DEBUG ((EFI_D_INFO, "STM_API_MANAGE_EVENT_LOG:\n"));

  EventLogRequest = (STM_EVENT_LOG_MANAGEMENT_REQUEST *)(UINTN)AddressParameter;

  if (!IsGuestAddressValid ((UINTN)AddressParameter, sizeof(STM_EVENT_LOG_MANAGEMENT_REQUEST), TRUE)) {
    DEBUG ((EFI_D_ERROR, "Security Violation!\n"));
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_SECURITY_VIOLATION;
  }

  StmVmcallHandler = GetSmiVmcallEventLogHandlerByIndex (EventLogRequest->SubFunctionIndex);
  if (StmVmcallHandler == NULL) {
    // Should not happen
    DEBUG ((EFI_D_INFO, "GetSmiVmcallEventLogHandlerByIndex - %x!\n", (UINTN)EventLogRequest->SubFunctionIndex));
    CpuDeadLoop ();
    Status = ERROR_INVALID_API;
  } else {
    Status = StmVmcallHandler (Index, AddressParameter);
  }

  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);

  return Status;
}


/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallAddTempPeVmHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_STATUS                         Status;
  PE_MODULE_INFO					 LocalBuffer;

  // STM_ADD_TEMP_PE
  AcquireSpinLock (&mHostContextCommon.SmiVmcallLock);
  DEBUG ((EFI_D_INFO, "STM_API_ADD_TEMP_VM:\n"));

  if (!IsGuestAddressValid ((UINTN)AddressParameter, sizeof(PE_MODULE_INFO), TRUE)) {
    DEBUG ((EFI_D_ERROR, "Security Violation!\n"));
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_SECURITY_VIOLATION;
  }

  //
  // Copy data to local, to prevent time of check VS time of use attack
  //
  CopyMem (&LocalBuffer, (VOID *)(UINTN)AddressParameter, sizeof(LocalBuffer));

  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
  Status = AddPeVm(Index, &LocalBuffer, PE_TEMP, 1);

  //Status = STM_SUCCESS;
 

  return Status;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallAddPermPeVmHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_STATUS                         Status;
  PE_MODULE_INFO					 LocalBuffer;

  // - STM_ADD_PERM_PE_VM
  AcquireSpinLock (&mHostContextCommon.SmiVmcallLock);
  DEBUG ((EFI_D_INFO, "STM_API_ADD_PERM_VM:\n"));

  if (!IsGuestAddressValid ((UINTN)AddressParameter, sizeof(PE_MODULE_INFO), TRUE)) {
    DEBUG ((EFI_D_ERROR, "Security Violation!\n"));
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_SECURITY_VIOLATION;
  }

  //
  // Copy data to local, to prevent time of check VS time of use attack
  //
  CopyMem (&LocalBuffer, (VOID *)(UINTN)AddressParameter, sizeof(LocalBuffer));

  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
  Status = AddPeVm(Index, &LocalBuffer, PE_PERM, 1);
 
  return Status;
}

/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallAddPermPeVmNoRunHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_STATUS                    Status;
  PE_MODULE_INFO		LocalBuffer;

  // - STM_ADD_PERM_PE_VM
  AcquireSpinLock (&mHostContextCommon.SmiVmcallLock);
  DEBUG ((EFI_D_INFO, 
		"%ld SmiVmcallAddPermPeVmNoRunHandler - STM_API_ADD_PERM_VM_NO_RUN:\n",
		Index));

  if (!IsGuestAddressValid ((UINTN)AddressParameter, sizeof(PE_MODULE_INFO), TRUE)) {
    DEBUG ((EFI_D_ERROR,
		"%ld SmiVmcallAddPermPeVmNoRunHandler - Security Violation!\n",
		Index));
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_SECURITY_VIOLATION;
  }

  //
  // Copy data to local, to prevent time of check VS time of use attack
  //
  CopyMem (&LocalBuffer, (VOID *)(UINTN)AddressParameter, sizeof(LocalBuffer));

  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
  Status = AddPeVm(Index, &LocalBuffer, PE_PERM, 0);  // do not run PE/VM

  return Status;
}
/**

  This function is VMCALL handler for SMI.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallRunPeVmHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_STATUS                    Status;
  PE_MODULE_INFO		LocalBuffer;

  UINT32 PeType = PE_PERM;
  // ECX:EBX - STM_VMCS_DATABASE_REQUEST
  AcquireSpinLock (&mHostContextCommon.SmiVmcallLock);
  DEBUG ((EFI_D_INFO, " %ld STM_API_RUN_PERM_VM:\n", Index));

  if (!IsGuestAddressValid ((UINTN)AddressParameter, sizeof(PE_MODULE_INFO), TRUE)) {
    DEBUG ((EFI_D_ERROR, " %ld Security Violation!\n", Index));
    ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
    return ERROR_STM_SECURITY_VIOLATION;
  }

  //
  // Copy data to local, to prevent time of check VS time of use attack
  //
  CopyMem (&LocalBuffer, (VOID *)(UINTN)AddressParameter, sizeof(LocalBuffer));

  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);
  // provide the root state for the measurement VM
  //GetRootVmxState(StmVmm, (ROOT_VMX_STATE *) ptData[CR3index].ShareModuleStm);
  PeVmData[PeType].StartMode = PEVM_START_VMCALL;
  RunPermVM(Index);
  
  Status = STM_SUCCESS;
  return Status;
}

/**

  This function instructs the STM to not allow for a
  Permanent PE VM to be created.

  @param Index             CPU index
  @param AddressParameter  Addresss parameter

  @return VMCALL status

**/
STM_STATUS
SmiVmcallEndPermVmHandler (
  IN UINT32  Index,
  IN UINT64  AddressParameter
  )
{
  STM_STATUS                    Status;
  PE_MODULE_INFO		LocalBuffer;

  // ECX:EBX - STM_VMCS_DATABASE_REQUEST
  AcquireSpinLock (&mHostContextCommon.SmiVmcallLock);
  DEBUG ((EFI_D_INFO, "STM_API_END_PERM_VM:\n"));

  // Check to see if there is an active PE VM, this
  // call will indicate an active PE VM

  if(PeVmData[PE_PERM].PeVmState == PE_VM_AVAIL)
  {
  	PeVmData[PE_PERM].PeVmState = PE_VM_OPT_OUT_PERM;
  	Status = STM_SUCCESS;
  }
  else
  {
	if(PeVmData[PE_PERM].PeVmState == PE_VM_OPT_OUT_PERM)
		Status = STM_SUCCESS;
	else
		Status = PE_VM_PERM_ALREADY_ESTABLISHED;
  }
  
  ReleaseSpinLock (&mHostContextCommon.SmiVmcallLock);

  return Status;
}


STM_VMCALL_HANDLER_STRUCT  mSmiVmcallHandler[] = {
  {STM_API_START,                              SmiVmcallStartHandler},
  {STM_API_STOP,                               SmiVmcallStopHandler},
  {STM_API_PROTECT_RESOURCE,                   SmiVmcallProtectResourceHandler},
  {STM_API_UNPROTECT_RESOURCE,                 SmiVmcallUnprotectResourceHandler},
  {STM_API_GET_BIOS_RESOURCES,                 SmiVmcallGetBiosResourcesHandler},
  {STM_API_MANAGE_VMCS_DATABASE,               SmiVmcallManageVmcsDatabaseHandler},
  {STM_API_INITIALIZE_PROTECTION,              SmiVmcallInitializeProtectionHandler},
  {STM_API_MANAGE_EVENT_LOG,                   SmiVmcallManageEventLogHandler},
#ifdef CONFIG_STM_STMPE_ENABLE
  {STM_API_ADD_TEMP_PE_VM,		       SmiVmcallAddTempPeVmHandler},
  {STM_API_ADD_PERM_PE_VM,			SmiVmcallAddPermPeVmHandler},
  {STM_API_ADD_PERM_PE_VM_NORUN,		SmiVmcallAddPermPeVmNoRunHandler},
  {STM_API_RUN_PE_VM,				SmiVmcallRunPeVmHandler},
  {STM_API_END_ADD_PERM_PE_VM,                 SmiVmcallEndPermVmHandler}
#endif
};

/**

  This function returns SMI VMCALL handler by FuncIndex.

  @param FuncIndex         VmCall function index

  @return VMCALL Handler

**/
STM_VMCALL_HANDLER
GetSmiVmcallHandlerByIndex (
  IN UINT32  FuncIndex
  )
{
  UINTN  Index;
  for (Index = 0; Index < sizeof(mSmiVmcallHandler)/sizeof(mSmiVmcallHandler[0]); Index++) {
    if (mSmiVmcallHandler[Index].FuncIndex == FuncIndex) {
      return mSmiVmcallHandler[Index].StmVmcallHandler;
    }
  }
  return NULL;
}

/**

  This function is VMCALL handler for SMI.

  @param Index CPU index

**/
VOID
SmiVmcallHandler (
  IN UINT32  Index
  )
{
  X86_REGISTER                       *Reg;
  STM_STATUS                         Status;
  STM_VMCALL_HANDLER                 StmVmcallHandler;
  UINT64                             AddressParameter;
  //DEBUG((EFI_D_ERROR, "%ld SmiVmcallHandler - entereda\n", Index));

  Reg = &mGuestContextCommonSmi.GuestContextPerCpu[Index].Register;
  StmVmcallHandler = GetSmiVmcallHandlerByIndex (ReadUnaligned32 ((UINT32 *)&Reg->Rax));
  if (StmVmcallHandler == NULL) {
    DEBUG ((EFI_D_ERROR,
		"%ld SmiVmcallHandler - GetSmiVmcallHandlerByIndex- Invalid API entry  - %x!\n",
		Index,
		(UINTN)ReadUnaligned32 ((UINT32 *)&Reg->Rax)));
    Status = ERROR_INVALID_API;
  } else {
    AddressParameter = ReadUnaligned32 ((UINT32 *)&Reg->Rbx) + LShiftU64 (ReadUnaligned32 ((UINT32 *)&Reg->Rcx), 32);

    Status = StmVmcallHandler (Index, AddressParameter);
  }

  if (Status == STM_SUCCESS) {
    VmWriteN (VMCS_N_GUEST_RFLAGS_INDEX, VmReadN(VMCS_N_GUEST_RFLAGS_INDEX) & ~RFLAGS_CF);
  } else {
    VmWriteN (VMCS_N_GUEST_RFLAGS_INDEX, VmReadN(VMCS_N_GUEST_RFLAGS_INDEX) | RFLAGS_CF);
    AddEventLogInvalidParameter (ReadUnaligned32 ((UINT32 *)&Reg->Rax));
  }
  WriteUnaligned32 ((UINT32 *)&Reg->Rax, Status);
  VmWriteN (VMCS_N_GUEST_RIP_INDEX, VmReadN (VMCS_N_GUEST_RIP_INDEX) + VmRead32 (VMCS_32_RO_VMEXIT_INSTRUCTION_LENGTH_INDEX));

  return ;
}

