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