#!/usr/bin/env python3 # # Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) # # SPDX-License-Identifier: BSD-2-Clause or GPL-2.0-only # # seL4 System Call ID Generator # ============================== from __future__ import print_function from jinja2 import Environment, BaseLoader import argparse import re import sys import xml.dom.minidom import pkg_resources # We require jinja2 to be at least version 2.10 as we use the 'namespace' feature from # that version pkg_resources.require("jinja2>=2.10") COMMON_HEADER = """ /* This header was generated by kernel/tools/syscall_header_gen.py. * * To add a system call number, edit kernel/include/api/syscall.xml * */""" KERNEL_HEADER_TEMPLATE = """/* * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) * * SPDX-License-Identifier: GPL-2.0-only */ """ + COMMON_HEADER + """ #ifndef __ARCH_API_SYSCALL_H #define __ARCH_API_SYSCALL_H #ifdef __ASSEMBLER__ /* System Calls */ {%- set ns = namespace(syscall_number=-1) -%} {%- for condition, list in assembler -%} {%- for syscall in list %} #define SYSCALL_{{upper(syscall)}} ({{ns.syscall_number}}) {%- set ns.syscall_number = ns.syscall_number -1 -%} {%- endfor %} {%- endfor %} #endif #define SYSCALL_MAX (-1) #define SYSCALL_MIN ({{ns.syscall_number+ 1}}) #ifndef __ASSEMBLER__ enum syscall { {%- set ns.syscall_number = -1 -%} {% for condition, list in enum %} {%- if condition | length > 0 %} #if {{condition}} {%- endif %} {%- for syscall in list %} Sys{{syscall}} = {{ns.syscall_number}}, {%- set ns.syscall_number = ns.syscall_number -1 -%} {%- endfor %} {%- if condition | length > 0 %} #endif /* {{condition}} */ {%- endif %} {%- endfor %} }; typedef word_t syscall_t; /* System call names */ #ifdef CONFIG_DEBUG_BUILD static char *syscall_names[] UNUSED = { {%- set ns.syscall_number = 1 -%} {%- for condition, list in assembler %} {%- for syscall in list %} [{{ns.syscall_number}}] = "{{syscall}}", {%- set ns.syscall_number = ns.syscall_number +1 -%} {%- endfor %} {%- endfor %} }; #endif /* CONFIG_DEBUG_BUILD */ #endif #endif /* __ARCH_API_SYSCALL_H */ """ LIBSEL4_HEADER_TEMPLATE = """/* * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) * * SPDX-License-Identifier: BSD-2-Clause */ """ + COMMON_HEADER + """ #ifndef __LIBSEL4_SYSCALL_H #define __LIBSEL4_SYSCALL_H #include typedef enum { {%- set ns = namespace(syscall_number=-1) -%} {%- for condition, list in enum %} {%- if condition | length > 0 %} #if {{condition}} {%- endif %} {%- for syscall in list %} seL4_Sys{{syscall}} = {{ns.syscall_number}}, {%- set ns.syscall_number = ns.syscall_number - 1 -%} {%- endfor %} {%- if condition | length > 0 %} #endif /* {{condition}} */ {%- endif %} {%- endfor %} SEL4_FORCE_LONG_ENUM(seL4_Syscall_ID) } seL4_Syscall_ID; #endif /* __ARCH_API_SYSCALL_H */ """ def parse_args(): parser = argparse.ArgumentParser(description="""Generate seL4 syscall API constants and associated header files""") parser.add_argument('--xml', type=argparse.FileType('r'), help='Name of xml file with syscall name definitions', required=True) parser.add_argument('--kernel_header', type=argparse.FileType('w'), help='Name of file to generate for kernel') parser.add_argument('--libsel4_header', type=argparse.FileType('w'), help='Name of file to generate for libsel4') parser.add_argument('--mcs', action='store_true', help='Generate MCS api') result = parser.parse_args() if result.kernel_header is None and result.libsel4_header is None: print("Error: must provide either kernel_header or libsel4_header", file=sys.stderr) parser.print_help() exit(-1) return result def parse_syscall_list(element): syscalls = [] for config in element.getElementsByTagName("config"): config_condition = config.getAttribute("condition") config_syscalls = [] for syscall in config.getElementsByTagName("syscall"): name = str(syscall.getAttribute("name")) config_syscalls.append(name) syscalls.append((config_condition, config_syscalls)) # sanity check assert len(syscalls) != 0 return syscalls def parse_xml(xml_file, mcs): # first check if the file is valid xml try: doc = xml.dom.minidom.parse(xml_file) except: print("Error: invalid xml file.", file=sys.stderr) sys.exit(-1) tag = "api-mcs" if mcs else "api-master" api = doc.getElementsByTagName(tag) if len(api) != 1: print("Error: malformed xml. Only one api element allowed", file=sys.stderr) sys.exit(-1) configs = api[0].getElementsByTagName("config") if len(configs) != 1: print("Error: api element only supports 1 config element", file=sys.stderr) sys.exit(-1) if len(configs[0].getAttribute("name")) != 0: print("Error: api element config only supports an empty name", file=sys.stderr) sys.exit(-1) # debug elements are optional debug = doc.getElementsByTagName("debug") if len(debug) != 1: debug_element = None else: debug_element = debug[0] api_elements = parse_syscall_list(api[0]) debug = parse_syscall_list(debug_element) return (api_elements, debug) def convert_to_assembler_format(s): words = re.findall('[A-Z][A-Z]?[^A-Z]*', s) return '_'.join(words).upper() def generate_kernel_file(kernel_header, api, debug): template = Environment(loader=BaseLoader, trim_blocks=False, lstrip_blocks=False).from_string(KERNEL_HEADER_TEMPLATE) data = template.render({'assembler': api, 'enum': api + debug, 'upper': convert_to_assembler_format}) kernel_header.write(data) def generate_libsel4_file(libsel4_header, syscalls): template = Environment(loader=BaseLoader, trim_blocks=False, lstrip_blocks=False).from_string(LIBSEL4_HEADER_TEMPLATE) data = template.render({'enum': syscalls}) libsel4_header.write(data) if __name__ == "__main__": args = parse_args() (api, debug) = parse_xml(args.xml, args.mcs) args.xml.close() if (args.kernel_header is not None): generate_kernel_file(args.kernel_header, api, debug) args.kernel_header.close() if (args.libsel4_header is not None): generate_libsel4_file(args.libsel4_header, api + debug) args.libsel4_header.close()