// This file was extracted from the TCG Published
// Trusted Platform Module Library
// Part 4: Supporting Routines
// Family "2.0"
// Level 00 Revision 01.16
// October 30, 2014

#include "InternalRoutines.h"
#include "Platform.h"
//          Functions
//
//           TimePowerOn()
//
//     This function initialize time info at _TPM_Init().
//
void
TimePowerOn(
    void
    )
{
    TPM_SU               orderlyShutDown;
    // Read orderly data info from NV memory
    NvReadReserved(NV_ORDERLY_DATA, &go);
    // Read orderly shut down state flag
    NvReadReserved(NV_ORDERLY, &orderlyShutDown);
    // If the previous cycle is orderly shut down, the value of the safe bit
    // the same as previously saved. Otherwise, it is not safe.
    if(orderlyShutDown == SHUTDOWN_NONE)
        go.clockSafe= NO;
    else
        go.clockSafe = YES;
    // Set the initial state of the DRBG
    CryptDrbgGetPutState(PUT_STATE);
    // Clear time since TPM power on
    g_time = 0;
    return;
}
//
//
//           TimeStartup()
//
//     This function updates the resetCount and restartCount components of TPMS_CLOCK_INFO structure at
//     TPM2_Startup().
//
void
TimeStartup(
    STARTUP_TYPE          type                // IN: start up type
    )
{
    if(type == SU_RESUME)
    {
        // Resume sequence
        gr.restartCount++;
    }
    else
    {
        if(type == SU_RESTART)
        {
             // Hibernate sequence
             gr.clearCount++;
             gr.restartCount++;
        }
        else
        {
             // Reset sequence
             // Increase resetCount
             gp.resetCount++;
              // Write resetCount to NV
              NvWriteReserved(NV_RESET_COUNT, &gp.resetCount);
              gp.totalResetCount++;
              // We do not expect the total reset counter overflow during the life
              // time of TPM. if it ever happens, TPM will be put to failure mode
              // and there is no way to recover it.
              // The reason that there is no recovery is that we don't increment
              // the NV totalResetCount when incrementing would make it 0. When the
              // TPM starts up again, the old value of totalResetCount will be read
              // and we will get right back to here with the increment failing.
              if(gp.totalResetCount == 0)
                  FAIL(FATAL_ERROR_INTERNAL);
              // Write total reset counter to NV
              NvWriteReserved(NV_TOTAL_RESET_COUNT, &gp.totalResetCount);
              // Reset restartCount
              gr.restartCount = 0;
         }
   }
   return;
}
//
//
//             TimeUpdateToCurrent()
//
//      This function updates the Time and Clock in the global TPMS_TIME_INFO structure.
//      In this implementation, Time and Clock are updated at the beginning of each command and the values
//      are unchanged for the duration of the command.
//      Because Clock updates may require a write to NV memory, Time and Clock are not allowed to advance if
//      NV is not available. When clock is not advancing, any function that uses Clock will fail and return
//      TPM_RC_NV_UNAVAILABLE or TPM_RC_NV_RATE.
//      This implementations does not do rate limiting. If the implementation does do rate limiting, then the Clock
//      update should not be inhibited even when doing rather limiting.
//
void
TimeUpdateToCurrent(
   void
   )
{
   UINT64          oldClock;
   UINT64          elapsed;
#define CLOCK_UPDATE_MASK ((1ULL << NV_CLOCK_UPDATE_INTERVAL)- 1)
   // Can't update time during the dark interval or when rate limiting.
   if(NvIsAvailable() != TPM_RC_SUCCESS)
       return;
   // Save the old clock value
   oldClock = go.clock;
   // Update the time info to current
   elapsed = _plat__ClockTimeElapsed();
   go.clock += elapsed;
   g_time += elapsed;
   // Check to see if the update has caused a need for an nvClock update
   // CLOCK_UPDATE_MASK is measured by second, while the value in go.clock is
   // recorded by millisecond. Align the clock value to second before the bit
//
   // operations
   if( ((go.clock/1000) | CLOCK_UPDATE_MASK)
           > ((oldClock/1000) | CLOCK_UPDATE_MASK))
   {
       // Going to update the time state so the safe flag
       // should be set
       go.clockSafe = YES;
         // Get the DRBG state before updating orderly data
         CryptDrbgGetPutState(GET_STATE);
         NvWriteReserved(NV_ORDERLY_DATA, &go);
   }
   // Call self healing logic for dictionary attack parameters
   DASelfHeal();
   return;
}
//
//
//           TimeSetAdjustRate()
//
//      This function is used to perform rate adjustment on Time and Clock.
//
void
TimeSetAdjustRate(
   TPM_CLOCK_ADJUST          adjust            // IN: adjust constant
   )
{
   switch(adjust)
   {
       case TPM_CLOCK_COARSE_SLOWER:
           _plat__ClockAdjustRate(CLOCK_ADJUST_COARSE);
           break;
       case TPM_CLOCK_COARSE_FASTER:
           _plat__ClockAdjustRate(-CLOCK_ADJUST_COARSE);
           break;
       case TPM_CLOCK_MEDIUM_SLOWER:
           _plat__ClockAdjustRate(CLOCK_ADJUST_MEDIUM);
           break;
       case TPM_CLOCK_MEDIUM_FASTER:
           _plat__ClockAdjustRate(-CLOCK_ADJUST_MEDIUM);
           break;
       case TPM_CLOCK_FINE_SLOWER:
           _plat__ClockAdjustRate(CLOCK_ADJUST_FINE);
           break;
       case TPM_CLOCK_FINE_FASTER:
           _plat__ClockAdjustRate(-CLOCK_ADJUST_FINE);
           break;
       case TPM_CLOCK_NO_CHANGE:
           break;
       default:
           pAssert(FALSE);
           break;
   }
   return;
}
//
//
//           TimeGetRange()
//
//      This function is used to access TPMS_TIME_INFO. The TPMS_TIME_INFO structure is treaded as an
//      array of bytes, and a byte offset and length determine what bytes are returned.
//
//      Error Returns                   Meaning
//
//      TPM_RC_RANGE                    invalid data range
//
TPM_RC
TimeGetRange(
   UINT16              offset,             // IN: offset in TPMS_TIME_INFO
   UINT16              size,               // IN: size of data
   TIME_INFO          *dataBuffer          // OUT: result buffer
   )
{
   TPMS_TIME_INFO            timeInfo;
   UINT16                    infoSize;
   BYTE                      infoData[sizeof(TPMS_TIME_INFO)];
   BYTE                      *buffer;
   INT32                     bufferSize;
   // Fill TPMS_TIME_INFO structure
   timeInfo.time = g_time;
   TimeFillInfo(&timeInfo.clockInfo);
   // Marshal TPMS_TIME_INFO to canonical form
   buffer = infoData;
   bufferSize = sizeof(TPMS_TIME_INFO);
   infoSize = TPMS_TIME_INFO_Marshal(&timeInfo, &buffer, &bufferSize);
   // Check if the input range is valid
   if(offset + size > infoSize) return TPM_RC_RANGE;
   // Copy info data to output buffer
   MemoryCopy(dataBuffer, infoData + offset, size, sizeof(TIME_INFO));
   return TPM_RC_SUCCESS;
}
//
//
//          TimeFillInfo
//
//      This function gathers information to fill in a TPMS_CLOCK_INFO structure.
//
void
TimeFillInfo(
   TPMS_CLOCK_INFO           *clockInfo
   )
{
   clockInfo->clock = go.clock;
   clockInfo->resetCount = gp.resetCount;
   clockInfo->restartCount = gr.restartCount;
   // If NV is not available, clock stopped advancing and the value reported is
   // not "safe".
   if(NvIsAvailable() == TPM_RC_SUCCESS)
       clockInfo->safe = go.clockSafe;
   else
       clockInfo->safe = NO;
   return;
}