// 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;
}