syscalls: introduce dispatcher class
authorDmitry Selyutin <ghostmansd@gmail.com>
Mon, 18 Sep 2023 19:22:07 +0000 (22:22 +0300)
committerDmitry Selyutin <ghostmansd@gmail.com>
Mon, 18 Sep 2023 19:22:07 +0000 (22:22 +0300)
src/openpower/syscalls/__init__.py

index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..28a05cc71f189d23b705247595e23a0d72671cb9 100644 (file)
@@ -0,0 +1,97 @@
+import ctypes
+import functools
+import inspect
+import json
+import pathlib
+
+
+class Dispatcher:
+    def __init__(self, guest, host, logger=None, table=None):
+        if table is None:
+            path = pathlib.Path(inspect.getfile(self.__class__))
+            path = (path.parent / "syscalls.json")
+            with open(path, "r", encoding="UTF-8") as stream:
+                table = json.load(stream)
+        if not isinstance(table, dict):
+            raise ValueError("dict instance expected")
+        if "sysnums" not in table or "sysargs" not in table:
+            raise ValueError("sysnums and sysargs keys expected")
+
+        if logger is None:
+            logger = lambda *args, **kwargs: None
+
+        def i386(sysnums):
+            yield from sysnums["x86-32"]["i386"].items()
+
+        def amd64(sysnums):
+            yield from sysnums["x86-64"]["common"].items()
+            yield from sysnums["x86-64"]["64"].items()
+
+        def ppc(sysnums):
+            yield from sysnums["ppc"]["nospu"].items()
+            yield from sysnums["ppc"]["common"].items()
+            yield from sysnums["ppc"]["32"].items()
+
+        def ppc64(sysnums):
+            yield from sysnums["ppc"]["nospu"].items()
+            yield from sysnums["ppc"]["common"].items()
+            yield from sysnums["ppc"]["64"].items()
+
+        arch = {
+            "i386": i386,
+            "amd64": amd64,
+            "ppc": ppc,
+            "ppc64": ppc64,
+        }
+        sysnums = table["sysnums"]
+        sysargs = table["sysargs"]
+
+        self.__guest = dict(arch[guest](sysnums))
+        self.__host = dict(arch[host](sysnums))
+        self.__parameters = sysargs
+        self.__logger = logger
+        self.__libc = ctypes.CDLL(None)
+
+        return super().__init__()
+
+    def __getattr__(self, identifier):
+        return functools.partial(self.__call__, identifier=identifier)
+
+    def __call__(self, *arguments, identifier=None):
+        if isinstance(identifier, int):
+            identifier = str(identifier)
+            if identifier not in self.__guest:
+                raise KeyError(identifier)
+            entry = self.__guest[identifier][1][0]
+            identifier = self.__guest[identifier][0]
+        else:
+            if not isinstance(identifier, str):
+                raise ValueError(identifier)
+            entry = identifier
+            if not entry.startswith(("compat_sys_", "sys_")):
+                entry = f"sys_{entry}"
+
+        if ((identifier not in self.__guest) or
+                (identifier not in self.__host)):
+            raise KeyError(identifier)
+
+        parameters = tuple(self.__parameters[entry].items())
+        if len(arguments) != len(parameters):
+            raise ValueError("conflict between arguments and parameters")
+
+        guest = int(self.__guest[identifier])
+        host = int(self.__host[identifier])
+        self.__logger(f"{identifier} {guest} => {host}")
+        for index in range(len(arguments)):
+            value = arguments[index]
+            if not isinstance(value, int):
+                raise ValueError("integer argument expected")
+            name = parameters[index][0]
+            ctype = parameters[index][1]
+            self.__logger(f"    0x{value:016x} {name} ({ctype})")
+
+        syscall = self.__libc.syscall
+        syscall.restype = ctypes.c_long
+        syscall.argtypes = ([ctypes.c_long] * len(arguments))
+
+        return int(syscall(ctypes.c_ulong(host)))