/** @file
  This file connects Tcg TPM reference code's platform specific functions
  to correspond functions in PlatformTpmLib.
  about each "_plat__XXX" interfaces in TPM reference libraries, see:
    - https://github.com/TrustedComputingGroup/TPM/blob/main/TPMCmd/Platform/src

  When TPM reference library is build, it links with PlatformTpmLib
  which have correspondant interfaces with "_plat_XXX" to "PlatformTpmLibXXX"
  By this, when TPM reference library calls "_plat_XXX",
  it calls "_plat_XXX" in this file and finally this file calls
  "PlatformTpmLibXXX" implemented in PlatformTpmLib.

  That's why this doesn't follow edk2 coding style
  But remain as it is including comment style in TPM reference library
  for clarification.

  Of course, this doesn't defines all _plat_XXX interfaces in TPM
  reference library because interfaces are enough with default implementation
  in TPM reference library.


  Copyright (c) 2025, Arm Limited. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/
typedef unsigned char  uint8_t;
typedef int            BOOL;

#include <Uefi/UefiBaseType.h>
#include <Library/BaseLib.h>
#include <Library/PlatformTpmLib.h>

#include <TpmConfiguration/TpmBuildSwitches.h>
#include <CompilerDependencies.h>
#include <ExecCommand_fp.h>
#include <platform_interface/tpm_to_platform_interface.h>

/**
  This function used to generate Endorsement Seed when
  first initailization of TPM.

  @param [in]   Size              Size of endorsement seed
  @param [out]  EndorsementSeed   Endorsement Seed.

**/
LIB_EXPORT void
_plat__GetEPS (
  UINT16   Size,
  uint8_t  *EndorsementSeed
  )
{
  PlatformTpmLibGetEPS (Size, EndorsementSeed);
}

/**
  This function sets current system clock time as t0 for counting TPM time.
  This function is called at a power on event to reset the clock. When the clock
  is reset, the indication that the clock was stopped is also set.

**/
LIB_EXPORT void
_plat__TimerReset (
  void
  )
{
  PlatformTpmLibTimerReset ();
}

/**
  This function should be called in order to simulate the restart of the timer
  should it be stopped while power is still applied.

**/
LIB_EXPORT void
_plat__TimerRestart (
  void
  )
{
  PlatformTpmLibTimerRestart ();
}

/**
  This is another, probably futile, attempt to define a portable function
  that will return a 64-bit clock value that has mSec resolution.

**/
LIB_EXPORT uint64_t
_plat__RealTime (
  void
  )
{
  return PlatformTpmLibRealTime ();
}

/**
 This function provides access to the tick timer of the platform. The TPM code
 uses this value to drive the TPM Clock.

 The tick timer is supposed to run when power is applied to the device. This timer
 should not be reset by time events including _TPM_Init. It should only be reset
 when TPM power is re-applied.

 If the TPM is run in a protected environment, that environment may provide the
 tick time to the TPM as long as the time provided by the environment is not
 allowed to go backwards. If the time provided by the system can go backwards
 during a power discontinuity, then the _plat__Signal_PowerOn should call

**/
LIB_EXPORT uint64_t
_plat__TimerRead (
  void
  )
{
  return PlatformTpmLibTimerRead ();
}

/**
  This function is used to interrogate the flag indicating if the tick timer has
  been reset.

  If the resetFlag parameter is SET, then the flag will be CLEAR before the
  function returns.

**/
LIB_EXPORT int
_plat__TimerWasReset (
  void
  )
{
  return PlatformTpmLibTimerWasReset ();
}

/**
  This function is used to interrogate the flag indicating if the tick timer has
  been stopped. If so, this is typically a reason to roll the nonce.

  This function will CLEAR the s_timerStopped flag before returning. This provides
  functionality that is similar to status register that is cleared when read. This
  is the model used here because it is the one that has the most impact on the TPM
  code as the flag can only be accessed by one entity in the TPM. Any other
  implementation of the hardware can be made to look like a read-once register.

**/
LIB_EXPORT BOOL
_plat__TimerWasStopped (
  void
  )
{
  return (BOOL)PlatformTpmLibTimerWasStopped ();
}

