9 from . import Dispatcher
10 from . import UnknownSyscall
13 def rename_entry(entry
):
14 if entry
== "sys_newuname":
19 def collect_sysnums(tree
):
20 whitespace
= re
.compile(r
"\s+")
23 name
= r
"[A-Za-z0-9_]+"
24 identifier
= rf
"__NR(?:3264)?_{name}"
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}\))"
31 pattern0
= re
.compile(rf
"^#define\s+({identifier})\s+({number})$", re
.M
)
32 pattern1
= re
.compile("|".join(map(transform
, {
39 path
= (tree
/ "include/uapi/asm-generic/unistd.h")
40 with
open(path
, mode
="r", encoding
="UTF-8") as stream
:
44 for match
in pattern0
.finditer(data
):
45 identifier
= match
.group(1)
46 number
= int(match
.group(2))
47 identifiers
[identifier
] = number
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
)
56 for identifier
in ("__NR_arch_specific_syscall", "__NR_syscalls"):
57 del identifiers
[identifier
]
60 "arch32": collections
.defaultdict(),
61 "arch64": collections
.defaultdict(),
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
77 table
[abi
][number
] = (name
, [])
78 table
[abi
][name
] = number
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
)
90 for abi
in dict(table
):
91 if not table
[abi
][number
][1]:
92 del table
[abi
][number
]
95 yield ("generic", table
)
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
))
105 raise ValueError(line
)
106 table
[abi
][number
] = (name
, entries
)
107 table
[abi
][name
] = number
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"),
129 for (arch
, path
) in tables
:
130 yield (arch
, parse(path
=(tree
/ path
)))
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\((.+?)\)")
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
:
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(","))
149 name
= f
"compat_sys_{name}"
152 yield (name
, dict(zip(arguments
[1::2], arguments
[0::2])))
155 def generate_json(tree
):
156 tree
= tree
.expanduser()
158 "sysnums": dict(collect_sysnums(tree
=tree
)),
159 "sysargs": dict(collect_sysargs(tree
=tree
)),
161 print(json
.dumps(table
, indent
=4))
164 class ECallGenerator
:
165 def __init__(self
, **arguments
):
168 return super().__init
__()
174 def __exit__(self
, exc_type
, exc_value
, exc_traceback
):
177 def print(self
, message
):
178 indent
= ((" " * 4 * self
.__level
) if message
else "")
179 print(f
"{indent}{message}")
181 def __call__(self
, guest
, host
):
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),
190 dispatcher
= Dispatcher(guest
=guest
, host
=host
)
191 for syscall
in dispatcher
:
192 limit
= max(limit
, syscall
.guest
)
193 syscalls
[syscall
.guest
] = syscall
195 self
.print("#include <sys/syscall.h>")
197 self
.print("struct ecall_entry {")
199 self
.print("long number;")
200 self
.print("char const *name;")
204 self
.print("static inline struct ecall_entry const *")
205 self
.print("ecall_entry(long id)")
208 (identifier
, *_
) = conventions
[guest
]
209 self
.print("static struct ecall_entry const table[] = {")
211 for index
in range(limit
+ 1):
212 syscall
= syscalls
.get(index
, UnknownSyscall(guest
=index
, entry
=f
"nil"))
213 self
.print(f
"[{index}] = {{")
215 self
.print(f
".number = {syscall.host},")
216 self
.print(f
".name = \"{syscall.entry}\",")
220 self
.print(f
"if (id > {limit})")
222 self
.print(f
"return NULL;")
224 self
.print("return &table[(size_t)id];")
228 self
.print("static inline long")
229 self
.print("ecall_fetch(struct core_t const *cpu, long arguments[6])")
232 (identifier
, *arguments
) = conventions
[guest
]
233 for (index
, argument
) in enumerate(arguments
):
234 self
.print(f
"arguments[{index}] = cpu->reg[{argument}].l;")
236 self
.print(f
"return cpu->reg[{identifier}].l;")
240 self
.print("static inline void")
241 self
.print("ecall_store(long const arguments[6], struct core_t *cpu)")
244 (identifier
, *arguments
) = conventions
[guest
]
245 for (index
, argument
) in enumerate(arguments
):
246 self
.print(f
"cpu->reg[{argument}].l = arguments[{index}];")
252 main_parser
= argparse
.ArgumentParser("lscmg",
253 description
="Linux system calls mapping generator")
254 main_subparsers
= main_parser
.add_subparsers(dest
="generate", required
=True)
256 json_parser
= main_subparsers
.add_parser("json")
257 json_parser
.add_argument("tree",
258 help="path to kernel source tree",
260 json_parser
.set_defaults(generate
=generate_json
)
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())
271 arguments
= dict(vars(main_parser
.parse_args()))
272 generate
= arguments
.pop("generate")
274 return generate(**arguments
)
277 if __name__
== "__main__":