// 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 <memory.h>
#include <stdio.h>
#include <string.h>
#include "PlatformData.h"
#include "TpmError.h"
#include "assert.h"
#ifndef EMBEDDED_MODE
#define FILE_BACKED_NV
#endif
#if defined FILE_BACKED_NV
static FILE* s_NVFile;
#endif
static unsigned char s_NV[NV_MEMORY_SIZE];
static BOOL s_NvIsAvailable;
static BOOL s_NV_unrecoverable;
static BOOL s_NV_recoverable;
//
//
// Functions
//
// _plat__NvErrors()
//
// This function is used by the simulator to set the error flags in the NV subsystem to simulate an error in the
// NV loading process
//
LIB_EXPORT void
_plat__NvErrors(
BOOL recoverable,
BOOL unrecoverable
)
{
s_NV_unrecoverable = unrecoverable;
s_NV_recoverable = recoverable;
}
//
//
// _plat__NVEnable()
//
// Enable NV memory.
// This version just pulls in data from a file. In a real TPM, with NV on chip, this function would verify the
// integrity of the saved context. If the NV memory was not on chip but was in something like RPMB, the NV
// state would be read in, decrypted and integrity checked.
// 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.
//
// Return Value Meaning
//
// 0 if success
// >0 if receive recoverable error
// <0 if unrecoverable error
//
LIB_EXPORT int
_plat__NVEnable(
void *platParameter // IN: platform specific parameter
)
{
// Start assuming everything is OK
s_NV_unrecoverable = FALSE;
s_NV_recoverable = FALSE;
#ifdef FILE_BACKED_NV
if(s_NVFile != NULL) return 0;
// Try to open an exist NVChip file for read/write
s_NVFile = fopen("NVChip", "r+b");
if(NULL != s_NVFile)
{
// See if the NVChip file is empty
fseek(s_NVFile, 0, SEEK_END);
if(0 == ftell(s_NVFile))
s_NVFile = NULL;
}
if(s_NVFile == NULL)
{
// Initialize all the byte in the new file to 0
memset(s_NV, 0, NV_MEMORY_SIZE);
// If NVChip file does not exist, try to create it for read/write
s_NVFile = fopen("NVChip", "w+b");
// Start initialize at the end of new file
fseek(s_NVFile, 0, SEEK_END);
// Write 0s to NVChip file
fwrite(s_NV, 1, NV_MEMORY_SIZE, s_NVFile);
}
else
{
// If NVChip file exist, assume the size is correct
fseek(s_NVFile, 0, SEEK_END);
assert(ftell(s_NVFile) == NV_MEMORY_SIZE);
// read NV file data to memory
fseek(s_NVFile, 0, SEEK_SET);
assert(1 == fread(s_NV, NV_MEMORY_SIZE, 1, s_NVFile));
}
#endif
// NV contents have been read and the error checks have been performed. For
// simulation purposes, use the signaling interface to indicate if an error is
// to be simulated and the type of the error.
if(s_NV_unrecoverable)
return -1;
return s_NV_recoverable;
}
//
//
// _plat__NVDisable()
//
// Disable NV memory
//
LIB_EXPORT void
_plat__NVDisable(
void
)
{
#ifdef FILE_BACKED_NV
assert(s_NVFile != NULL);
// Close NV file
fclose(s_NVFile);
// Set file handle to NULL
//
s_NVFile = NULL;
#endif
return;
}
//
//
// _plat__IsNvAvailable()
//
// Check if NV is available
//
// Return Value Meaning
//
// 0 NV is available
// 1 NV is not available due to write failure
// 2 NV is not available due to rate limit
//
LIB_EXPORT int
_plat__IsNvAvailable(
void
)
{
// NV is not available if the TPM is in failure mode
if(!s_NvIsAvailable)
return 1;
#ifdef FILE_BACKED_NV
if(s_NVFile == NULL)
return 1;
#endif
return 0;
}
//
//
// _plat__NvMemoryRead()
//
// Function: Read a chunk of NV memory
//
LIB_EXPORT void
_plat__NvMemoryRead(
unsigned int startOffset, // IN: read start
unsigned int size, // IN: size of bytes to read
void *data // OUT: data buffer
)
{
assert(startOffset + size <= NV_MEMORY_SIZE);
// Copy data from RAM
memcpy(data, &s_NV[startOffset], size);
return;
}
//
//
// _plat__NvIsDifferent()
//
// This function checks to see if the NV is different from the test value. This is so that NV will not be written if
// it has not changed.
//
//
//
//
// Return Value Meaning
//
// TRUE the NV location is different from the test value
// FALSE the NV location is the same as the test value
//
LIB_EXPORT BOOL
_plat__NvIsDifferent(
unsigned int startOffset, // IN: read start
unsigned int size, // IN: size of bytes to read
void *data // IN: data buffer
)
{
return (memcmp(&s_NV[startOffset], data, size) != 0);
}
//
//
// _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.
//
LIB_EXPORT void
_plat__NvMemoryWrite(
unsigned int startOffset, // IN: write start
unsigned int size, // IN: size of bytes to write
void *data // OUT: data buffer
)
{
assert(startOffset + size <= NV_MEMORY_SIZE);
// Copy the data to the NV image
memcpy(&s_NV[startOffset], data, size);
}
//
//
// _plat__NvMemoryMove()
//
// Function: 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
//
LIB_EXPORT void
_plat__NvMemoryMove(
unsigned int sourceOffset, // IN: source offset
unsigned int destOffset, // IN: destination offset
unsigned int size // IN: size of data being moved
)
{
assert(sourceOffset + size <= NV_MEMORY_SIZE);
assert(destOffset + size <= NV_MEMORY_SIZE);
// Move data in RAM
memmove(&s_NV[destOffset], &s_NV[sourceOffset], size);
return;
}
//
//
// _plat__NvCommit()
//
// Update NV chip
//
//
//
// Return Value Meaning
//
// 0 NV write success
// non-0 NV write fail
//
LIB_EXPORT int
_plat__NvCommit(
void
)
{
#ifdef FILE_BACKED_NV
// If NV file is not available, return failure
if(s_NVFile == NULL)
return 1;
// Write RAM data to NV
fseek(s_NVFile, 0, SEEK_SET);
fwrite(s_NV, 1, NV_MEMORY_SIZE, s_NVFile);
return 0;
#else
return 0;
#endif
}
//
//
// _plat__SetNvAvail()
//
// 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
)
{
s_NvIsAvailable = TRUE;
return;
}
//
//
// _plat__ClearNvAvail()
//
// 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
)
{
s_NvIsAvailable = FALSE;
return;
}