Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

#!/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()