From fa94ee74602a04b2f159371cbe90721f85a5478e Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 28 Sep 2017 16:54:35 -0700 Subject: [PATCH] syscalls: greatly simplify system call declaration To define a system call, it's now sufficient to simply tag the inline prototype with "__syscall" or "__syscall_inline" and include a special generated header at the end of the header file. The system call dispatch table and enumeration of system call IDs is now automatically generated. Signed-off-by: Andrew Boie --- Makefile | 25 +++- doc/zephyr.doxyfile | 4 +- include/kernel.h | 33 ++--- include/syscall_list.h | 26 ---- include/toolchain/gcc.h | 4 + kernel/include/kernel_offsets.h | 2 + kernel/userspace.c | 17 +-- scripts/checkpatch.pl | 4 +- scripts/gen_syscall_header.py | 3 - scripts/gen_syscalls.py | 238 ++++++++++++++++++++++++++++++++ 10 files changed, 291 insertions(+), 65 deletions(-) delete mode 100644 include/syscall_list.h create mode 100755 scripts/gen_syscalls.py diff --git a/Makefile b/Makefile index ac761b531f1..f20c8030ae4 100644 --- a/Makefile +++ b/Makefile @@ -1023,7 +1023,28 @@ include/generated/syscall_macros.h: $(GEN_SYSCALL_HEADER) $(Q)mkdir -p $(dir $@) $(Q)$(GEN_SYSCALL_HEADER) > $@ -syscall_macros: include/generated/syscall_macros.h +GEN_SYSCALLS := $(srctree)/scripts/gen_syscalls.py + +define filechk_syscall_list.h + $(GEN_SYSCALLS) \ + --include $(ZEPHYR_BASE)/include \ + --base-output include/generated/syscalls \ + --syscall-dispatch include/generated/dispatch.c.tmp +endef + +include/generated/syscall_list.h: include/config/auto.conf FORCE + $(call filechk,syscall_list.h) + +define filechk_syscall_dispatch.c + cat include/generated/dispatch.c.tmp +endef + +include/generated/syscall_dispatch.c: include/generated/syscall_list.h FORCE + $(call filechk,syscall_dispatch.c) + +syscall_generated: include/generated/syscall_macros.h \ + include/generated/syscall_dispatch.c \ + include/generated/syscall_list.h define filechk_.config-sanitycheck (cat .config; \ @@ -1093,7 +1114,7 @@ archprepare = $(strip \ ) # All the preparing.. -prepare: $(archprepare) dts syscall_macros FORCE +prepare: $(archprepare) dts syscall_generated FORCE $(Q)$(MAKE) $(build)=. # Generate some files diff --git a/doc/zephyr.doxyfile b/doc/zephyr.doxyfile index 16a47215731..a13588243ca 100644 --- a/doc/zephyr.doxyfile +++ b/doc/zephyr.doxyfile @@ -281,7 +281,9 @@ PREDEFINED = "CONFIG_SYS_CLOCK_EXISTS=y" \ "CONFIG_BT_BREDR=y" \ "__deprecated=" \ "__printf_like(x, y)=" \ - "__attribute(x)__=" + "__attribute(x)__=" \ + "__syscall=" \ + "__syscall_inline=" EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = NO #--------------------------------------------------------------------------- diff --git a/include/kernel.h b/include/kernel.h index cbdb1da130a..aacc59a2d3f 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -2477,13 +2477,9 @@ struct k_sem { * * @return N/A */ -static inline void k_sem_init(struct k_sem *sem, unsigned int initial_count, - unsigned int limit); - -K_SYSCALL_DECLARE3_VOID(K_SYSCALL_SEM_INIT, k_sem_init, - struct k_sem *, sem, - unsigned int, initial_count, - unsigned int, limit); +__syscall static inline void k_sem_init(struct k_sem *sem, + unsigned int initial_count, + unsigned int limit); /** * @brief Take a semaphore. @@ -2506,11 +2502,7 @@ K_SYSCALL_DECLARE3_VOID(K_SYSCALL_SEM_INIT, k_sem_init, * @retval -EBUSY Returned without waiting. * @retval -EAGAIN Waiting period timed out. */ -static inline int k_sem_take(struct k_sem *sem, s32_t timeout); - -K_SYSCALL_DECLARE2(K_SYSCALL_SEM_TAKE, k_sem_take, int, - struct k_sem *, sem, - s32_t, timeout); +__syscall static inline int k_sem_take(struct k_sem *sem, s32_t timeout); /** * @brief Give a semaphore. @@ -2524,10 +2516,7 @@ K_SYSCALL_DECLARE2(K_SYSCALL_SEM_TAKE, k_sem_take, int, * * @return N/A */ -static inline void k_sem_give(struct k_sem *sem); - -K_SYSCALL_DECLARE1_VOID(K_SYSCALL_SEM_GIVE, k_sem_give, - struct k_sem *, sem); +__syscall static inline void k_sem_give(struct k_sem *sem); /** * @brief Reset a semaphore's count to zero. @@ -2538,16 +2527,13 @@ K_SYSCALL_DECLARE1_VOID(K_SYSCALL_SEM_GIVE, k_sem_give, * * @return N/A */ -static inline void k_sem_reset(struct k_sem *sem); +__syscall_inline static inline void k_sem_reset(struct k_sem *sem); static inline void _impl_k_sem_reset(struct k_sem *sem) { sem->count = 0; } -K_SYSCALL_DECLARE1_VOID_INLINE(K_SYSCALL_SEM_RESET, k_sem_reset, - struct k_sem *, sem); - /** * @brief Get a semaphore's count. * @@ -2557,16 +2543,13 @@ K_SYSCALL_DECLARE1_VOID_INLINE(K_SYSCALL_SEM_RESET, k_sem_reset, * * @return Current semaphore count. */ -static inline unsigned int k_sem_count_get(struct k_sem *sem); +__syscall_inline static inline unsigned int k_sem_count_get(struct k_sem *sem); static inline unsigned int _impl_k_sem_count_get(struct k_sem *sem) { return sem->count; } -K_SYSCALL_DECLARE1_INLINE(K_SYSCALL_SEM_COUNT_GET, k_sem_count_get, - unsigned int, struct k_sem *, sem); - /** * @brief Statically define and initialize a semaphore. * @@ -4142,6 +4125,8 @@ inline void *operator new[](size_t size, void *ptr) #endif /* defined(CONFIG_CPLUSPLUS) && defined(__cplusplus) */ +#include + #endif /* !_ASMLANGUAGE */ #endif /* _kernel__h_ */ diff --git a/include/syscall_list.h b/include/syscall_list.h deleted file mode 100644 index 8798d131f51..00000000000 --- a/include/syscall_list.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2017, Intel Corporation - * - * SPDX-License-Identifier: Apache 2.0 - */ - - -#ifndef _ZEPHYR_SYSCALL_LIST_H_ -#define _ZEPHYR_SYSCALL_LIST_H_ - -#ifndef _ASMLANGUAGE - -enum { - K_SYSCALL_BAD, - K_SYSCALL_SEM_INIT, - K_SYSCALL_SEM_GIVE, - K_SYSCALL_SEM_TAKE, - K_SYSCALL_SEM_RESET, - K_SYSCALL_SEM_COUNT_GET, - - K_SYSCALL_LIMIT /* Always last */ -}; - -#endif /* _ASMLANGUAGE */ - -#endif /* _ZEPHYR_SYSCALL_LIST_H_ */ diff --git a/include/toolchain/gcc.h b/include/toolchain/gcc.h index 349051329da..a937c19741c 100644 --- a/include/toolchain/gcc.h +++ b/include/toolchain/gcc.h @@ -95,6 +95,10 @@ do { \ #define __deprecated __attribute__((deprecated)) #define ARG_UNUSED(x) (void)(x) +/* Only used by gen_syscalls.py */ +#define __syscall +#define __syscall_inline + #define likely(x) __builtin_expect((long)!!(x), 1L) #define unlikely(x) __builtin_expect((long)!!(x), 0L) diff --git a/kernel/include/kernel_offsets.h b/kernel/include/kernel_offsets.h index cf967dc11f3..cdb41c86601 100644 --- a/kernel/include/kernel_offsets.h +++ b/kernel/include/kernel_offsets.h @@ -8,6 +8,8 @@ #ifndef _kernel_offsets__h_ #define _kernel_offsets__h_ +#include + /* * The final link step uses the symbol _OffsetAbsSyms to force the linkage of * offsets.o into the ELF image. diff --git a/kernel/userspace.c b/kernel/userspace.c index ac2a6fb797c..009d599e527 100644 --- a/kernel/userspace.c +++ b/kernel/userspace.c @@ -190,12 +190,13 @@ static u32_t _handler_bad_syscall(u32_t bad_id, u32_t arg2, u32_t arg3, CODE_UNREACHABLE; } -const _k_syscall_handler_t _k_syscall_table[K_SYSCALL_LIMIT] = { - [K_SYSCALL_BAD] = _handler_bad_syscall, +static u32_t _handler_no_syscall(u32_t arg1, u32_t arg2, u32_t arg3, + u32_t arg4, u32_t arg5, u32_t arg6, void *ssf) +{ + printk("Unimplemented system call\n"); + _arch_syscall_oops(ssf); + CODE_UNREACHABLE; +} + +#include - [K_SYSCALL_SEM_INIT] = _handler_k_sem_init, - [K_SYSCALL_SEM_GIVE] = _handler_k_sem_give, - [K_SYSCALL_SEM_TAKE] = _handler_k_sem_take, - [K_SYSCALL_SEM_RESET] = _handler_k_sem_reset, - [K_SYSCALL_SEM_COUNT_GET] = _handler_k_sem_count_get, -}; diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 4e47c0dc952..247e51d29c3 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -356,7 +356,9 @@ our $Attribute = qr{ ____cacheline_aligned| ____cacheline_aligned_in_smp| ____cacheline_internodealigned_in_smp| - __weak + __weak| + __syscall| + __syscall_inline }x; our $Modifier; our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; diff --git a/scripts/gen_syscall_header.py b/scripts/gen_syscall_header.py index 507ff9a742b..bff36c01121 100755 --- a/scripts/gen_syscall_header.py +++ b/scripts/gen_syscall_header.py @@ -56,9 +56,6 @@ def gen_defines_inner(ret, argc, kernel_only=False, user_only=False): gen_macro(ret, argc, inline=True) newline() - if not user_only: - sys.stdout.write("\textern u32_t _handler_##name(u32_t, u32_t, u32_t, u32_t, u32_t, u32_t, void *); \\\n") - gen_fn(ret, argc, "name"); newline() sys.stdout.write("\t{") diff --git a/scripts/gen_syscalls.py b/scripts/gen_syscalls.py new file mode 100755 index 00000000000..8a35dbf44f7 --- /dev/null +++ b/scripts/gen_syscalls.py @@ -0,0 +1,238 @@ +#!/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|syscall_inline)\s+ # __syscall or __syscall_inline +static\s+inline\s+ # All prototypes are static inline functions +([^(]+) # 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") + + m = typename_regex.match(item).groups() + return (m[0].strip(), m[1]) + + +def analyze_fn(match_group, fn): + variant, 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() + 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%s" % (len(args), + "_VOID" if is_void else "", + "_INLINE" if variant == "syscall_inline" else "") + + # 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: + if not fn.endswith(".h"): + continue + + with open(os.path.join(root, fn)) 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 unles 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 +#include + +#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() +