diff options
author | Waldemar Brodkorb <wbx@openadk.org> | 2015-10-08 20:28:39 +0200 |
---|---|---|
committer | Waldemar Brodkorb <wbx@uclibc-ng.org> | 2015-10-09 05:34:32 +0200 |
commit | 4d8e5484afb4978f672a8568ddd12e628fb02724 (patch) | |
tree | 58be2b52d5afb90974cee8303c6966fb0298f7a0 /libc/sysdeps/linux/or1k/clone.c | |
parent | e78a0f58f23347c822c182d1c01f6eb9b9866d60 (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.c | 88 |
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; +} |