diff options
-rw-r--r-- | libc/sysdeps/linux/or1k/Makefile.arch | 2 | ||||
-rw-r--r-- | libc/sysdeps/linux/or1k/clone.c | 127 | ||||
-rw-r--r-- | libc/sysdeps/linux/or1k/or1k_clone.S | 77 | ||||
-rw-r--r-- | libc/sysdeps/linux/or1k/sysdep.h | 104 |
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 */ |