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