summaryrefslogtreecommitdiff
path: root/libc/sysdeps/linux/or1k/clone.c
diff options
context:
space:
mode:
authorWaldemar Brodkorb <wbx@openadk.org>2015-10-08 20:28:39 +0200
committerWaldemar Brodkorb <wbx@uclibc-ng.org>2015-10-09 05:34:32 +0200
commit4d8e5484afb4978f672a8568ddd12e628fb02724 (patch)
tree58be2b52d5afb90974cee8303c6966fb0298f7a0 /libc/sysdeps/linux/or1k/clone.c
parente78a0f58f23347c822c182d1c01f6eb9b9866d60 (diff)
add new architecture support for or1k
Information about Openrisc: http://opencores.org/or1k/Main_Page Integrated from: https://github.com/openrisc/uClibc-or1k
Diffstat (limited to 'libc/sysdeps/linux/or1k/clone.c')
-rw-r--r--libc/sysdeps/linux/or1k/clone.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/libc/sysdeps/linux/or1k/clone.c b/libc/sysdeps/linux/or1k/clone.c
new file mode 100644
index 000000000..ebb048ad4
--- /dev/null
+++ b/libc/sysdeps/linux/or1k/clone.c
@@ -0,0 +1,88 @@
+/*
+ * clone syscall for OpenRISC
+ *
+ * Copyright (c) 2010 Jonas Bonn <jonas@southpole.se>
+ * Copyright (C) 2003 John Williams <jwilliams@itee.uq.edu.au>
+ * Copyright (C) 2002,03 NEC Electronics Corporation
+ * Copyright (C) 2002,03 Miles Bader <miles@gnu.org>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License. See the file COPYING.LIB in the main
+ * directory of this archive for more details.
+ *
+ * OpenRISC port by Jonas Bonn <jonas@southpole.se>
+ */
+
+#include <errno.h>
+#include <sys/syscall.h>
+#include <sched.h>
+#include <unistd.h>
+
+/* The userland implementation is:
+ int clone (int (*fn)(void *arg), void *child_stack, int flags, void *arg, ...)
+ the kernel entry is:
+ int clone (long flags, void *child_stack)
+*/
+
+int
+clone (int (*fn)(void *arg), void *child_stack, int flags, void *arg, ...)
+{
+ int err;
+
+ /* OK, here's the skinny on this one...
+ * OR1K GCC does weird things with varargs functions... the last
+ * parameter is NEVER passed on the stack -- i.e. arg, in this case.
+ * So we need to push at least 'arg' onto the child stack so that
+ * the new thread can find it. Just to be totally safe, we'll
+ * push both 'fn' and 'arg'; that way we don't need to care what
+ * GCC does with parameters, whether they are passed in registers
+ * or on stack.
+ */
+
+ /* Put 'fn' and 'arg' on child stack */
+ __asm__ __volatile__ (
+ "l.sw -4(%0),%1;"
+ "l.sw -8(%0),%2;"
+ :
+ : "r" (child_stack), "r" (fn), "r" (arg)
+ );
+
+ /* Sanity check the arguments */
+ err = -EINVAL;
+ if (!fn)
+ goto syscall_error;
+ if (!child_stack)
+ goto syscall_error;
+
+ err = INLINE_SYSCALL(clone, 2, flags, child_stack);
+
+ /* NB: from here you are in child thread or parent thread.
+ *
+ * Do not use any functions here that may write data _up_
+ * onto the stack because they will overwrite the child's
+ * thread descriptor... i.e. don't use printf
+ */
+
+ if (err < 0)
+ goto syscall_error;
+ else if (err != 0) {
+ return err;
+ }
+
+ /* NB: from here you exclusively in child thread */
+
+ /* Grab 'fn' and 'arg' from child stack */
+ __asm__ __volatile__ (
+ "l.lwz %0,-4(%2);"
+ "l.lwz %1,-8(%2);"
+ : "=&r" (fn), "=r" (arg)
+ : "r" (child_stack)
+ : "0", "1"
+ );
+
+ _exit(fn(arg));
+
+syscall_error:
+ __set_errno (-err);
+ return -1;
+}