about summary refs log tree commit diff
path: root/sysdeps/unix/sysv/linux/hppa/clone.S
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux/hppa/clone.S')
-rw-r--r--sysdeps/unix/sysv/linux/hppa/clone.S179
1 files changed, 179 insertions, 0 deletions
diff --git a/sysdeps/unix/sysv/linux/hppa/clone.S b/sysdeps/unix/sysv/linux/hppa/clone.S
new file mode 100644
index 0000000000..1a3c6c800d
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/hppa/clone.S
@@ -0,0 +1,179 @@
+/* Copyright (C) 1996-2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by David Huggins-Daines <dhd@debian.org>, 2000.
+   Based on the Alpha version by Richard Henderson <rth@tamu.edu>, 1996.
+
+   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/>.  */
+
+/* clone() is even more special than fork() as it mucks with stacks
+   and invokes a function in the right context after its all over.  */
+
+#include <asm/unistd.h>
+#include <sysdep.h>
+#define _ERRNO_H	1
+#include <bits/errno.h>
+
+/* Non-thread code calls __clone with the following parameters:
+   int clone(int (*fn)(void *arg),
+	     void *child_stack,
+	     int flags,
+	     void *arg)
+
+   NPTL Code will call __clone with the following parameters:
+   int clone(int (*fn)(void *arg),
+	     void *child_stack,
+	     int flags,
+	     void *arg,
+	     int *parent_tidptr,
+	     struct user_desc *newtls,
+	     int *child_pidptr)
+
+   The code should not mangle the extra input registers.
+   Syscall expects:				Input to __clone:
+	4(r25) - function pointer 		(r26, arg0)
+	0(r25) - argument			(r23, arg3)
+	r26 - clone flags.			(r24, arg2)
+	r25+64 - user stack pointer.		(r25, arg1)
+	r24 - parent tid pointer.		(stack - 52)
+	r23 - struct user_desc newtls pointer.	(stack - 56)
+	r22 - child tid pointer.		(stack - 60)
+	r20 - clone syscall number		(constant)
+
+   Return:
+
+	On success the thread ID of the child process is returend in
+	the callers context.
+	On error return -1, and set errno to the value returned by
+	the syscall.
+ */
+
+        .text
+ENTRY(__clone)
+	/* Prologue */
+	stwm	%r4, 64(%sp)
+	stw	%sp, -4(%sp)
+#ifdef PIC
+	stw	%r19, -32(%sp)
+#endif
+
+	/* Sanity check arguments.  */
+	comib,=,n  0, %arg0, .LerrorSanity        /* no NULL function pointers */
+	comib,=,n  0, %arg1, .LerrorSanity        /* no NULL stack pointers */
+
+	/* Save the function pointer, arg, and flags on the new stack.  */
+	stwm    %r26, 64(%r25)
+	stw	%r23, -60(%r25)
+	stw     %r24, -56(%r25)
+	/* Clone arguments are (int flags, void * child_stack) */
+	copy	%r24, %r26		/* flags are first */
+	/* User stack pointer is in the correct register already */
+
+	/* Load args from stack... */
+	ldw	-116(%sp), %r24		/* Load parent_tidptr */
+	ldw	-120(%sp), %r23 	/* Load newtls */
+	ldw	-124(%sp), %r22		/* Load child_tidptr */
+
+	/* Save the PIC register. */
+#ifdef PIC
+	copy	%r19, %r4		/* parent */
+#endif
+
+	/* Do the system call */
+	ble     0x100(%sr2, %r0)
+	ldi	__NR_clone, %r20
+
+	ldi	-4096, %r1
+	comclr,>>= %r1, %ret0, %r0	/* Note: unsigned compare. */
+	b,n	.LerrorRest
+
+	/* Restore the PIC register.  */
+#ifdef PIC
+	copy	%r4, %r19		/* parent */
+#endif
+
+	comib,=,n 0, %ret0, .LthreadStart
+
+	/* Successful return from the parent
+	   No need to restore the PIC register,
+	   since we return immediately. */
+
+	ldw	-84(%sp), %rp
+	bv	%r0(%rp)
+	ldwm	-64(%sp), %r4
+
+.LerrorRest:
+	/* Something bad happened -- no child created */
+	bl	__syscall_error, %rp
+	sub     %r0, %ret0, %arg0
+	ldw	-84(%sp), %rp
+	/* Return after setting errno, ret0 is set to -1 by __syscall_error. */
+	bv	%r0(%rp)
+	ldwm	-64(%sp), %r4
+
+.LerrorSanity:
+	/* Sanity checks failed, return -1, and set errno to EINVAL. */
+	bl	__syscall_error, %rp
+	ldi     EINVAL, %arg0
+	ldw	-84(%sp), %rp
+	bv	%r0(%rp)
+	ldwm	-64(%sp), %r4
+
+.LthreadStart:
+#ifdef RESET_PID
+# define CLONE_VM_BIT		23	/* 0x00000100  */
+# define CLONE_THREAD_BIT	15	/* 0x00010000  */
+	/* Load original clone flags.
+	   If CLONE_THREAD was passed, don't reset the PID/TID.
+	   If CLONE_VM was passed, we need to store -1 to PID/TID.
+	   If CLONE_VM and CLONE_THREAD were not set store the result
+	   of getpid to PID/TID.  */
+	ldw	-56(%sp), %r26
+	bb,<,n	%r26, CLONE_THREAD_BIT, 1f
+	bb,<	%r26, CLONE_VM_BIT, 2f
+	ldi	-1, %ret0
+	ble     0x100(%sr2, %r0)
+	ldi	__NR_getpid, %r20
+2:
+	mfctl	%cr27, %r26
+	stw	%ret0, PID_THREAD_OFFSET(%r26)
+	stw	%ret0, TID_THREAD_OFFSET(%r26)
+1:
+#endif
+	/* Load up the arguments.  */
+	ldw	-60(%sp), %arg0
+	ldw     -64(%sp), %r22
+
+	/* $$dyncall fixes child's PIC register */
+
+	/* Call the user's function */
+#ifdef PIC
+	copy	%r19, %r4
+#endif
+	bl	$$dyncall, %r31
+	copy	%r31, %rp
+#ifdef PIC
+	copy	%r4, %r19
+#endif
+	/* The call to _exit needs saved r19.  */
+	bl	_exit, %rp
+	copy	%ret0, %arg0
+
+	/* We should not return from _exit.
+           We do not restore r4, or the stack state.  */
+	iitlbp	%r0, (%sr0, %r0)
+
+PSEUDO_END(__clone)
+
+weak_alias (__clone, clone)