/**
 ClockRateAdjust uses predefined signal values and encapsulates the platform
 specifics regarding the number of ticks the underlying clock is running at.

 The adjustment must be one of these values. A COARSE adjustment is 1%, MEDIUM
 is 0.1%, and FINE is the smallest amount supported by the platform.  The
 total (cumulative) adjustment is limited to ~15% total.  Attempts to adjust
 the clock further are silently ignored as are any invalid values.  These
 values are defined here to insulate them from spec changes and to avoid
 needing visibility to the doc-generated structure headers.

 @param [in] Adjust  The adjust number.

**/
LIB_EXPORT void
_plat__ClockRateAdjust (
  _plat__ClockAdjustStep  adjust
  )
{
  PlatformTpmLibClockAdjustRate (adjust);
}

/**
  _plat__GetEntropy()

  This function is used to get available hardware entropy. In a hardware
  implementation of this function, there would be no call to the system
  to get entropy.

  @param [out] Entropy
  @param [in]  Amount    amount reuqested.

  @return < 0   Failed to generate entropy
  @return >= 0  The returned amount of entropy (bytes)

**/
LIB_EXPORT int32_t
_plat__GetEntropy (
  unsigned char  *entropy,                                    // output buffer
  uint32_t       amount                                       // amount requested
  )
{
  return PlatformTpmLibGetEntropy (entropy, amount);
}

/**
  Enable NV memory.

  the NV state would be read in, decrypted and integrity checked if needs.

  The recovery from an integrity failure depends on where the error occurred. It
  it was in the state that is discarded by TPM Reset, then the error is
  recoverable if the TPM is reset. Otherwise, the TPM must go into failure mode.

  @param [in] PlatParameter   Platform parameter to enable NV storage
  @param [in] ParamSize

  @return 0        if success
  @return > 0      if receive recoverable error
  @return < 0      if unrecoverable error

**/
LIB_EXPORT int
_plat__NVEnable (
  void    *platParameter, // IN: platform specific parameters
  size_t  paramSize
  )
{
  return PlatformTpmLibNVEnable (platParameter, paramSize);
}

/**
  Disable NV memory.

**/
LIB_EXPORT void
_plat__NVDisable (
  VOID
  )
{
  PlatformTpmLibNVDisable ();
}

/**
  Check if NV is available

  @return    0               NV is available
  @return    1               NV is not available due to write failure
  @return    2               NV is not available due to rate limit

**/
LIB_EXPORT int
_plat__GetNvReadyState (
  void
  )
{
  return PlatformTpmLibGetNvReadyState ();
}

/**
  Read a chunk of NV memory

  @param [in] StartOffset   Read start offset
  @param [in] Size          Size to read
  @param [out] Data         Data buffer

  @return 1 Success to read
  @return 0 Failed to read

**/
LIB_EXPORT int
_plat__NvMemoryRead (
  unsigned int  startOffset,                            // IN: read start
  unsigned int  size,                                   // IN: size of bytes to read
  void          *data                                   // OUT: data buffer
  )
{
  return PlatformTpmLibNvMemoryRead (
           startOffset,
           size,
           data
           );
}

/**
  This function checks to see if the NV is different from the test value
  so that NV will not be written if it has not changed.

  @param [in] StartOffset Start offset to compare
  @param [in] Size        Size for compare
  @param [in] Data        Data to be compared

  @return NV_HAS_CHANGED(1)       the NV location is different from the test value
  @return NV_IS_SAME(0)           the NV location is the same as the test value
  @return NV_INVALID_LOCATION(-1) the NV location is invalid; also triggers failure mode

**/
LIB_EXPORT int
_plat__NvGetChangedStatus (
  unsigned int  startOffset,                            // IN: read start
  unsigned int  size,                                   // IN: size of bytes to read
  void          *data                                   // IN: data buffer
  )
{
  return PlatformTpmLibNvGetChangedStatus (
           startOffset,
           size,
           data
           );
}

/**
  _plat__NvMemoryWrite()

  This function is used to update NV memory. The "write" is to a memory copy of
  NV. At the end of the current command, any changes are written to
  the actual NV memory.

  NOTE: A useful optimization would be for this code to compare the current
  contents of NV with the local copy and note the blocks that have changed. Then
  only write those blocks when _plat__NvCommit() is called.

  @param [in] StartOffset   Start Offset to write
  @param [in] Size          Size to wrrite
  @param [in] Data          Data

  @return    0               Failed to write
  @return    1               Success to write

**/
LIB_EXPORT int
_plat__NvMemoryWrite (
  unsigned int  startOffset,                            // IN: write start
  unsigned int  size,                                   // IN: size of bytes to write
  void          *data                                   // OUT: data buffer
  )
{
  return PlatformTpmLibNvMemoryWrite (
           startOffset,
           size,
           data
           );
}

