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