syscalls: refactor calls chain
[openpower-isa.git] / src / openpower / syscalls / __init__.py
1 import ctypes
2 import functools
3 import inspect
4 import json
5 import pathlib
6
7
8 class Dispatcher:
9 def __init__(self, guest, host, logger=None, table=None):
10 if table is None:
11 path = pathlib.Path(inspect.getfile(self.__class__))
12 path = (path.parent / "syscalls.json")
13 with open(path, "r", encoding="UTF-8") as stream:
14 table = json.load(stream)
15 if not isinstance(table, dict):
16 raise ValueError("dict instance expected")
17 if "sysnums" not in table or "sysargs" not in table:
18 raise ValueError("sysnums and sysargs keys expected")
19
20 if logger is None:
21 logger = lambda *args, **kwargs: None
22
23 def i386(sysnums):
24 yield from sysnums["x86-32"]["i386"].items()
25
26 def amd64(sysnums):
27 yield from sysnums["x86-64"]["common"].items()
28 yield from sysnums["x86-64"]["64"].items()
29
30 def ppc(sysnums):
31 yield from sysnums["ppc"]["nospu"].items()
32 yield from sysnums["ppc"]["common"].items()
33 yield from sysnums["ppc"]["32"].items()
34
35 def ppc64(sysnums):
36 yield from sysnums["ppc"]["nospu"].items()
37 yield from sysnums["ppc"]["common"].items()
38 yield from sysnums["ppc"]["64"].items()
39
40 arch = {
41 "i386": i386,
42 "amd64": amd64,
43 "ppc": ppc,
44 "ppc64": ppc64,
45 }
46 sysnums = table["sysnums"]
47 sysargs = table["sysargs"]
48
49 self.__guest = dict(arch[guest](sysnums))
50 self.__host = dict(arch[host](sysnums))
51 self.__parameters = sysargs
52 self.__logger = logger
53 self.__libc = ctypes.CDLL(None)
54
55 return super().__init__()
56
57 def __getattr__(self, entry):
58 if entry.startswith("compat_sys_"):
59 identifier = entry[len("compat_sys_"):]
60 elif entry.startswith("sys_"):
61 identifier = entry[len("sys_"):]
62 else:
63 raise AttributeError(entry)
64
65 if entry not in self.__parameters:
66 raise AttributeError(entry)
67
68 if identifier not in self.__guest:
69 raise AttributeError(entry)
70 identifier = int(self.__guest[identifier])
71
72 def syscall(identifier, *arguments):
73 parameters = tuple(self.__parameters[entry].items())
74 if len(arguments) != len(parameters):
75 raise ValueError("conflict between arguments and parameters")
76
77 identifier = str(identifier)
78 identifier = self.__guest[identifier][0]
79 guest = int(self.__guest[identifier])
80 host = int(self.__host[identifier])
81 self.__logger(f"{identifier} {guest} => {host}")
82 for index in range(len(arguments)):
83 value = arguments[index]
84 if not isinstance(value, int):
85 raise ValueError("integer argument expected")
86 name = parameters[index][0]
87 ctype = parameters[index][1]
88 self.__logger(f" 0x{value:016x} {name} ({ctype})")
89
90 syscall = self.__libc.syscall
91 syscall.restype = ctypes.c_long
92 syscall.argtypes = ([ctypes.c_long] * len(arguments))
93
94 return int(syscall(ctypes.c_ulong(host)))
95
96 return functools.partial(syscall, identifier)
97
98 def __call__(self, identifier, *arguments):
99 if not isinstance(identifier, int):
100 raise ValueError(identifier)
101
102 identifier = str(identifier)
103 entry = self.__guest[identifier][1][0]
104 syscall = getattr(self, entry)
105
106 return syscall(*arguments)