/**
  Function is used to set a range of NV memory bytes to an implementation-dependent
  value. The value represents the erase state of the memory.

  @param [in] StartOffset   Start offset to clear
  @param [in] SIze          Size to be clear


  @return 0 Failed to clear
  @return 1 Success

**/
LIB_EXPORT int
_plat__NvMemoryClear (
  unsigned int  start,   // IN: clear start
  unsigned int  size     // IN: number of bytes to clear
  )
{
  return PlatformTpmLibNvMemoryClear (
           start,
           size
           );
}

/**
  Move a chunk of NV memory from source to destination
  This function should ensure that if there overlap, the original data is
  copied before it is written

  @param [in] SourceOffset  Source offset to move
  @param [in] DestOffset    Destination offset to move
  @param [in] Size          Size to be moved

  @return 0 Failed to move
  @return 1 Success

**/
LIB_EXPORT int
_plat__NvMemoryMove (
  unsigned int  sourceOffset,   // IN: source offset
  unsigned int  destOffset,     // IN: destination offset
  unsigned int  size            // IN: size of data being moved
  )
{
  return PlatformTpmLibNvMemoryMove (
           sourceOffset,
           destOffset,
           size
           );
}

/**
  This function writes the local copy of NV to NV for permanent store.
  It will write TPM_NV_MEMORY_SIZE bytes to NV.

   @return 0       NV write success
   @return non-0   NV write fail

**/
LIB_EXPORT int
_plat__NvCommit (
  void
  )
{
  return PlatformTpmLibNvCommit ();
}

/**
   Set the current NV state to available.
   This function is for testing purpose only.
   It is not part of the platform NV logic

**/
LIB_EXPORT void
_plat__SetNvAvail (
  void
  )
{
  PlatformTpmLibSetNvAvail ();
}

/**
  Set the current NV state to unavailable.
  This function is for testing purpose only.
  It is not part of the platform NV logic

**/
LIB_EXPORT void
_plat__ClearNvAvail (
  void
  )
{
  PlatformTpmLibClearNvAvail ();
}

/**
  _plat__NVNeedsManufacture()

  This function checks whether TPM's NV state needs to be manufactured

**/
LIB_EXPORT int
_plat__NVNeedsManufacture (
  void
  )
{
  return (int)PlatformTpmLibNVNeedsManufacture ();
}

/**
  Excecute TPM command

  @param[in]    requestSize    command buffer size
  @param[in]    request        command buffer
  @param[inout] responseSize   buffer size
  @param[inout] response       buffer

**/
LIB_EXPORT void
_plat__RunCommand (
  uint32_t       requestSize,
  unsigned char  *request,
  uint32_t       *responseSize,
  unsigned char  **response
  )
{
  ExecuteCommand (
    requestSize,
    request,
    responseSize,
    response
    );
}

/**
  This function is used to access the platform-specific unique value.
  This function places the unique value in the provided buffer ('b')
  and returns the number of bytes transferred. The function will not
  copy more data than 'bSize'.

  NOTE: If a platform unique value has unequal distribution of uniqueness
  and 'bSize' is smaller than the size of the unique value, the 'bSize'
  portion with the most uniqueness should be returned.

  @param [in] Which    if == 0, Copy unique value from start
                       otherwise, copy from end
  @param [in] Size     Size of Buffer
  @param [out] Buffer

  @return Size of unique value.

**/
LIB_EXPORT uint32_t
_plat__GetUnique (
  uint32_t       which,                                // authorities (0) or details
  uint32_t       bSize,                                // size of the buffer
  unsigned char  *b                                    // output buffer
  )
{
  return PlatformTpmLibGetUnique (
           which,
           bSize,
           b
           );
}

/**
  return the 4 character Manufacturer Capability code.
  This should come from the platform library since
  that is provided by the manufacturer.


  @return               Manufacturer capability Code.

**/
LIB_EXPORT uint32_t
_plat__GetManufacturerCapabilityCode (
  void
  )
{
  return PlatformTpmLibGetManufacturerCapabilityCode ();
}

