1#!/usr/bin/env python3 2# 3# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 4# 5# SPDX-License-Identifier: BSD-2-Clause or GPL-2.0-only 6# 7 8# seL4 System Call ID Generator 9# ============================== 10 11from __future__ import print_function 12from jinja2 import Environment, BaseLoader 13import argparse 14import re 15import sys 16import xml.dom.minidom 17import pkg_resources 18# We require jinja2 to be at least version 2.10 as we use the 'namespace' feature from 19# that version 20pkg_resources.require("jinja2>=2.10") 21 22 23COMMON_HEADER = """ 24/* This header was generated by kernel/tools/syscall_header_gen.py. 25 * 26 * To add a system call number, edit kernel/include/api/syscall.xml 27 * 28 */""" 29 30KERNEL_HEADER_TEMPLATE = """/* 31 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 32 * 33 * SPDX-License-Identifier: GPL-2.0-only 34 */ 35 36""" + COMMON_HEADER + """ 37#ifndef __ARCH_API_SYSCALL_H 38#define __ARCH_API_SYSCALL_H 39 40#ifdef __ASSEMBLER__ 41 42/* System Calls */ 43{%- set ns = namespace(syscall_number=-1) -%} 44{%- for condition, list in assembler -%} 45 {%- for syscall in list %} 46#define SYSCALL_{{upper(syscall)}} ({{ns.syscall_number}}) 47 {%- set ns.syscall_number = ns.syscall_number -1 -%} 48 {%- endfor %} 49{%- endfor %} 50 51#endif 52 53#define SYSCALL_MAX (-1) 54#define SYSCALL_MIN ({{ns.syscall_number+ 1}}) 55 56#ifndef __ASSEMBLER__ 57 58enum syscall { 59{%- set ns.syscall_number = -1 -%} 60{% for condition, list in enum %} 61 {%- if condition | length > 0 %} 62#if {{condition}} 63 {%- endif %} 64 {%- for syscall in list %} 65 Sys{{syscall}} = {{ns.syscall_number}}, 66 {%- set ns.syscall_number = ns.syscall_number -1 -%} 67 {%- endfor %} 68 {%- if condition | length > 0 %} 69#endif /* {{condition}} */ 70 {%- endif %} 71{%- endfor %} 72}; 73typedef word_t syscall_t; 74 75/* System call names */ 76#ifdef CONFIG_DEBUG_BUILD 77static char *syscall_names[] UNUSED = { 78{%- set ns.syscall_number = 1 -%} 79{%- for condition, list in assembler %} 80 {%- for syscall in list %} 81 [{{ns.syscall_number}}] = "{{syscall}}", 82 {%- set ns.syscall_number = ns.syscall_number +1 -%} 83 {%- endfor %} 84{%- endfor %} 85}; 86#endif /* CONFIG_DEBUG_BUILD */ 87#endif 88 89#endif /* __ARCH_API_SYSCALL_H */ 90 91""" 92 93LIBSEL4_HEADER_TEMPLATE = """/* 94 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 95 * 96 * SPDX-License-Identifier: BSD-2-Clause 97 */ 98 99""" + COMMON_HEADER + """ 100#ifndef __LIBSEL4_SYSCALL_H 101#define __LIBSEL4_SYSCALL_H 102 103#include <autoconf.h> 104 105typedef enum { 106{%- set ns = namespace(syscall_number=-1) -%} 107{%- for condition, list in enum %} 108 {%- if condition | length > 0 %} 109#if {{condition}} 110 {%- endif %} 111 {%- for syscall in list %} 112 seL4_Sys{{syscall}} = {{ns.syscall_number}}, 113 {%- set ns.syscall_number = ns.syscall_number - 1 -%} 114 {%- endfor %} 115 {%- if condition | length > 0 %} 116#endif /* {{condition}} */ 117 {%- endif %} 118{%- endfor %} 119 SEL4_FORCE_LONG_ENUM(seL4_Syscall_ID) 120} seL4_Syscall_ID; 121 122#endif /* __ARCH_API_SYSCALL_H */ 123 124""" 125 126 127def parse_args(): 128 parser = argparse.ArgumentParser(description="""Generate seL4 syscall API constants 129 and associated header files""") 130 parser.add_argument('--xml', type=argparse.FileType('r'), 131 help='Name of xml file with syscall name definitions', required=True) 132 parser.add_argument('--kernel_header', type=argparse.FileType('w'), 133 help='Name of file to generate for kernel') 134 parser.add_argument('--libsel4_header', type=argparse.FileType('w'), 135 help='Name of file to generate for libsel4') 136 parser.add_argument('--mcs', action='store_true', 137 help='Generate MCS api') 138 139 result = parser.parse_args() 140 141 if result.kernel_header is None and result.libsel4_header is None: 142 print("Error: must provide either kernel_header or libsel4_header", 143 file=sys.stderr) 144 parser.print_help() 145 exit(-1) 146 147 return result 148 149 150def parse_syscall_list(element): 151 syscalls = [] 152 for config in element.getElementsByTagName("config"): 153 config_condition = config.getAttribute("condition") 154 config_syscalls = [] 155 for syscall in config.getElementsByTagName("syscall"): 156 name = str(syscall.getAttribute("name")) 157 config_syscalls.append(name) 158 syscalls.append((config_condition, config_syscalls)) 159 160 # sanity check 161 assert len(syscalls) != 0 162 163 return syscalls 164 165 166def parse_xml(xml_file, mcs): 167 # first check if the file is valid xml 168 try: 169 doc = xml.dom.minidom.parse(xml_file) 170 except: 171 print("Error: invalid xml file.", file=sys.stderr) 172 sys.exit(-1) 173 174 tag = "api-mcs" if mcs else "api-master" 175 api = doc.getElementsByTagName(tag) 176 if len(api) != 1: 177 print("Error: malformed xml. Only one api element allowed", 178 file=sys.stderr) 179 sys.exit(-1) 180 181 configs = api[0].getElementsByTagName("config") 182 if len(configs) != 1: 183 print("Error: api element only supports 1 config element", 184 file=sys.stderr) 185 sys.exit(-1) 186 187 if len(configs[0].getAttribute("name")) != 0: 188 print("Error: api element config only supports an empty name", 189 file=sys.stderr) 190 sys.exit(-1) 191 192 # debug elements are optional 193 debug = doc.getElementsByTagName("debug") 194 if len(debug) != 1: 195 debug_element = None 196 else: 197 debug_element = debug[0] 198 199 api_elements = parse_syscall_list(api[0]) 200 debug = parse_syscall_list(debug_element) 201 202 return (api_elements, debug) 203 204 205def convert_to_assembler_format(s): 206 words = re.findall('[A-Z][A-Z]?[^A-Z]*', s) 207 return '_'.join(words).upper() 208 209 210def generate_kernel_file(kernel_header, api, debug): 211 template = Environment(loader=BaseLoader, trim_blocks=False, 212 lstrip_blocks=False).from_string(KERNEL_HEADER_TEMPLATE) 213 data = template.render({'assembler': api, 'enum': api + debug, 214 'upper': convert_to_assembler_format}) 215 kernel_header.write(data) 216 217 218def generate_libsel4_file(libsel4_header, syscalls): 219 template = Environment(loader=BaseLoader, trim_blocks=False, 220 lstrip_blocks=False).from_string(LIBSEL4_HEADER_TEMPLATE) 221 data = template.render({'enum': syscalls}) 222 libsel4_header.write(data) 223 224 225if __name__ == "__main__": 226 args = parse_args() 227 228 (api, debug) = parse_xml(args.xml, args.mcs) 229 args.xml.close() 230 231 if (args.kernel_header is not None): 232 generate_kernel_file(args.kernel_header, api, debug) 233 args.kernel_header.close() 234 235 if (args.libsel4_header is not None): 236 generate_libsel4_file(args.libsel4_header, api + debug) 237 args.libsel4_header.close() 238