/* 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. */ #define STR_TRACE_USER_TA "fTPM" #include #include #include #include "fTPM.h" #define TA_ALL_PARAM_TYPE(type) TEE_PARAM_TYPES(type, type, type, type) // // Ensure we have only one active session // static bool fTPMSessionActive = false; // // Initialization // bool fTPMInitialized = false; // // Local (SW) command buffer // static uint8_t fTPMCommand[MAX_COMMAND_SIZE]; // // A subset of TPM return codes (see TpmTypes.h) // typedef uint32_t TPM_RC; #define RC_VER1 (TPM_RC) (0x100) #define TPM_RC_SUCCESS (TPM_RC) (0x000) #define TPM_RC_FAILURE (TPM_RC) (RC_VER1+0x001) // // Helper functions for byte ordering of TPM commands/responses // static uint16_t SwapBytes16(uint16_t Value) { return (uint16_t)((Value << 8) | (Value >> 8)); } static uint32_t SwapBytes32(uint32_t Value) { uint32_t LowerBytes; uint32_t HigherBytes; LowerBytes = (uint32_t)SwapBytes16((uint16_t)Value); HigherBytes = (uint32_t)SwapBytes16((uint16_t)(Value >> 16)); return (LowerBytes << 16 | HigherBytes); } // // Helper function to read response codes from TPM responses // static uint32_t fTPMResponseCode(uint32_t ResponseSize, uint8_t *ResponseBuffer) { uint32_t ResponseCode; union { uint32_t Data; uint8_t Index[4]; } Value; // In case of too-small response size, assume failure. if (ResponseSize < 0xA) { return TPM_RC_FAILURE; } Value.Index[0] = ResponseBuffer[6]; Value.Index[1] = ResponseBuffer[7]; Value.Index[2] = ResponseBuffer[8]; Value.Index[3] = ResponseBuffer[9]; ResponseCode = SwapBytes32(Value.Data); return ResponseCode; } // // Called when TA instance is created. This is the first call to the TA. // TEE_Result TA_CreateEntryPoint(void) { #define STARTUP_SIZE 0x0C uint8_t startupClear[STARTUP_SIZE] = { 0x80, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00 }; uint8_t startupState[STARTUP_SIZE] = { 0x80, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x44, 0x00, 0x01 }; uint32_t respLen; uint8_t *respBuf; #ifdef fTPMDebug DMSG("Entry Point\n"); #endif // If we've been here before, don't init again. if (fTPMInitialized) { // We may have had TA_DestroyEntryPoint called but we didn't // actually get torn down. Re-NVEnable, just in case. if (_plat__NVEnable(NULL) == 0) { TEE_Panic(TEE_ERROR_BAD_STATE); } return TEE_SUCCESS; } // Initialize NV admin state _admin__NvInitState(); // If we fail to open fTPM storage we cannot continue. if (_plat__NVEnable(NULL) == 0) { TEE_Panic(TEE_ERROR_BAD_STATE); } #ifdef fTPMDebug DMSG("NVEnable Complete\n"); #endif // This only occurs when there is no previous NV state, i.e., on first // boot, after recovering from data loss, we reset the platform, etc. if (_plat__NvNeedsManufacture()) { #ifdef fTPMDebug DMSG("TPM_Manufacture\n"); #endif TPM_Manufacture(1); } // "Power-On" the platform _plat__Signal_PowerOn(); // Internal init for reference implementation _TPM_Init(); #ifdef fTPMDebug DMSG("Init Complete\n"); #endif // Startup with state if (g_chipFlags.fields.TpmStatePresent) { // Re-use request buffer for response (ignored) respBuf = startupState; respLen = STARTUP_SIZE; ExecuteCommand(STARTUP_SIZE, startupState, &respLen, &respBuf); if (fTPMResponseCode(respLen, respBuf) == TPM_RC_SUCCESS) { goto Exit; } #ifdef fTPMDebug DMSG("Fall through to startup clear\n"); #endif goto Clear; } #ifdef fTPMDebug DMSG("No TPM state present\n"); #endif Clear: // Re-use request buffer for response (ignored) respBuf = startupClear; respLen = STARTUP_SIZE; // Fall back to a Startup Clear ExecuteCommand(STARTUP_SIZE, startupClear, &respLen, &respBuf); Exit: // Init is complete, indicate so in fTPM admin state. g_chipFlags.fields.TpmStatePresent = 1; _admin__SaveChipFlags(); // Initialization complete fTPMInitialized = true; return TEE_SUCCESS; } // // Called when TA instance destroyed. This is the last call in the TA. // void TA_DestroyEntryPoint(void) { // We should only see this called after the OS has shutdown and there // will be no further commands sent to the TPM. Right now, just close // our storage object, becasue the TPM driver should have already // shutdown cleanly. _plat__NVDisable(); return; } // // Called when a new session is opened to the TA. // TEE_Result TA_OpenSessionEntryPoint(uint32_t param_types, TEE_Param params[4], void **sess_ctx) { uint32_t exp_param_types = TA_ALL_PARAM_TYPE(TEE_PARAM_TYPE_NONE); // Unreferenced parameters UNREFERENCED_PARAMETER(params); UNREFERENCED_PARAMETER(sess_ctx); // Validate parameter types if (param_types != exp_param_types) { return TEE_ERROR_BAD_PARAMETERS; } // Only one active session to the fTPM is permitted if (fTPMSessionActive) { return TEE_ERROR_ACCESS_CONFLICT; } // Active session fTPMSessionActive = true; // If return value != TEE_SUCCESS the session will not be created. return TEE_SUCCESS; } // // Called when a session is closed. // void TA_CloseSessionEntryPoint(void *sess_ctx) { // Unused parameter(s) UNREFERENCED_PARAMETER(sess_ctx); // Clear active session if (fTPMSessionActive) { fTPMSessionActive = false; } } // // Called to handle command submission. // static TEE_Result fTPM_Submit_Command(uint32_t param_types, TEE_Param params[4] ) { uint8_t *cmdBuf, *respBuf; uint32_t cmdLen, respLen; uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); // Validate parameter types if (param_types != exp_param_types) { #ifdef fTPMDebug IMSG("Bad param type(s)\n"); #endif return TEE_ERROR_BAD_PARAMETERS; } // Sanity check our buffer sizes if ((params[0].memref.size == 0) || (params[1].memref.size == 0) || (params[0].memref.size > MAX_COMMAND_SIZE) || (params[1].memref.size > MAX_RESPONSE_SIZE)) { #ifdef fTPMDebug IMSG("Bad param size(s)\n"); #endif return TEE_ERROR_BAD_PARAMETERS; } // Copy command locally memcpy(fTPMCommand, params[0].memref.buffer, params[0].memref.size); // Pull the command length from the actual TPM command. The memref size // field descibes the buffer containing the command, not the command. cmdBuf = fTPMCommand; cmdLen = BYTE_ARRAY_TO_UINT32((uint8_t *)&(cmdBuf[2])); // Sanity check cmd length included in TPM command if (cmdLen > params[0].memref.size) { return TEE_ERROR_BAD_PARAMETERS; } respBuf = (uint8_t *)(params[1].memref.buffer); respLen = params[1].memref.size; // Check if this is a PPI Command if (!_admin__PPICommand(cmdLen, cmdBuf, &respLen, &respBuf)) { // If not, pass through to TPM ExecuteCommand(cmdLen, cmdBuf, &respLen, &respBuf); } // Unfortunately, this cannot be done until after we have our response in // hand. We will, however, make an effort to return at least a portion of // the response along with TEE_ERROR_SHORT_BUFFER. if (respLen > params[1].memref.size) { #ifdef fTPMDebug IMSG("Insufficient buffer length RS: 0x%x > BL: 0x%x\n", respLen, params[1].memref.size); #endif return TEE_ERROR_SHORT_BUFFER; } #ifdef fTPMDebug DMSG("Success, RS: 0x%x\n", respLen); #endif return TEE_SUCCESS; } // // Called to handle PPI commands // static TEE_Result fTPM_Emulate_PPI(uint32_t param_types, TEE_Param params[4] ) { uint8_t *cmdBuf, *respBuf; uint32_t cmdLen, respLen; uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); // Validate parameter types if (param_types != exp_param_types) { #ifdef fTPMDebug IMSG("Bad param type(s)\n"); #endif return TEE_ERROR_BAD_PARAMETERS; } // Sanity check our buffer sizes if ((params[0].memref.size == 0) || (params[1].memref.size == 0) || (params[0].memref.size > MAX_COMMAND_SIZE) || (params[1].memref.size > MAX_RESPONSE_SIZE)) { #ifdef fTPMDebug IMSG("Bad param size(s)\n"); #endif return TEE_ERROR_BAD_PARAMETERS; } // Copy command locally memcpy(fTPMCommand, params[0].memref.buffer, params[0].memref.size); cmdBuf = fTPMCommand; cmdLen = params[0].memref.size; respBuf = (uint8_t *)(params[1].memref.buffer); respLen = params[1].memref.size; // Pass along to platform PPI processing if (_admin__PPIRequest(cmdLen, cmdBuf, &respLen, &respBuf)) { #ifdef fTPMDebug DMSG("Handled PPI command via TA interface\n"); #endif } else { #ifdef fTPMDebug IMSG("Failed to handle PPI command via TA interface\n"); #endif } if (respLen > params[1].memref.size) { #ifdef fTPMDebug IMSG("Insufficient buffer length RS: 0x%x > BL: 0x%x\n", respLen, params[1].memref.size); #endif return TEE_ERROR_SHORT_BUFFER; } params[1].memref.size = respLen; return TEE_SUCCESS; } // // Called when a TA is invoked. Note, paramters come from normal world. // TEE_Result TA_InvokeCommandEntryPoint(void *sess_ctx, uint32_t cmd_id, uint32_t param_types, TEE_Param params[4]) { // Unused parameter(s) UNREFERENCED_PARAMETER(sess_ctx); // Handle command invocation switch (cmd_id) { case TA_FTPM_SUBMIT_COMMAND: { return fTPM_Submit_Command(param_types, params); } case TA_FTPM_EMULATE_PPI: { return fTPM_Emulate_PPI(param_types, params); } default: { return TEE_ERROR_BAD_PARAMETERS; } } }