/**
  _plat__GetVendorCapabilityCode()

  return the 4 character VendorStrings for Capabilities.
  Index is ONE-BASED, and may be in the range [1,4] inclusive.
  Any other index returns all zeros. The return value will be interpreted
  as an array of 4 ASCII characters (with no null terminator).

  @param[in] Index      index

  @return               Vendor specific capability code.

**/
LIB_EXPORT uint32_t
_plat__GetVendorCapabilityCode (
  int  index
  )
{
  return PlatformTpmLibGetVendorCapabilityCode (index);
}

/**
  return the most-significant 32-bits of the TPM Firmware Version

  @return               High 32-bits of TPM Firmware Version.

**/
LIB_EXPORT uint32_t
_plat__GetTpmFirmwareVersionHigh (
  void
  )
{
  return PlatformTpmLibGetTpmFirmwareVersionHigh ();
}

/**
  return the least-significant 32-bits of the TPM Firmware Version

  @return               Low 32-bits of TPM Firmware Version.

**/
LIB_EXPORT uint32_t
_plat__GetTpmFirmwareVersionLow (
  void
  )
{
  return PlatformTpmLibGetTpmFirmwareVersionLow ();
}

/**
  return the TPM Firmware current SVN.

  @return               current SVN.

**/
LIB_EXPORT uint16_t
_plat__GetTpmFirmwareSvn (
  void
  )
{
  return PlatformTpmLibGetTpmFirmwareSvn ();
}

/**
  return the TPM Firmware maximum SVN.

  @return               Maximum SVN.

**/
LIB_EXPORT uint16_t
_plat__GetTpmFirmwareMaxSvn (
  void
  )
{
  return PlatformTpmLibGetTpmFirmwareMaxSvn ();
}

/**
  return the TPM Firmware SVN Secret value associated with SVN

  @param[in]            Svn                     Svn
  @param[in]            SecretBufferSize        Secret Buffer Size
  @param[out]           SecretBuffer            Secret Buffer
  @param[out]           SecretSize              Secret Svn Size

  @return               0                       Success
  @return               < 0                     Error

**/
LIB_EXPORT int
_plat__GetTpmFirmwareSvnSecret (
  uint16_t  svn,
  uint16_t  secret_buf_size,
  uint8_t   *secret_buf,
  uint16_t  *secret_size
  )
{
  return PlatformTpmLibGetTpmFirmwareSvnSecret (
           svn,
           secret_buf_size,
           secret_buf,
           secret_size
           );
}

/**
  return the TPM Firmware Secret value associated with SVN

  @param[in]            SecretBufferSize        Secret Buffer Size
  @param[out]           SecretBuffer            Secret Buffer
  @param[out]           SecretSize              Secret Svn Size

  @return               0                       Success
  @return               < 0                     Error

**/
LIB_EXPORT int
_plat__GetTpmFirmwareSecret (
  uint16_t  secret_buf_size,
  uint8_t   *secret_buf,
  uint16_t  *secret_size
  )
{
  return PlatformTpmLibGetTpmFirmwareSecret (
           secret_buf_size,
           secret_buf,
           secret_size
           );
}

/**
  This function allows the platform to provide a small amount of data to be
  stored as part of the TPM's PERSISTENT_DATA structure during manufacture.
  Of course the platform can store data separately as well,
  but this allows a simple platform implementation to store
  a few bytes of data without implementing a multi-layer storage system.
  This function is called on manufacture and CLEAR.
  The buffer will contain the last value provided
  to the Core library.

  @param[out]  PlatformPersistentData          Platform data.
  @param[in]   Size                            Size of PlatformPersistentData.

**/
LIB_EXPORT void
_plat__GetPlatformManufactureData (
  uint8_t   *pPlatformPersistentData,
  uint32_t  bufferSize
  )
{
  return PlatformTpmLibGetPlatformManufactureData (
           pPlatformPersistentData,
           bufferSize
           );
}

/**
  Reset platform specific failure data.

**/
void
_plat_internal_resetFailureData (
  void
  )
{
  return PlatformTpmLibInternalResetFailureData ();
}

