/** @file
  Map TPM MMIO range unencrypted when SEV-ES is active.
  Install gQemuTpmMmioAccessiblePpiGuid unconditionally.

  Copyright (C) 2021, Advanced Micro Devices, Inc.

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <PiPei.h>

#include <Library/DebugLib.h>
#include <Library/MemEncryptSevLib.h>
#include <Library/PcdLib.h>
#include <Library/PeiServicesLib.h>

STATIC CONST EFI_PEI_PPI_DESCRIPTOR  mTpmMmioRangeAccessible = {
  EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
  &gQemuTpmMmioAccessiblePpiGuid,
  NULL
};

/**
  The entry point for TPM MMIO range mapping driver.

  @param[in]  FileHandle   Handle of the file being invoked.
  @param[in]  PeiServices  Describes the list of possible PEI Services.

  @retval  EFI_ABORTED  No need to keep this PEIM resident
**/
EFI_STATUS
EFIAPI
TpmMmioSevDecryptPeimEntryPoint (
  IN       EFI_PEI_FILE_HANDLE  FileHandle,
  IN CONST EFI_PEI_SERVICES     **PeiServices
  )
{
  RETURN_STATUS  DecryptStatus;
  EFI_STATUS     Status;

  DEBUG ((DEBUG_INFO, "%a\n", __func__));

  //
  // If SEV is active, MMIO succeeds against an encrypted physical address
  // because the nested page fault (NPF) that occurs on access does not
  // include the encryption bit in the guest physical address provided to the
  // hypervisor.
  //
  // If SEV-ES is active, MMIO would succeed against an encrypted physical
  // address because the #VC handler uses the virtual address (which is an
  // identity mapped physical address without the encryption bit) as the guest
  // physical address of the MMIO target in the VMGEXIT.
  //
  // However, if SEV-ES is active, before performing the actual MMIO, an
  // additional MMIO mitigation check is performed in the #VC handler to ensure
  // that MMIO is being done to/from an unencrypted address. To prevent guest
  // termination in this scenario, mark the range unencrypted ahead of access.
  //
  if (MemEncryptSevEsIsEnabled ()) {
    DEBUG ((
      DEBUG_INFO,
      "%a: mapping TPM MMIO address range unencrypted\n",
      __func__
      ));

    DecryptStatus = MemEncryptSevClearMmioPageEncMask (
                      0,
                      FixedPcdGet64 (PcdTpmBaseAddress),
                      EFI_SIZE_TO_PAGES ((UINTN)0x5000)
                      );

    if (RETURN_ERROR (DecryptStatus)) {
      DEBUG ((
        DEBUG_ERROR,
        "%a: failed to map TPM MMIO address range unencrypted\n",
        __func__
        ));
      ASSERT_RETURN_ERROR (DecryptStatus);
    }
  }

  //
  // MMIO range available
  //
  Status = PeiServicesInstallPpi (&mTpmMmioRangeAccessible);
  ASSERT_EFI_ERROR (Status);

  return EFI_ABORTED;
}
