• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/sh
2"." "`dirname $0`/../../../../../trusty/vendor/google/aosp/scripts/envsetup.sh"
3"exec" "$PY3" "$0" "$@"
4
5# Copyright 2013-2017 Google Inc. +All rights reserved.
6#
7# Permission is hereby granted, free of charge, to any person obtaining
8# a copy of this software and associated documentation files
9# (the "Software"), to deal in the Software without restriction,
10# including without limitation the rights to use, copy, modify, merge,
11# publish, distribute, sublicense, and/or sell copies of the Software,
12# and to permit persons to whom the Software is furnished to do so,
13# subject to the following conditions:
14#
15# The above copyright notice and this permission notice shall be
16# included in all copies or substantial portions of the Software.
17#
18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26import re
27import sys
28from optparse import OptionParser
29
30"""
31Script to generate syscall stubs from a syscall table file
32with definitions like this:
33
34DEF_SYSCALL(nr_syscall, syscall_name, return_type, nr_args, arg_list...)
35
36For e.g.,
37DEF_SYSCALL(0x3, read, 3, int fd, void *buf, int size)
38DEF_SYSCALL(0x4, write, 4, int fd, void *buf, int size)
39
40FUNCTION(read)
41    ldr     r12, =__NR_read
42    swi     #0
43    bx      lr
44
45FUNCTION(write)
46    ldr     r12, =__NR_write
47    swi     #0
48    bx      lr
49
50Another file with a enumeration of all syscalls is also generated:
51
52#define __NR_read  0x3
53#define __NR_write 0x4
54...
55
56
57Only syscalls with 4 or less arguments are supported.
58"""
59
60copyright_header = """/*
61 * Copyright (c) 2012-2018 LK Trusty Authors. All Rights Reserved.
62 *
63 * Permission is hereby granted, free of charge, to any person obtaining
64 * a copy of this software and associated documentation files
65 * (the "Software"), to deal in the Software without restriction,
66 * including without limitation the rights to use, copy, modify, merge,
67 * publish, distribute, sublicense, and/or sell copies of the Software,
68 * and to permit persons to whom the Software is furnished to do so,
69 * subject to the following conditions:
70 *
71 * The above copyright notice and this permission notice shall be
72 * included in all copies or substantial portions of the Software.
73 *
74 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
75 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
76 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
77 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
78 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
79 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
80 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
81 */
82"""
83
84autogen_header = """
85/* This file is auto-generated. !!! DO NOT EDIT !!! */
86
87"""
88clang_format_off = "/* clang-format off */\n\n"
89
90
91includes_header = "#include <%s>\n"
92
93
94class Architecture:
95    def __init__(self, syscall_stub, footer=""):
96        self.syscall_stub = syscall_stub
97        self.footer = footer
98
99arch_dict = {
100    "arm" : Architecture (
101        syscall_stub = """
102.section .text._trusty_%(sys_fn)s
103.arm
104.balign 4
105FUNCTION(_trusty_%(sys_fn)s)
106    ldr     r12, =__NR_%(sys_fn)s
107    svc     #0
108    bx      lr
109.size _trusty_%(sys_fn)s,.-_trusty_%(sys_fn)s
110"""),
111    "arm64" : Architecture (
112        # Note: for arm64 we're using "mov" to set the syscall number instead of
113        # "ldr" because of an assembler bug. The ldr instruction would always
114        # load from a constant pool instead of encoding the constant in the
115        # instruction. For arm64, "mov" should work for the range of constants
116        # we care about.
117        syscall_stub = """
118.section .text._trusty_%(sys_fn)s
119.balign 4
120FUNCTION(_trusty_%(sys_fn)s)
121    mov     x12, #__NR_%(sys_fn)s
122    svc     #0
123    ret
124.size _trusty_%(sys_fn)s,.-_trusty_%(sys_fn)s
125""",
126        footer = """
127SECTION_GNU_NOTE_PROPERTY_AARCH64_FEATURES(GNU_NOTE_FEATURE_AARCH64_BTI)
128"""),
129    "x86" : Architecture (
130        syscall_stub = """
131.global _trusty_%(sys_fn)s
132.type _trusty_%(sys_fn)s,STT_FUNC
133_trusty_%(sys_fn)s:
134    pushfq
135    pushq %%rbp
136    pushq %%rbx
137    pushq %%r15
138    movq $__NR_%(sys_fn)s, %%rax
139    leaq .L%(sys_fn)s_sysreturn(%%rip), %%rbx
140    movq %%rsp, %%rbp
141    sysenter
142.L%(sys_fn)s_sysreturn:
143    popq %%r15
144    popq %%rbx
145    popq %%rbp
146    popfq
147    ret
148.size _trusty_%(sys_fn)s,.-_trusty_%(sys_fn)s
149"""),
150}
151
152syscall_define = "#define __NR_%(sys_fn)s\t\t%(sys_nr)s\n"
153
154syscall_proto = "%(sys_rt)s _trusty_%(sys_fn)s(%(sys_args)s);\n"
155
156asm_ifdef = "\n#ifndef ASSEMBLY\n"
157asm_endif = "\n#endif\n"
158
159beg_cdecls = "\n__BEGIN_CDECLS\n"
160end_cdecls = "\n__END_CDECLS\n"
161
162syscall_def = "DEF_SYSCALL"
163
164syscall_pat = (
165    r'DEF_SYSCALL\s*\('
166    r'\s*(?P<sys_nr>\d+|0x[\da-fA-F]+)\s*,'      # syscall nr
167    r'\s*(?P<sys_fn>\w+)\s*,'                    # syscall name
168    r'\s*(?P<sys_rt>[\w*\s]+)\s*,'               # return type
169    r'\s*(?P<sys_nr_args>\d+)\s*'                # nr ags
170    r'('
171    r'\)\s*$|'                                   # empty arg list or
172    r',\s*(?P<sys_args>[\w,*\s]+)'               # arg list
173    r'\)\s*$'
174    r')')
175
176syscall_re = re.compile(syscall_pat)
177
178syscall_rust_proto = '    pub fn _trusty_%(sys_fn)s(%(rust_args)s) -> %(rust_rt)s;\n'
179beg_rust = 'extern "C" {\n'
180end_rust = '}\n'
181
182def fatal_parse_error(line, err_str):
183    sys.stderr.write("Error processing line %r:\n%s\n" % (line, err_str))
184    sys.exit(2)
185
186
187BUILTIN_TYPES = set(['char', 'int', 'long', 'void'])
188for i in [8, 16, 32, 64]:
189    BUILTIN_TYPES.add('int%d_t' % i)
190    BUILTIN_TYPES.add('uint%d_t' % i)
191
192def reformat_c_to_rust(arg):
193    """Reformat a C-style argument into corresponding Rust argument
194
195    Raises:
196        NotImplementedError: If argument type was too complex to reformat.
197    """
198    m = re.match(r"(const )?(struct )?(.*?)\s*( ?\* ?)?$", arg)
199    is_const = m.group(1) is not None
200    ty = m.group(3)
201    is_ptr = m.group(4) is not None
202    rust_arg = ''
203    if '*' in ty:
204        raise NotImplementedError("Rust arg reformatting needs to be extended "
205                                  f"to handle double indirection in arg: {arg}")
206    if is_ptr:
207        rust_arg += '*%s ' % ('const' if is_const else 'mut')
208    rust_arg += ty
209    return rust_arg
210
211def parse_check_def(line, struct_types):
212    """
213    Parse a DEF_SYSCALL line and check for errors
214    Returns various components from the line.
215    """
216
217    m = syscall_re.match(line)
218    if m is None:
219        fatal_parse_error(line, "Line did not match expected pattern.")
220    gd = m.groupdict()
221
222    sys_nr_args = int(gd['sys_nr_args'])
223    sys_args = gd['sys_args']
224    sys_args_list = re.split(r'\s*,\s*', sys_args) if sys_args else []
225
226    if sys_nr_args > 4:
227        fatal_parse_error(line, "Only syscalls with up to 4 arguments are "
228                          "supported.")
229
230    if sys_nr_args != len(sys_args_list):
231        fatal_parse_error(line, "Expected %d syscall arguments, got %d." %
232                          (sys_nr_args, len(sys_args_list)))
233
234    # Find struct types in the arguments.
235    for arg in sys_args_list:
236        # Remove arg name.
237        arg = re.sub(r"\s*\w+$", "", arg)
238        # Remove trailing pointer.
239        arg = re.sub(r"\s*\*$", "", arg)
240        # Remove initial const.
241        arg = re.sub(r"^const\s+", "", arg)
242        # Ignore the type if it's obviously not a struct.
243        if arg in BUILTIN_TYPES:
244            continue
245        # Require explicit struct declarations, because forward declaring
246        # typedefs is tricky.
247        if not arg.startswith("struct "):
248            fatal_parse_error(line, "Not an integer type or explicit struct "
249                              "type: %r. Don't use typedefs." % arg)
250        struct_types.add(arg)
251
252    # Reformat arguments into Rust syntax
253    rust_args = []
254    try:
255        for arg in sys_args_list:
256            m = re.match(r"(.*?)(\w+)$", arg)
257            ty = m.group(1)
258            name = m.group(2)
259            rust_args.append('%s: %s' % (name, reformat_c_to_rust(ty)))
260        gd['rust_args'] = ', '.join(rust_args)
261        gd['rust_rt'] = reformat_c_to_rust(gd['sys_rt'])
262    except NotImplementedError as err:
263        # reformat_c_to_rust failed to convert argument or return type
264        fatal_parse_error(line, err)
265
266    # In C, a forward declaration with an empty list of arguments has an
267    # unknown number of arguments. Set it to 'void' to declare there are
268    # zero arguments.
269    if sys_nr_args == 0:
270        gd['sys_args'] = 'void'
271
272    return gd
273
274
275def process_table(table_file, std_file, stubs_file, rust_file, verify, arch):
276    """
277    Process a syscall table and generate:
278    1. A sycall stubs file
279    2. A trusty_std.h header file with syscall definitions
280       and function prototypes
281    """
282    define_lines = ""
283    proto_lines = "\n"
284    stub_lines = ""
285    rust_lines = ""
286
287    struct_types = set()
288
289    tbl = open(table_file, "r")
290    for line in tbl:
291        line = line.strip()
292
293        # skip all lines that don't start with a syscall definition
294        # multi-line defintions are not supported.
295        if not line.startswith(syscall_def):
296            continue
297
298        params = parse_check_def(line, struct_types)
299
300        if not verify:
301            define_lines += syscall_define % params
302            proto_lines += syscall_proto % params
303            stub_lines += arch.syscall_stub % params
304            rust_lines += syscall_rust_proto % params
305
306
307    tbl.close()
308
309    if verify:
310        return
311
312    if std_file is not None:
313        with open(std_file, "w") as std:
314            std.writelines(copyright_header + autogen_header)
315            std.writelines(clang_format_off)
316            std.writelines(define_lines + asm_ifdef)
317            std.writelines("\n")
318            std.writelines(includes_header % "lk/compiler.h")
319            std.writelines(includes_header % "stdint.h")
320            std.writelines(beg_cdecls)
321            # Forward declare the struct types.
322            std.writelines("\n")
323            std.writelines([t + ";\n" for t in sorted(struct_types)])
324            std.writelines(proto_lines + end_cdecls + asm_endif)
325
326    if stubs_file is not None:
327        with open(stubs_file, "w") as stubs:
328            stubs.writelines(copyright_header + autogen_header)
329            stubs.writelines(includes_header % "lk/asm.h")
330            stubs.writelines(includes_header % "trusty_syscalls.h")
331            stubs.writelines(stub_lines)
332            stubs.writelines(arch.footer)
333
334    if rust_file is not None:
335        with open(rust_file, "w") as rust:
336            rust.writelines(copyright_header + autogen_header)
337            rust.writelines(beg_rust)
338            rust.writelines(rust_lines)
339            rust.writelines(end_rust)
340
341
342def main():
343
344    usage = "usage:  %prog [options] <syscall-table>"
345
346    op = OptionParser(usage=usage)
347    op.add_option("-v", "--verify", action="store_true",
348            dest="verify", default=False,
349            help="Check syscall table. Do not generate any files.")
350    op.add_option("-d", "--std-header", type="string",
351            dest="std_file", default=None,
352            help="path to syscall defintions header file.")
353    op.add_option("-s", "--stubs-file", type="string",
354            dest="stub_file", default=None,
355            help="path to syscall assembly stubs file.")
356    op.add_option("-r", "--rust-file", type="string",
357            dest="rust_file", default=None,
358            help="path to rust declarations file")
359    op.add_option("-a", "--arch", type="string",
360            dest="arch", default="arm",
361            help="arch of stub assembly files: " + str(arch_dict.keys()))
362
363    (opts, args) = op.parse_args()
364
365    if len(args) == 0:
366        op.print_help()
367        sys.exit(1)
368
369    if not opts.verify:
370        if opts.std_file is None and opts.stub_file is None:
371            op.print_help()
372            sys.exit(1)
373
374    process_table(args[0], opts.std_file, opts.stub_file, opts.rust_file,
375                  opts.verify, arch_dict[opts.arch])
376
377
378if __name__ == '__main__':
379    main()
380