/**
  Check whether platform is in the failure mode.

**/
LIB_EXPORT BOOL
_plat__InFailureMode (
  void
  )
{
  return (BOOL)PlatformTpmLibInFailureMode ();
}

/**
  A function for the TPM to call the platform to indicate the
  TPM code has detected a failure.

**/
LIB_EXPORT NORETURN void
_plat__Fail (
  const char  *function,
  int         line,
  uint64_t    locationCode,
  int         failureCode
  )
{
  PlatformTpmLibFail (function, line, locationCode, failureCode);

  /*
   * for NORETURN
   */
  while (1) {
  }
}

/**
  Get last failure code.

  @return Last failure code.

**/
LIB_EXPORT UINT32
_plat__GetFailureCode (
  void
  )
{
  return PlatformTpmLibGetFailureCode ();
}

/**
  _plat__GetFailureLocation()

  Get last failure location.

  @return Last failure location code.

**/
LIB_EXPORT uint64_t
_plat__GetFailureLocation (
  void
  )
{
  return PlatformTpmLibGetFailureLocation ();
}

/**
  Get last function name where failed.

  @return Last function name where failed.

**/
LIB_EXPORT const char *
_plat__GetFailureFunctionName (
  void
  )
{
  return PlatformTpmLibGetFailureFunctionName ();
}

/**
  Get last failure line.

  @return Last line number where failed.

**/
LIB_EXPORT uint32_t
_plat__GetFailureLine (
  void
  )
{
  return PlatformTpmLibGetFailureLine ();
}

/**
  return the Platform TPM type.

  @return               TPM type.

**/
LIB_EXPORT uint32_t
_plat__GetVendorTpmType (
  void
  )
{
  return PlatformTpmLibGetVendorTpmType ();
}

/**
  return TPM spec information.
  This is copied from TPM/TPMCmd/Platform/src/VendorInfo.c

  @param[out] returnData  Spec information.

**/
LIB_EXPORT void
_plat_GetSpecCapabilityValue (
  SPEC_CAPABILITY_VALUE  *returnData
  )
{
  // clang-format off
  // this is on the title page of part1 of the TPM spec
  returnData->tpmSpecLevel = 0;
  // these come from part2 of the TPM spec
  returnData->tpmSpecVersion   = 184;
  returnData->tpmSpecYear      = 2025;
  returnData->tpmSpecDayOfYear = 79;    // March 20
  // these come from the PC Client Platform TPM Profile Specification
  returnData->platformFamily = 1;
  returnData->platfromLevel  = 0;
  // The platform spec version is recorded such that 0x00000101 means version 1.01
  // Note this differs from some TPM/TCG specifications, but matches the behavior of Windows.
  // more recent TCG specs have discontinued using this field, but Windows displays it, so we
  // retain it using the historical encoding.
  returnData->platformRevision  = 0x105;
  returnData->platformYear      = 0;
  returnData->platformDayOfYear = 0;
  // clang-format on
  return;
}

/**
  TpmBuildSwitch.h which includes in tpm_to_platform_interface.h
  defines its own DEBUG macro to compile TPM.
  So, undef DEBUG macro defined by TpmBuildSwitch.h
  and use DEBUG macro defined in DebugLib.h

**/
#undef DEBUG
#include <Library/DebugLib.h>

#if CERTIFYX509_DEBUG

/**
  This function opens the file used to hold the debug data.

  @retval  0             Success
  @retval  != 0          Error
**/
INT32
EFIAPI
DebugFileInit (
  VOID
  )
{
  return 0;
}

/**
  Dump Buffer

  @param[in] size        Size of buf
  @param[in] buf         Buffer
  @param[in] identifier  Print identifier

**/
VOID
EFIAPI
DebugDumpBuffer (
  IN INT32          size,
  IN unsigned char  *buf,
  IN CONST CHAR8    *identifier
  )
{
  INT32  Idx;

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

  for (Idx = 0; Idx < size; Idx++) {
    if (((Idx % 16) == 0) && (Idx != 0)) {
      DEBUG ((DEBUG_INFO, "\n"));
    }

    DEBUG ((DEBUG_INFO, "%02x\n", buf[Idx]));
  }

  if ((size % 16) != 0) {
    DEBUG ((DEBUG_INFO, "\n"));
  }
}

#endif // CERTIFYX509_DEBUG
