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            # split each line on newlines
934            comment_lines = reduce(operator.add, [l.split("\n") for l in comment_lines], [])
935
936            # place the comment text in a c comment
937            comment = "\n".join(["/**"] + [" * %s" % l for l in comment_lines] + [" */"])
938
939            methods.append((interface_name, method_name, method_id, input_params,
940                            output_params, method_condition, comment))
941
942    return (methods, structs, api)
943
944
945def generate_stub_file(arch, wordsize, input_files, output_file, use_only_ipc_buffer, mcs):
946    """
947    Generate a header file containing system call stubs for seL4.
948    """
949    result = []
950
951    # Ensure architecture looks sane.
952    if arch not in WORD_SIZE_BITS_ARCH.keys():
953        raise Exception("Invalid architecture.")
954
955    data_types = init_data_types(wordsize)
956    arch_types = init_arch_types(wordsize)
957
958    # Parse XML
959    methods = []
960    structs = []
961    for infile in input_files:
962        method, struct, _ = parse_xml_file(infile, data_types + arch_types[arch])
963        methods += method
964        structs += struct
965
966    # Print header.
967    result.append("""
968/*
969 * Automatically generated system call stubs.
970 */
971
972#ifndef __LIBSEL4_SEL4_CLIENT_H
973#define __LIBSEL4_SEL4_CLIENT_H
974""")
975
976    # Emit the includes
977    result.append('\n'.join(['#include <%s>' % include for include in INCLUDES]))
978
979    #
980    # Emit code to ensure that all of our type sizes are consistent with
981    # the compiler's.
982    #
983    result.append("""
984/*
985 * The following code generates a compile-time error if the system call
986 * stub generator has an incorrect understanding of how large a type is.
987 *
988 * If you receive a compile-time error here, you will need to adjust
989 * the type information in the stub generator.
990 */
991#define assert_size_correct(type, expected_bytes) \\
992        typedef unsigned long __type_##type##_size_incorrect[ \\
993                (sizeof(type) == expected_bytes) ? 1 : -1]
994""")
995    for x in data_types + arch_types[arch]:
996        result.append("assert_size_correct(%s, %d);" % (x.name, x.native_size_bits / 8))
997    result.append("")
998
999    #
1000    # Generate structures needed to return results back to the user.
1001    #
1002    # We can not use pass-by-reference (except for really large objects), as
1003    # the verification framework does not support them.
1004    #
1005    result.append("/*")
1006    result.append(" * Return types for generated methods.")
1007    result.append(" */")
1008    for (interface_name, method_name, _, _, output_params, _, _) in methods:
1009        results_structure = generate_result_struct(interface_name, method_name, output_params)
1010        if results_structure:
1011            result.append(results_structure)
1012
1013    #
1014    # Generate the actual stub code.
1015    #
1016    result.append("/*")
1017    result.append(" * Generated stubs.")
1018    result.append(" */")
1019    for (interface_name, method_name, method_id, inputs, outputs, condition, comment) in methods:
1020        if condition != "":
1021            result.append("#if %s" % condition)
1022        result.append(generate_stub(arch, wordsize, interface_name, method_name,
1023                                    method_id, inputs, outputs, structs, use_only_ipc_buffer, comment, mcs))
1024        if condition != "":
1025            result.append("#endif")
1026
1027    # Print footer.
1028    result.append("#endif /* __LIBSEL4_SEL4_CLIENT_H */")
1029    result.append("")
1030
1031    # Write the output
1032    output = open(output_file, "w")
1033    output.write("\n".join(result))
1034    output.close()
1035
1036
1037def process_args():
1038    usage_str = """
1039    %(prog)s [OPTIONS] [FILES] """
1040    epilog_str = """
1041
1042    """
1043    parser = ArgumentParser(description='seL4 System Call Stub Generator.',
1044                            usage=usage_str,
1045                            epilog=epilog_str)
1046    parser.add_argument("-o", "--output", dest="output", default="/dev/stdout",
1047                        help="Output file to write stub to. (default: %(default)s).")
1048    parser.add_argument("-b", "--buffer", dest="buffer", action="store_true", default=False,
1049                        help="Use IPC buffer exclusively, i.e. do not pass syscall arguments by registers. (default: %(default)s)")
1050    parser.add_argument("-a", "--arch", dest="arch", required=True, choices=WORD_SIZE_BITS_ARCH,
1051                        help="Architecture to generate stubs for.")
1052    parser.add_argument("--mcs", dest="mcs", action="store_true",
1053                        help="Generate MCS api.")
1054
1055    wsizegroup = parser.add_mutually_exclusive_group()
1056    wsizegroup.add_argument("-w", "--word-size", dest="wsize",
1057                            help="Word size(in bits), for the platform.")
1058    wsizegroup.add_argument("-c", "--cfile", dest="cfile",
1059                            help="Config file for Kbuild, used to get Word size.")
1060
1061    parser.add_argument("files", metavar="FILES", nargs="+",
1062                        help="Input XML files.")
1063
1064    return parser
1065
1066
1067def main():
1068
1069    parser = process_args()
1070    args = parser.parse_args()
1071
1072    if not (args.wsize or args.cfile):
1073        parser.error("Require either -w/--word-size or -c/--cfile argument.")
1074        sys.exit(2)
1075
1076    # Get word size
1077    wordsize = -1
1078
1079    if args.cfile:
1080        try:
1081            with open(args.cfile) as conffile:
1082                for line in conffile:
1083                    if line.startswith('CONFIG_WORD_SIZE'):
1084                        wordsize = int(line.split('=')[1].strip())
1085        except IndexError:
1086            print("Invalid word size in configuration file.")
1087            sys.exit(2)
1088    else:
1089        wordsize = int(args.wsize)
1090
1091    if wordsize == -1:
1092        print("Invalid word size.")
1093        sys.exit(2)
1094
1095    # Generate the stubs.
1096    generate_stub_file(args.arch, wordsize, args.files, args.output, args.buffer, args.mcs)
1097
1098
1099if __name__ == "__main__":
1100    sys.exit(main())
1101