zephyr/scripts/gen_syscalls.py

248 lines
6.2 KiB
Python
Executable File

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