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