Home | History | Annotate | Download | only in generator
      1 """Module for processing TCG TPM2 library object descriptions.
      2 
      3 The descriptions are scraped from the tables of parts 2 and 3 of the
      4 specification by a different module and fed through this module for
      5 processing.
      6 """
      7 
      8 from __future__ import print_function
      9 
     10 import re
     11 import sys
     12 
     13 from command_generator import Command
     14 from structure_generator import AttributeStructure
     15 from structure_generator import ConstantType
     16 from structure_generator import Field
     17 from structure_generator import Interface
     18 from structure_generator import Structure
     19 from structure_generator import Typedef
     20 from structure_generator import Union
     21 
     22 
     23 def _DebugLog(*args, **kwargs):
     24   """When used - sends its inputs to stderr.
     25 
     26   This function can be used when debugging this module. Its footprint is
     27   similar to print(), but the default destination is sys.stderr, which is
     28   handy when the script generates stdio output redirected into a file.
     29 
     30   Args:
     31     *args: a list of items of various types to print. Printed space separated,
     32            each one converted to str before being printed.
     33     **kwargs: a dictionary of variables to pass to print(), if any. In fact the
     34               only key this function cares about is 'endl', which allows to
     35               suppress adding a newline to the printed string.
     36   """
     37   endl = kwargs.get('endl', '\n')
     38   print(' '.join(str(x) for x in args), end=endl, file=sys.stderr)
     39 
     40 
     41 class Table(object):
     42   """Representation of TCG TPM2 library specification tables.
     43 
     44   The purpose of this class is to both generate new TPM2 objects and to keep
     45   track of the previously generated objects for post processing (generating C
     46   code).
     47 
     48   The HTML scraper finds tables in the specifications and builds up the
     49   tables' contents in this object, one at a time. This object's table
     50   representation includes table title, table header and one or more then table
     51   rows.
     52 
     53   The table title must start with 'Table ### xxx', where ### is monotonously
     54   increasing table number and xxx is some description allowing to deduce the
     55   type of the object defined by this table.
     56 
     57   The cells of the table include names and types of the components, various
     58   decorations are used to convey additional information: array boundaries,
     59   values' limits, return values, selectors, etc, etc.
     60 
     61   Once the entire table is scraped, the scraper invokes a method to process it
     62   (ProcessTable). The title of the table is examined by this module and the
     63   appropriate processing method is invoked to actually convert the internal
     64   table representation into a TPM2 object.
     65 
     66   Two maps are maintained persistently over the life time of this object, the
     67   map of types (keyed by the type name scraped from part 2) and map of
     68   commands (keyed by the command name, scraped from part 3).
     69 
     70   One other thing this module produces is the text for the .h file defining
     71   all structures and types this module encountered.
     72 
     73   Attributes:
     74 
     75     _alg_id_table: actual table of various TPM2 algorithms, a copy of Table 9
     76                    from part 2. It is used to convert encoded algorithm specs
     77                    used in other tables into a list of matching algorithms.
     78     _h_file: a multiline string, the accumulated .h file defining all TPM
     79                    objects processed so far.
     80     _type_map: a dictionary of various TPM types, keyed by the string - the
     81                    type name
     82     _command_map: a dictionary of command_generator.Command objects, keyed by
     83                    the string, the command name
     84     skip_tables: a tuple of integers, the numbers of tables which should not
     85                    be included in the .h file, as the same information was
     86                    derived from part 4 earlier.
     87     _title: a string, title of the currently being processed specification
     88                    table
     89     _title_type: a string, base type of the object defined by the currently
     90                    being processed specification table
     91     _alg_type: a string, in some tables included in the title in curly
     92                    brackets, to indicate what type of the algorithm this table
     93                    deals with (usually RSA or ECC)
     94     _body: a list of strings, rows of the currently being processed
     95                    specification table
     96     _has_selector_column: a Boolean, set to True if the third column of the
     97                    table is the selector to be used to process this row (as in
     98                    picking the object type when the table represents a union)
     99     _skip_printing: a Boolean, set to True if the table contents should not be
    100                    included on tpm_types.h - some tables are also defined in
    101                    files extracted from Part 4 of the specification.
    102 
    103   """
    104 
    105   # Match table titles with processing routines.
    106   TABLE_ROUTER = (
    107       (re.compile('(Constants|Defines for Logic Values)'), '_ProcessConstants'),
    108       (re.compile('(of Types for|Base Types)'), '_ProcessTypes'),
    109       (re.compile('Definition of .* Type'), '_ProcessInterfaceOrType'),
    110       (re.compile('Unmarshaling Errors'), '_ProcessEnum'),
    111       (re.compile(r'Definition of [\S]+ (Structure|Union)'),
    112        '_ProcessStructureOrUnion'),
    113       (re.compile('Definition of .* Bits'), '_ProcessBits'),
    114       (re.compile(r' TPM2_\S+ (Command|Response)'), '_ProcessCommand'),
    115   )
    116 
    117 
    118   # The TCG spec in some cases uses so called 'Algorithm macros' to describe
    119   # all algorithms a type should apply to. The macros are described in details
    120   # in section 4.12 of part 2 of the spec.
    121   #
    122   # Basically, the macro consists of the prefix '!ALG' followed by dot
    123   # separated descriptions of algorithm types this marco applies to.
    124   #
    125   # The algorithm types are expressed as sequences or lower or upper case
    126   # letters, and should match the third column of Table 9 either inclusively
    127   # (in case the type letters are in upper case, or exclusively, in case the
    128   # type letters are in lower case.
    129   _alg_macro = re.compile(r'!ALG\.([a-z\.]+)', re.IGNORECASE)
    130 
    131   def __init__(self):
    132     """Create a Table class instance."""
    133     self._alg_id_table = []
    134     # Allow re-initializing attributes outside __init__() (in Init())
    135     self.Init()
    136     self._h_file = ''
    137     self._type_map = {}
    138     self._command_map = {}
    139     self.skip_tables = ()
    140 
    141   def Init(self, title=''):
    142     """Initialize a new table.
    143 
    144     This function is invoked each time a new table is encountered in the spec.
    145 
    146     A typical table header could look like this:
    147 
    148     'Table 10 - Definition of (UINT16) {ECC} TPM_ECC_CURVE Constants'
    149 
    150     The algorithm type in curly brackets, if present, is redundant, it is
    151     stripped off before the table header comment is generated for the .h file.
    152 
    153     Some titles include the parenthesized base type the defined object should
    154     be typedef'ed from.
    155 
    156     Args:
    157       title: a string, the title of the table as included in the TCG spec.
    158     """
    159     title_bracket_filter = re.compile(r'({.*?}) ?')
    160     title_type_filter = re.compile(r'(\(.*?\)) ?')
    161     # Retrieve base type, if present in the table title.
    162     m = title_type_filter.search(title)
    163     if m:
    164       # the header shown in the docstring above would result in the match of
    165       # '(UINT16)', remove the parenthesis and save the base type.
    166       self._title_type = m.groups()[0][1:-1]
    167       self._title = title_type_filter.sub('', title).strip()
    168     else:
    169       self._title_type = ''
    170       self._title = title.strip()
    171     # Now retrieve algorithm type, if present in the table title.
    172     m = title_bracket_filter.search(self._title)
    173     self._alg_type = ''
    174     if m:
    175       self._title = title_bracket_filter.sub('', self._title).strip()
    176       alg_type = m.groups()[0][1:-1].strip()
    177       if not alg_type.startswith('!'):
    178         self._alg_type = alg_type
    179     self._body = []
    180     self._has_selector_column = False
    181     self._skip_printing = False
    182 
    183   def _SplitByAlg(self, word):
    184     """Split the passed in word by the regex used to pick TPM algorithms.
    185 
    186     The TPM algorithm regex is used all over the spec in situations where
    187     similar code needs to be generated for different algorithms of a certain
    188     type.
    189 
    190     A string in the spec might look like one of the following:
    191     TPMI_!ALG.S_KEY_BITS or !ALG.S_KEY_SIZES_BITS.
    192 
    193     The split would result in a three element list: the part before !ALG
    194     (could be empty), the letters between '!ALG.' and _ or end of the string,
    195     and the part after the letters included in the algorithm regex.
    196 
    197     TPMI_!ALG.S_KEY_BITS => ['TPMI_', 'S', '_KEY_BITS']
    198     !ALG.S_KEY_SIZES_BITS => ['', 'S', '_KEY_SIZES_BITS']
    199 
    200     The first and last elements of the split are used as the prefix and suffix
    201     of the type names included in the generated file.
    202 
    203     In some cases there is no regex suffix present, only the !ALG string, as
    204     in the selector column in table 127 (TPM_ALG_!ALG) In this case the split
    205     by the !ALG string is attempted, and the generated list has just two
    206     elements.
    207 
    208     In yet some other cases, say in Table 126 where the type field does not
    209     change at all set to TPMI_ALG_SYM_MODE for all fields. In such cases the
    210     split returns a single element list, the second element set to None is
    211     added to the list.
    212 
    213     Args:
    214       word: a string, the encoded algorithm string to be split.
    215 
    216     Returns:
    217       a tuple of two strings, first and last elements of the split, either one
    218       could be empty.
    219 
    220     """
    221     parts = self._alg_macro.split(word)
    222     if len(parts) == 1:
    223       parts = word.split('!ALG')
    224       if len(parts) == 1:
    225         return word, None
    226     return parts[0].strip('_'), parts[-1].strip('_')
    227 
    228   def SetSkipTables(self, skip_tables):
    229     """Set the tuple of table numbers to be ignored by the parser."""
    230     self.skip_tables = skip_tables
    231 
    232   def _AddToHfile(self, text=''):
    233     self._h_file += text + '\n'
    234 
    235   def _SetBaseType(self, old_type, tpm_obj):
    236     """Set the base type for a new object.
    237 
    238     Many TPM objects are typedefed hierarchically, for instance
    239 
    240     uint16_t => UINT16 => TPM_ALG_ID_Marshal => TPMI_ALG_HASH_Marshal
    241 
    242     This causes excessive nesting when marshaling and unmarshaling, which is
    243     bad from both performance and stack size requirements point of view.
    244 
    245     This function will discover the 'basest' type and set it in the tpm
    246     object, this would help to generate direct marshaling/unmarshaling
    247     functions.
    248 
    249     Args:
    250       old_type: a string, name of the immediate type this tpm object typedefed
    251                 from.
    252       tpm_obj: a tpm object, derived from TPMType
    253     """
    254     base_type = old_type
    255     while base_type in self._type_map:
    256       try:
    257         base_type = self._type_map[base_type].old_type
    258       except AttributeError:
    259         break  # The underlying type is not a typedef
    260     tpm_obj.SetBaseType(base_type)
    261 
    262   def _AddTypedef(self, old_type, new_type):
    263     if not self._skip_printing:
    264       self._AddToHfile('typedef %s %s;' % (old_type, new_type))
    265     # No need to generate marshaling/unmarshaling code for BOOL type.
    266     if new_type != 'BOOL':
    267       self._type_map[new_type] = Typedef(old_type, new_type)
    268       self._SetBaseType(old_type, self._type_map[new_type])
    269 
    270   def InProgress(self):
    271     """Return True when the parser is in the middle of a table."""
    272     return self._title
    273 
    274   def _GetMaxLengths(self, table):
    275     """Find out maximum lengths of the table columns.
    276 
    277     This function helps to generate nicely aligned definitions in the output
    278     .h file, by making sure that each field's name starts in the same column,
    279     far enough for all fields' types to fit.
    280 
    281     Args:
    282       table: a list of string lists. Each component consists of at least two
    283              elements, the first one the field or constant type, the
    284              second one the field name or constant value.
    285 
    286     Returns:
    287       a tuple of two integers, the first one - the length of the longest
    288               string in the first colume, the second one - the length of the
    289               longest string in the second column.
    290     """
    291     lengths = [0, 0]
    292     for row in table:
    293       for i in range(len(lengths)):
    294         if len(row[i]) > lengths[i]:
    295           lengths[i] = len(row[i])
    296     return tuple(lengths)
    297 
    298   def NewRow(self):
    299     """Start a new row in the internal table representation."""
    300     self._body.append([])
    301 
    302   def NewCell(self):
    303     """Start a new cell in the last row."""
    304     self._body[-1].append('')
    305 
    306   def AddData(self, data):
    307     """Add data to the last cell of the last row."""
    308     if not self._body:
    309       return  # Ignore end of line and whitespace formatting.
    310     self._body[-1][-1] += data
    311 
    312   def ProcessTable(self):
    313     """Process accumulated table contents.
    314 
    315     This function is invoked when the parser state machine detects that the
    316     entire HTML table has been processed. The received contents is handled
    317     based on the table title by finding the matching entry in TABLE_ROUTER
    318     tuple.
    319 
    320     The result of processing is adding a new TPM type to the _type_map
    321     dictionary, or a new command descriptor to the _command_map dictionary.
    322     """
    323 
    324     # The table has a selector column if it has at least three columns, and
    325     # the third column is titled 'Selector'.
    326     self._has_selector_column = (len(self._body[0]) >= 3 and
    327                                  self._body[0][2].strip() == 'Selector')
    328     # Preprocess representation of the table scraped from the spec. Namely,
    329     # remove the header row, and strip all other cells before adding them to
    330     # self._body[], which becomes a list including all scraped table cells,
    331     # stripped.
    332     self._body = [[cell.strip() for cell in row] for row in self._body[1:]]
    333     if 'TPM_ALG_ID Constants' in self._title:
    334       self._alg_id_table = [[x[0], x[2].replace(' ', ''), x[3]]
    335                             for x in self._body]
    336 
    337     # The name of the type defined in the table, when present, is always the
    338     # fifth element in the stripped header, for instance:
    339     # 'Table 10 - Definition of TPM_ECC_CURVE Constants'
    340     try:
    341       type_name = self._title.split()[4]
    342     except IndexError:
    343       type_name = ''
    344 
    345     # Based on the table title, find the function to process the table and
    346     # generate a TPM specification object of a certain type.
    347     table_func = ''
    348     for regex, func in self.TABLE_ROUTER:
    349       if regex.search(self._title):
    350         table_func = func
    351         break
    352     else:
    353       self._AddToHfile('// Unprocessed: %s' % self._title)
    354       return
    355 
    356     if int(self._title.split()[1]) in self.skip_tables:
    357       self._skip_printing = True
    358       self._AddToHfile('// Skipped: %s' % self._title)
    359     else:
    360       self._AddToHfile('// %s' % self._title)
    361 
    362     # Invoke a TPM type specific processing function.
    363     getattr(self, table_func)(type_name)
    364 
    365   def _ProcessCommand(self, _):
    366     """Process command description table from part 3.
    367 
    368     Each TPM command has two tables associated with it, one describing the
    369     request structure, and another one describing the response structure. The
    370     first time a TPM command is encountered, a Command object is created and
    371     its 'request_args' property is set, the second time it is encountered -
    372     the existing object's 'response_args' property is set.
    373     """
    374     command_name = self._title.split()[2]
    375     if command_name not in self._command_map:
    376       command = Command(command_name)
    377       self._command_map[command_name] = command
    378     else:
    379       command = self._command_map[command_name]
    380     params = []
    381     # The first three fields in each request and response are always the same
    382     # and are not included in the generated structures. Let's iterate over the
    383     # rest of the fields.
    384     for row in self._body[3:]:
    385       # A dictionary describing a request or response structure field.
    386       field = {}
    387       # Ignore the '@' decoration for now.
    388       field_type, field['name'] = row[0], row[1].lstrip('@')
    389       # The '+' decoration means this field can be conditional.
    390       if field_type.endswith('+'):
    391         field_type = field_type[:-1]
    392         field['has_conditional'] = 'TRUE'
    393       else:
    394         field['has_conditional'] = 'FALSE'
    395       field['type'] = field_type
    396       if len(row) > 2:
    397         field['description'] = row[2]
    398       else:
    399         field['description'] = ''
    400       # Add the new field to the list of request or response fields.
    401       params.append(field)
    402     if ' Command' in self._title:
    403       command.request_args = params
    404     else:
    405       command.response_args = params
    406 
    407   def _PickAlgEntries(self, alg_type_str):
    408     """Process algorithm id table and find all matching entries.
    409 
    410     See comments to _alg_macro above.
    411 
    412     Args:
    413      alg_type_str: a string, one or more dot separated encoded algorithm types.
    414 
    415     Returns:
    416       A table of alg_type (Table 9 of part 2) entries matching the passed in
    417       encoded type string.
    418     """
    419     filtered_table = []
    420     for alg_type in alg_type_str.split('.'):
    421       if re.match('^[A-Z]+$', alg_type):
    422         # Exclusive selection, must exactly match algorithm type from table 9
    423         # (which is in the second column). Add to the return value all
    424         # matching rows of table 9.
    425         extension = []
    426         for row in self._alg_id_table:
    427           if row[1] == alg_type:
    428             if self._alg_type and self._alg_type != row[2]:
    429               continue
    430             extension.append(row)
    431         filtered_table.extend(extension)
    432       elif re.match('^[a-z]+$', alg_type):
    433         # Inclusive selection. All type letters must be present in the type
    434         # column, but no exact match is required.
    435         for row in self._alg_id_table:
    436           for char in alg_type.upper():
    437             if char not in row[1]:
    438               break
    439           else:
    440             if not self._alg_type or self._alg_type == row[2]:
    441               filtered_table.append(row)
    442     return filtered_table
    443 
    444   def _ParseAlgorithmRegex(self, token):
    445     """Process a token as an algorithm regex.
    446 
    447     This function tries to interpret the passed in token as an encoded
    448     algorithm specification.
    449 
    450     In case the encoded algorithm regex matches, the function splits the token
    451     into prefix, algorithm description and suffix, and then retrieves the list
    452     of all algorithms matching the algorithm description.
    453 
    454     Args:
    455       token: a string, potentially including the algorithm regex.
    456 
    457     Returns:
    458       in case the regex matched returns a tri-tuple of two strings (prefix and
    459       suffix, either one could be empty) and a list of matching algorithms
    460       from the algorithm descriptors table. If there has been no match -
    461       returns None.
    462     """
    463     elements = self._alg_macro.split(token)
    464     if len(elements) == 3:
    465       # The token matched the algorithm regex, Find out prefix and suffix to
    466       # be used on the generated type names, and the algorithm regex suffix to
    467       # use to find matching entries in the algorithm table.
    468       name_prefix, alg_suffix, name_suffix = tuple(elements)
    469       name_prefix = name_prefix.strip('_')
    470       name_suffix = name_suffix.strip('_')
    471       return name_prefix, name_suffix, self._PickAlgEntries(alg_suffix)
    472 
    473   def _ProcessInterface(self, type_name):
    474     """Processes spec tables describing interfaces."""
    475     result = self._ParseAlgorithmRegex(type_name)
    476     if result:
    477       name_prefix, name_suffix, alg_list = tuple(result)
    478       # Process all matching algorithm types
    479       for alg_desc in alg_list:
    480         alg_base = alg_desc[0].replace('TPM_ALG_', '')
    481         new_name = '_'.join([name_prefix,
    482                              alg_base, name_suffix]).strip('_')
    483         new_if = Interface(self._title_type, new_name)
    484         self._AddTypedef(self._title_type, new_name)
    485         for row in self._body:
    486           new_value = row[0]
    487           if new_value.startswith('$!ALG'):
    488             new_if.supported_values = alg_base + '_' + '_'.join(
    489                 new_value.split('_')[1:])
    490           elif new_value.startswith('$'):
    491             new_if.supported_values = new_value[1:]
    492           elif new_value.startswith('#'):
    493             new_if.error_code = new_value[1:]
    494         self._type_map[new_name] = new_if
    495       self._AddToHfile('\n')
    496       return
    497     new_if = Interface(self._title_type, type_name)
    498     self._AddTypedef(self._title_type, type_name)
    499     self._type_map[type_name] = new_if
    500     self._SetBaseType(type_name, new_if)
    501     for row in self._body:
    502       new_value = row[0]
    503       result = self._ParseAlgorithmRegex(new_value)
    504       if result:
    505         # The field is described using the algorithm regex. The above comment
    506         # applies.
    507         name_prefix, name_suffix, alg_list = tuple(result)
    508         for alg_desc in alg_list:
    509           alg_base = alg_desc[0].replace('TPM_ALG_', '')
    510           new_if.valid_values.append('_'.join(
    511               [name_prefix, alg_base, name_suffix]).strip('_'))
    512       else:
    513         if new_value.startswith('{'):
    514           bounds = tuple(
    515               [x.strip() for x in new_value[1:-1].strip().split(':')])
    516           new_if.bounds.append(bounds)
    517         elif new_value.startswith('+'):
    518           new_if.conditional_value = new_value[1:]
    519         elif new_value.startswith('#'):
    520           new_if.error_code = new_value[1:]
    521         elif new_value.startswith('$'):
    522           new_if.supported_values = new_value[1:]
    523         else:
    524           new_if.valid_values.append(new_value)
    525     return
    526 
    527   def _ProcessTypedefs(self, type_name):
    528     """Processes spec tables defining new types."""
    529     result = self._ParseAlgorithmRegex(type_name)
    530     if result:
    531       name_prefix, name_suffix, alg_list = tuple(result)
    532       for alg_desc in alg_list:
    533         alg_base = alg_desc[0].replace('TPM_ALG_', '')
    534         new_type = '%s_%s_%s' % (name_prefix, alg_base, name_suffix)
    535         self._AddTypedef(self._title_type, new_type)
    536       self._AddToHfile('\n')
    537     else:
    538       self._AddTypedef(self._title_type, type_name)
    539 
    540   def _ProcessBits(self, type_name):
    541     """Processes spec tables describing attributes (bit fields)."""
    542     bits_lines = []
    543     base_bit = 0
    544     tpm_obj = AttributeStructure(self._title_type, type_name)
    545     self._type_map[type_name] = tpm_obj
    546     self._SetBaseType(self._title_type, tpm_obj)
    547     for bits_line in self._body:
    548       field, name = tuple(bits_line[:2])
    549       if not field:
    550         continue
    551       if name.startswith('TPM_'):
    552         # Spec inconsistency fix.
    553         name_pieces = [x.lower() for x in name.split('_')[1:]]
    554         name = name_pieces[0]
    555         for piece in name_pieces[1:]:
    556           name += piece[0].upper() + piece[1:]
    557       bit_range = [x.replace(' ', '') for x in field.split(':')]
    558       field_base = int(bit_range[-1])
    559       if field_base != base_bit:
    560         field_name = 'reserved%d' % base_bit
    561         field_width = field_base - base_bit
    562         if field_width > 1:
    563           field_name += '_%d' % (field_base - 1)
    564         bits_lines.append(['%s : %d' % (field_name, field_width)])
    565         tpm_obj.reserved.append(field_name.replace('reserved', ''))
    566       if len(bit_range) > 1:
    567         field_width = int(bit_range[0]) - field_base + 1
    568       else:
    569         field_width = 1
    570       if re.match('reserved', name, re.IGNORECASE):
    571         name = 'reserved%d' % field_base
    572         if field_width > 1:
    573           name += '_%d' % (field_base + field_width - 1)
    574         tpm_obj.reserved.append(name.replace('reserved', ''))
    575       bits_lines.append([name, ': %d' % field_width])
    576       base_bit = field_base + field_width
    577     max_type_len, _ = self._GetMaxLengths(bits_lines)
    578     self._AddToHfile('typedef struct {')
    579     for bits_line in bits_lines:
    580       self._AddToHfile('  %s %-*s %s;' % (self._title_type, max_type_len,
    581                                           bits_line[0], bits_line[1]))
    582     self._AddToHfile('} %s;\n' % type_name)
    583 
    584   def _ExpandAlgs(self, row):
    585     """Find all algorithms encoded in the variable name.
    586 
    587     Args:
    588       row: a list of strings, a row of a structure or union table scraped from
    589            part 2.
    590 
    591     Returns:
    592       A list for structure_generator.Field objects, one per expanded
    593       algorithm.
    594 
    595     """
    596     alg_spec = row[0].split()
    597     expansion = []
    598     m = self._alg_macro.search(alg_spec[0])
    599     if m:
    600       alg_type = m.groups()[0]
    601       # Find all algorithms of this type in the alg id table
    602       alg_entries = self._PickAlgEntries(alg_type)
    603       if len(alg_spec) == 2 and alg_spec[1][0] == '[':
    604         # This is the case of a union of arrays.
    605         raw_size_parts = self._alg_macro.split(alg_spec[1][1:-1])
    606         size_prefix = raw_size_parts[0].strip('_')
    607         size_suffix = raw_size_parts[2].strip('_')
    608         for alg_desc in alg_entries:
    609           alg_base = alg_desc[0].replace('TPM_ALG_', '')
    610           size = '_'.join([size_prefix, alg_base, size_suffix]).strip('_')
    611           if self._has_selector_column:
    612             selector_parts = self._alg_macro.split(row[2])
    613             selector_prefix = selector_parts[0].strip('_')
    614             selector_suffix = selector_parts[2].strip('_')
    615             selector = '_'.join([selector_prefix,
    616                                  alg_base, selector_suffix]).strip('_')
    617           else:
    618             selector = ''
    619           expansion.append(Field(row[1], alg_base.lower(),
    620                                  selector=selector, array_size=size))
    621       else:
    622         type_prefix, type_suffix = self._SplitByAlg(row[1])
    623         if self._has_selector_column:
    624           selector_prefix, selector_suffix = self._SplitByAlg(row[2])
    625         else:
    626           selector = ''
    627         for alg_desc in alg_entries:
    628           alg_base = alg_desc[0].replace('TPM_ALG_', '')
    629           if type_suffix is not None:
    630             var_type = '_'.join([type_prefix, alg_base, type_suffix]).strip('_')
    631           else:
    632             var_type = type_prefix
    633           if self._has_selector_column:
    634             selector = '_'.join([selector_prefix, alg_base,
    635                                  selector_suffix]).strip('_')
    636           expansion.append(Field(var_type, alg_base.lower(),
    637                                  selector=selector))
    638     return expansion
    639 
    640   def _ProcessInterfaceOrType(self, type_name):
    641     if type_name.startswith('TPMI_'):
    642       self._ProcessInterface(type_name)
    643     else:
    644       self._ProcessTypedefs(type_name)
    645 
    646   def _StructOrUnionToHfile(self, body_fields, type_name, union_mode, tpm_obj):
    647     body_lines = []
    648     for field in body_fields:
    649       tpm_obj.AddField(field)
    650       body_lines.append([field.field_type, field.field_name])
    651       if field.array_size:
    652         body_lines[-1][-1] += '[%s]' % field.array_size
    653       if field.selector_value:
    654         body_lines[-1].append(field.selector_value)
    655     max_type_len, _ = self._GetMaxLengths(body_lines)
    656     tpm2b_mode = type_name.startswith('TPM2B_')
    657     space_prefix = ''
    658     if union_mode:
    659       self._AddToHfile('typedef union {')
    660     else:
    661       if tpm2b_mode:
    662         self._AddToHfile('typedef union {')
    663         space_prefix = '  '
    664         self._AddToHfile('  struct {')
    665       else:
    666         self._AddToHfile('typedef struct {')
    667     for line in body_lines:
    668       guard_required = len(line) > 2 and line[2].startswith('TPM_ALG_')
    669       if not line[1]:
    670         continue
    671       if guard_required:
    672         self._AddToHfile('#ifdef %s' % line[2])
    673       self._AddToHfile(space_prefix + '  %-*s  %s;' % (
    674           max_type_len, line[0], line[1]))
    675       if guard_required:
    676         self._AddToHfile('#endif')
    677     if tpm2b_mode:
    678       self._AddToHfile('  } t;')
    679       self._AddToHfile('  TPM2B b;')
    680     self._AddToHfile('} %s;\n' % type_name)
    681     self._type_map[type_name] = tpm_obj
    682 
    683 
    684   def _ProcessStructureOrUnion(self, type_name):
    685     """Processes spec tables describing structure and unions.
    686 
    687     Both of these object types share a lot of similarities. Union types have
    688     the word 'Union' in the table title.
    689 
    690     Args:
    691       type_name: a string, name of the TPM object type
    692     """
    693     union_mode = 'Union' in self._title
    694     if union_mode:
    695       tpm_obj = Union(type_name)
    696     else:
    697       tpm_obj = Structure(type_name)
    698     body_fields = []
    699     for row in self._body:
    700       if row[0].startswith('#'):
    701         tpm_obj.error_code = row[0][1:]
    702         continue
    703       if (len(row) < 2 or
    704           row[1].startswith('#') or
    705           row[0].startswith('//')):
    706         continue
    707       value = row[0]
    708       if value.endswith('='):
    709         value = value[:-1]
    710         tpm_obj.size_check = True
    711       if self._alg_macro.search(value):
    712         body_fields.extend(self._ExpandAlgs(row))
    713         continue
    714       array_size = None
    715       run_time_size = None
    716       vm = re.search(r'^(\S+)\s*\[(\S+)\]\s*\{(.*:*)\}', value)
    717       selector = ''
    718       if vm:
    719         value, run_time_size, bounds = vm.groups()
    720         lower, upper = [x.strip() for x in bounds.split(':')]
    721         if upper:
    722           array_size = upper
    723           tpm_obj.AddUpperBound(run_time_size, upper)
    724         else:
    725           array_size = run_time_size
    726         if lower:
    727           tpm_obj.AddLowerBound(run_time_size, lower)
    728       else:
    729         vm = re.search(r'^\[(\S+)\]\s*(\S+)', value)
    730         if vm:
    731           selector, value = vm.groups()
    732         else:
    733           vm = re.search(r'^(\S+)\s*\{(.+)\}', value)
    734           if vm:
    735             value, bounds = vm.groups()
    736             if ':' in bounds:
    737               lower, upper = [x.strip() for x in bounds.split(':')]
    738               if upper:
    739                 tpm_obj.AddUpperBound(value, upper)
    740               if lower:
    741                 tpm_obj.AddLowerBound(value, lower)
    742       if self._has_selector_column:
    743         selector = row[2]
    744       field_type = row[1]
    745       if field_type.startswith('+') or field_type.endswith('+'):
    746         field_type = field_type.strip('+')
    747         conditional = 'TRUE'
    748       else:
    749         conditional = 'FALSE'
    750       if field_type or value:
    751         body_fields.append(Field(field_type,
    752                                  value,
    753                                  array_size=array_size,
    754                                  run_time_size=run_time_size,
    755                                  selector=selector,
    756                                  conditional_value=conditional))
    757 
    758     self._StructOrUnionToHfile(body_fields, type_name, union_mode, tpm_obj)
    759 
    760   def _ProcessEnum(self, _):
    761     """Processes spec tables describing enums."""
    762     if self._skip_printing:
    763       return
    764     self._AddToHfile('enum {')
    765     for value, row in enumerate(self._body):
    766       self._AddToHfile('  %s = %d,' % (row[0], value + 1))
    767     self._AddToHfile('};\n')
    768 
    769   def _ProcessTypes(self, _):
    770     """Processes spec tables describing new types."""
    771     for type_, name_, _ in self._body:
    772       m = self._alg_macro.search(name_)
    773       if not m:
    774         self._AddTypedef(type_, name_)
    775         continue
    776       qualifier = [x for x in ('ECC', 'RSA') if x == type_.split('_')[-1]]
    777       alg_suffix = m.groups()[0]
    778       type_base = self._alg_macro.split(name_)[0]
    779       for alg_desc in self._PickAlgEntries(alg_suffix):
    780         if qualifier and alg_desc[2] != qualifier[0]:
    781           continue
    782         self._AddTypedef(type_, alg_desc[0].replace('TPM_ALG_', type_base))
    783     self._AddToHfile()
    784 
    785   def _ProcessConstants(self, type_name):
    786     """Processes spec tables describing constants."""
    787     if self._title_type:
    788       self._AddTypedef(self._title_type, type_name)
    789     constant_defs = []
    790     tpm_obj = ConstantType(self._title_type, type_name)
    791     for row in self._body:
    792       name = row[0].strip()
    793       if not name:
    794         continue
    795       if name.startswith('#'):
    796         tpm_obj.error_code = name[1:]
    797         continue
    798       if name == 'reserved' or len(row) < 2:
    799         continue
    800       value = row[1].strip()
    801       rm = re.match(r'^(\(.*?\))', value)
    802       if rm:
    803         value = '%s' % rm.groups()[0]
    804       else:
    805         v_list = value.split()
    806         if len(v_list) > 2 and v_list[1] == '+':
    807           value = '((%s)(%s))' % (type_name, ' '.join(v_list[:3]))
    808       if ' ' in value and not value.startswith('('):
    809         value = '(%s)' % value
    810       constant_defs.append([name, value])
    811       tpm_obj.valid_values.append(name)
    812     if self._title_type:
    813       self._type_map[type_name] = tpm_obj
    814       self._SetBaseType(self._title_type, tpm_obj)
    815     if self._skip_printing:
    816       return
    817     max_name_len, max_value_len = self._GetMaxLengths(constant_defs)
    818     for row in constant_defs:
    819       self._AddToHfile('#define %-*s  %*s' % (max_name_len, row[0],
    820                                               max_value_len, row[1]))
    821     self._AddToHfile()
    822 
    823   def GetHFile(self):
    824     return self._h_file
    825 
    826   def GetCommandList(self):
    827     return sorted(self._command_map.values(),
    828                   cmp=lambda x, y: cmp(x.name, y.name))
    829 
    830   def GetTypeMap(self):
    831     return self._type_map
    832