| #!/usr/bin/env python3 |
| # |
| # Copyright (c) 2017 Intel Corporation |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| """ |
| Generation script for syscall_macros.h |
| |
| The generation of macros for invoking system calls of various number |
| of arguments, in different execution types (supervisor only, user only, |
| mixed supervisor/user code) is tedious and repetitive. Rather than writing |
| by hand, this script generates it. |
| |
| This script has no inputs, and emits the generated header to stdout. |
| """ |
| |
| import sys |
| from enum import Enum |
| |
| |
| class Retval(Enum): |
| VOID = 0 |
| U32 = 1 |
| U64 = 2 |
| |
| |
| def gen_macro(ret, argc): |
| if ret == Retval.VOID: |
| suffix = "_VOID" |
| elif ret == Retval.U64: |
| suffix = "_RET64" |
| else: |
| suffix = "" |
| |
| sys.stdout.write("K_SYSCALL_DECLARE%d%s(id, name" % (argc, suffix)) |
| if ret != Retval.VOID: |
| sys.stdout.write(", ret") |
| for i in range(argc): |
| sys.stdout.write(", t%d, p%d" % (i, i)) |
| sys.stdout.write(")") |
| |
| |
| def gen_fn(ret, argc, name, extern=False): |
| sys.stdout.write("\t%s %s %s(" % |
| (("extern" if extern else "static inline"), |
| ("ret" if ret != Retval.VOID else "void"), name)) |
| if argc == 0: |
| sys.stdout.write("void") |
| else: |
| for i in range(argc): |
| sys.stdout.write("t%d p%d" % (i, i)) |
| if i != (argc - 1): |
| sys.stdout.write(", ") |
| sys.stdout.write(")") |
| |
| |
| def tabs(count): |
| sys.stdout.write("\t" * count) |
| |
| |
| def gen_make_syscall(ret, argc, tabcount): |
| tabs(tabcount) |
| |
| # The core kernel is built with the --no-whole-archive linker option. |
| # For all the individual .o files which make up the kernel, if there |
| # are no external references to symbols within these object files, |
| # everything in the object file is dropped. |
| # |
| # This has a subtle interaction with system call handlers. If an object |
| # file has system call handler inside it, and nothing else in the |
| # object file is referenced, then the linker will prefer the weak |
| # version of the handler in the generated syscall_dispatch.c. The |
| # user will get an "unimplemented system call" error if the associated |
| # system call for that handler is made. |
| # |
| # Fix this by making a fake reference to the handler function at the |
| # system call site. The address gets stored inside a special section |
| # "hndlr_ref". This is enough to prevent the handlers from being |
| # dropped, and the hndlr_ref section is itself dropped from the binary |
| # from gc-sections; these references will not consume space. |
| |
| sys.stdout.write( |
| "static Z_GENERIC_SECTION(hndlr_ref) __used void *href = (void *)&z_hdlr_##name; \\\n") |
| tabs(tabcount) |
| if ret != Retval.VOID: |
| sys.stdout.write("return (ret)") |
| else: |
| sys.stdout.write("return (void)") |
| if (argc <= 6 and ret != Retval.U64): |
| sys.stdout.write("z_arch_syscall%s_invoke%d(" % |
| (("_ret64" if ret == Retval.U64 else ""), argc)) |
| else: |
| sys.stdout.write("z_syscall%s_invoke%d(" % |
| (("_ret64" if ret == Retval.U64 else ""), argc)) |
| for i in range(argc): |
| sys.stdout.write("(u32_t)p%d, " % (i)) |
| sys.stdout.write("id); \\\n") |
| |
| |
| def gen_call_impl(ret, argc): |
| if ret != Retval.VOID: |
| sys.stdout.write("return ") |
| sys.stdout.write("z_impl_##name(") |
| for i in range(argc): |
| sys.stdout.write("p%d" % (i)) |
| if i != (argc - 1): |
| sys.stdout.write(", ") |
| sys.stdout.write("); \\\n") |
| |
| |
| def newline(): |
| sys.stdout.write(" \\\n") |
| |
| |
| def gen_defines_inner(ret, argc, kernel_only=False, user_only=False): |
| sys.stdout.write("#define ") |
| gen_macro(ret, argc) |
| newline() |
| |
| if not user_only: |
| gen_fn(ret, argc, "z_impl_##name", extern=True) |
| sys.stdout.write(";") |
| newline() |
| |
| gen_fn(ret, argc, "name") |
| newline() |
| sys.stdout.write("\t{") |
| newline() |
| |
| if kernel_only: |
| sys.stdout.write("\t\t") |
| gen_call_impl(ret, argc) |
| elif user_only: |
| gen_make_syscall(ret, argc, 2) |
| else: |
| sys.stdout.write("\t\tif (_is_user_context()) {") |
| newline() |
| |
| gen_make_syscall(ret, argc, 3) |
| |
| sys.stdout.write("\t\t} else {") |
| newline() |
| |
| # Prevent memory access issues if the implementation function gets |
| # inlined |
| sys.stdout.write("\t\t\tcompiler_barrier();") |
| newline() |
| |
| sys.stdout.write("\t\t\t") |
| gen_call_impl(ret, argc) |
| sys.stdout.write("\t\t}") |
| newline() |
| |
| sys.stdout.write("\t}\n\n") |
| |
| |
| def gen_defines(argc, kernel_only=False, user_only=False): |
| gen_defines_inner(Retval.VOID, argc, kernel_only, user_only) |
| gen_defines_inner(Retval.U32, argc, kernel_only, user_only) |
| gen_defines_inner(Retval.U64, argc, kernel_only, user_only) |
| |
| |
| sys.stdout.write( |
| "/* Auto-generated by gen_syscall_header.py, do not edit! */\n\n") |
| sys.stdout.write("#ifndef GEN_SYSCALL_H\n#define GEN_SYSCALL_H\n\n") |
| sys.stdout.write("#include <syscall.h>\n") |
| |
| for i in range(11): |
| sys.stdout.write( |
| "#if !defined(CONFIG_USERSPACE) || defined(__ZEPHYR_SUPERVISOR__)\n") |
| gen_defines(i, kernel_only=True) |
| sys.stdout.write("#elif defined(__ZEPHYR_USER__)\n") |
| gen_defines(i, user_only=True) |
| sys.stdout.write("#else /* mixed kernel/user macros */\n") |
| gen_defines(i) |
| sys.stdout.write("#endif /* mixed kernel/user macros */\n\n") |
| |
| sys.stdout.write("#endif /* GEN_SYSCALL_H */\n") |