| """Module for processing TCG TPM2 library object descriptions. |
| |
| The descriptions are scraped from the tables of parts 2 and 3 of the |
| specification by a different module and fed through this module for |
| processing. |
| """ |
| |
| from __future__ import print_function |
| |
| import re |
| import sys |
| |
| from command_generator import Command |
| from structure_generator import AttributeStructure |
| from structure_generator import ConstantType |
| from structure_generator import Field |
| from structure_generator import Interface |
| from structure_generator import Structure |
| from structure_generator import Typedef |
| from structure_generator import Union |
| |
| |
| def _DebugLog(*args, **kwargs): |
| """When used - sends its inputs to stderr. |
| |
| This function can be used when debugging this module. Its footprint is |
| similar to print(), but the default destination is sys.stderr, which is |
| handy when the script generates stdio output redirected into a file. |
| |
| Args: |
| *args: a list of items of various types to print. Printed space separated, |
| each one converted to str before being printed. |
| **kwargs: a dictionary of variables to pass to print(), if any. In fact the |
| only key this function cares about is 'endl', which allows to |
| suppress adding a newline to the printed string. |
| """ |
| endl = kwargs.get('endl', '\n') |
| print(' '.join(str(x) for x in args), end=endl, file=sys.stderr) |
| |
| |
| class Table(object): |
| """Representation of TCG TPM2 library specification tables. |
| |
| The purpose of this class is to both generate new TPM2 objects and to keep |
| track of the previously generated objects for post processing (generating C |
| code). |
| |
| The HTML scraper finds tables in the specifications and builds up the |
| tables' contents in this object, one at a time. This object's table |
| representation includes table title, table header and one or more then table |
| rows. |
| |
| The table title must start with 'Table ### xxx', where ### is monotonously |
| increasing table number and xxx is some description allowing to deduce the |
| type of the object defined by this table. |
| |
| The cells of the table include names and types of the components, various |
| decorations are used to convey additional information: array boundaries, |
| values' limits, return values, selectors, etc, etc. |
| |
| Once the entire table is scraped, the scraper invokes a method to process it |
| (ProcessTable). The title of the table is examined by this module and the |
| appropriate processing method is invoked to actually convert the internal |
| table representation into a TPM2 object. |
| |
| Two maps are maintained persistently over the life time of this object, the |
| map of types (keyed by the type name scraped from part 2) and map of |
| commands (keyed by the command name, scraped from part 3). |
| |
| One other thing this module produces is the text for the .h file defining |
| all structures and types this module encountered. |
| |
| Attributes: |
| |
| _alg_id_table: actual table of various TPM2 algorithms, a copy of Table 9 |
| from part 2. It is used to convert encoded algorithm specs |
| used in other tables into a list of matching algorithms. |
| _h_file: a multiline string, the accumulated .h file defining all TPM |
| objects processed so far. |
| _type_map: a dictionary of various TPM types, keyed by the string - the |
| type name |
| _command_map: a dictionary of command_generator.Command objects, keyed by |
| the string, the command name |
| skip_tables: a tuple of integers, the numbers of tables which should not |
| be included in the .h file, as the same information was |
| derived from part 4 earlier. |
| _title: a string, title of the currently being processed specification |
| table |
| _title_type: a string, base type of the object defined by the currently |
| being processed specification table |
| _alg_type: a string, in some tables included in the title in curly |
| brackets, to indicate what type of the algorithm this table |
| deals with (usually RSA or ECC) |
| _body: a list of strings, rows of the currently being processed |
| specification table |
| _has_selector_column: a Boolean, set to True if the third column of the |
| table is the selector to be used to process this row (as in |
| picking the object type when the table represents a union) |
| _skip_printing: a Boolean, set to True if the table contents should not be |
| included on tpm_types.h - some tables are also defined in |
| files extracted from Part 4 of the specification. |
| |
| """ |
| |
| # Match table titles with processing routines. |
| TABLE_ROUTER = ( |
| (re.compile('(Constants|Defines for Logic Values)'), '_ProcessConstants'), |
| (re.compile('(of Types for|Base Types)'), '_ProcessTypes'), |
| (re.compile('Definition of .* Type'), '_ProcessInterfaceOrType'), |
| (re.compile('Unmarshaling Errors'), '_ProcessEnum'), |
| (re.compile(r'Definition of [\S]+ (Structure|Union)'), |
| '_ProcessStructureOrUnion'), |
| (re.compile('Definition of .* Bits'), '_ProcessBits'), |
| (re.compile(r' TPM2_\S+ (Command|Response)'), '_ProcessCommand'), |
| ) |
| |
| |
| # The TCG spec in some cases uses so called 'Algorithm macros' to describe |
| # all algorithms a type should apply to. The macros are described in details |
| # in section 4.12 of part 2 of the spec. |
| # |
| # Basically, the macro consists of the prefix '!ALG' followed by dot |
| # separated descriptions of algorithm types this marco applies to. |
| # |
| # The algorithm types are expressed as sequences or lower or upper case |
| # letters, and should match the third column of Table 9 either inclusively |
| # (in case the type letters are in upper case, or exclusively, in case the |
| # type letters are in lower case. |
| _alg_macro = re.compile(r'!ALG\.([a-z\.]+)', re.IGNORECASE) |
| |
| def __init__(self): |
| """Create a Table class instance.""" |
| self._alg_id_table = [] |
| # Allow re-initializing attributes outside __init__() (in Init()) |
| self.Init() |
| self._h_file = '' |
| self._type_map = {} |
| self._command_map = {} |
| self.skip_tables = () |
| |
| def Init(self, title=''): |
| """Initialize a new table. |
| |
| This function is invoked each time a new table is encountered in the spec. |
| |
| A typical table header could look like this: |
| |
| 'Table 10 - Definition of (UINT16) {ECC} TPM_ECC_CURVE Constants' |
| |
| The algorithm type in curly brackets, if present, is redundant, it is |
| stripped off before the table header comment is generated for the .h file. |
| |
| Some titles include the parenthesized base type the defined object should |
| be typedef'ed from. |
| |
| Args: |
| title: a string, the title of the table as included in the TCG spec. |
| """ |
| title_bracket_filter = re.compile(r'({.*?}) ?') |
| title_type_filter = re.compile(r'(\(.*?\)) ?') |
| # Retrieve base type, if present in the table title. |
| m = title_type_filter.search(title) |
| if m: |
| # the header shown in the docstring above would result in the match of |
| # '(UINT16)', remove the parenthesis and save the base type. |
| self._title_type = m.groups()[0][1:-1] |
| self._title = title_type_filter.sub('', title).strip() |
| else: |
| self._title_type = '' |
| self._title = title.strip() |
| # Now retrieve algorithm type, if present in the table title. |
| m = title_bracket_filter.search(self._title) |
| self._alg_type = '' |
| if m: |
| self._title = title_bracket_filter.sub('', self._title).strip() |
| alg_type = m.groups()[0][1:-1].strip() |
| if not alg_type.startswith('!'): |
| self._alg_type = alg_type |
| self._body = [] |
| self._has_selector_column = False |
| self._skip_printing = False |
| |
| def _SplitByAlg(self, word): |
| """Split the passed in word by the regex used to pick TPM algorithms. |
| |
| The TPM algorithm regex is used all over the spec in situations where |
| similar code needs to be generated for different algorithms of a certain |
| type. |
| |
| A string in the spec might look like one of the following: |
| TPMI_!ALG.S_KEY_BITS or !ALG.S_KEY_SIZES_BITS. |
| |
| The split would result in a three element list: the part before !ALG |
| (could be empty), the letters between '!ALG.' and _ or end of the string, |
| and the part after the letters included in the algorithm regex. |
| |
| TPMI_!ALG.S_KEY_BITS => ['TPMI_', 'S', '_KEY_BITS'] |
| !ALG.S_KEY_SIZES_BITS => ['', 'S', '_KEY_SIZES_BITS'] |
| |
| The first and last elements of the split are used as the prefix and suffix |
| of the type names included in the generated file. |
| |
| In some cases there is no regex suffix present, only the !ALG string, as |
| in the selector column in table 127 (TPM_ALG_!ALG) In this case the split |
| by the !ALG string is attempted, and the generated list has just two |
| elements. |
| |
| In yet some other cases, say in Table 126 where the type field does not |
| change at all set to TPMI_ALG_SYM_MODE for all fields. In such cases the |
| split returns a single element list, the second element set to None is |
| added to the list. |
| |
| Args: |
| word: a string, the encoded algorithm string to be split. |
| |
| Returns: |
| a tuple of two strings, first and last elements of the split, either one |
| could be empty. |
| |
| """ |
| parts = self._alg_macro.split(word) |
| if len(parts) == 1: |
| parts = word.split('!ALG') |
| if len(parts) == 1: |
| return word, None |
| return parts[0].strip('_'), parts[-1].strip('_') |
| |
| def SetSkipTables(self, skip_tables): |
| """Set the tuple of table numbers to be ignored by the parser.""" |
| self.skip_tables = skip_tables |
| |
| def _AddToHfile(self, text=''): |
| self._h_file += text + '\n' |
| |
| def _SetBaseType(self, old_type, tpm_obj): |
| """Set the base type for a new object. |
| |
| Many TPM objects are typedefed hierarchically, for instance |
| |
| uint16_t => UINT16 => TPM_ALG_ID_Marshal => TPMI_ALG_HASH_Marshal |
| |
| This causes excessive nesting when marshaling and unmarshaling, which is |
| bad from both performance and stack size requirements point of view. |
| |
| This function will discover the 'basest' type and set it in the tpm |
| object, this would help to generate direct marshaling/unmarshaling |
| functions. |
| |
| Args: |
| old_type: a string, name of the immediate type this tpm object typedefed |
| from. |
| tpm_obj: a tpm object, derived from TPMType |
| """ |
| base_type = old_type |
| while base_type in self._type_map: |
| try: |
| base_type = self._type_map[base_type].old_type |
| except AttributeError: |
| break # The underlying type is not a typedef |
| tpm_obj.SetBaseType(base_type) |
| |
| def _AddTypedef(self, old_type, new_type): |
| if not self._skip_printing: |
| self._AddToHfile('typedef %s %s;' % (old_type, new_type)) |
| # No need to generate marshaling/unmarshaling code for BOOL type. |
| if new_type != 'BOOL': |
| self._type_map[new_type] = Typedef(old_type, new_type) |
| self._SetBaseType(old_type, self._type_map[new_type]) |
| |
| def InProgress(self): |
| """Return True when the parser is in the middle of a table.""" |
| return self._title |
| |
| def _GetMaxLengths(self, table): |
| """Find out maximum lengths of the table columns. |
| |
| This function helps to generate nicely aligned definitions in the output |
| .h file, by making sure that each field's name starts in the same column, |
| far enough for all fields' types to fit. |
| |
| Args: |
| table: a list of string lists. Each component consists of at least two |
| elements, the first one the field or constant type, the |
| second one the field name or constant value. |
| |
| Returns: |
| a tuple of two integers, the first one - the length of the longest |
| string in the first colume, the second one - the length of the |
| longest string in the second column. |
| """ |
| lengths = [0, 0] |
| for row in table: |
| for i in range(len(lengths)): |
| if len(row[i]) > lengths[i]: |
| lengths[i] = len(row[i]) |
| return tuple(lengths) |
| |
| def NewRow(self): |
| """Start a new row in the internal table representation.""" |
| self._body.append([]) |
| |
| def NewCell(self): |
| """Start a new cell in the last row.""" |
| self._body[-1].append('') |
| |
| def AddData(self, data): |
| """Add data to the last cell of the last row.""" |
| if not self._body: |
| return # Ignore end of line and whitespace formatting. |
| self._body[-1][-1] += data |
| |
| def ProcessTable(self): |
| """Process accumulated table contents. |
| |
| This function is invoked when the parser state machine detects that the |
| entire HTML table has been processed. The received contents is handled |
| based on the table title by finding the matching entry in TABLE_ROUTER |
| tuple. |
| |
| The result of processing is adding a new TPM type to the _type_map |
| dictionary, or a new command descriptor to the _command_map dictionary. |
| """ |
| |
| # The table has a selector column if it has at least three columns, and |
| # the third column is titled 'Selector'. |
| self._has_selector_column = (len(self._body[0]) >= 3 and |
| self._body[0][2].strip() == 'Selector') |
| # Preprocess representation of the table scraped from the spec. Namely, |
| # remove the header row, and strip all other cells before adding them to |
| # self._body[], which becomes a list including all scraped table cells, |
| # stripped. |
| self._body = [[cell.strip() for cell in row] for row in self._body[1:]] |
| if 'TPM_ALG_ID Constants' in self._title: |
| self._alg_id_table = [[x[0], x[2].replace(' ', ''), x[3]] |
| for x in self._body] |
| |
| # The name of the type defined in the table, when present, is always the |
| # fifth element in the stripped header, for instance: |
| # 'Table 10 - Definition of TPM_ECC_CURVE Constants' |
| try: |
| type_name = self._title.split()[4] |
| except IndexError: |
| type_name = '' |
| |
| # Based on the table title, find the function to process the table and |
| # generate a TPM specification object of a certain type. |
| table_func = '' |
| for regex, func in self.TABLE_ROUTER: |
| if regex.search(self._title): |
| table_func = func |
| break |
| else: |
| self._AddToHfile('// Unprocessed: %s' % self._title) |
| return |
| |
| if int(self._title.split()[1]) in self.skip_tables: |
| self._skip_printing = True |
| self._AddToHfile('// Skipped: %s' % self._title) |
| else: |
| self._AddToHfile('// %s' % self._title) |
| |
| # Invoke a TPM type specific processing function. |
| getattr(self, table_func)(type_name) |
| |
| def _ProcessCommand(self, _): |
| """Process command description table from part 3. |
| |
| Each TPM command has two tables associated with it, one describing the |
| request structure, and another one describing the response structure. The |
| first time a TPM command is encountered, a Command object is created and |
| its 'request_args' property is set, the second time it is encountered - |
| the existing object's 'response_args' property is set. |
| """ |
| command_name = self._title.split()[2] |
| if command_name not in self._command_map: |
| command = Command(command_name) |
| self._command_map[command_name] = command |
| else: |
| command = self._command_map[command_name] |
| params = [] |
| # The first three fields in each request and response are always the same |
| # and are not included in the generated structures. Let's iterate over the |
| # rest of the fields. |
| for row in self._body[3:]: |
| # A dictionary describing a request or response structure field. |
| field = {} |
| # Ignore the '@' decoration for now. |
| field_type, field['name'] = row[0], row[1].lstrip('@') |
| # The '+' decoration means this field can be conditional. |
| if field_type.endswith('+'): |
| field_type = field_type[:-1] |
| field['has_conditional'] = 'TRUE' |
| else: |
| field['has_conditional'] = 'FALSE' |
| field['type'] = field_type |
| if len(row) > 2: |
| field['description'] = row[2] |
| else: |
| field['description'] = '' |
| # Add the new field to the list of request or response fields. |
| params.append(field) |
| if ' Command' in self._title: |
| command.request_args = params |
| else: |
| command.response_args = params |
| |
| def _PickAlgEntries(self, alg_type_str): |
| """Process algorithm id table and find all matching entries. |
| |
| See comments to _alg_macro above. |
| |
| Args: |
| alg_type_str: a string, one or more dot separated encoded algorithm types. |
| |
| Returns: |
| A table of alg_type (Table 9 of part 2) entries matching the passed in |
| encoded type string. |
| """ |
| filtered_table = [] |
| for alg_type in alg_type_str.split('.'): |
| if re.match('^[A-Z]+$', alg_type): |
| # Exclusive selection, must exactly match algorithm type from table 9 |
| # (which is in the second column). Add to the return value all |
| # matching rows of table 9. |
| extension = [] |
| for row in self._alg_id_table: |
| if row[1] == alg_type: |
| if self._alg_type and self._alg_type != row[2]: |
| continue |
| extension.append(row) |
| filtered_table.extend(extension) |
| elif re.match('^[a-z]+$', alg_type): |
| # Inclusive selection. All type letters must be present in the type |
| # column, but no exact match is required. |
| for row in self._alg_id_table: |
| for char in alg_type.upper(): |
| if char not in row[1]: |
| break |
| else: |
| if not self._alg_type or self._alg_type == row[2]: |
| filtered_table.append(row) |
| return filtered_table |
| |
| def _ParseAlgorithmRegex(self, token): |
| """Process a token as an algorithm regex. |
| |
| This function tries to interpret the passed in token as an encoded |
| algorithm specification. |
| |
| In case the encoded algorithm regex matches, the function splits the token |
| into prefix, algorithm description and suffix, and then retrieves the list |
| of all algorithms matching the algorithm description. |
| |
| Args: |
| token: a string, potentially including the algorithm regex. |
| |
| Returns: |
| in case the regex matched returns a tri-tuple of two strings (prefix and |
| suffix, either one could be empty) and a list of matching algorithms |
| from the algorithm descriptors table. If there has been no match - |
| returns None. |
| """ |
| elements = self._alg_macro.split(token) |
| if len(elements) == 3: |
| # The token matched the algorithm regex, Find out prefix and suffix to |
| # be used on the generated type names, and the algorithm regex suffix to |
| # use to find matching entries in the algorithm table. |
| name_prefix, alg_suffix, name_suffix = tuple(elements) |
| name_prefix = name_prefix.strip('_') |
| name_suffix = name_suffix.strip('_') |
| return name_prefix, name_suffix, self._PickAlgEntries(alg_suffix) |
| |
| def _ProcessInterface(self, type_name): |
| """Processes spec tables describing interfaces.""" |
| result = self._ParseAlgorithmRegex(type_name) |
| if result: |
| name_prefix, name_suffix, alg_list = tuple(result) |
| # Process all matching algorithm types |
| for alg_desc in alg_list: |
| alg_base = alg_desc[0].replace('TPM_ALG_', '') |
| new_name = '_'.join([name_prefix, |
| alg_base, name_suffix]).strip('_') |
| new_if = Interface(self._title_type, new_name) |
| self._AddTypedef(self._title_type, new_name) |
| for row in self._body: |
| new_value = row[0] |
| if new_value.startswith('$!ALG'): |
| new_if.supported_values = alg_base + '_' + '_'.join( |
| new_value.split('_')[1:]) |
| elif new_value.startswith('$'): |
| new_if.supported_values = new_value[1:] |
| elif new_value.startswith('#'): |
| new_if.error_code = new_value[1:] |
| self._type_map[new_name] = new_if |
| self._AddToHfile('\n') |
| return |
| new_if = Interface(self._title_type, type_name) |
| self._AddTypedef(self._title_type, type_name) |
| self._type_map[type_name] = new_if |
| self._SetBaseType(type_name, new_if) |
| for row in self._body: |
| new_value = row[0] |
| result = self._ParseAlgorithmRegex(new_value) |
| if result: |
| # The field is described using the algorithm regex. The above comment |
| # applies. |
| name_prefix, name_suffix, alg_list = tuple(result) |
| for alg_desc in alg_list: |
| alg_base = alg_desc[0].replace('TPM_ALG_', '') |
| new_if.valid_values.append('_'.join( |
| [name_prefix, alg_base, name_suffix]).strip('_')) |
| else: |
| if new_value.startswith('{'): |
| bounds = tuple( |
| [x.strip() for x in new_value[1:-1].strip().split(':')]) |
| new_if.bounds.append(bounds) |
| elif new_value.startswith('+'): |
| new_if.conditional_value = new_value[1:] |
| elif new_value.startswith('#'): |
| new_if.error_code = new_value[1:] |
| elif new_value.startswith('$'): |
| new_if.supported_values = new_value[1:] |
| else: |
| new_if.valid_values.append(new_value) |
| return |
| |
| def _ProcessTypedefs(self, type_name): |
| """Processes spec tables defining new types.""" |
| result = self._ParseAlgorithmRegex(type_name) |
| if result: |
| name_prefix, name_suffix, alg_list = tuple(result) |
| for alg_desc in alg_list: |
| alg_base = alg_desc[0].replace('TPM_ALG_', '') |
| new_type = '%s_%s_%s' % (name_prefix, alg_base, name_suffix) |
| self._AddTypedef(self._title_type, new_type) |
| self._AddToHfile('\n') |
| else: |
| self._AddTypedef(self._title_type, type_name) |
| |
| def _ProcessBits(self, type_name): |
| """Processes spec tables describing attributes (bit fields).""" |
| bits_lines = [] |
| base_bit = 0 |
| tpm_obj = AttributeStructure(self._title_type, type_name) |
| self._type_map[type_name] = tpm_obj |
| self._SetBaseType(self._title_type, tpm_obj) |
| for bits_line in self._body: |
| field, name = tuple(bits_line[:2]) |
| if not field: |
| continue |
| if name.startswith('TPM_'): |
| # Spec inconsistency fix. |
| name_pieces = [x.lower() for x in name.split('_')[1:]] |
| name = name_pieces[0] |
| for piece in name_pieces[1:]: |
| name += piece[0].upper() + piece[1:] |
| bit_range = [x.replace(' ', '') for x in field.split(':')] |
| field_base = int(bit_range[-1]) |
| if field_base != base_bit: |
| field_name = 'reserved%d' % base_bit |
| field_width = field_base - base_bit |
| if field_width > 1: |
| field_name += '_%d' % (field_base - 1) |
| bits_lines.append(['%s : %d' % (field_name, field_width)]) |
| tpm_obj.reserved.append(field_name.replace('reserved', '')) |
| if len(bit_range) > 1: |
| field_width = int(bit_range[0]) - field_base + 1 |
| else: |
| field_width = 1 |
| if re.match('reserved', name, re.IGNORECASE): |
| name = 'reserved%d' % field_base |
| if field_width > 1: |
| name += '_%d' % (field_base + field_width - 1) |
| tpm_obj.reserved.append(name.replace('reserved', '')) |
| bits_lines.append([name, ': %d' % field_width]) |
| base_bit = field_base + field_width |
| max_type_len, _ = self._GetMaxLengths(bits_lines) |
| self._AddToHfile('typedef struct {') |
| for bits_line in bits_lines: |
| self._AddToHfile(' %s %-*s %s;' % (self._title_type, max_type_len, |
| bits_line[0], bits_line[1])) |
| self._AddToHfile('} %s;\n' % type_name) |
| |
| def _ExpandAlgs(self, row): |
| """Find all algorithms encoded in the variable name. |
| |
| Args: |
| row: a list of strings, a row of a structure or union table scraped from |
| part 2. |
| |
| Returns: |
| A list for structure_generator.Field objects, one per expanded |
| algorithm. |
| |
| """ |
| alg_spec = row[0].split() |
| expansion = [] |
| m = self._alg_macro.search(alg_spec[0]) |
| if m: |
| alg_type = m.groups()[0] |
| # Find all algorithms of this type in the alg id table |
| alg_entries = self._PickAlgEntries(alg_type) |
| if len(alg_spec) == 2 and alg_spec[1][0] == '[': |
| # This is the case of a union of arrays. |
| raw_size_parts = self._alg_macro.split(alg_spec[1][1:-1]) |
| size_prefix = raw_size_parts[0].strip('_') |
| size_suffix = raw_size_parts[2].strip('_') |
| for alg_desc in alg_entries: |
| alg_base = alg_desc[0].replace('TPM_ALG_', '') |
| size = '_'.join([size_prefix, alg_base, size_suffix]).strip('_') |
| if self._has_selector_column: |
| selector_parts = self._alg_macro.split(row[2]) |
| selector_prefix = selector_parts[0].strip('_') |
| selector_suffix = selector_parts[2].strip('_') |
| selector = '_'.join([selector_prefix, |
| alg_base, selector_suffix]).strip('_') |
| else: |
| selector = '' |
| expansion.append(Field(row[1], alg_base.lower(), |
| selector=selector, array_size=size)) |
| else: |
| type_prefix, type_suffix = self._SplitByAlg(row[1]) |
| if self._has_selector_column: |
| selector_prefix, selector_suffix = self._SplitByAlg(row[2]) |
| else: |
| selector = '' |
| for alg_desc in alg_entries: |
| alg_base = alg_desc[0].replace('TPM_ALG_', '') |
| if type_suffix is not None: |
| var_type = '_'.join([type_prefix, alg_base, type_suffix]).strip('_') |
| else: |
| var_type = type_prefix |
| if self._has_selector_column: |
| selector = '_'.join([selector_prefix, alg_base, |
| selector_suffix]).strip('_') |
| expansion.append(Field(var_type, alg_base.lower(), |
| selector=selector)) |
| return expansion |
| |
| def _ProcessInterfaceOrType(self, type_name): |
| if type_name.startswith('TPMI_'): |
| self._ProcessInterface(type_name) |
| else: |
| self._ProcessTypedefs(type_name) |
| |
| def _StructOrUnionToHfile(self, body_fields, type_name, union_mode, tpm_obj): |
| body_lines = [] |
| for field in body_fields: |
| tpm_obj.AddField(field) |
| body_lines.append([field.field_type, field.field_name]) |
| if field.array_size: |
| body_lines[-1][-1] += '[%s]' % field.array_size |
| if field.selector_value: |
| body_lines[-1].append(field.selector_value) |
| max_type_len, _ = self._GetMaxLengths(body_lines) |
| tpm2b_mode = type_name.startswith('TPM2B_') |
| space_prefix = '' |
| if union_mode: |
| self._AddToHfile('typedef union {') |
| else: |
| if tpm2b_mode: |
| self._AddToHfile('typedef union {') |
| space_prefix = ' ' |
| self._AddToHfile(' struct {') |
| else: |
| self._AddToHfile('typedef struct {') |
| for line in body_lines: |
| guard_required = len(line) > 2 and line[2].startswith('TPM_ALG_') |
| if not line[1]: |
| continue |
| if guard_required: |
| self._AddToHfile('#ifdef %s' % line[2]) |
| self._AddToHfile(space_prefix + ' %-*s %s;' % ( |
| max_type_len, line[0], line[1])) |
| if guard_required: |
| self._AddToHfile('#endif') |
| if tpm2b_mode: |
| self._AddToHfile(' } t;') |
| self._AddToHfile(' TPM2B b;') |
| self._AddToHfile('} %s;\n' % type_name) |
| self._type_map[type_name] = tpm_obj |
| |
| |
| def _ProcessStructureOrUnion(self, type_name): |
| """Processes spec tables describing structure and unions. |
| |
| Both of these object types share a lot of similarities. Union types have |
| the word 'Union' in the table title. |
| |
| Args: |
| type_name: a string, name of the TPM object type |
| """ |
| union_mode = 'Union' in self._title |
| if union_mode: |
| tpm_obj = Union(type_name) |
| else: |
| tpm_obj = Structure(type_name) |
| body_fields = [] |
| for row in self._body: |
| if row[0].startswith('#'): |
| tpm_obj.error_code = row[0][1:] |
| continue |
| if (len(row) < 2 or |
| row[1].startswith('#') or |
| row[0].startswith('//')): |
| continue |
| value = row[0] |
| if value.endswith('='): |
| value = value[:-1] |
| tpm_obj.size_check = True |
| if self._alg_macro.search(value): |
| body_fields.extend(self._ExpandAlgs(row)) |
| continue |
| array_size = None |
| run_time_size = None |
| vm = re.search(r'^(\S+)\s*\[(\S+)\]\s*\{(.*:*)\}', value) |
| selector = '' |
| if vm: |
| value, run_time_size, bounds = vm.groups() |
| lower, upper = [x.strip() for x in bounds.split(':')] |
| if upper: |
| array_size = upper |
| tpm_obj.AddUpperBound(run_time_size, upper) |
| else: |
| array_size = run_time_size |
| if lower: |
| tpm_obj.AddLowerBound(run_time_size, lower) |
| else: |
| vm = re.search(r'^\[(\S+)\]\s*(\S+)', value) |
| if vm: |
| selector, value = vm.groups() |
| else: |
| vm = re.search(r'^(\S+)\s*\{(.+)\}', value) |
| if vm: |
| value, bounds = vm.groups() |
| if ':' in bounds: |
| lower, upper = [x.strip() for x in bounds.split(':')] |
| if upper: |
| tpm_obj.AddUpperBound(value, upper) |
| if lower: |
| tpm_obj.AddLowerBound(value, lower) |
| if self._has_selector_column: |
| selector = row[2] |
| field_type = row[1] |
| if field_type.startswith('+') or field_type.endswith('+'): |
| field_type = field_type.strip('+') |
| conditional = 'TRUE' |
| else: |
| conditional = 'FALSE' |
| if field_type or value: |
| body_fields.append(Field(field_type, |
| value, |
| array_size=array_size, |
| run_time_size=run_time_size, |
| selector=selector, |
| conditional_value=conditional)) |
| |
| self._StructOrUnionToHfile(body_fields, type_name, union_mode, tpm_obj) |
| |
| def _ProcessEnum(self, _): |
| """Processes spec tables describing enums.""" |
| if self._skip_printing: |
| return |
| self._AddToHfile('enum {') |
| for value, row in enumerate(self._body): |
| self._AddToHfile(' %s = %d,' % (row[0], value + 1)) |
| self._AddToHfile('};\n') |
| |
| def _ProcessTypes(self, _): |
| """Processes spec tables describing new types.""" |
| for type_, name_, _ in self._body: |
| m = self._alg_macro.search(name_) |
| if not m: |
| self._AddTypedef(type_, name_) |
| continue |
| qualifier = [x for x in ('ECC', 'RSA') if x == type_.split('_')[-1]] |
| alg_suffix = m.groups()[0] |
| type_base = self._alg_macro.split(name_)[0] |
| for alg_desc in self._PickAlgEntries(alg_suffix): |
| if qualifier and alg_desc[2] != qualifier[0]: |
| continue |
| self._AddTypedef(type_, alg_desc[0].replace('TPM_ALG_', type_base)) |
| self._AddToHfile() |
| |
| def _ProcessConstants(self, type_name): |
| """Processes spec tables describing constants.""" |
| if self._title_type: |
| self._AddTypedef(self._title_type, type_name) |
| constant_defs = [] |
| tpm_obj = ConstantType(self._title_type, type_name) |
| for row in self._body: |
| name = row[0].strip() |
| if not name: |
| continue |
| if name.startswith('#'): |
| tpm_obj.error_code = name[1:] |
| continue |
| if name == 'reserved' or len(row) < 2: |
| continue |
| value = row[1].strip() |
| rm = re.match(r'^(\(.*?\))', value) |
| if rm: |
| value = '%s' % rm.groups()[0] |
| else: |
| v_list = value.split() |
| if len(v_list) > 2 and v_list[1] == '+': |
| value = '((%s)(%s))' % (type_name, ' '.join(v_list[:3])) |
| if ' ' in value and not value.startswith('('): |
| value = '(%s)' % value |
| constant_defs.append([name, value]) |
| tpm_obj.valid_values.append(name) |
| if self._title_type: |
| self._type_map[type_name] = tpm_obj |
| self._SetBaseType(self._title_type, tpm_obj) |
| if self._skip_printing: |
| return |
| max_name_len, max_value_len = self._GetMaxLengths(constant_defs) |
| for row in constant_defs: |
| self._AddToHfile('#define %-*s %*s' % (max_name_len, row[0], |
| max_value_len, row[1])) |
| self._AddToHfile() |
| |
| def GetHFile(self): |
| return self._h_file |
| |
| def GetCommandList(self): |
| return sorted(self._command_map.values(), |
| cmp=lambda x, y: cmp(x.name, y.name)) |
| |
| def GetTypeMap(self): |
| return self._type_map |