/* Microsoft Reference Implementation for TPM 2.0 * * The copyright in this software is being made available under the BSD License, * included below. This software may be subject to other third party and * contributor rights, including patent rights, and no such rights are granted * under this license. * * Copyright (c) Microsoft Corporation * * All rights reserved. * * BSD License * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS"" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //** Description // // This file contains the NV read and write access methods. This implementation // uses RAM/file and does not manage the RAM/file as NV blocks. // The implementation may become more sophisticated over time. // //** Includes and Local #include #include #include #include "Platform.h" #if FILE_BACKED_NV # include static FILE *s_NvFile = NULL; static int s_NeedsManufacture = FALSE; #endif //**Functions #if FILE_BACKED_NV //*** NvFileOpen() // This function opens the file used to hold the NV image. // Return Type: int // >= 0 success // -1 error static int NvFileOpen( const char *mode ) { #if defined(NV_FILE_PATH) # define TO_STRING(s) TO_STRING_IMPL(s) # define TO_STRING_IMPL(s) #s const char* s_NvFilePath = TO_STRING(NV_FILE_PATH); # undef TO_STRING # undef TO_STRING_IMPL #else const char* s_NvFilePath = "NVChip"; #endif // Try to open an exist NVChip file for read/write # if defined _MSC_VER && 1 if(fopen_s(&s_NvFile, s_NvFilePath, mode) != 0) s_NvFile = NULL; # else s_NvFile = fopen(s_NvFilePath, mode); # endif return (s_NvFile == NULL) ? -1 : 0; } //*** NvFileCommit() // Write all of the contents of the NV image to a file. // Return Type: int // TRUE(1) success // FALSE(0) failure static int NvFileCommit( void ) { int OK; // 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); OK = (NV_MEMORY_SIZE == fwrite(s_NV, 1, NV_MEMORY_SIZE, s_NvFile)); OK = OK && (0 == fflush(s_NvFile)); assert(OK); return OK; } //*** NvFileSize() // This function gets the size of the NV file and puts the file pointer where desired // using the seek method values. SEEK_SET => beginning; SEEK_CUR => current position // and SEEK_END => to the end of the file. static long NvFileSize( int leaveAt ) { long fileSize; long filePos = ftell(s_NvFile); // assert(NULL != s_NvFile); int fseek_result = fseek(s_NvFile, 0, SEEK_END); NOT_REFERENCED(fseek_result); // Fix compiler warning for NDEBUG assert(fseek_result == 0); fileSize = ftell(s_NvFile); assert(fileSize >= 0); switch(leaveAt) { case SEEK_SET: filePos = 0; case SEEK_CUR: fseek(s_NvFile, filePos, SEEK_SET); break; case SEEK_END: break; default: assert(FALSE); break; } return fileSize; } #endif //*** _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( int recoverable, int 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 Type: int // 0 if success // > 0 if receive recoverable error // <0 if unrecoverable error LIB_EXPORT int _plat__NVEnable( void *platParameter // IN: platform specific parameters ) { NOT_REFERENCED(platParameter); // to keep compiler quiet // // Start assuming everything is OK s_NV_unrecoverable = FALSE; s_NV_recoverable = FALSE; #if FILE_BACKED_NV if(s_NvFile != NULL) return 0; // Initialize all the bytes in the ram copy of the NV _plat__NvMemoryClear(0, NV_MEMORY_SIZE); // If the file exists if(NvFileOpen("r+b") >= 0) { long fileSize = NvFileSize(SEEK_SET); // get the file size and leave the // file pointer at the start // // If the size is right, read the data if (NV_MEMORY_SIZE == fileSize) { s_NeedsManufacture = fread(s_NV, 1, NV_MEMORY_SIZE, s_NvFile) != NV_MEMORY_SIZE; } else { printf("acs 6\n"); NvFileCommit(); // for any other size, initialize it s_NeedsManufacture = TRUE; } } // If NVChip file does not exist, try to create it for read/write. else if(NvFileOpen("w+b") >= 0) { NvFileCommit(); // Initialize the file s_NeedsManufacture = TRUE; } assert(NULL != s_NvFile); // Just in case we are broken for some reason. #endif // NV contents have been initialized 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( int delete // IN: If TRUE, delete the NV contents. ) { #if FILE_BACKED_NV if(NULL != s_NvFile) { fclose(s_NvFile); // Close NV file // Alternative to deleting the file is to set its size to 0. This will not // match the NV size so the TPM will need to be remanufactured. if(delete) { // Open for writing at the start. Sets the size to zero. if(NvFileOpen("w") >= 0) { fflush(s_NvFile); fclose(s_NvFile); } } } s_NvFile = NULL; // Set file handle to NULL #endif return; } //***_plat__IsNvAvailable() // Check if NV is available // Return Type: int // 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 ) { int retVal = 0; // NV is not available if the TPM is in failure mode if(!s_NvIsAvailable) retVal = 1; #if FILE_BACKED_NV else retVal = (s_NvFile == NULL); #endif return retVal; } //***_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); memcpy(data, &s_NV[startOffset], size); // Copy data from RAM 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 Type: int // TRUE(1) the NV location is different from the test value // FALSE(0) the NV location is the same as the test value LIB_EXPORT int _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. // 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. 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 ) { if(startOffset + size <= NV_MEMORY_SIZE) { memcpy(&s_NV[startOffset], data, size); // Copy the data to the NV image return TRUE; } return FALSE; } //***_plat__NvMemoryClear() // 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. LIB_EXPORT void _plat__NvMemoryClear( unsigned int start, // IN: clear start unsigned int size // IN: number of bytes to clear ) { assert(start + size <= NV_MEMORY_SIZE); // In this implementation, assume that the erase value for NV is all 1s memset(&s_NV[start], 0xff, 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); memmove(&s_NV[destOffset], &s_NV[sourceOffset], size); // Move data in RAM return; } //***_plat__NvCommit() // This function writes the local copy of NV to NV for permanent store. It will write // NV_MEMORY_SIZE bytes to NV. If a file is use, the entire file is written. // Return Type: int // 0 NV write success // non-0 NV write fail LIB_EXPORT int _plat__NvCommit( void ) { #if FILE_BACKED_NV return (NvFileCommit() ? 0 : 1); #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; } //*** _plat__NVNeedsManufacture() // This function is used by the simulator to determine when the TPM's NV state // needs to be manufactured. LIB_EXPORT int _plat__NVNeedsManufacture( void ) { #if FILE_BACKED_NV return s_NeedsManufacture; #else return FALSE; #endif }