summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWaldemar Brodkorb <wbx@openadk.org>2017-04-17 14:48:50 +0200
committerWaldemar Brodkorb <wbx@openadk.org>2017-05-14 10:29:09 +0200
commit69ea4c1f65bff99575cd7f0210312dc355929240 (patch)
tree43fe11a4e27c85b742e2434f6e8ec0b25576d8af
parentbe6a02b9a0317a7441de9aa2e3e07fe838787d57 (diff)
or1k: add clone() from old GNU libc implementation
-rw-r--r--libc/sysdeps/linux/or1k/Makefile.arch2
-rw-r--r--libc/sysdeps/linux/or1k/clone.c127
-rw-r--r--libc/sysdeps/linux/or1k/or1k_clone.S77
-rw-r--r--libc/sysdeps/linux/or1k/sysdep.h104
4 files changed, 231 insertions, 79 deletions
diff --git a/libc/sysdeps/linux/or1k/Makefile.arch b/libc/sysdeps/linux/or1k/Makefile.arch
index 53d4ed576..f6758fa63 100644
--- a/libc/sysdeps/linux/or1k/Makefile.arch
+++ b/libc/sysdeps/linux/or1k/Makefile.arch
@@ -6,4 +6,4 @@
#
CSRC-y := __syscall_error.c __init_brk.c brk.c sbrk.c clone.c
-SSRC-y := __longjmp.S setjmp.S
+SSRC-y := __longjmp.S setjmp.S or1k_clone.S
diff --git a/libc/sysdeps/linux/or1k/clone.c b/libc/sysdeps/linux/or1k/clone.c
index ebb048ad4..2b61b638f 100644
--- a/libc/sysdeps/linux/or1k/clone.c
+++ b/libc/sysdeps/linux/or1k/clone.c
@@ -1,88 +1,59 @@
-/*
- * 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>
- */
+/* Copyright (C) 1998, 2002, 2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
-#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.
- */
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- /* 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)
- );
+ The GNU C Library 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
+ Lesser General Public License for more details.
- /* Sanity check the arguments */
- err = -EINVAL;
- if (!fn)
- goto syscall_error;
- if (!child_stack)
- goto syscall_error;
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
- 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;
- }
+#include <stdarg.h>
+#include <sysdep.h>
+#include <unistd.h>
- /* NB: from here you exclusively in child thread */
+extern int __or1k_clone (int (*fn)(void *), void *child_stack,
+ int flags, void *arg, pid_t *ptid,
+ void *tls, pid_t *ctid);
- /* 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));
+/* or1k ABI uses stack for varargs, syscall uses registers.
+ * This function moves from varargs to regs. */
+int
+__clone (int (*fn)(void *), void *child_stack,
+ int flags, void *arg, ...
+ /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ )
+{
+ void *ptid;
+ void *tls;
+ void *ctid;
+ va_list ap;
+ int err;
+
+ va_start (ap, arg);
+ ptid = va_arg (ap, void *);
+ tls = va_arg (ap, void *);
+ ctid = va_arg (ap, void *);
+ va_end (ap);
+
+ /* Sanity check the arguments */
+ err = -EINVAL;
+ if (!fn)
+ goto syscall_error;
+ if (!child_stack)
+ goto syscall_error;
+
+ return __or1k_clone (fn, child_stack, flags, arg, ptid, tls, ctid);
syscall_error:
- __set_errno (-err);
- return -1;
+ __set_errno (-err);
+ return -1;
}
+weak_alias (__clone, clone)
diff --git a/libc/sysdeps/linux/or1k/or1k_clone.S b/libc/sysdeps/linux/or1k/or1k_clone.S
new file mode 100644
index 000000000..a2c16ac9e
--- /dev/null
+++ b/libc/sysdeps/linux/or1k/or1k_clone.S
@@ -0,0 +1,77 @@
+#include <sysdep.h>
+
+#define CLONE_VM 0x00000100
+#define CLONE_THREAD 0x00010000
+
+.text
+ENTRY(__or1k_clone)
+
+ /* To handle GCC varargs we need to use our __clone wrapper to pop
+ everything from the stack for us.
+ Now everything is placed in the registers which saves us a lot
+ of trouble.
+
+ The userland implementation is:
+ int clone (int (*fn)(void *), void *child_stack,
+ int flags, void *arg, pid_t *ptid,
+ struct user_desc *tls, pid_t *ctid);
+
+ The kernel entry is:
+ int clone (long flags, void *child_stack, int *parent_tid,
+ int *child_tid, struct void *tls)
+ NB: tls isn't really an argument, it is read from r7 directly.
+ */
+
+ /* Put 'fn', 'arg' and 'flags' on child stack */
+ l.addi r4, r4, -12
+ l.sw 8(r4), r3
+ l.sw 4(r4), r6
+ l.sw 0(r4), r5
+
+ l.ori r3, r5, 0
+ /* child_stack is already in r4 */
+ l.ori r5, r7, 0
+ l.lwz r6, 0(r1)
+ l.ori r7, r8, 0
+
+ DO_CALL (clone)
+
+ l.sfgeui r11, 0xf001
+ l.bf L(error)
+ l.nop
+
+ /* If we are not the child, return the pid */
+ l.sfeqi r11, 0
+ l.bf L(child)
+ l.nop
+
+ l.jr r9
+ l.nop
+
+L(child):
+ /* Load flags */
+ l.lwz r3, 0(r1)
+
+ /* Update PID, but only if we do not share the same PID
+ as our parent */
+ l.srli r4, r3, 16
+ l.andi r4, r4, hi(CLONE_THREAD)
+ l.sfnei r4, 0
+ l.bf L(oldpid)
+ l.nop
+
+L(oldpid):
+ /* Load function from stack */
+ l.lwz r11, 8(r1)
+ l.jalr r11
+ l.lwz r3, 4(r1)
+
+ /* Exit the child thread */
+ l.jal HIDDEN_JUMPTARGET(_exit)
+ l.ori r3, r11, 0
+
+L(error):
+ l.j SYSCALL_ERROR_NAME
+ l.ori r3,r11,0
+
+PSEUDO_END (__or1k_clone)
diff --git a/libc/sysdeps/linux/or1k/sysdep.h b/libc/sysdeps/linux/or1k/sysdep.h
new file mode 100644
index 000000000..4dba3f2a0
--- /dev/null
+++ b/libc/sysdeps/linux/or1k/sysdep.h
@@ -0,0 +1,104 @@
+/* Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+The GNU C Library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with the GNU C Library; if not, see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef _LINUX_OR1K_SYSDEP_H
+#define _LINUX_OR1K_SYSDEP_H 1
+
+#include <common/sysdep.h>
+#include <sys/syscall.h>
+
+/* In order to get __set_errno() definition in INLINE_SYSCALL. */
+#ifndef __ASSEMBLER__
+#include <errno.h>
+#endif
+
+#undef SYS_ify
+#define SYS_ify(syscall_name) (__NR_##syscall_name)
+
+#ifdef __ASSEMBLER__
+
+/* Local label name for asm code. */
+#define L(name) .L##name
+
+#undef ret_ERRVAL
+#define ret_ERRVAL l.jr r9; l.nop
+#define ret_NOERRNO l.jr r9; l.nop
+
+#undef DO_CALL
+#define DO_CALL(syscall_name) \
+ l.addi r11, r0, SYS_ify (syscall_name); \
+ l.sys 1; \
+ l.nop
+
+#define PSEUDO(name, syscall_name, args) \
+ ENTRY (name); \
+ DO_CALL(syscall_name); \
+ /* if -4096 < ret < 0 holds, it's an error */ \
+ l.sfgeui r11, 0xf001; \
+ l.bf L(pseudo_end); \
+ l.nop
+
+#define PSEUDO_NOERRNO(name, syscall_name, args) \
+ ENTRY (name); \
+ DO_CALL(syscall_name)
+
+#define PSEUDO_END(name) \
+L(pseudo_end): \
+ l.j SYSCALL_ERROR_NAME; \
+ l.ori r3,r11,0; \
+ END (name)
+
+#define PSEUDO_END_NOERRNO(name) \
+ END (name)
+
+#ifndef PIC
+/* For static code, on error jump to __syscall_error directly. */
+# define SYSCALL_ERROR_NAME __syscall_error
+#elif NOT_IN_libc
+/* Use the internal name for libc/libpthread shared objects. */
+# define SYSCALL_ERROR_NAME __GI___syscall_error
+#else
+/* Otherwise, on error do a full PLT jump. */
+# define SYSCALL_ERROR_NAME plt(__syscall_error)
+#endif
+
+/* Make use of .size directive. */
+#define ASM_SIZE_DIRECTIVE(name) .size name,.-name;
+
+/* Define an entry point visible from C. */
+#define ENTRY(name) \
+ .globl C_SYMBOL_NAME(name); \
+ .type C_SYMBOL_NAME(name),@function; \
+ .align 4; \
+ C_LABEL(name) \
+ cfi_startproc; \
+
+#undef END
+#define END(name) \
+ cfi_endproc; \
+ ASM_SIZE_DIRECTIVE(name)
+
+/* Since C identifiers are not normally prefixed with an underscore
+ on this system, the asm identifier `syscall_error' intrudes on the
+ C name space. Make sure we use an innocuous name. */
+#define syscall_error __syscall_error
+
+/* Specify the size in bytes of a machine register. */
+#define REGSIZE 4
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* linux/or1k/sysdep.h */