syscall: handle arm and aarch64 architectures
[openpower-isa.git] / src / openpower / syscalls / __main__.py
1 import argparse
2 import collections
3 import json
4 import os
5 import pathlib
6 import re
7
8
9 from . import Dispatcher
10 from . import UnknownSyscall
11
12
13 def rename_entry(entry):
14 if entry == "sys_newuname":
15 return "sys_uname"
16 return entry
17
18
19 def collect_sysnums(tree):
20 whitespace = re.compile(r"\s+")
21
22 number = r"[0-9]+"
23 name = r"[A-Za-z0-9_]+"
24 identifier = rf"__NR(?:3264)?_{name}"
25
26 def transform(macro_nr_args):
27 (macro, nr_args) = macro_nr_args
28 args = r",\s*\\*\s*".join([f"({name})"] * nr_args)
29 return rf"(?:({macro})\(({identifier}),\s*\\*\s*{args}\))"
30
31 pattern0 = re.compile(rf"^#define\s+({identifier})\s+({number})$", re.M)
32 pattern1 = re.compile("|".join(map(transform, {
33 "__SC_COMP_3264": 3,
34 "__SC_3264": 2,
35 "__SC_COMP": 2,
36 "__SYSCALL": 1,
37 }.items())))
38
39 path = (tree / "include/uapi/asm-generic/unistd.h")
40 with open(path, mode="r", encoding="UTF-8") as stream:
41 identifiers = {}
42 data = stream.read()
43
44 for match in pattern0.finditer(data):
45 identifier = match.group(1)
46 number = int(match.group(2))
47 identifiers[identifier] = number
48
49 for match in pattern1.finditer(data):
50 groups = (group for group in match.groups() if group is not None)
51 (category, identifier, *entries) = groups
52 entries = tuple(map(rename_entry, entries))
53 number = identifiers[identifier]
54 identifiers[identifier] = (category, number, entries)
55
56 for identifier in ("__NR_arch_specific_syscall", "__NR_syscalls"):
57 del identifiers[identifier]
58
59 table = {
60 "arch32": collections.defaultdict(),
61 "arch64": collections.defaultdict(),
62 }
63
64 for (identifier, (category, number, entries)) in identifiers.items():
65 name = identifier.replace("__NR3264_", "").replace("__NR_", "")
66 (entry, entry32, entry64, compat) = ([None] * 4)
67 if category == "__SC_COMP_3264":
68 (entry32, entry64, compat) = entries
69 elif category == "__SC_3264":
70 (entry32, entry64) = entries
71 elif category == "__SC_COMP":
72 (entry, compat) = entries
73 else:
74 (entry,) = entries
75
76 for abi in table:
77 table[abi][number] = (name, [])
78 table[abi][name] = number
79
80 if entry is not None:
81 table["arch32"][number][1].append(entry)
82 table["arch64"][number][1].append(entry)
83 if entry64 is not None:
84 table["arch64"][number][1].append(entry64)
85 if entry32 is not None:
86 table["arch32"][number][1].append(entry32)
87 if compat is not None:
88 table["arch64"][number][1].append(compat)
89
90 for abi in dict(table):
91 if not table[abi][number][1]:
92 del table[abi][number]
93 del table[abi][name]
94
95 yield ("generic", table)
96
97 def parse(path):
98 table = collections.defaultdict(dict)
99 with open(path, mode="r", encoding="UTF-8") as stream:
100 lines = filter(lambda line: not line.strip().startswith("#"), stream)
101 for line in filter(bool, map(str.strip, lines)):
102 (number, abi, name, *entries) = map(str.strip, whitespace.split(line))
103 entries = tuple(map(rename_entry, entries))
104 if len(entries) > 2:
105 raise ValueError(line)
106 table[abi][number] = (name, entries)
107 table[abi][name] = number
108
109 return table
110
111 tables = (
112 ("alpha", "arch/alpha/kernel/syscalls/syscall.tbl"),
113 ("arm", "arch/arm/tools/syscall.tbl"),
114 ("ia64", "arch/ia64/kernel/syscalls/syscall.tbl"),
115 ("m68k", "arch/m68k/kernel/syscalls/syscall.tbl"),
116 ("microblaze", "arch/microblaze/kernel/syscalls/syscall.tbl"),
117 ("mips-n32", "arch/mips/kernel/syscalls/syscall_n32.tbl"),
118 ("mips-n64", "arch/mips/kernel/syscalls/syscall_n64.tbl"),
119 ("mips-o32", "arch/mips/kernel/syscalls/syscall_o32.tbl"),
120 ("parisc", "arch/parisc/kernel/syscalls/syscall.tbl"),
121 ("ppc", "arch/powerpc/kernel/syscalls/syscall.tbl"),
122 ("s390", "arch/s390/kernel/syscalls/syscall.tbl"),
123 ("sh", "arch/sh/kernel/syscalls/syscall.tbl"),
124 ("sparc", "arch/sparc/kernel/syscalls/syscall.tbl"),
125 ("x86-32", "arch/x86/entry/syscalls/syscall_32.tbl"),
126 ("x86-64", "arch/x86/entry/syscalls/syscall_64.tbl"),
127 ("xtensa", "arch/xtensa/kernel/syscalls/syscall.tbl"),
128 )
129 for (arch, path) in tables:
130 yield (arch, parse(path=(tree / path)))
131
132
133 def collect_sysargs(tree):
134 pattern = re.compile(r"(COMPAT_)?SYSCALL_DEFINE[0-7]\((.*?)\)", re.S | re.M)
135 compat_arg_u64_pattern = re.compile(r"compat_arg_u64_dual\((.+?)\)")
136
137 for (root, _, paths) in os.walk(top=tree):
138 root = pathlib.Path(root)
139 paths = map(lambda path: (root / path), paths)
140 for path in filter(lambda path: path.suffix == ".c", paths):
141 with open(path, mode="r", encoding="UTF-8") as stream:
142 code = stream.read()
143 code = compat_arg_u64_pattern.sub(r"u32, \1_a, u32, \1_b", code)
144 for match in pattern.finditer(code):
145 compat = (match.group(1) is not None)
146 match = match.group(2).replace("\t", "").replace("\n", "")
147 (name, *arguments) = map(str.strip, match.split(","))
148 if compat:
149 name = f"compat_sys_{name}"
150 else:
151 name = f"sys_{name}"
152 yield (name, dict(zip(arguments[1::2], arguments[0::2])))
153
154
155 def generate_json(tree):
156 tree = tree.expanduser()
157 table = {
158 "sysnums": dict(collect_sysnums(tree=tree)),
159 "sysargs": dict(collect_sysargs(tree=tree)),
160 }
161 print(json.dumps(table, indent=4))
162
163
164 class ECallGenerator:
165 def __init__(self, **arguments):
166 self.__level = 0
167
168 return super().__init__()
169
170 def __enter__(self):
171 self.__level += 1
172 return self
173
174 def __exit__(self, exc_type, exc_value, exc_traceback):
175 self.__level -= 1
176
177 def print(self, message):
178 indent = ((" " * 4 * self.__level) if message else "")
179 print(f"{indent}{message}")
180
181 def __call__(self, guest, host):
182 conventions = {
183 "riscv64": (17, 10, 11, 12, 13, 14, 15),
184 "ppc": (0, 3, 4, 5, 6, 7, 8),
185 "ppc64": (0, 3, 4, 5, 6, 7, 8),
186 }
187
188 limit = -1
189 syscalls = {}
190 dispatcher = Dispatcher(guest=guest, host=host)
191 for syscall in dispatcher:
192 limit = max(limit, syscall.guest)
193 syscalls[syscall.guest] = syscall
194
195 self.print("#include <sys/syscall.h>")
196 self.print("")
197 self.print("struct ecall_entry {")
198 with self:
199 self.print("long number;")
200 self.print("char const *name;")
201 self.print("};")
202 self.print("")
203
204 self.print("static inline struct ecall_entry const *")
205 self.print("ecall_entry(long id)")
206 self.print("{")
207 with self:
208 (identifier, *_) = conventions[guest]
209 self.print("static struct ecall_entry const table[] = {")
210 with self:
211 for index in range(limit + 1):
212 syscall = syscalls.get(index, UnknownSyscall(guest=index, entry=f"nil"))
213 self.print(f"[{index}] = {{")
214 with self:
215 self.print(f".number = {syscall.host},")
216 self.print(f".name = \"{syscall.entry}\",")
217 self.print(f"}},")
218 self.print("};")
219 self.print("")
220 self.print(f"if (id > {limit})")
221 with self:
222 self.print(f"return NULL;")
223 self.print("")
224 self.print("return &table[(size_t)id];")
225 self.print("}")
226 self.print("")
227
228 self.print("static inline long")
229 self.print("ecall_fetch(struct core_t const *cpu, long arguments[6])")
230 self.print("{")
231 with self:
232 (identifier, *arguments) = conventions[guest]
233 for (index, argument) in enumerate(arguments):
234 self.print(f"arguments[{index}] = cpu->reg[{argument}].l;")
235 self.print("")
236 self.print(f"return cpu->reg[{identifier}].l;")
237 self.print("}")
238 self.print("")
239
240 self.print("static inline void")
241 self.print("ecall_store(long const arguments[6], struct core_t *cpu)")
242 self.print("{")
243 with self:
244 (identifier, *arguments) = conventions[guest]
245 for (index, argument) in enumerate(arguments):
246 self.print(f"cpu->reg[{argument}].l = arguments[{index}];")
247 self.print("}")
248
249
250
251 def main():
252 main_parser = argparse.ArgumentParser("lscmg",
253 description="Linux system calls mapping generator")
254 main_subparsers = main_parser.add_subparsers(dest="generate", required=True)
255
256 json_parser = main_subparsers.add_parser("json")
257 json_parser.add_argument("tree",
258 help="path to kernel source tree",
259 type=pathlib.Path)
260 json_parser.set_defaults(generate=generate_json)
261
262 ecall_parser = main_subparsers.add_parser("ecall")
263 ecall_parser.add_argument("guest",
264 help="guest architecture",
265 choices=("riscv64", "ppc", "ppc64"))
266 ecall_parser.add_argument("host",
267 help="amd64 architecture",
268 choices=("amd64", "arm", "aarch64"))
269 ecall_parser.set_defaults(generate=ECallGenerator())
270
271 arguments = dict(vars(main_parser.parse_args()))
272 generate = arguments.pop("generate")
273
274 return generate(**arguments)
275
276
277 if __name__ == "__main__":
278 main()