syscall: handle architecture aliases
[openpower-isa.git] / src / openpower / syscalls / __init__.py
1 import ctypes
2 import errno
3 import inspect
4 import json
5 import pathlib
6
7
8
9 ARCH = {
10 "powerpc": "ppc",
11 "powerpc64": "ppc64",
12 "ppc64le": "ppc64",
13 "i686": "i386",
14 "x86_64": "amd64",
15 "x64": "amd64",
16 "arm64": "aarch64",
17 "aarch64_be": "aarch64",
18 "armv8b": "aarch64",
19 "armv8l": "aarch64",
20 }
21
22
23 class Syscall:
24 def __init__(self, entry, guest, host, parameters):
25 if not isinstance(entry, str):
26 raise ValueError(entry)
27 if not isinstance(guest, int):
28 raise ValueError(guest)
29 if not isinstance(parameters, tuple):
30 raise ValueError(parameters)
31
32 self.__entry = entry
33 self.__guest = guest
34 self.__host = host
35 self.__parameters = parameters
36
37 return super().__init__()
38
39 @property
40 def entry(self):
41 return self.__entry
42
43 @property
44 def guest(self):
45 return self.__guest
46
47 @property
48 def host(self):
49 return self.__host
50
51 def __len__(self):
52 return len(self.__parameters)
53
54 def __repr__(self):
55 return f"{self.__class__.__name__}({self.entry} {self.guest}=>{self.host})"
56
57 def __call__(self, *arguments):
58 if len(arguments) < len(self):
59 raise ValueError("conflict between arguments and parameters")
60
61 for index in range(len(arguments)):
62 value = arguments[index]
63 if not isinstance(value, int):
64 raise ValueError("integer argument expected")
65
66 libc = ctypes.CDLL(None)
67 syscall = libc.syscall
68 restype = syscall.restype
69 argtypes = syscall.argtypes
70 syscall.restype = ctypes.c_long
71 syscall.argtypes = ([ctypes.c_long] * len(arguments))
72 res = int(syscall(ctypes.c_long(self.host), *map(ctypes.c_long, arguments)))
73 syscall.restype = restype
74 syscall.argtypes = argtypes
75 return res
76
77
78 class UnimplementedSyscall(Syscall):
79 def __init__(self, guest):
80 return super().__init__(entry="sys_ni_syscall", guest=guest, host=-1, parameters=tuple())
81
82 def __call__(self, *arguments):
83 return -errno.ENOSYS
84
85
86 class UnknownSyscall(Syscall):
87 def __init__(self, entry, guest):
88 return super().__init__(entry=entry, guest=guest, host=-1, parameters=tuple())
89
90 def __call__(self, *arguments):
91 raise NotImplemented
92
93
94 class Dispatcher:
95 def __init__(self, guest, host, logger=None, table=None):
96 if table is None:
97 path = (pathlib.Path(__file__).parent / "syscalls.json")
98 with open(path, "r", encoding="UTF-8") as stream:
99 table = json.load(stream)
100 if not isinstance(table, dict):
101 raise ValueError("dict instance expected")
102 if "sysnums" not in table or "sysargs" not in table:
103 raise ValueError("sysnums and sysargs keys expected")
104
105 if logger is None:
106 logger = lambda *args, **kwargs: None
107
108 guest = ARCH.get(guest, guest)
109 host = ARCH.get(host, host)
110
111 def i386(sysnums):
112 yield from sysnums["x86-32"]["i386"].items()
113
114 def amd64(sysnums):
115 yield from sysnums["x86-64"]["common"].items()
116 yield from sysnums["x86-64"]["64"].items()
117
118 def arm(sysnums):
119 yield from sysnums["arm"]["common"].items()
120
121 def aarch64(sysnums):
122 yield from sysnums["generic"]["arch64"].items()
123
124 def ppc(sysnums):
125 yield from sysnums["ppc"]["nospu"].items()
126 yield from sysnums["ppc"]["common"].items()
127 yield from sysnums["ppc"]["32"].items()
128
129 def ppc64(sysnums):
130 yield from sysnums["ppc"]["nospu"].items()
131 yield from sysnums["ppc"]["common"].items()
132 yield from sysnums["ppc"]["64"].items()
133
134 def riscv32(sysnums):
135 yield from sysnums["generic"]["arch32"].items()
136
137 def riscv64(sysnums):
138 yield from sysnums["generic"]["arch64"].items()
139
140 arch = {
141 "i386": i386,
142 "amd64": amd64,
143 "arm": arm,
144 "aarch64": aarch64,
145 "ppc": ppc,
146 "ppc64": ppc64,
147 "riscv32": riscv32,
148 "riscv64": riscv64,
149 }
150 sysnums = table["sysnums"]
151 sysargs = table["sysargs"]
152
153 self.__guest = dict(arch[guest](sysnums))
154 self.__host = dict(arch[host](sysnums))
155 self.__parameters = sysargs
156 self.__logger = logger
157 self.__libc = ctypes.CDLL(None)
158
159 return super().__init__()
160
161 def __iter__(self):
162 identifiers = sorted(map(int, filter(str.isnumeric, self.__guest)))
163 for identifier in identifiers:
164 entry = self.__guest[str(identifier)][1][0]
165 name = self.__guest[str(identifier)][0]
166 syscall = getattr(self, entry, None)
167 if syscall is None:
168 if entry == "sys_ni_syscall":
169 syscall = UnimplementedSyscall(guest=identifier)
170 else:
171 syscall = UnknownSyscall(entry=entry, guest=identifier)
172 yield syscall
173
174 def __getitem__(self, identifier):
175 if not isinstance(identifier, int):
176 raise ValueError(identifier)
177
178 identifier = str(identifier)
179 entry = self.__guest[identifier][1][0]
180
181 return getattr(self, entry)
182
183 def __getattr__(self, entry):
184 if entry.startswith("compat_sys_"):
185 identifier = entry[len("compat_sys_"):]
186 elif entry.startswith("sys_"):
187 identifier = entry[len("sys_"):]
188 else:
189 raise AttributeError(entry)
190
191 if entry not in self.__parameters:
192 raise AttributeError(entry)
193
194 if identifier not in self.__guest:
195 raise AttributeError(entry)
196
197 if identifier not in self.__host:
198 raise AttributeError(entry)
199
200 guest = int(self.__guest[identifier])
201 host = int(self.__host[identifier])
202 parameters = tuple(self.__parameters[entry].items())
203
204 return Syscall(entry=entry, guest=guest, host=host, parameters=parameters)
205
206 def __call__(self, identifier, *arguments):
207 syscall = self[identifier]
208
209 return syscall(*arguments)