| #!/usr/bin/env python3 |
| # |
| # Copyright (c) 2017 Intel Corporation |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| """ |
| Script to generate system call invocation macros |
| |
| This script parses the system call metadata JSON file emitted by |
| parse_syscalls.py to create several files: |
| |
| - A file containing weak aliases of any potentially unimplemented system calls, |
| as well as the system call dispatch table, which maps system call type IDs |
| to their handler functions. |
| |
| - A header file defing the system call type IDs, as well as function |
| prototypes for all system call handler functions. |
| |
| - A directory containing header files. Each header corresponds to a header |
| that was identified as containing system call declarations. These |
| generated headers contain the inline invocation functions for each system |
| call in that header. |
| """ |
| |
| import sys |
| import re |
| import argparse |
| import os |
| import json |
| |
| 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 |
| |
| %s |
| |
| #ifndef _ASMLANGUAGE |
| |
| #include <zephyr/types.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| %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); |
| """ |
| |
| |
| 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): |
| 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 = "z_hdlr_" + func_name |
| |
| # Entry in _k_syscall_table |
| table_entry = "[%s] = %s" % (sys_id, handler) |
| |
| return (handler, invocation, sys_id, table_entry) |
| |
| def parse_args(): |
| global args |
| parser = argparse.ArgumentParser( |
| description=__doc__, |
| formatter_class=argparse.RawDescriptionHelpFormatter) |
| |
| parser.add_argument("-i", "--json-file", required=True, |
| help="Read syscall information from json file") |
| parser.add_argument("-d", "--syscall-dispatch", required=True, |
| help="output C system call dispatch table file") |
| parser.add_argument("-l", "--syscall-list", required=True, |
| help="output C system call list header") |
| parser.add_argument("-o", "--base-output", required=True, |
| help="Base output directory for syscall macro headers") |
| args = parser.parse_args() |
| |
| |
| def main(): |
| parse_args() |
| |
| with open(args.json_file, 'r') as fd: |
| syscalls = json.load(fd) |
| |
| invocations = {} |
| ids = [] |
| table_entries = [] |
| handlers = [] |
| |
| for match_group, fn in syscalls: |
| handler, inv, sys_id, entry = analyze_fn(match_group) |
| |
| 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"]) |
| |
| ids_as_defines = "" |
| for i, item in enumerate(ids): |
| ids_as_defines += "#define {} {}\n".format(item, i) |
| |
| handler_defines = "".join([handler_template % name for name in handlers]) |
| with open(args.syscall_list, "w") as fp: |
| fp.write(list_template % (ids_as_defines, 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) |
| |
| with open(out_fn, "w") as fp: |
| fp.write(header) |
| |
| |
| if __name__ == "__main__": |
| main() |