|  | #!/usr/bin/env python3 | 
|  | # | 
|  | # Copyright (c) 2017 Intel Corporation | 
|  | # | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | import sys | 
|  | import re | 
|  | import argparse | 
|  | import os | 
|  |  | 
|  |  | 
|  | api_regex = re.compile(r''' | 
|  | __syscall\s+                    # __syscall attribute, must be first | 
|  | ([^(]+)                         # type and name of system call (split later) | 
|  | [(]                             # Function opening parenthesis | 
|  | ([^)]*)                         # Arg list (split later) | 
|  | [)]                             # Closing parenthesis | 
|  | ''', re.MULTILINE | re.VERBOSE) | 
|  |  | 
|  | typename_regex = re.compile(r'(.*?)([A-Za-z0-9_]+)$') | 
|  |  | 
|  | class SyscallParseException(Exception): | 
|  | pass | 
|  |  | 
|  |  | 
|  | def typename_split(item): | 
|  | if "[" in item: | 
|  | raise SyscallParseException("Please pass arrays to syscalls as pointers, unable to process '%s'" | 
|  | % item) | 
|  |  | 
|  | if "(" in item: | 
|  | raise SyscallParseException("Please use typedefs for function pointers") | 
|  |  | 
|  | mo = typename_regex.match(item) | 
|  | if not mo: | 
|  | raise SyscallParseException("Malformed system call invocation") | 
|  |  | 
|  | m = mo.groups() | 
|  | return (m[0].strip(), m[1]) | 
|  |  | 
|  |  | 
|  | def analyze_fn(match_group, fn): | 
|  | func, args = match_group | 
|  |  | 
|  | try: | 
|  | if args == "void": | 
|  | args = [] | 
|  | else: | 
|  | args = [typename_split(a.strip()) for a in args.split(",")] | 
|  |  | 
|  | func_type, func_name = typename_split(func) | 
|  | except SyscallParseException: | 
|  | sys.stderr.write("In declaration of %s\n" % func) | 
|  | raise | 
|  |  | 
|  | sys_id = "K_SYSCALL_" + func_name.upper() | 
|  |  | 
|  | if func_type == "void": | 
|  | suffix = "_VOID" | 
|  | is_void = True | 
|  | else: | 
|  | is_void = False | 
|  | if func_type in ["s64_t", "u64_t"]: | 
|  | suffix = "_RET64" | 
|  | else: | 
|  | suffix = "" | 
|  |  | 
|  | is_void = (func_type == "void") | 
|  |  | 
|  | # Get the proper system call macro invocation, which depends on the | 
|  | # number of arguments, the return type, and whether the implementation | 
|  | # is an inline function | 
|  | macro = "K_SYSCALL_DECLARE%d%s" % (len(args), suffix) | 
|  |  | 
|  | # Flatten the argument lists and generate a comma separated list | 
|  | # of t0, p0, t1, p1, ... tN, pN as expected by the macros | 
|  | flat_args = [i for sublist in args for i in sublist] | 
|  | if not is_void: | 
|  | flat_args = [func_type] + flat_args | 
|  | flat_args = [sys_id, func_name] + flat_args | 
|  | argslist = ", ".join(flat_args) | 
|  |  | 
|  | invocation = "%s(%s);" % (macro, argslist) | 
|  |  | 
|  | handler = "_handler_" + func_name | 
|  |  | 
|  | # Entry in _k_syscall_table | 
|  | table_entry = "[%s] = %s" % (sys_id, handler) | 
|  |  | 
|  | return (fn, handler, invocation, sys_id, table_entry) | 
|  |  | 
|  |  | 
|  | def analyze_headers(base_path): | 
|  | ret = [] | 
|  |  | 
|  | for root, dirs, files in os.walk(base_path): | 
|  | for fn in files: | 
|  |  | 
|  | # toolchain/common.h has the definition of __syscall which we | 
|  | # don't want to trip over | 
|  | path = os.path.join(root, fn) | 
|  | if not fn.endswith(".h") or path.endswith("toolchain/common.h"): | 
|  | continue | 
|  |  | 
|  | with open(path, "r", encoding="utf-8") as fp: | 
|  | try: | 
|  | result = [analyze_fn(mo.groups(), fn) | 
|  | for mo in api_regex.finditer(fp.read())] | 
|  | except Exception: | 
|  | sys.stderr.write("While parsing %s\n" % fn) | 
|  | raise | 
|  |  | 
|  | ret.extend(result) | 
|  |  | 
|  | return ret | 
|  |  | 
|  | table_template = """/* auto-generated by gen_syscalls.py, don't edit */ | 
|  |  | 
|  | /* Weak handler functions that get replaced by the real ones unless a system | 
|  | * call is not implemented due to kernel configuration. | 
|  | */ | 
|  | %s | 
|  |  | 
|  | const _k_syscall_handler_t _k_syscall_table[K_SYSCALL_LIMIT] = { | 
|  | \t%s | 
|  | }; | 
|  | """ | 
|  |  | 
|  | list_template = """ | 
|  | /* auto-generated by gen_syscalls.py, don't edit */ | 
|  | #ifndef _ZEPHYR_SYSCALL_LIST_H_ | 
|  | #define _ZEPHYR_SYSCALL_LIST_H_ | 
|  |  | 
|  | #ifndef _ASMLANGUAGE | 
|  |  | 
|  | #ifdef __cplusplus | 
|  | extern "C" { | 
|  | #endif | 
|  |  | 
|  | enum { | 
|  | \t%s | 
|  | }; | 
|  |  | 
|  | %s | 
|  |  | 
|  | #ifdef __cplusplus | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #endif /* _ASMLANGUAGE */ | 
|  |  | 
|  | #endif /* _ZEPHYR_SYSCALL_LIST_H_ */ | 
|  | """ | 
|  |  | 
|  | syscall_template = """ | 
|  | /* auto-generated by gen_syscalls.py, don't edit */ | 
|  |  | 
|  | #ifndef _ASMLANGUAGE | 
|  |  | 
|  | #include <syscall_list.h> | 
|  | #include <syscall_macros.h> | 
|  |  | 
|  | #ifdef __cplusplus | 
|  | extern "C" { | 
|  | #endif | 
|  |  | 
|  | %s | 
|  |  | 
|  | #ifdef __cplusplus | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #endif | 
|  | """ | 
|  |  | 
|  | handler_template = """ | 
|  | extern u32_t %s(u32_t arg1, u32_t arg2, u32_t arg3, | 
|  | u32_t arg4, u32_t arg5, u32_t arg6, void *ssf); | 
|  | """ | 
|  |  | 
|  | weak_template = """ | 
|  | __weak ALIAS_OF(_handler_no_syscall) | 
|  | u32_t %s(u32_t arg1, u32_t arg2, u32_t arg3, | 
|  | u32_t arg4, u32_t arg5, u32_t arg6, void *ssf); | 
|  | """ | 
|  |  | 
|  |  | 
|  | def parse_args(): | 
|  | global args | 
|  | parser = argparse.ArgumentParser(description = __doc__, | 
|  | formatter_class = argparse.RawDescriptionHelpFormatter) | 
|  |  | 
|  | parser.add_argument("-i", "--include", required=True, | 
|  | help="Base include directory") | 
|  | parser.add_argument("-d", "--syscall-dispatch", required=True, | 
|  | help="output C system call dispatch table file") | 
|  | parser.add_argument("-o", "--base-output", required=True, | 
|  | help="Base output directory for syscall macro headers") | 
|  | args = parser.parse_args() | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | parse_args() | 
|  |  | 
|  | syscalls = analyze_headers(args.include) | 
|  | invocations = {} | 
|  | ids = [] | 
|  | table_entries = [] | 
|  | handlers = [] | 
|  |  | 
|  | for fn, handler, inv, sys_id, entry in syscalls: | 
|  | if fn not in invocations: | 
|  | invocations[fn] = [] | 
|  |  | 
|  | invocations[fn].append(inv) | 
|  | ids.append(sys_id) | 
|  | table_entries.append(entry) | 
|  | handlers.append(handler) | 
|  |  | 
|  | with open(args.syscall_dispatch, "w") as fp: | 
|  | table_entries.append("[K_SYSCALL_BAD] = _handler_bad_syscall") | 
|  |  | 
|  | weak_defines = "".join([weak_template % name for name in handlers]) | 
|  |  | 
|  | fp.write(table_template % (weak_defines, ",\n\t".join(table_entries))) | 
|  |  | 
|  | # Listing header emitted to stdout | 
|  | ids.sort() | 
|  | ids.extend(["K_SYSCALL_BAD", "K_SYSCALL_LIMIT"]) | 
|  | handler_defines = "".join([handler_template % name for name in handlers]) | 
|  | sys.stdout.write(list_template % (",\n\t".join(ids), handler_defines)) | 
|  |  | 
|  | os.makedirs(args.base_output, exist_ok=True) | 
|  | for fn, invo_list in invocations.items(): | 
|  | out_fn = os.path.join(args.base_output, fn) | 
|  |  | 
|  | header = syscall_template % "\n\n".join(invo_list) | 
|  |  | 
|  | # Check if the file already exists, and if there are no changes, | 
|  | # don't touch it since that will force an incremental rebuild | 
|  | if os.path.exists(out_fn): | 
|  | with open(out_fn, "r") as fp: | 
|  | old_data = fp.read() | 
|  |  | 
|  | if old_data == header: | 
|  | continue | 
|  |  | 
|  | with open(out_fn, "w") as fp: | 
|  | fp.write(header) | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main() | 
|  |  |