# Copyright 2014 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """A code generator for TPM 2.0 commands. The command generator takes as input a list of command objects generated by parsing the TCG specification and outputs valid C code to marshal command input and output structures, and also generates functions ParseHandleBuffer and CommandDispatcher defined by the TCG TPM2.0 Library Specification. """ from __future__ import print_function import re from subprocess import call from structure_generator import COPYRIGHT_HEADER from structure_generator import Field _HEADER_FILE_GUARD_HEADER = """ #ifndef TPM2_%(name)s_FP_H_ #define TPM2_%(name)s_FP_H_ """ _HEADER_FILE_GUARD_FOOTER = """ #endif // TPM2_%(name)s_FP_H """ _HEADER_FILE_INCLUDES = """ #include "tpm_generated.h" """ _IMPLEMENTATION_FILE_INCLUDES = """ #include "MemoryLib_fp.h" #include "%(command_name)s_fp.h" """ _COMMAND_DISPATCHER_INCLUDES = '#include "%(command_name)s_fp.h"\n' _COMMAND_DISPATCHER_START = """ #include "Implementation.h" #include "CommandDispatcher_fp.h" TPM_RC CommandDispatcher( TPMI_ST_COMMAND_TAG tag, TPM_CC command_code, INT32 *request_parameter_buffer_size, BYTE *request_parameter_buffer_start, TPM_HANDLE request_handles[], UINT32 *response_handle_buffer_size, UINT32 *response_parameter_buffer_size) { BYTE *request_parameter_buffer = request_parameter_buffer_start; switch(command_code) {""" _COMMAND_DISPATCHER_CASE = """ #ifdef %(command_code)s case %(command_code)s: return Exec_%(command_name)s(tag, &request_parameter_buffer, request_parameter_buffer_size, request_handles, response_handle_buffer_size, response_parameter_buffer_size); #endif""" _COMMAND_DISPATCHER_END = """ default: return TPM_RC_COMMAND_CODE; } }""" _HANDLE_PROCESS_START = """ #include "tpm_generated.h" #include "HandleProcess_fp.h" #include "Implementation.h" #include "TPM_Types.h" TPM_RC ParseHandleBuffer( TPM_CC command_code, BYTE **request_handle_buffer_start, INT32 *request_buffer_remaining_size, TPM_HANDLE request_handles[], UINT32 *num_request_handles) { TPM_RC result = TPM_RC_SUCCESS; *num_request_handles = 0; switch(command_code) { """ _HANDLE_PROCESS_CASE_START = """ #ifdef %(command_code)s case %(command_code)s:""" _HANDLE_PROCESS_CASE_UNMARSHAL = """ result = %(handle_type)s_Unmarshal( (%(handle_type)s*)&request_handles[*num_request_handles], request_handle_buffer_start, request_buffer_remaining_size);""" _HANDLE_PROCESS_CASE_UNMARSHAL_FLAG = """ result = %(handle_type)s_Unmarshal( (%(handle_type)s*)&request_handles[*num_request_handles], request_handle_buffer_start, request_buffer_remaining_size, %(flag_val)s);""" _HANDLE_PROCESS_CASE_CHECK = """ if (result != TPM_RC_SUCCESS) { return result; } ++(*num_request_handles);""" _HANDLE_PROCESS_CASE_END = """ return TPM_RC_SUCCESS; #endif""" _HANDLE_PROCESS_END = """ default: return TPM_RC_COMMAND_CODE; } }""" _GET_COMMAND_CODE_STRING_HEADER = """ #ifndef TPM2_GET_COMMAND_CODE_STRING_FP_H_ #define TPM2_GET_COMMAND_CODE_STRING_FP_H_ #include "TPM_Types.h" const char* GetCommandCodeString(TPM_CC command_code); #endif // TPM2_GET_COMMAND_CODE_STRING_FP_H_""" _GET_COMMAND_CODE_STRING_START = """ #include "GetCommandCodeString_fp.h" const char* GetCommandCodeString(TPM_CC command_code) { switch(command_code) {""" _GET_COMMAND_CODE_STRING_CASE = """ #ifdef TPM_CC_%(command_name)s case TPM_CC_%(command_name)s: return "%(command_name)s"; #endif""" _GET_COMMAND_CODE_STRING_END = """ default: return "Unknown command"; } }""" class Command(object): """Represents a TPM command. Attributes: name: The command name (e.g. 'TPM2_Startup'). command_code: The name of the command code constant (e.g. TPM2_CC_Startup). request_args: A list to hold command input arguments. Each element is a dict and has these keys: 'type': The argument type. 'name': The argument name. 'command_code': The optional value of the command code constant. 'description': Optional descriptive text for the argument. 'has_conditional': String literal 'TRUE' or 'FALSE' indicating whether 'type' is allowed to have a conditional value. response_args: A list identical in form to request_args but to hold command output arguments. """ _HANDLE_RE = re.compile(r'TPMI_.H_.*') _STRUCT_DECL_START = """ typedef struct {""" _STRUCT_DECL_FIELD = """ %(type)s %(name)s;""" _STRUCT_DECL_END = """ } %(command_name)s_%(direction)s; """ _FUNCTION_DECL_IN_OUT = """ // Executes %(command_name)s with request handles and parameters from // |in| and computes response handles and parameters to |out|. TPM_RC TPM2_%(command_name)s( %(command_name)s_In *in, %(command_name)s_Out *out); // Initializes handle fields in |target| from |request_handles|. Unmarshals // parameter fields in |target| from |buffer|. TPM_RC %(command_name)s_In_Unmarshal( %(command_name)s_In *target, TPM_HANDLE request_handles[], BYTE **buffer, INT32 *size); // Marshals response handles and parameters from |source| to |buffer|. Computes // and marshals the size of the parameter area (parameter_size) if |tag| == // TPM_ST_SESSIONS. Returns size of (parameter area + handle area) in bytes. // Return value does not include parameter_size field. UINT16 %(command_name)s_Out_Marshal( %(command_name)s_Out *source, TPMI_ST_COMMAND_TAG tag, BYTE **buffer, INT32 *size); """ _FUNCTION_DECL_IN = """ // Executes %(command_name)s with request handles and parameters from |in|. TPM_RC TPM2_%(command_name)s( %(command_name)s_In *in); // Initializes handle fields in |target| from |request_handles|. Unmarshals // parameter fields in |target| from |buffer|. TPM_RC %(command_name)s_In_Unmarshal( %(command_name)s_In *target, TPM_HANDLE request_handles[], BYTE **buffer, INT32 *size); """ _FUNCTION_DECL_OUT = """ // Executes %(command_name)s and computes response handles and parameters // to |out|. TPM_RC TPM2_%(command_name)s( %(command_name)s_Out *out); // Marshals response handles and parameters from |source| to |buffer|. Computes // and marshals the size of the parameter area (parameter_size) if |tag| == // TPM_ST_SESSIONS. Returns size of (parameter area + handle area) in bytes. // Does not include parameter_size field. UINT16 %(command_name)s_Out_Marshal( %(command_name)s_Out *source, TPMI_ST_COMMAND_TAG tag, BYTE **buffer, INT32 *size); """ _EXEC_DECL = """ // Unmarshals any request parameters starting at |request_parameter_buffer|. // Executes command. Marshals any response handles and parameters to the // global response buffer and computes |*response_handle_buffer_size| and // |*response_parameter_buffer_size|. If |tag| == TPM_ST_SESSIONS, marshals // parameter_size indicating the size of the parameter area. parameter_size // field is located between the handle area and parameter area. TPM_RC Exec_%(command_name)s( TPMI_ST_COMMAND_TAG tag, BYTE **request_parameter_buffer, INT32 *request_parameter_buffer_size, TPM_HANDLE request_handles[], UINT32 *response_handle_buffer_size, UINT32 *response_parameter_buffer_size); """ _EXEC_COMMAND_IMPL_START = """ TPM_RC Exec_%(command_name)s( TPMI_ST_COMMAND_TAG tag, BYTE **request_parameter_buffer, INT32 *request_parameter_buffer_size, TPM_HANDLE request_handles[], UINT32 *response_handle_buffer_size, UINT32 *response_parameter_buffer_size) { TPM_RC result = TPM_RC_SUCCESS;""" _EXEC_COMMAND_IMPL_IN_OUT = """ %(command_name)s_In in; %(command_name)s_Out out; #ifdef %(command_code)s BYTE *response_buffer; INT32 response_buffer_size; UINT16 bytes_marshalled; UINT16 num_response_handles = %(num_response_handles)s; #endif *response_handle_buffer_size = 0; *response_parameter_buffer_size = 0; // Unmarshal request parameters to input structure. result = %(command_name)s_In_Unmarshal(&in, request_handles, request_parameter_buffer, request_parameter_buffer_size); if (result != TPM_RC_SUCCESS) { return result; } // Execute command. result = TPM2_%(command_name)s(&in, &out); if (result != TPM_RC_SUCCESS) { return result; } // Marshal output structure to global response buffer. #ifdef %(command_code)s response_buffer = MemoryGetResponseBuffer(%(command_code)s) + 10; response_buffer_size = MAX_RESPONSE_SIZE - 10; bytes_marshalled = %(command_name)s_Out_Marshal( &out, tag, &response_buffer, &response_buffer_size); *response_handle_buffer_size = num_response_handles*sizeof(TPM_HANDLE); *response_parameter_buffer_size = bytes_marshalled - *response_handle_buffer_size; return TPM_RC_SUCCESS; #endif return TPM_RC_COMMAND_CODE; } """ _EXEC_COMMAND_IMPL_IN = """ %(command_name)s_In in; #ifdef %(command_code)s BYTE *response_buffer; INT32 response_buffer_size; #endif *response_handle_buffer_size = 0; *response_parameter_buffer_size = 0; // Unmarshal request parameters to input structure. result = %(command_name)s_In_Unmarshal(&in, request_handles, request_parameter_buffer, request_parameter_buffer_size); if (result != TPM_RC_SUCCESS) { return result; } // Execute command. result = TPM2_%(command_name)s(&in); if (result != TPM_RC_SUCCESS) { return result; } #ifdef %(command_code)s response_buffer = MemoryGetResponseBuffer(%(command_code)s) + 10; response_buffer_size = MAX_RESPONSE_SIZE - 10; // Add parameter_size field, always equal to 0 here. if (tag == TPM_ST_SESSIONS) { UINT32_Marshal(response_parameter_buffer_size, &response_buffer, &response_buffer_size); } return TPM_RC_SUCCESS; #endif return TPM_RC_COMMAND_CODE; } """ _EXEC_COMMAND_IMPL_OUT = """ %(command_name)s_Out out; #ifdef %(command_code)s BYTE *response_buffer; INT32 response_buffer_size; UINT16 bytes_marshalled; UINT16 num_response_handles = %(num_response_handles)s; #endif *response_handle_buffer_size = 0; *response_parameter_buffer_size = 0; // Execute command. result = TPM2_%(command_name)s(&out); if (result != TPM_RC_SUCCESS) { return result; } // Marshal output structure containing response handles and parameters to // response buffer. #ifdef %(command_code)s response_buffer = MemoryGetResponseBuffer(%(command_code)s) + 10; response_buffer_size = MAX_RESPONSE_SIZE - 10; bytes_marshalled = %(command_name)s_Out_Marshal( &out, tag, &response_buffer, &response_buffer_size); *response_handle_buffer_size = num_response_handles*sizeof(TPM_HANDLE); *response_parameter_buffer_size = bytes_marshalled - *response_handle_buffer_size; return TPM_RC_SUCCESS; #endif return TPM_RC_COMMAND_CODE; } """ _UNMARSHAL_COMMAND_START = """ TPM_RC %(command_name)s_In_Unmarshal( %(command_name)s_In *target, TPM_HANDLE request_handles[], BYTE **buffer, INT32 *size) { TPM_RC result = TPM_RC_SUCCESS;""" _MARSHAL_COMMAND_START = """ UINT16 %(command_name)s_Out_Marshal( %(command_name)s_Out *source, TPMI_ST_COMMAND_TAG tag, BYTE **buffer, INT32 *size) { UINT16 total_size = 0; UINT32 parameter_size = 0; BYTE *parameter_size_location; INT32 parameter_size_size = sizeof(UINT32); UINT32 num_response_handles = %(num_response_handles)s;""" _UNMARSHAL_END = """ if ((result == TPM_RC_SUCCESS) && *size) { result = TPM_RC_SIZE; } return result; } """ _MARSHAL_END = """ // Compute actual parameter_size. Don't add result to total_size. if (tag == TPM_ST_SESSIONS) { parameter_size = total_size - num_response_handles*sizeof(TPM_HANDLE); UINT32_Marshal( ¶meter_size, ¶meter_size_location, ¶meter_size_size); } return total_size; } """ _SET_COMMAND_HANDLE = """ target->%(field_name)s = request_handles[%(num)s];""" _PARAMETERSIZE_CHECK = """ // Add parameter_size=0 to indicate size of the parameter area. Will be // replaced later by computed parameter_size. if (tag == TPM_ST_SESSIONS) { parameter_size_location = *buffer; // Don't add to total_size, but increment *buffer and decrement *size. UINT32_Marshal(¶meter_size, buffer, size); }""" def __init__(self, name): """Initializes a Command instance. Initially the request_args and response_args attributes are not set. Args: name: The command name (e.g. 'TPM2_Startup'). """ self.name = name self.request_args = None self.response_args = None if name.startswith('TPM2_'): self.command_code = name.replace('TPM2_', 'TPM_CC_') else: self.command_code = '' def __str__(self): s = ['%s:' % self.name,] if self.request_args: s.append(' req:') for r in self.request_args: s.append(' %s: %s' % (r['type'], r['name'])) if self.response_args: s.append(' resp:') for r in self.response_args: s.append(' %s: %s' % (r['type'], r['name'])) s.append('') return '\n'.join(s) def OutputMarshalFunction(self, out_file, typemap): """Generates a marshal function for the command output structure. Args: out_file: File to be written to opened by the caller. typemap: A dict mapping type names to the corresponding object. Generated by structure_generator. """ if not self.response_args: return # Categorize arguments as either handles or parameters. handles, parameters = self._SplitArgs(self.response_args) out_file.write(self._MARSHAL_COMMAND_START % { 'command_name': self.MethodName(), 'num_response_handles': self._GetNumberOfResponseHandles()}) if handles: out_file.write('\n // Marshal response handles.') for handle in handles: typemap[handle['type']].OutputMarshalCall( out_file, Field(handle['type'], handle['name'], conditional_value=handle['has_conditional'])) out_file.write(self._PARAMETERSIZE_CHECK) if parameters: out_file.write('\n // Marshal response parameters.') for parameter in parameters: typemap[parameter['type']].OutputMarshalCall( out_file, Field(parameter['type'], parameter['name'], conditional_value=parameter['has_conditional'])) out_file.write(self._MARSHAL_END) def OutputUnmarshalFunction(self, out_file, typemap): """Generates a unmarshal function for the command input structure. Args: out_file: File to be written to opened by the caller. typemap: A dict mapping type names to the corresponding object. Generated by structure_generator. """ if not self.request_args: return # Categorize arguments as either handles or parameters. handles, parameters = self._SplitArgs(self.request_args) out_file.write(self._UNMARSHAL_COMMAND_START % { 'command_name': self.MethodName()}) if handles: out_file.write('\n // Get request handles from request_handles array.') for index, handle in enumerate(handles): out_file.write(self._SET_COMMAND_HANDLE % {'field_name': handle['name'], 'num': index}) if parameters: out_file.write('\n // Unmarshal request parameters.') for parameter in parameters: typemap[parameter['type']].OutputUnmarshalCall( out_file, Field(parameter['type'], parameter['name'], conditional_value=parameter['has_conditional'])) out_file.write(self._UNMARSHAL_END) def OutputExecFunction(self, out_file): """Generates an exec function for the command. Args: out_file: File to be written to opened by the caller. """ out_file.write( self._EXEC_COMMAND_IMPL_START % {'command_name': self.MethodName()}) if self.request_args and self.response_args: out_file.write(self._EXEC_COMMAND_IMPL_IN_OUT % { 'command_name': self.MethodName(), 'command_code': self.command_code, 'num_response_handles': self._GetNumberOfResponseHandles()}) elif self.request_args: out_file.write(self._EXEC_COMMAND_IMPL_IN % { 'command_name': self.MethodName(), 'command_code': self.command_code}) elif self.response_args: out_file.write(self._EXEC_COMMAND_IMPL_OUT % { 'command_name': self.MethodName(), 'command_code': self.command_code, 'num_response_handles': self._GetNumberOfResponseHandles()}) def OutputDecl(self, out_file): """Generates a TPM object declaration.""" if self.request_args: out_file.write(self._STRUCT_DECL_START) for arg in self.request_args: out_file.write(self._STRUCT_DECL_FIELD % {'type': arg['type'], 'name': arg['name']}) out_file.write(self._STRUCT_DECL_END % {'command_name': self.MethodName(), 'direction': 'In'}) if self.response_args: out_file.write(self._STRUCT_DECL_START) for arg in self.response_args: out_file.write(self._STRUCT_DECL_FIELD % {'type': arg['type'], 'name': arg['name']}) out_file.write(self._STRUCT_DECL_END % {'command_name': self.MethodName(), 'direction': 'Out'}) if len(self.response_args) and len(self.request_args): out_file.write( self._FUNCTION_DECL_IN_OUT % {'command_name': self.MethodName()}) elif self.response_args: out_file.write( self._FUNCTION_DECL_OUT % {'command_name': self.MethodName()}) elif self.request_args: out_file.write( self._FUNCTION_DECL_IN % {'command_name': self.MethodName()}) out_file.write(self._EXEC_DECL % {'command_name': self.MethodName()}) def _GetNumberOfRequestHandles(self): """Returns the number of input handles for this command.""" return len(self._SplitArgs(self.request_args)[0]) def _GetNumberOfResponseHandles(self): """Returns the number of output handles for this command.""" return len(self._SplitArgs(self.response_args)[0]) def MethodName(self): """Creates an appropriate generated method name for the command. We use the command name without the TPM2_ prefix. Returns: The method name. """ if not self.name.startswith('TPM2_'): return self.name return self.name[5:] def GetRequestHandles(self): """Returns a list of input handles for this command.""" return self._SplitArgs(self.request_args)[0] def _SplitArgs(self, args): """Splits a list of args into handles and parameters.""" always_params = { 'TPM_CC_FlushContext': 'TPMI_DH_CONTEXT', 'TPM_CC_Hash': 'TPMI_RH_HIERARCHY', 'TPM_CC_LoadExternal': 'TPMI_RH_HIERARCHY', 'TPM_CC_SequenceComplete': 'TPMI_RH_HIERARCHY', } handles = [] parameters = [] always_handle = set(['TPM_HANDLE']) # Handle types that appear as command parameters. always_parameter = set(['TPMI_RH_ENABLES', 'TPMI_DH_PERSISTENT']) if self.command_code in always_params: always_parameter.add(always_params[self.command_code]) for arg in args: if (arg['type'] in always_handle or (self._HANDLE_RE.search(arg['type']) and arg['type'] not in always_parameter)): handles.append(arg) else: parameters.append(arg) return handles, parameters def _OutputCommandDispatcher(commands): """Generates implementation file for CommandDispatcher function. Args: commands: A list of Command objects. """ with open('CommandDispatcher.c', 'w') as out_file: out_file.write(COPYRIGHT_HEADER) for command in commands: out_file.write(_COMMAND_DISPATCHER_INCLUDES % {'command_name': command.MethodName()}) out_file.write(_COMMAND_DISPATCHER_START) for command in commands: command_code = 'TPM_CC_' + command.MethodName() out_file.write(_COMMAND_DISPATCHER_CASE % {'command_code': command_code, 'command_name': command.MethodName()}) out_file.write(_COMMAND_DISPATCHER_END) call(['clang-format', '-i', '-style=Chromium', 'CommandDispatcher.c']) def _OutputHandleProcess(commands, typemap): """Generates implementation file for ParseHandleBuffer function. Args: commands: A list of Command objects. typemap: A dict mapping type names to the corresponding object. Generated by structure_generator. """ with open('HandleProcess.c', 'w') as out_file: out_file.write(COPYRIGHT_HEADER) out_file.write(_HANDLE_PROCESS_START) for command in commands: command_code = 'TPM_CC_' + command.MethodName() out_file.write(_HANDLE_PROCESS_CASE_START % {'command_code': command_code}) for handle in command.GetRequestHandles(): if typemap[handle['type']].HasConditional(): out_file.write(_HANDLE_PROCESS_CASE_UNMARSHAL_FLAG % {'handle_type': handle['type'], 'flag_val': handle['has_conditional']}) else: out_file.write(_HANDLE_PROCESS_CASE_UNMARSHAL % {'handle_type': handle['type']}) out_file.write(_HANDLE_PROCESS_CASE_CHECK) out_file.write(_HANDLE_PROCESS_CASE_END) out_file.write(_HANDLE_PROCESS_END) call(['clang-format', '-i', '-style=Chromium', 'HandleProcess.c']) def _OutputGetCommandCodeString(commands): """Generates header and implementation files for GetCommandCodeString. Args: commands: A list of Command objects. """ with open('GetCommandCodeString_fp.h', 'w') as out_file: out_file.write(COPYRIGHT_HEADER) out_file.write(_GET_COMMAND_CODE_STRING_HEADER) call(['clang-format', '-i', '-style=Chromium', 'GetCommandCodeString_fp.h']) with open('GetCommandCodeString.c', 'w') as out_file: out_file.write(COPYRIGHT_HEADER) out_file.write(_GET_COMMAND_CODE_STRING_START) for command in commands: out_file.write(_GET_COMMAND_CODE_STRING_CASE % {'command_name': command.MethodName()}) out_file.write(_GET_COMMAND_CODE_STRING_END) call(['clang-format', '-i', '-style=Chromium', 'GetCommandCodeString.c']) def GenerateHeader(commands): """Generates a header file with declarations for all given generator objects. Args: commands: A list of Command objects. """ for command in commands: command_header_file = command.MethodName()+'_fp.h' with open(command_header_file, 'w') as out_file: out_file.write(COPYRIGHT_HEADER) out_file.write( _HEADER_FILE_GUARD_HEADER % {'name': command.MethodName().upper()}) out_file.write(_HEADER_FILE_INCLUDES) command.OutputDecl(out_file) out_file.write( _HEADER_FILE_GUARD_FOOTER % {'name': command.MethodName().upper()}) call(['clang-format', '-i', '-style=Chromium', command_header_file]) def GenerateImplementation(commands, typemap): """Generates implementation code for each command. Args: commands: A list of Command objects. typemap: A dict mapping type names to the corresponding object. Generated by structure_generator. """ for command in commands: marshal_command_file = 'Marshal_'+command.MethodName()+'.c' with open(marshal_command_file, 'w') as out_file: out_file.write(COPYRIGHT_HEADER) out_file.write(_IMPLEMENTATION_FILE_INCLUDES % {'command_name': command.MethodName()}) command.OutputMarshalFunction(out_file, typemap) command.OutputUnmarshalFunction(out_file, typemap) command.OutputExecFunction(out_file) call(['clang-format', '-i', '-style=Chromium', marshal_command_file]) _OutputHandleProcess(commands, typemap) _OutputCommandDispatcher(commands) _OutputGetCommandCodeString(commands)