blob: 6b5594980b616251b9113884b88bbcd6ab306594 [file] [log] [blame]
# 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(
&parameter_size, &parameter_size_location, &parameter_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(&parameter_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)