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 pushq %%r15 135 movq $__NR_%(sys_fn)s, %%rax 136 movq %%rcx, %%r10 137 movq %%rsp, %%r15 138 syscall 139 movq %%r15, %%rsp 140 popq %%r15 141 ret 142.size _trusty_%(sys_fn)s,.-_trusty_%(sys_fn)s 143"""), 144} 145 146syscall_define = "#define __NR_%(sys_fn)s\t\t%(sys_nr)s\n" 147 148syscall_proto = "%(sys_rt)s _trusty_%(sys_fn)s(%(sys_args)s);\n" 149 150asm_ifdef = "\n#ifndef ASSEMBLY\n" 151asm_endif = "\n#endif\n" 152 153beg_cdecls = "\n__BEGIN_CDECLS\n" 154end_cdecls = "\n__END_CDECLS\n" 155 156syscall_def = "DEF_SYSCALL" 157 158syscall_pat = ( 159 r'DEF_SYSCALL\s*\(' 160 r'\s*(?P<sys_nr>\d+|0x[\da-fA-F]+)\s*,' # syscall nr 161 r'\s*(?P<sys_fn>\w+)\s*,' # syscall name 162 r'\s*(?P<sys_rt>[\w*\s]+)\s*,' # return type 163 r'\s*(?P<sys_nr_args>\d+)\s*' # nr ags 164 r'(' 165 r'\)\s*$|' # empty arg list or 166 r',\s*(?P<sys_args>[\w,*\s]+)' # arg list 167 r'\)\s*$' 168 r')') 169 170syscall_re = re.compile(syscall_pat) 171 172syscall_rust_proto = ' pub fn _trusty_%(sys_fn)s(%(rust_args)s) -> %(rust_rt)s;\n' 173beg_rust = 'extern "C" {\n' 174end_rust = '}\n' 175 176def fatal_parse_error(line, err_str): 177 sys.stderr.write("Error processing line %r:\n%s\n" % (line, err_str)) 178 sys.exit(2) 179 180 181BUILTIN_TYPES = set(['char', 'int', 'long', 'void']) 182for i in [8, 16, 32, 64]: 183 BUILTIN_TYPES.add('int%d_t' % i) 184 BUILTIN_TYPES.add('uint%d_t' % i) 185 186def reformat_c_to_rust(arg): 187 """Reformat a C-style argument into corresponding Rust argument 188 189 Raises: 190 NotImplementedError: If argument type was too complex to reformat. 191 """ 192 m = re.match(r"(const )?(struct )?(.*?)\s*( ?\* ?)?$", arg) 193 is_const = m.group(1) is not None 194 ty = m.group(3) 195 is_ptr = m.group(4) is not None 196 rust_arg = '' 197 if '*' in ty: 198 raise NotImplementedError("Rust arg reformatting needs to be extended " 199 f"to handle double indirection in arg: {arg}") 200 if is_ptr: 201 rust_arg += '*%s ' % ('const' if is_const else 'mut') 202 rust_arg += ty 203 return rust_arg 204 205def parse_check_def(line, struct_types): 206 """ 207 Parse a DEF_SYSCALL line and check for errors 208 Returns various components from the line. 209 """ 210 211 m = syscall_re.match(line) 212 if m is None: 213 fatal_parse_error(line, "Line did not match expected pattern.") 214 gd = m.groupdict() 215 216 sys_nr_args = int(gd['sys_nr_args']) 217 sys_args = gd['sys_args'] 218 sys_args_list = re.split(r'\s*,\s*', sys_args) if sys_args else [] 219 220 if sys_nr_args > 4: 221 fatal_parse_error(line, "Only syscalls with up to 4 arguments are " 222 "supported.") 223 224 if sys_nr_args != len(sys_args_list): 225 fatal_parse_error(line, "Expected %d syscall arguments, got %d." % 226 (sys_nr_args, len(sys_args_list))) 227 228 # Find struct types in the arguments. 229 for arg in sys_args_list: 230 # Remove arg name. 231 arg = re.sub(r"\s*\w+$", "", arg) 232 # Remove trailing pointer. 233 arg = re.sub(r"\s*\*$", "", arg) 234 # Remove initial const. 235 arg = re.sub(r"^const\s+", "", arg) 236 # Ignore the type if it's obviously not a struct. 237 if arg in BUILTIN_TYPES: 238 continue 239 # Require explicit struct declarations, because forward declaring 240 # typedefs is tricky. 241 if not arg.startswith("struct "): 242 fatal_parse_error(line, "Not an integer type or explicit struct " 243 "type: %r. Don't use typedefs." % arg) 244 struct_types.add(arg) 245 246 # Reformat arguments into Rust syntax 247 rust_args = [] 248 try: 249 for arg in sys_args_list: 250 m = re.match(r"(.*?)(\w+)$", arg) 251 ty = m.group(1) 252 name = m.group(2) 253 rust_args.append('%s: %s' % (name, reformat_c_to_rust(ty))) 254 gd['rust_args'] = ', '.join(rust_args) 255 gd['rust_rt'] = reformat_c_to_rust(gd['sys_rt']) 256 except NotImplementedError as err: 257 # reformat_c_to_rust failed to convert argument or return type 258 fatal_parse_error(line, err) 259 260 # In C, a forward declaration with an empty list of arguments has an 261 # unknown number of arguments. Set it to 'void' to declare there are 262 # zero arguments. 263 if sys_nr_args == 0: 264 gd['sys_args'] = 'void' 265 266 return gd 267 268 269def process_table(table_file, std_file, stubs_file, rust_file, verify, arch): 270 """ 271 Process a syscall table and generate: 272 1. A sycall stubs file 273 2. A trusty_std.h header file with syscall definitions 274 and function prototypes 275 """ 276 define_lines = "" 277 proto_lines = "\n" 278 stub_lines = "" 279 rust_lines = "" 280 281 struct_types = set() 282 283 tbl = open(table_file, "r") 284 for line in tbl: 285 line = line.strip() 286 287 # skip all lines that don't start with a syscall definition 288 # multi-line defintions are not supported. 289 if not line.startswith(syscall_def): 290 continue 291 292 params = parse_check_def(line, struct_types) 293 294 if not verify: 295 define_lines += syscall_define % params 296 proto_lines += syscall_proto % params 297 stub_lines += arch.syscall_stub % params 298 rust_lines += syscall_rust_proto % params 299 300 301 tbl.close() 302 303 if verify: 304 return 305 306 if std_file is not None: 307 with open(std_file, "w") as std: 308 std.writelines(copyright_header + autogen_header) 309 std.writelines(clang_format_off) 310 std.writelines(define_lines + asm_ifdef) 311 std.writelines("\n") 312 std.writelines(includes_header % "lk/compiler.h") 313 std.writelines(includes_header % "stdint.h") 314 std.writelines(beg_cdecls) 315 # Forward declare the struct types. 316 std.writelines("\n") 317 std.writelines([t + ";\n" for t in sorted(struct_types)]) 318 std.writelines(proto_lines + end_cdecls + asm_endif) 319 320 if stubs_file is not None: 321 with open(stubs_file, "w") as stubs: 322 stubs.writelines(copyright_header + autogen_header) 323 stubs.writelines(includes_header % "lk/asm.h") 324 stubs.writelines(includes_header % "trusty_syscalls.h") 325 stubs.writelines(stub_lines) 326 stubs.writelines(arch.footer) 327 328 if rust_file is not None: 329 with open(rust_file, "w") as rust: 330 rust.writelines(copyright_header + autogen_header) 331 rust.writelines(beg_rust) 332 rust.writelines(rust_lines) 333 rust.writelines(end_rust) 334 335 336def main(): 337 338 usage = "usage: %prog [options] <syscall-table>" 339 340 op = OptionParser(usage=usage) 341 op.add_option("-v", "--verify", action="store_true", 342 dest="verify", default=False, 343 help="Check syscall table. Do not generate any files.") 344 op.add_option("-d", "--std-header", type="string", 345 dest="std_file", default=None, 346 help="path to syscall defintions header file.") 347 op.add_option("-s", "--stubs-file", type="string", 348 dest="stub_file", default=None, 349 help="path to syscall assembly stubs file.") 350 op.add_option("-r", "--rust-file", type="string", 351 dest="rust_file", default=None, 352 help="path to rust declarations file") 353 op.add_option("-a", "--arch", type="string", 354 dest="arch", default="arm", 355 help="arch of stub assembly files: " + str(arch_dict.keys())) 356 357 (opts, args) = op.parse_args() 358 359 if len(args) == 0: 360 op.print_help() 361 sys.exit(1) 362 363 if not opts.verify: 364 if opts.std_file is None and opts.stub_file is None: 365 op.print_help() 366 sys.exit(1) 367 368 process_table(args[0], opts.std_file, opts.stub_file, opts.rust_file, 369 opts.verify, arch_dict[opts.arch]) 370 371 372if __name__ == '__main__': 373 main() 374