>From eac6fc3bc1adbfeb95da16e82df4851a9ef6c468 Mon Sep 17 00:00:00 2001
From: Lionel Landwerlin <lionel.landwerlin@openwide.fr>
Date: Tue, 2 Dec 2008 07:40:03 +0100
Subject: [PATCH] Added syscall hooks

---
 linux-user/syscall_hook.c |  178 +++++++++++++++++++++++++++++++++++++++++++++
 linux-user/syscall_hook.h |   43 +++++++++++
 2 files changed, 221 insertions(+), 0 deletions(-)
 create mode 100644 linux-user/syscall_hook.c
 create mode 100644 linux-user/syscall_hook.h

diff --git a/linux-user/syscall_hook.c b/linux-user/syscall_hook.c
new file mode 100644
index 0000000..06fbff3
--- /dev/null
+++ b/linux-user/syscall_hook.c
@@ -0,0 +1,178 @@
+/*
+ *  Syscall hooks
+ *
+ *  Copyright (c) 2008 Lionel Landwerlin
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdlib.h>
+
+#define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0]))
+
+typedef struct _qemu_syscall_hook_t {
+    struct _qemu_syscall_hook_t prev;
+    struct _qemu_syscall_hook_t next;
+
+    unsigned int priority;
+
+    syscall_hook_cb_t callback;
+} qemu_syscall_hook_t;
+
+static qemu_syscall_hook_t *syscall_hooks[MAX_SYSCALL];
+
+static qemu_syscall_hook_t *
+_hook_new (syscall_hook_cb_t callback, unsigned int prio)
+{
+    qemu_syscall_hook_t *hook =
+        (qemu_syscall_hook_t *) calloc (1, sizeof (qemu_syscall_hook_t));
+
+    if (hook) {
+        hook->callback = callback;
+        hook->prio = prio;
+    }
+
+    return hook;
+}
+
+static void
+_hook_free (qemu_syscall_hook_t *hook)
+{
+    if (hook)
+        free (hook);
+}
+
+static void
+_hook_insert_after (int num,
+                    qemu_syscall_hook_t *old,
+                    qemu_syscall_hook_t *new)
+{
+    new->prev = old;
+    new->next = old->next;
+    if (old->next)
+        old->next->prev = new;
+}
+
+static void
+_hook_insert_before (int num,
+                     qemu_syscall_hook_t *old,
+                     qemu_syscall_hook_t *new)
+{
+    new->prev = old->prev;
+    new->next = old;
+    if (old->prev)
+        old->prev->next = new;
+}
+
+static void
+_hook_remove (int num,
+              qemu_syscall_hook_t *hook)
+{
+    if (list->prev)
+        list->prev->next = list->next;
+    if (list->next)
+        list->next->prev = list->prev;
+    if (list == syscall_hooks[num])
+        syscall_hooks[num] = list->prev ? list->prev : list->next;
+}
+
+void
+qemu_syscall_hook_init (void)
+{
+    int i;
+
+    for (i = 0 ; i < ARRAY_SIZE (syscall_hooks) ; i++)
+        syscall_hooks[i] = NULL;
+}
+
+int
+qemu_syscall_hook_register (int num, syscall_hook_cb_t callback, unsigned int prio)
+{
+    qemu_syscall_hook_t *new;
+
+    if (num >= ARRAY_SIZE (syscall_hooks))
+        return 0;
+
+    new = _hook_new (callback, prio);
+
+    if (syscall_hooks[num] == NULL) {
+        syscall_hooks[num] = new;
+    } else {
+        qemu_syscall_hook_t *plist, *nlist;
+
+        plist = NULL;
+        nlist = syscall_hooks[num];
+
+        while (nlist &&
+               nlist->prio < prio) {
+            plist = nlist;
+            nlist = nlist->next;
+        }
+
+        if (nlist) {
+            _insert_before (nlist, new);
+            if (nlist == syscall_hooks[num])
+                syscall_hooks[num] = new;
+        } else {
+            _insert_after (plist, new);
+        }
+    }
+
+    return 1;
+}
+
+void
+qemu_syscall_hook_unregister (int num, syscall_hook_cb_t callback)
+{
+    qemu_syscall_hook_t *list;
+
+    if (num >= ARRAY_SIZE (syscall_hooks))
+        return;
+
+    list = syscall_hooks[num];
+
+    while (list &&
+           (list->callback != callback))
+        list = list->next;
+
+    if (list) {
+        _hook_remove (num, list);
+        _hook_free (list);
+    }
+}
+
+int
+qemu_syscall_traverse (void *cpu_env, int num,
+                       abi_long arg1, abi_long arg2,
+                       abi_long arg3, abi_long arg4,
+                       abi_long arg5, abi_long arg6)
+{
+    qemu_syscall_hook_t *list;
+
+    if (num >= ARRAY_SIZE (syscall_hooks))
+        return 0;
+
+    list = syscall_hooks[num];
+
+    while (list) {
+        if (list->callback (cpu_env, num,
+                            arg1, arg2, arg3,
+                            arg4, arg5, arg5))
+            break;
+
+        list = list->next;
+    }
+
+    return (list == NULL);
+}
diff --git a/linux-user/syscall_hook.h b/linux-user/syscall_hook.h
new file mode 100644
index 0000000..ad6a69b
--- /dev/null
+++ b/linux-user/syscall_hook.h
@@ -0,0 +1,43 @@
+/*
+ *  Syscall hooks
+ *
+ *  Copyright (c) 2008 Lionel Landwerlin
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef __SYSCALL_HOOK_H__
+#define __SYSCALL_HOOK_H__
+
+#include "qemu.h"
+
+#define MAX_SYSCALL (500) // How much ???
+
+typedef int (*syscall_hook_cb_t) (void *cpu_env, int num,
+                                  abi_long arg1, abi_long arg2,
+                                  abi_long arg3, abi_long arg4,
+                                  abi_long arg5, abi_long arg6);
+
+void qemu_syscall_hook_init (void);
+
+int qemu_syscall_hook_register (int num, syscall_hook_cb_t callback,
+                                unsigned int prio);
+void qemu_syscall_hook_unregister (int num, syscall_hook_cb_t callback);
+
+int qemu_syscall_traverse (void *cpu_env, int num,
+                           abi_long arg1, abi_long arg2,
+                           abi_long arg3, abi_long arg4,
+                           abi_long arg5, abi_long arg6);
+
+#endif /* __SYSCALL_HOOK_H__ */
-- 
1.5.6.5

