1#!/usr/bin/env python3 2# 3# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 4# 5# SPDX-License-Identifier: BSD-2-Clause 6# 7 8# 9# seL4 System Call Stub Generator 10# =============================== 11# 12# 2009 David Greenaway 13# 14# This script generates system call stubs based on an XML specification of the 15# objects that the kernel exports (and the methods those objects export). 16# 17# Previously, Magpie (an IDL compiler) was used to generate these stubs. As 18# Magpie development progressed, support for a fixed ABI (i.e., the ABI 19# implemented by the seL4 kernel) was lost, and support for generating 20# alignment-safe code (required by platforms such as ARM) was also removed. 21# 22# This script is a stop-gap until these features can be restored in Magpie 23# once again. 24# 25# The script has certain limitations: 26# 27# * It must be told the size of all types. This includes complex types 28# such as structures. 29# 30# We generate code that will cause compilation to fail if we get any 31# object's size wrong, which should help mitigate the number of bugs caused 32# because of this script becoming out of date compared to the source files. 33# 34# * The script has only been tested on the actual seL4 API XML description. 35# 36# No stress testing has taken place; there may be bugs if new and wonderful 37# XML method descriptions are added. 38# 39 40import operator 41import itertools 42import xml.dom.minidom 43from argparse import ArgumentParser 44import sys 45from functools import reduce 46 47# Number of bits in a standard word 48WORD_SIZE_BITS_ARCH = { 49 "aarch32": 32, 50 "ia32": 32, 51 "aarch64": 64, 52 "ia64": 64, 53 "x86_64": 64, 54 "arm_hyp": 32, 55 "riscv32": 32, 56 "riscv64": 64, 57} 58 59MESSAGE_REGISTERS_FOR_ARCH = { 60 "aarch32": 4, 61 "aarch64": 4, 62 "ia32": 2, 63 "ia32-mcs": 1, 64 "x86_64": 4, 65 "arm_hyp": 4, 66 "riscv32": 4, 67 "riscv64": 4, 68} 69 70WORD_CONST_SUFFIX_BITS = { 71 32: "ul", 72 64: "ull", 73} 74 75# Maximum number of words that will be in a message. 76MAX_MESSAGE_LENGTH = 64 77 78# Headers to include 79INCLUDES = [ 80 'autoconf.h', 'sel4/types.h' 81] 82 83TYPES = { 84 8: "seL4_Uint8", 85 16: "seL4_Uint16", 86 32: "seL4_Uint32", 87 64: "seL4_Uint64" 88} 89 90 91class Type(object): 92 """ 93 This class represents a C type (such as an 'int', structure or 94 pointer. 95 """ 96 97 def __init__(self, name, size_bits, wordsize, double_word=False, native_size_bits=None): 98 """ 99 Define a new type, named 'name' that is 'size_bits' bits 100 long. 101 """ 102 103 self.name = name 104 self.size_bits = size_bits 105 self.wordsize = wordsize 106 self.double_word = double_word 107 108 # 109 # Store the number of bits C will use for this type 110 # in its native unpacked form. 111 # 112 # Required for 'bool', for example, which only uses 1 113 # bit when packed, but 32 bits when unpacked. 114 # 115 if native_size_bits: 116 self.native_size_bits = native_size_bits 117 else: 118 self.native_size_bits = size_bits 119 120 def pass_by_reference(self): 121 return self.size_bits > self.wordsize and not self.double_word 122 123 def render_parameter_name(self, name): 124 """ 125 Return a string of C code that would be used in a function 126 parameter declaration. 127 """ 128 return "%s %s" % (self.name, name) 129 130 def pointer(self): 131 """ 132 Return a new Type class representing a pointer to this 133 object. 134 """ 135 return PointerType(self, self.wordsize) 136 137 def c_expression(self, var_name, word_num=0): 138 """ 139 Return code for a C expression that gets word 'word_num' 140 of this type. 141 """ 142 assert word_num == 0 143 return "%s" % var_name 144 145 def double_word_expression(self, var_name, word_num, word_size): 146 147 assert word_num == 0 or word_num == 1 148 149 if word_num == 0: 150 return "({0}) {1}".format(TYPES[self.size_bits], var_name) 151 elif word_num == 1: 152 return "({0}) ({1} >> {2})".format(TYPES[self.size_bits], var_name, 153 word_size) 154 155 156class PointerType(Type): 157 """ 158 A pointer to a standard type. 159 """ 160 161 def __init__(self, base_type, wordsize): 162 Type.__init__(self, base_type.name, wordsize, wordsize) 163 self.base_type = base_type 164 165 def render_parameter_name(self, name): 166 return "%s *%s" % (self.name, name) 167 168 def c_expression(self, var_name, word_num=0): 169 assert word_num == 0 170 return "*%s" % var_name 171 172 def pointer(self): 173 raise NotImplementedError() 174 175 176class CapType(Type): 177 """ 178 A type that is just a typedef of seL4_CPtr. 179 """ 180 181 def __init__(self, name, wordsize): 182 Type.__init__(self, name, wordsize, wordsize) 183 184 185class StructType(Type): 186 """ 187 A C 'struct' definition. 188 """ 189 190 def __init__(self, name, size_bits, wordsize): 191 Type.__init__(self, name, size_bits, wordsize) 192 193 def c_expression(self, var_name, word_num, member_name): 194 assert word_num < self.size_bits / self.wordsize 195 196 # Multiword structure. 197 assert self.pass_by_reference() 198 return "%s->%s" % (var_name, member_name[word_num]) 199 200 201class BitFieldType(Type): 202 """ 203 A special C 'struct' generated by the bitfield generator 204 """ 205 206 def __init__(self, name, size_bits, wordsize): 207 Type.__init__(self, name, size_bits, wordsize) 208 209 def c_expression(self, var_name, word_num=0): 210 211 return "%s.words[%d]" % (var_name, word_num) 212 213 214class Parameter(object): 215 def __init__(self, name, type): 216 self.name = name 217 self.type = type 218 219 220class Api(object): 221 def __init__(self, node): 222 self.name = node.getAttribute("name") 223 self.label_prefix = node.getAttribute("label_prefix") or "" 224 225# 226# Types 227# 228 229 230def init_data_types(wordsize): 231 types = [ 232 # Simple Types 233 Type("int", 32, wordsize), 234 Type("long", wordsize, wordsize), 235 236 Type("seL4_Uint8", 8, wordsize), 237 Type("seL4_Uint16", 16, wordsize), 238 Type("seL4_Uint32", 32, wordsize), 239 Type("seL4_Uint64", 64, wordsize, double_word=(wordsize == 32)), 240 Type("seL4_Time", 64, wordsize, double_word=(wordsize == 32)), 241 Type("seL4_Word", wordsize, wordsize), 242 Type("seL4_Bool", 1, wordsize, native_size_bits=8), 243 244 # seL4 Structures 245 BitFieldType("seL4_CapRights_t", wordsize, wordsize), 246 247 # Object types 248 CapType("seL4_CPtr", wordsize), 249 CapType("seL4_CNode", wordsize), 250 CapType("seL4_IRQHandler", wordsize), 251 CapType("seL4_IRQControl", wordsize), 252 CapType("seL4_TCB", wordsize), 253 CapType("seL4_Untyped", wordsize), 254 CapType("seL4_DomainSet", wordsize), 255 CapType("seL4_SchedContext", wordsize), 256 CapType("seL4_SchedControl", wordsize), 257 ] 258 259 return types 260 261 262def init_arch_types(wordsize): 263 arm_smmu = [ 264 CapType("seL4_ARM_SIDControl", wordsize), 265 CapType("seL4_ARM_SID", wordsize), 266 CapType("seL4_ARM_CBControl", wordsize), 267 CapType("seL4_ARM_CB", wordsize), 268 ] 269 arch_types = { 270 "aarch32": [ 271 Type("seL4_ARM_VMAttributes", wordsize, wordsize), 272 CapType("seL4_ARM_Page", wordsize), 273 CapType("seL4_ARM_PageTable", wordsize), 274 CapType("seL4_ARM_PageDirectory", wordsize), 275 CapType("seL4_ARM_ASIDControl", wordsize), 276 CapType("seL4_ARM_ASIDPool", wordsize), 277 CapType("seL4_ARM_VCPU", wordsize), 278 CapType("seL4_ARM_IOSpace", wordsize), 279 CapType("seL4_ARM_IOPageTable", wordsize), 280 StructType("seL4_UserContext", wordsize * 19, wordsize), 281 ] + arm_smmu, 282 283 "aarch64": [ 284 Type("seL4_ARM_VMAttributes", wordsize, wordsize), 285 CapType("seL4_ARM_Page", wordsize), 286 CapType("seL4_ARM_PageTable", wordsize), 287 CapType("seL4_ARM_PageDirectory", wordsize), 288 CapType("seL4_ARM_PageUpperDirectory", wordsize), 289 CapType("seL4_ARM_PageGlobalDirectory", wordsize), 290 CapType("seL4_ARM_VSpace", wordsize), 291 CapType("seL4_ARM_ASIDControl", wordsize), 292 CapType("seL4_ARM_ASIDPool", wordsize), 293 CapType("seL4_ARM_VCPU", wordsize), 294 CapType("seL4_ARM_IOSpace", wordsize), 295 CapType("seL4_ARM_IOPageTable", wordsize), 296 StructType("seL4_UserContext", wordsize * 36, wordsize), 297 ] + arm_smmu, 298 299 "arm_hyp": [ 300 Type("seL4_ARM_VMAttributes", wordsize, wordsize), 301 CapType("seL4_ARM_Page", wordsize), 302 CapType("seL4_ARM_PageTable", wordsize), 303 CapType("seL4_ARM_PageDirectory", wordsize), 304 CapType("seL4_ARM_ASIDControl", wordsize), 305 CapType("seL4_ARM_ASIDPool", wordsize), 306 CapType("seL4_ARM_VCPU", wordsize), 307 CapType("seL4_ARM_IOSpace", wordsize), 308 CapType("seL4_ARM_IOPageTable", wordsize), 309 StructType("seL4_UserContext", wordsize * 19, wordsize), 310 ] + arm_smmu, 311 312 "ia32": [ 313 Type("seL4_X86_VMAttributes", wordsize, wordsize), 314 CapType("seL4_X86_IOPort", wordsize), 315 CapType("seL4_X86_IOPortControl", wordsize), 316 CapType("seL4_X86_ASIDControl", wordsize), 317 CapType("seL4_X86_ASIDPool", wordsize), 318 CapType("seL4_X86_IOSpace", wordsize), 319 CapType("seL4_X86_Page", wordsize), 320 CapType("seL4_X86_PageDirectory", wordsize), 321 CapType("seL4_X86_PageTable", wordsize), 322 CapType("seL4_X86_IOPageTable", wordsize), 323 CapType("seL4_X86_VCPU", wordsize), 324 CapType("seL4_X86_EPTPML4", wordsize), 325 CapType("seL4_X86_EPTPDPT", wordsize), 326 CapType("seL4_X86_EPTPD", wordsize), 327 CapType("seL4_X86_EPTPT", wordsize), 328 StructType("seL4_VCPUContext", wordsize * 7, wordsize), 329 StructType("seL4_UserContext", wordsize * 12, wordsize), 330 ], 331 332 "x86_64": [ 333 Type("seL4_X86_VMAttributes", wordsize, wordsize), 334 CapType("seL4_X86_IOPort", wordsize), 335 CapType("seL4_X86_IOPortControl", wordsize), 336 CapType("seL4_X86_ASIDControl", wordsize), 337 CapType("seL4_X86_ASIDPool", wordsize), 338 CapType("seL4_X86_IOSpace", wordsize), 339 CapType("seL4_X86_Page", wordsize), 340 CapType("seL4_X64_PML4", wordsize), 341 CapType("seL4_X86_PDPT", wordsize), 342 CapType("seL4_X86_PageDirectory", wordsize), 343 CapType("seL4_X86_PageTable", wordsize), 344 CapType("seL4_X86_IOPageTable", wordsize), 345 CapType("seL4_X86_VCPU", wordsize), 346 CapType("seL4_X86_EPTPML4", wordsize), 347 CapType("seL4_X86_EPTPDPT", wordsize), 348 CapType("seL4_X86_EPTPD", wordsize), 349 CapType("seL4_X86_EPTPT", wordsize), 350 StructType("seL4_VCPUContext", wordsize * 7, wordsize), 351 StructType("seL4_UserContext", wordsize * 20, wordsize), 352 ], 353 "riscv32": [ 354 Type("seL4_RISCV_VMAttributes", wordsize, wordsize), 355 CapType("seL4_RISCV_Page", wordsize), 356 CapType("seL4_RISCV_PageTable", wordsize), 357 CapType("seL4_RISCV_ASIDControl", wordsize), 358 CapType("seL4_RISCV_ASIDPool", wordsize), 359 StructType("seL4_UserContext", wordsize * 32, wordsize), 360 ], 361 "riscv64": [ 362 Type("seL4_RISCV_VMAttributes", wordsize, wordsize), 363 CapType("seL4_RISCV_Page", wordsize), 364 CapType("seL4_RISCV_PageTable", wordsize), 365 CapType("seL4_RISCV_ASIDControl", wordsize), 366 CapType("seL4_RISCV_ASIDPool", wordsize), 367 StructType("seL4_UserContext", wordsize * 32, wordsize), 368 ] 369 } 370 371 return arch_types 372 373# Retrieve a member list for a given struct type 374 375 376def struct_members(typ, structs): 377 members = [member for struct_name, member in structs if struct_name == typ.name] 378 assert len(members) == 1 379 return members[0] 380 381# Keep increasing the given number 'x' until 'x % a == 0'. 382 383 384def align_up(x, a): 385 if x % a == 0: 386 return x 387 return x + a - (x % a) 388 389 390def get_parameter_positions(parameters, wordsize): 391 """ 392 Determine where each parameter should be packed in the generated message. 393 We generate a list of: 394 395 (param_name, param_type, first_bit, num_bits) 396 397 tuples. 398 399 We guarantee that either (num_words == 1) or (bit_offset == 0). 400 """ 401 bits_used = 0 402 results = [] 403 404 for param in parameters: 405 # How big are we? 406 type_size = param.type.size_bits 407 408 # We need everything to be a power of two, or word sized. 409 assert ((type_size & (type_size - 1)) == 0) or (type_size % wordsize == 0) 410 411 # Align up to our own size, or the next word. (Whichever is smaller) 412 bits_used = align_up(bits_used, min(type_size, wordsize)) 413 414 # Place ourself. 415 results.append((param, bits_used, type_size)) 416 bits_used += type_size 417 418 return results 419 420 421def generate_param_list(input_params, output_params): 422 # Generate parameters 423 params = [] 424 for param in input_params: 425 if not param.type.pass_by_reference(): 426 params.append(param.type.render_parameter_name(param.name)) 427 else: 428 params.append(param.type.pointer().render_parameter_name(param.name)) 429 for param in output_params: 430 if param.type.pass_by_reference(): 431 params.append(param.type.pointer().render_parameter_name(param.name)) 432 433 return ", ".join(params) 434 435 436def generate_marshal_expressions(params, num_mrs, structs, wordsize): 437 """ 438 Generate marshalling expressions for the given set of inputs. 439 440 We return a list of expressions; one expression per word required 441 to marshal all the inputs. 442 """ 443 444 def generate_param_code(param, first_bit, num_bits, word_array, wordsize): 445 """ 446 Generate code to marshal the given parameter into the correct 447 location in the message. 448 449 'word_array' is an array of the final contents of the message. 450 word_array[k] contains what should be placed in the k'th message 451 register, and is an array of expressions that will (eventually) 452 be bitwise-or'ed into it. 453 """ 454 455 target_word = first_bit // wordsize 456 target_offset = first_bit % wordsize 457 458 # double word type 459 if param.type.double_word: 460 word_array[target_word].append( 461 param.type.double_word_expression(param.name, 0, wordsize)) 462 word_array[target_word + 463 1].append(param.type.double_word_expression(param.name, 1, wordsize)) 464 return 465 466 # Single full word? 467 if num_bits == wordsize: 468 assert target_offset == 0 469 expr = param.type.c_expression(param.name) 470 word_array[target_word].append(expr) 471 return 472 473 # Part of a word? 474 if num_bits < wordsize: 475 expr = param.type.c_expression(param.name) 476 expr = "(%s & %#x%s)" % (expr, (1 << num_bits) - 1, 477 WORD_CONST_SUFFIX_BITS[wordsize]) 478 if target_offset: 479 expr = "(%s << %d)" % (expr, target_offset) 480 word_array[target_word].append(expr) 481 return 482 483 # Multiword array 484 assert target_offset == 0 485 num_words = num_bits // wordsize 486 for i in range(num_words): 487 expr = param.type.c_expression(param.name, i, struct_members(param.type, structs)) 488 word_array[target_word + i].append(expr) 489 490 # Get their marshalling positions 491 positions = get_parameter_positions(params, wordsize) 492 493 # Generate marshal code. 494 words = [[] for _ in range(num_mrs, MAX_MESSAGE_LENGTH)] 495 for (param, first_bit, num_bits) in positions: 496 generate_param_code(param, first_bit, num_bits, words, wordsize) 497 498 # Return list of expressions. 499 return [" | ".join(x) for x in words if len(x) > 0] 500 501 502def generate_unmarshal_expressions(params, wordsize): 503 """ 504 Generate unmarshalling expressions for the given set of outputs. 505 506 We return a list of list of expressions; one list per variable, containing 507 expressions for the words in it that must be unmarshalled. The expressions 508 will have tokens of the form: 509 "%(w0)s" 510 in them, indicating a read from a word in the message. 511 """ 512 513 def unmarshal_single_param(first_bit, num_bits, wordsize): 514 """ 515 Unmarshal a single parameter. 516 """ 517 first_word = first_bit // wordsize 518 bit_offset = first_bit % wordsize 519 520 # Multiword type? 521 if num_bits > wordsize: 522 result = [] 523 for x in range(num_bits // wordsize): 524 result.append("%%(w%d)s" % (x + first_word)) 525 return result 526 527 # Otherwise, bit packed. 528 if num_bits == wordsize: 529 return ["%%(w%d)s" % first_word] 530 elif bit_offset == 0: 531 return ["(%%(w%d)s & %#x)" % ( 532 first_word, (1 << num_bits) - 1)] 533 else: 534 return ["(%%(w%d)s >> %d) & %#x" % ( 535 first_word, bit_offset, (1 << num_bits) - 1)] 536 537 # Get their marshalling positions 538 positions = get_parameter_positions(params, wordsize) 539 540 # Generate the unmarshal code. 541 results = [] 542 for (param, first_bit, num_bits) in positions: 543 results.append((param, unmarshal_single_param(first_bit, num_bits, wordsize))) 544 return results 545 546 547def is_result_struct_required(output_params): 548 return len([x for x in output_params if not x.type.pass_by_reference()]) != 0 549 550 551def generate_result_struct(interface_name, method_name, output_params): 552 """ 553 Generate a structure definition to be returned by the system call stubs to 554 the user. 555 556 We have a few constraints: 557 558 * We always need an 'error' output parameter, even though it won't 559 appear in the list 'output_params' given to us. 560 561 * Output parameters may be marked as 'pass_by_reference', indicating 562 that we only ever see pointers to the item. 563 564 If no structure is needed (i.e., we just return an error code), we return 565 'None'. 566 """ 567 568 # Do we actually need a structure? 569 if not is_result_struct_required(output_params): 570 return None 571 572 # 573 # Generate the structure: 574 # 575 # struct seL4_CNode_Copy { 576 # int error; 577 # seL4_Word foo; 578 # }; 579 # typedef struct seL4_CNode_Copy seL4_CNode_Copy_t; 580 # 581 result = [] 582 result.append("struct %s_%s {" % (interface_name, method_name)) 583 result.append("\tint error;") 584 for i in output_params: 585 if not i.type.pass_by_reference(): 586 result.append("\t%s;" % i.type.render_parameter_name(i.name)) 587 result.append("};") 588 result.append("typedef struct %s_%s %s_%s_t;" % ( 589 (interface_name, method_name, interface_name, method_name))) 590 result.append("") 591 592 return "\n".join(result) 593 594 595def generate_stub(arch, wordsize, interface_name, method_name, method_id, input_params, output_params, structs, use_only_ipc_buffer, comment, mcs): 596 result = [] 597 598 if use_only_ipc_buffer: 599 num_mrs = 0 600 else: 601 if mcs and "%s-mcs" % arch in MESSAGE_REGISTERS_FOR_ARCH: 602 num_mrs = MESSAGE_REGISTERS_FOR_ARCH["%s-mcs" % arch] 603 else: 604 num_mrs = MESSAGE_REGISTERS_FOR_ARCH[arch] 605 606 # Split out cap parameters and standard parameters 607 standard_params = [] 608 cap_params = [] 609 for x in input_params: 610 if isinstance(x.type, CapType): 611 cap_params.append(x) 612 else: 613 standard_params.append(x) 614 615 # Determine if we are returning a structure, or just the error code. 616 returning_struct = False 617 results_structure = generate_result_struct(interface_name, method_name, output_params) 618 if results_structure: 619 return_type = "%s_%s_t" % (interface_name, method_name) 620 returning_struct = True 621 else: 622 return_type = "seL4_Error" 623 624 # 625 # Print doxygen comment. 626 # 627 result.append(comment) 628 629 # 630 # Print function header. 631 # 632 # static inline int 633 # seL4_Untyped_Retype(...) 634 # { 635 # 636 result.append("LIBSEL4_INLINE %s" % return_type) 637 result.append("%s_%s(%s)" % (interface_name, method_name, 638 generate_param_list(input_params, output_params))) 639 result.append("{") 640 641 # 642 # Get a list of expressions for our caps and inputs. 643 # 644 input_expressions = generate_marshal_expressions(standard_params, num_mrs, 645 structs, wordsize) 646 cap_expressions = [x.name for x in cap_params] 647 service_cap = cap_expressions[0] 648 cap_expressions = cap_expressions[1:] 649 650 # 651 # Compute how many words the inputs and output will require. 652 # 653 input_param_words = len(input_expressions) 654 output_param_words = sum([p.type.size_bits for p in output_params]) / wordsize 655 656 # 657 # Setup variables we will need. 658 # 659 result.append("\t%s result;" % return_type) 660 result.append("\tseL4_MessageInfo_t tag = seL4_MessageInfo_new(%s, 0, %d, %d);" % 661 (method_id, len(cap_expressions), len(input_expressions))) 662 result.append("\tseL4_MessageInfo_t output_tag;") 663 for i in range(num_mrs): 664 result.append("\tseL4_Word mr%d;" % i) 665 result.append("") 666 667 # 668 # Copy capabilities. 669 # 670 # /* Setup input capabilities. */ 671 # seL4_SetCap(i, cap); 672 # 673 if len(cap_expressions) > 0: 674 result.append("\t/* Setup input capabilities. */") 675 for i in range(len(cap_expressions)): 676 result.append("\tseL4_SetCap(%d, %s);" % (i, cap_expressions[i])) 677 result.append("") 678 679 # 680 # Copy in the inputs. 681 # 682 # /* Marshal input parameters. */ 683 # seL4_SetMR(i, v); 684 # ... 685 # 686 if max(num_mrs, len(input_expressions)) > 0: 687 result.append("\t/* Marshal and initialise parameters. */") 688 # Initialise in-register parameters 689 for i in range(num_mrs): 690 if i < len(input_expressions): 691 result.append("\tmr%d = %s;" % (i, input_expressions[i])) 692 else: 693 result.append("\tmr%d = 0;" % i) 694 # Initialise buffered parameters 695 for i in range(num_mrs, len(input_expressions)): 696 result.append("\tseL4_SetMR(%d, %s);" % (i, input_expressions[i])) 697 result.append("") 698 699 # 700 # Generate the call. 701 # 702 if use_only_ipc_buffer: 703 result.append("\t/* Perform the call. */") 704 result.append("\toutput_tag = seL4_Call(%s, tag);" % service_cap) 705 else: 706 result.append("\t/* Perform the call, passing in-register arguments directly. */") 707 result.append("\toutput_tag = seL4_CallWithMRs(%s, tag," % (service_cap)) 708 result.append("\t\t%s);" % ', '.join( 709 ("&mr%d" % i) for i in range(num_mrs))) 710 711 # 712 # Prepare the result. 713 # 714 label = "result.error" if returning_struct else "result" 715 cast = " (%s)" % return_type if not returning_struct else "" 716 result.append("\t%s =%s seL4_MessageInfo_get_label(output_tag);" % (label, cast)) 717 result.append("") 718 719 if not use_only_ipc_buffer: 720 result.append("\t/* Unmarshal registers into IPC buffer on error. */") 721 result.append("\tif (%s != seL4_NoError) {" % label) 722 for i in range(num_mrs): 723 result.append("\t\tseL4_SetMR(%d, mr%d);" % (i, i)) 724 result.append("#ifdef CONFIG_KERNEL_INVOCATION_REPORT_ERROR_IPC") 725 result.append("\t\tif (seL4_CanPrintError()) {") 726 result.append("\t\t\tseL4_DebugPutString(seL4_GetDebugError());") 727 result.append("\t\t}") 728 result.append("#endif") 729 if returning_struct: 730 result.append("\t\treturn result;") 731 result.append("\t}") 732 result.append("") 733 734 # 735 # Generate unmarshalling code. 736 # 737 if len(output_params) > 0: 738 result.append("\t/* Unmarshal result. */") 739 source_words = {} 740 for i in range(MAX_MESSAGE_LENGTH): 741 if i < num_mrs: 742 source_words["w%d" % i] = "mr%d" % i 743 else: 744 source_words["w%d" % i] = "seL4_GetMR(%d)" % i 745 unmashalled_params = generate_unmarshal_expressions(output_params, wordsize) 746 for (param, words) in unmashalled_params: 747 if param.type.pass_by_reference(): 748 members = struct_members(param.type, structs) 749 for i in range(len(words)): 750 result.append("\t%s->%s = %s;" % 751 (param.name, members[i], words[i] % source_words)) 752 else: 753 if param.type.double_word: 754 result.append("\tresult.%s = ((%s)%s + ((%s)%s << 32));" % 755 (param.name, TYPES[64], words[0] % source_words, 756 TYPES[64], words[1] % source_words)) 757 else: 758 for word in words: 759 result.append("\tresult.%s = %s;" % (param.name, word % source_words)) 760 761 # 762 # } 763 # 764 result.append("\treturn result;") 765 result.append("}") 766 767 return "\n".join(result) + "\n" 768 769 770def get_xml_element_contents(element): 771 """ 772 Converts the contents of an xml element into a string, with all 773 child xml nodes unchanged. 774 """ 775 return "".join([c.toxml() for c in element.childNodes]) 776 777 778def get_xml_element_content_with_xmlonly(element): 779 """ 780 Converts the contents of an xml element into a string, wrapping 781 all child xml nodes in doxygen @xmlonly/@endxmlonly keywords. 782 """ 783 784 result = [] 785 prev_element = False 786 for node in element.childNodes: 787 if node.nodeType == xml.dom.Node.TEXT_NODE: 788 if prev_element: 789 # text node following element node 790 result.append(" @endxmlonly ") 791 prev_element = False 792 else: 793 if not prev_element: 794 # element node following text node 795 result.append(" @xmlonly ") 796 prev_element = True 797 798 result.append(node.toxml()) 799 800 return "".join(result) 801 802 803def normalise_text(text): 804 """ 805 Removes leading and trailing whitespace from each line of text. 806 Removes leading and trailing blank lines from text. 807 """ 808 stripped = text.strip() 809 stripped_lines = [line.strip() for line in text.split("\n")] 810 # remove leading and trailing empty lines 811 stripped_head = list(itertools.dropwhile(lambda s: not s, stripped_lines)) 812 stripped_tail = itertools.dropwhile(lambda s: not s, reversed(stripped_head)) 813 return "\n".join(reversed(list(stripped_tail))) 814 815 816def parse_xml_file(input_file, valid_types): 817 """ 818 Parse an XML file containing method definitions. 819 """ 820 821 # Create a dictionary of type name to type. 822 type_names = {} 823 for i in valid_types: 824 type_names[i.name] = i 825 826 # Parse the XML to generate method structures. 827 methods = [] 828 structs = [] 829 doc = xml.dom.minidom.parse(input_file) 830 831 api = Api(doc.getElementsByTagName("api")[0]) 832 833 for struct in doc.getElementsByTagName("struct"): 834 _struct_members = [] 835 struct_name = struct.getAttribute("name") 836 for members in struct.getElementsByTagName("member"): 837 member_name = members.getAttribute("name") 838 _struct_members.append(member_name) 839 structs.append((struct_name, _struct_members)) 840 841 for interface in doc.getElementsByTagName("interface"): 842 interface_name = interface.getAttribute("name") 843 interface_manual_name = interface.getAttribute("manual_name") or interface_name 844 845 interface_cap_description = interface.getAttribute("cap_description") 846 847 for method in interface.getElementsByTagName("method"): 848 method_name = method.getAttribute("name") 849 method_id = method.getAttribute("id") 850 method_condition = method.getAttribute("condition") 851 method_manual_name = method.getAttribute("manual_name") or method_name 852 method_manual_label = method.getAttribute("manual_label") 853 854 if not method_manual_label: 855 # If no manual label is specified, infer one from the interface and method 856 # names by combining the interface name and method name. 857 method_manual_label = ("%s_%s" % (interface_manual_name, method_manual_name)) \ 858 .lower() \ 859 .replace(" ", "_") \ 860 .replace("/", "") 861 862 # Prefix the label with an api-wide label prefix 863 method_manual_label = "%s%s" % (api.label_prefix, method_manual_label) 864 865 comment_lines = ["@xmlonly <manual name=\"%s\" label=\"%s\"/> @endxmlonly" % 866 (method_manual_name, method_manual_label)] 867 868 method_brief = method.getElementsByTagName("brief") 869 if method_brief: 870 method_brief_text = get_xml_element_contents(method_brief[0]) 871 normalised_method_brief_text = normalise_text(method_brief_text) 872 comment_lines.append("@brief @xmlonly %s @endxmlonly" % 873 normalised_method_brief_text) 874 875 method_description = method.getElementsByTagName("description") 876 if method_description: 877 method_description_text = get_xml_element_contents(method_description[0]) 878 normalised_method_description_text = normalise_text(method_description_text) 879 comment_lines.append("\n@xmlonly\n%s\n@endxmlonly\n" % 880 normalised_method_description_text) 881 882 # 883 # Get parameters. 884 # 885 # We always have an implicit cap parameter. 886 # 887 input_params = [Parameter("_service", type_names[interface_name])] 888 889 cap_description = interface_cap_description 890 cap_param = method.getElementsByTagName("cap_param") 891 if cap_param: 892 append_description = cap_param[0].getAttribute("append_description") 893 if append_description: 894 cap_description += append_description 895 896 comment_lines.append("@param[in] _service %s" % cap_description) 897 output_params = [] 898 for param in method.getElementsByTagName("param"): 899 param_name = param.getAttribute("name") 900 param_type = type_names.get(param.getAttribute("type")) 901 if not param_type: 902 raise Exception("Unknown type '%s'." % (param.getAttribute("type"))) 903 param_dir = param.getAttribute("dir") 904 assert (param_dir == "in") or (param_dir == "out") 905 if param_dir == "in": 906 input_params.append(Parameter(param_name, param_type)) 907 else: 908 output_params.append(Parameter(param_name, param_type)) 909 910 if param_dir == "in" or param_type.pass_by_reference(): 911 param_description = param.getAttribute("description") 912 if not param_description: 913 param_description_element = param.getElementsByTagName("description") 914 if param_description_element: 915 param_description_text = get_xml_element_content_with_xmlonly( 916 param_description_element[0]) 917 param_description = normalise_text(param_description_text) 918 919 comment_lines.append("@param[%s] %s %s " % 920 (param_dir, param_name, param_description)) 921 922 method_return_description = method.getElementsByTagName("return") 923 if method_return_description: 924 comment_lines.append("@return @xmlonly %s @endxmlonly" % 925 get_xml_element_contents(method_return_description[0])) 926 else: 927 # no return documentation given - default to something sane 928 if is_result_struct_required(output_params): 929 comment_lines.append("@return @xmlonly @endxmlonly") 930 else: 931 comment_lines.append("@return @xmlonly <errorenumdesc/> @endxmlonly") 932 933 for error in method.getElementsByTagName("error"): 934 error_name = error.getAttribute("name") 935 error_description = error.getAttribute("description") 936 if not error_description: 937 error_description_element = error.getElementsByTagName("description") 938 if error_description_element: 939 error_description_text = get_xml_element_content_with_xmlonly( 940 error_description_element[0]) 941 error_description = normalise_text(error_description_text) 942 comment_lines.append("@retval %s %s " % (error_name, error_description)) 943 944 # split each line on newlines 945 comment_lines = reduce(operator.add, [l.split("\n") for l in comment_lines], []) 946 947 # place the comment text in a c comment 948 comment = "\n".join(["/**"] + [" * %s" % l for l in comment_lines] + [" */"]) 949 950 methods.append((interface_name, method_name, method_id, input_params, 951 output_params, method_condition, comment)) 952 953 return (methods, structs, api) 954 955 956def generate_stub_file(arch, wordsize, input_files, output_file, use_only_ipc_buffer, mcs): 957 """ 958 Generate a header file containing system call stubs for seL4. 959 """ 960 result = [] 961 962 # Ensure architecture looks sane. 963 if arch not in WORD_SIZE_BITS_ARCH.keys(): 964 raise Exception("Invalid architecture.") 965 966 data_types = init_data_types(wordsize) 967 arch_types = init_arch_types(wordsize) 968 969 # Parse XML 970 methods = [] 971 structs = [] 972 for infile in input_files: 973 method, struct, _ = parse_xml_file(infile, data_types + arch_types[arch]) 974 methods += method 975 structs += struct 976 977 # Print header. 978 result.append(""" 979/* 980 * Automatically generated system call stubs. 981 */ 982 983#ifndef __LIBSEL4_SEL4_CLIENT_H 984#define __LIBSEL4_SEL4_CLIENT_H 985""") 986 987 # Emit the includes 988 result.append('\n'.join(['#include <%s>' % include for include in INCLUDES])) 989 990 # 991 # Emit code to ensure that all of our type sizes are consistent with 992 # the compiler's. 993 # 994 result.append(""" 995/* 996 * The following code generates a compile-time error if the system call 997 * stub generator has an incorrect understanding of how large a type is. 998 * 999 * If you receive a compile-time error here, you will need to adjust 1000 * the type information in the stub generator. 1001 */ 1002#define assert_size_correct(type, expected_bytes) \\ 1003 typedef unsigned long __type_##type##_size_incorrect[ \\ 1004 (sizeof(type) == expected_bytes) ? 1 : -1] 1005""") 1006 for x in data_types + arch_types[arch]: 1007 result.append("assert_size_correct(%s, %d);" % (x.name, x.native_size_bits / 8)) 1008 result.append("") 1009 1010 # 1011 # Generate structures needed to return results back to the user. 1012 # 1013 # We can not use pass-by-reference (except for really large objects), as 1014 # the verification framework does not support them. 1015 # 1016 result.append("/*") 1017 result.append(" * Return types for generated methods.") 1018 result.append(" */") 1019 for (interface_name, method_name, _, _, output_params, _, _) in methods: 1020 results_structure = generate_result_struct(interface_name, method_name, output_params) 1021 if results_structure: 1022 result.append(results_structure) 1023 1024 # 1025 # Generate the actual stub code. 1026 # 1027 result.append("/*") 1028 result.append(" * Generated stubs.") 1029 result.append(" */") 1030 for (interface_name, method_name, method_id, inputs, outputs, condition, comment) in methods: 1031 if condition != "": 1032 result.append("#if %s" % condition) 1033 result.append(generate_stub(arch, wordsize, interface_name, method_name, 1034 method_id, inputs, outputs, structs, use_only_ipc_buffer, comment, mcs)) 1035 if condition != "": 1036 result.append("#endif") 1037 1038 # Print footer. 1039 result.append("#endif /* __LIBSEL4_SEL4_CLIENT_H */") 1040 result.append("") 1041 1042 # Write the output 1043 output = open(output_file, "w") 1044 output.write("\n".join(result)) 1045 output.close() 1046 1047 1048def process_args(): 1049 usage_str = """ 1050 %(prog)s [OPTIONS] [FILES] """ 1051 epilog_str = """ 1052 1053 """ 1054 parser = ArgumentParser(description='seL4 System Call Stub Generator.', 1055 usage=usage_str, 1056 epilog=epilog_str) 1057 parser.add_argument("-o", "--output", dest="output", default="/dev/stdout", 1058 help="Output file to write stub to. (default: %(default)s).") 1059 parser.add_argument("-b", "--buffer", dest="buffer", action="store_true", default=False, 1060 help="Use IPC buffer exclusively, i.e. do not pass syscall arguments by registers. (default: %(default)s)") 1061 parser.add_argument("-a", "--arch", dest="arch", required=True, choices=WORD_SIZE_BITS_ARCH, 1062 help="Architecture to generate stubs for.") 1063 parser.add_argument("--mcs", dest="mcs", action="store_true", 1064 help="Generate MCS api.") 1065 1066 wsizegroup = parser.add_mutually_exclusive_group() 1067 wsizegroup.add_argument("-w", "--word-size", dest="wsize", 1068 help="Word size(in bits), for the platform.") 1069 wsizegroup.add_argument("-c", "--cfile", dest="cfile", 1070 help="Config file for Kbuild, used to get Word size.") 1071 1072 parser.add_argument("files", metavar="FILES", nargs="+", 1073 help="Input XML files.") 1074 1075 return parser 1076 1077 1078def main(): 1079 1080 parser = process_args() 1081 args = parser.parse_args() 1082 1083 if not (args.wsize or args.cfile): 1084 parser.error("Require either -w/--word-size or -c/--cfile argument.") 1085 sys.exit(2) 1086 1087 # Get word size 1088 wordsize = -1 1089 1090 if args.cfile: 1091 try: 1092 with open(args.cfile) as conffile: 1093 for line in conffile: 1094 if line.startswith('CONFIG_WORD_SIZE'): 1095 wordsize = int(line.split('=')[1].strip()) 1096 except IndexError: 1097 print("Invalid word size in configuration file.") 1098 sys.exit(2) 1099 else: 1100 wordsize = int(args.wsize) 1101 1102 if wordsize == -1: 1103 print("Invalid word size.") 1104 sys.exit(2) 1105 1106 # Generate the stubs. 1107 generate_stub_file(args.arch, wordsize, args.files, args.output, args.buffer, args.mcs) 1108 1109 1110if __name__ == "__main__": 1111 sys.exit(main()) 1112