1#!/usr/bin/env python3 2# 3# Copyright (c) 2017 Intel Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6 7""" 8Script to generate system call invocation macros 9 10This script parses the system call metadata JSON file emitted by 11parse_syscalls.py to create several files: 12 13- A file containing weak aliases of any potentially unimplemented system calls, 14 as well as the system call dispatch table, which maps system call type IDs 15 to their handler functions. 16 17- A header file defining the system call type IDs, as well as function 18 prototypes for all system call handler functions. 19 20- A directory containing header files. Each header corresponds to a header 21 that was identified as containing system call declarations. These 22 generated headers contain the inline invocation functions for each system 23 call in that header. 24""" 25 26import sys 27import re 28import argparse 29import os 30import json 31 32types64 = ["int64_t", "uint64_t"] 33 34# The kernel linkage is complicated. These functions from 35# userspace_handlers.c are present in the kernel .a library after 36# userspace.c, which contains the weak fallbacks defined here. So the 37# linker finds the weak one first and stops searching, and thus won't 38# see the real implementation which should override. Yet changing the 39# order runs afoul of a comment in CMakeLists.txt that the order is 40# critical. These are core syscalls that won't ever be unconfigured, 41# just disable the fallback mechanism as a simple workaround. 42noweak = ["z_mrsh_k_object_release", 43 "z_mrsh_k_object_access_grant", 44 "z_mrsh_k_object_alloc"] 45 46table_template = """/* auto-generated by gen_syscalls.py, don't edit */ 47 48/* Weak handler functions that get replaced by the real ones unless a system 49 * call is not implemented due to kernel configuration. 50 */ 51%s 52 53const _k_syscall_handler_t _k_syscall_table[K_SYSCALL_LIMIT] = { 54\t%s 55}; 56""" 57 58list_template = """/* auto-generated by gen_syscalls.py, don't edit */ 59 60#ifndef ZEPHYR_SYSCALL_LIST_H 61#define ZEPHYR_SYSCALL_LIST_H 62 63%s 64 65#ifndef _ASMLANGUAGE 66 67#include <stdint.h> 68 69#endif /* _ASMLANGUAGE */ 70 71#endif /* ZEPHYR_SYSCALL_LIST_H */ 72""" 73 74syscall_template = """ 75/* auto-generated by gen_syscalls.py, don't edit */ 76%s 77 78#ifndef _ASMLANGUAGE 79 80#include <syscall_list.h> 81#include <syscall.h> 82 83#include <linker/sections.h> 84 85#ifdef __cplusplus 86extern "C" { 87#endif 88 89%s 90 91#ifdef __cplusplus 92} 93#endif 94 95#endif 96#endif /* include guard */ 97""" 98 99handler_template = """ 100extern uintptr_t z_hdlr_%s(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, 101 uintptr_t arg4, uintptr_t arg5, uintptr_t arg6, void *ssf); 102""" 103 104weak_template = """ 105__weak ALIAS_OF(handler_no_syscall) 106uintptr_t %s(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, 107 uintptr_t arg4, uintptr_t arg5, uintptr_t arg6, void *ssf); 108""" 109 110 111typename_regex = re.compile(r'(.*?)([A-Za-z0-9_]+)$') 112 113 114class SyscallParseException(Exception): 115 pass 116 117 118def typename_split(item): 119 if "[" in item: 120 raise SyscallParseException( 121 "Please pass arrays to syscalls as pointers, unable to process '%s'" % 122 item) 123 124 if "(" in item: 125 raise SyscallParseException( 126 "Please use typedefs for function pointers") 127 128 mo = typename_regex.match(item) 129 if not mo: 130 raise SyscallParseException("Malformed system call invocation") 131 132 m = mo.groups() 133 return (m[0].strip(), m[1]) 134 135def need_split(argtype): 136 return (not args.long_registers) and (argtype in types64) 137 138# Note: "lo" and "hi" are named in little endian conventions, 139# but it doesn't matter as long as they are consistently 140# generated. 141def union_decl(type, split): 142 middle = "struct { uintptr_t lo, hi; } split" if split else "uintptr_t x" 143 return "union { %s; %s val; }" % (middle, type) 144 145def wrapper_defs(func_name, func_type, args): 146 ret64 = need_split(func_type) 147 mrsh_args = [] # List of rvalue expressions for the marshalled invocation 148 149 decl_arglist = ", ".join([" ".join(argrec) for argrec in args]) or "void" 150 151 wrap = "extern %s z_impl_%s(%s);\n" % (func_type, func_name, decl_arglist) 152 wrap += "\n" 153 wrap += "__pinned_func\n" 154 wrap += "static inline %s %s(%s)\n" % (func_type, func_name, decl_arglist) 155 wrap += "{\n" 156 wrap += "#ifdef CONFIG_USERSPACE\n" 157 wrap += ("\t" + "uint64_t ret64;\n") if ret64 else "" 158 wrap += "\t" + "if (z_syscall_trap()) {\n" 159 160 valist_args = [] 161 for argnum, (argtype, argname) in enumerate(args): 162 split = need_split(argtype) 163 wrap += "\t\t%s parm%d" % (union_decl(argtype, split), argnum) 164 if argtype != "va_list": 165 wrap += " = { .val = %s };\n" % argname 166 else: 167 # va_list objects are ... peculiar. 168 wrap += ";\n" + "\t\t" + "va_copy(parm%d.val, %s);\n" % (argnum, argname) 169 valist_args.append("parm%d.val" % argnum) 170 if split: 171 mrsh_args.append("parm%d.split.lo" % argnum) 172 mrsh_args.append("parm%d.split.hi" % argnum) 173 else: 174 mrsh_args.append("parm%d.x" % argnum) 175 176 if ret64: 177 mrsh_args.append("(uintptr_t)&ret64") 178 179 if len(mrsh_args) > 6: 180 wrap += "\t\t" + "uintptr_t more[] = {\n" 181 wrap += "\t\t\t" + (",\n\t\t\t".join(mrsh_args[5:])) + "\n" 182 wrap += "\t\t" + "};\n" 183 mrsh_args[5:] = ["(uintptr_t) &more"] 184 185 syscall_id = "K_SYSCALL_" + func_name.upper() 186 invoke = ("arch_syscall_invoke%d(%s)" 187 % (len(mrsh_args), 188 ", ".join(mrsh_args + [syscall_id]))) 189 190 if ret64: 191 invoke = "\t\t" + "(void) %s;\n" % invoke 192 retcode = "\t\t" + "return (%s) ret64;\n" % func_type 193 elif func_type == "void": 194 invoke = "\t\t" + "(void) %s;\n" % invoke 195 retcode = "\t\t" + "return;\n" 196 elif valist_args: 197 invoke = "\t\t" + "%s retval = %s;\n" % (func_type, invoke) 198 retcode = "\t\t" + "return retval;\n" 199 else: 200 invoke = "\t\t" + "return (%s) %s;\n" % (func_type, invoke) 201 retcode = "" 202 203 wrap += invoke 204 for argname in valist_args: 205 wrap += "\t\t" + "va_end(%s);\n" % argname 206 wrap += retcode 207 wrap += "\t" + "}\n" 208 wrap += "#endif\n" 209 210 # Otherwise fall through to direct invocation of the impl func. 211 # Note the compiler barrier: that is required to prevent code from 212 # the impl call from being hoisted above the check for user 213 # context. 214 impl_arglist = ", ".join([argrec[1] for argrec in args]) 215 impl_call = "z_impl_%s(%s)" % (func_name, impl_arglist) 216 wrap += "\t" + "compiler_barrier();\n" 217 wrap += "\t" + "%s%s;\n" % ("return " if func_type != "void" else "", 218 impl_call) 219 220 wrap += "}\n" 221 222 return wrap 223 224# Returns an expression for the specified (zero-indexed!) marshalled 225# parameter to a syscall, with handling for a final "more" parameter. 226def mrsh_rval(mrsh_num, total): 227 if mrsh_num < 5 or total <= 6: 228 return "arg%d" % mrsh_num 229 else: 230 return "(((uintptr_t *)more)[%d])" % (mrsh_num - 5) 231 232def marshall_defs(func_name, func_type, args): 233 mrsh_name = "z_mrsh_" + func_name 234 235 nmrsh = 0 # number of marshalled uintptr_t parameter 236 vrfy_parms = [] # list of (argtype, bool_is_split) 237 for (argtype, _) in args: 238 split = need_split(argtype) 239 vrfy_parms.append((argtype, split)) 240 nmrsh += 2 if split else 1 241 242 # Final argument for a 64 bit return value? 243 if need_split(func_type): 244 nmrsh += 1 245 246 decl_arglist = ", ".join([" ".join(argrec) for argrec in args]) 247 mrsh = "extern %s z_vrfy_%s(%s);\n" % (func_type, func_name, decl_arglist) 248 249 mrsh += "uintptr_t %s(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,\n" % mrsh_name 250 if nmrsh <= 6: 251 mrsh += "\t\t" + "uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, void *ssf)\n" 252 else: 253 mrsh += "\t\t" + "uintptr_t arg3, uintptr_t arg4, void *more, void *ssf)\n" 254 mrsh += "{\n" 255 mrsh += "\t" + "_current->syscall_frame = ssf;\n" 256 257 for unused_arg in range(nmrsh, 6): 258 mrsh += "\t(void) arg%d;\t/* unused */\n" % unused_arg 259 260 if nmrsh > 6: 261 mrsh += ("\tZ_OOPS(Z_SYSCALL_MEMORY_READ(more, " 262 + str(nmrsh - 5) + " * sizeof(uintptr_t)));\n") 263 264 argnum = 0 265 for i, (argtype, split) in enumerate(vrfy_parms): 266 mrsh += "\t%s parm%d;\n" % (union_decl(argtype, split), i) 267 if split: 268 mrsh += "\t" + "parm%d.split.lo = %s;\n" % (i, mrsh_rval(argnum, nmrsh)) 269 argnum += 1 270 mrsh += "\t" + "parm%d.split.hi = %s;\n" % (i, mrsh_rval(argnum, nmrsh)) 271 else: 272 mrsh += "\t" + "parm%d.x = %s;\n" % (i, mrsh_rval(argnum, nmrsh)) 273 argnum += 1 274 275 # Finally, invoke the verify function 276 out_args = ", ".join(["parm%d.val" % i for i in range(len(args))]) 277 vrfy_call = "z_vrfy_%s(%s)" % (func_name, out_args) 278 279 if func_type == "void": 280 mrsh += "\t" + "%s;\n" % vrfy_call 281 mrsh += "\t" + "_current->syscall_frame = NULL;\n" 282 mrsh += "\t" + "return 0;\n" 283 else: 284 mrsh += "\t" + "%s ret = %s;\n" % (func_type, vrfy_call) 285 286 if need_split(func_type): 287 ptr = "((uint64_t *)%s)" % mrsh_rval(nmrsh - 1, nmrsh) 288 mrsh += "\t" + "Z_OOPS(Z_SYSCALL_MEMORY_WRITE(%s, 8));\n" % ptr 289 mrsh += "\t" + "*%s = ret;\n" % ptr 290 mrsh += "\t" + "_current->syscall_frame = NULL;\n" 291 mrsh += "\t" + "return 0;\n" 292 else: 293 mrsh += "\t" + "_current->syscall_frame = NULL;\n" 294 mrsh += "\t" + "return (uintptr_t) ret;\n" 295 296 mrsh += "}\n" 297 298 return mrsh, mrsh_name 299 300def analyze_fn(match_group): 301 func, args = match_group 302 303 try: 304 if args == "void": 305 args = [] 306 else: 307 args = [typename_split(a.strip()) for a in args.split(",")] 308 309 func_type, func_name = typename_split(func) 310 except SyscallParseException: 311 sys.stderr.write("In declaration of %s\n" % func) 312 raise 313 314 sys_id = "K_SYSCALL_" + func_name.upper() 315 316 marshaller = None 317 marshaller, handler = marshall_defs(func_name, func_type, args) 318 invocation = wrapper_defs(func_name, func_type, args) 319 320 # Entry in _k_syscall_table 321 table_entry = "[%s] = %s" % (sys_id, handler) 322 323 return (handler, invocation, marshaller, sys_id, table_entry) 324 325def parse_args(): 326 global args 327 parser = argparse.ArgumentParser( 328 description=__doc__, 329 formatter_class=argparse.RawDescriptionHelpFormatter) 330 331 parser.add_argument("-i", "--json-file", required=True, 332 help="Read syscall information from json file") 333 parser.add_argument("-d", "--syscall-dispatch", required=True, 334 help="output C system call dispatch table file") 335 parser.add_argument("-l", "--syscall-list", required=True, 336 help="output C system call list header") 337 parser.add_argument("-o", "--base-output", required=True, 338 help="Base output directory for syscall macro headers") 339 parser.add_argument("-s", "--split-type", action="append", 340 help="A long type that must be split/marshalled on 32-bit systems") 341 parser.add_argument("-x", "--long-registers", action="store_true", 342 help="Indicates we are on system with 64-bit registers") 343 args = parser.parse_args() 344 345 346def main(): 347 parse_args() 348 349 if args.split_type is not None: 350 for t in args.split_type: 351 types64.append(t) 352 353 with open(args.json_file, 'r') as fd: 354 syscalls = json.load(fd) 355 356 invocations = {} 357 mrsh_defs = {} 358 mrsh_includes = {} 359 ids = [] 360 table_entries = [] 361 handlers = [] 362 363 for match_group, fn in syscalls: 364 handler, inv, mrsh, sys_id, entry = analyze_fn(match_group) 365 366 if fn not in invocations: 367 invocations[fn] = [] 368 369 invocations[fn].append(inv) 370 ids.append(sys_id) 371 table_entries.append(entry) 372 handlers.append(handler) 373 374 if mrsh: 375 syscall = typename_split(match_group[0])[1] 376 mrsh_defs[syscall] = mrsh 377 mrsh_includes[syscall] = "#include <syscalls/%s>" % fn 378 379 with open(args.syscall_dispatch, "w") as fp: 380 table_entries.append("[K_SYSCALL_BAD] = handler_bad_syscall") 381 382 weak_defines = "".join([weak_template % name 383 for name in handlers 384 if not name in noweak]) 385 386 # The "noweak" ones just get a regular declaration 387 weak_defines += "\n".join(["extern uintptr_t %s(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6, void *ssf);" 388 % s for s in noweak]) 389 390 fp.write(table_template % (weak_defines, 391 ",\n\t".join(table_entries))) 392 393 # Listing header emitted to stdout 394 ids.sort() 395 ids.extend(["K_SYSCALL_BAD", "K_SYSCALL_LIMIT"]) 396 397 ids_as_defines = "" 398 for i, item in enumerate(ids): 399 ids_as_defines += "#define {} {}\n".format(item, i) 400 401 with open(args.syscall_list, "w") as fp: 402 fp.write(list_template % ids_as_defines) 403 404 os.makedirs(args.base_output, exist_ok=True) 405 for fn, invo_list in invocations.items(): 406 out_fn = os.path.join(args.base_output, fn) 407 408 ig = re.sub("[^a-zA-Z0-9]", "_", "Z_INCLUDE_SYSCALLS_" + fn).upper() 409 include_guard = "#ifndef %s\n#define %s\n" % (ig, ig) 410 header = syscall_template % (include_guard, "\n\n".join(invo_list)) 411 412 with open(out_fn, "w") as fp: 413 fp.write(header) 414 415 # Likewise emit _mrsh.c files for syscall inclusion 416 for fn in mrsh_defs: 417 mrsh_fn = os.path.join(args.base_output, fn + "_mrsh.c") 418 419 with open(mrsh_fn, "w") as fp: 420 fp.write("/* auto-generated by gen_syscalls.py, don't edit */\n\n") 421 fp.write(mrsh_includes[fn] + "\n") 422 fp.write("\n") 423 fp.write(mrsh_defs[fn] + "\n") 424 425if __name__ == "__main__": 426 main() 427