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