1/* Copyright (C) 1996-2022 Free Software Foundation, Inc.
2   This file is part of the GNU C Library.
3
4   The GNU C Library is free software; you can redistribute it and/or
5   modify it under the terms of the GNU Lesser General Public
6   License as published by the Free Software Foundation; either
7   version 2.1 of the License, or (at your option) any later version.
8
9   The GNU C Library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with the GNU C Library.  If not, see
16   <https://www.gnu.org/licenses/>.  */
17
18/* clone() is even more special than fork() as it mucks with stacks
19   and invokes a function in the right context after its all over.  */
20
21#include <sys/asm.h>
22#include <sysdep.h>
23#define _ERRNO_H	1
24#include <bits/errno.h>
25#include <tls.h>
26
27/* int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg,
28	     void *parent_tidptr, void *tls, void *child_tidptr) */
29
30	.text
31	.set		nomips16
32#if _MIPS_SIM == _ABIO32
33# define EXTRA_LOCALS 1
34#else
35# define EXTRA_LOCALS 0
36#endif
37LOCALSZ= 4
38FRAMESZ= (((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK
39GPOFF= FRAMESZ-(1*SZREG)
40NESTED(__clone,4*SZREG,sp)
41#ifdef __PIC__
42	SETUP_GP
43#endif
44	PTR_SUBU sp, FRAMESZ
45	cfi_adjust_cfa_offset (FRAMESZ)
46	SETUP_GP64_STACK (GPOFF, __clone)
47#ifdef __PIC__
48	SAVE_GP (GPOFF)
49#endif
50#ifdef PROF
51	.set		noat
52	move		$1,ra
53	jal		_mcount
54	.set		at
55#endif
56
57	/* Align stack to 8/16 bytes per the ABI.  */
58#if _MIPS_SIM == _ABIO32
59	li		t0,-8
60#else
61	li		t0,-16
62#endif
63	and		a1,a1,t0
64
65	/* Sanity check arguments.  */
66	li		v0,EINVAL
67	beqz		a0,L(error)	/* No NULL function pointers.  */
68	beqz		a1,L(error)	/* No NULL stack pointers.  */
69
70	PTR_SUBU	a1,32		/* Reserve argument save space.  */
71	PTR_S		a0,0(a1)	/* Save function pointer.  */
72	PTR_S		a3,PTRSIZE(a1)	/* Save argument pointer.  */
73	LONG_S		a2,(PTRSIZE*2)(a1)	/* Save clone flags.  */
74
75	move		a0,a2
76
77	/* Shuffle in the last three arguments - arguments 5, 6, and 7 to
78	   this function, but arguments 3, 4, and 5 to the syscall.  */
79#if _MIPS_SIM == _ABIO32
80	PTR_L		a2,(FRAMESZ+PTRSIZE+PTRSIZE+16)(sp)
81	PTR_S		a2,16(sp)
82	PTR_L		a2,(FRAMESZ+16)(sp)
83	PTR_L		a3,(FRAMESZ+PTRSIZE+16)(sp)
84#else
85	move		a2,a4
86	move		a3,a5
87	move		a4,a6
88#endif
89
90	/* Do the system call */
91	li		v0,__NR_clone
92	cfi_endproc
93	syscall
94
95	bnez		a3,L(error)
96	beqz		v0,L(thread_start)
97
98	/* Successful return from the parent */
99	cfi_startproc
100	cfi_adjust_cfa_offset (FRAMESZ)
101	SETUP_GP64_STACK_CFI (GPOFF)
102	cfi_remember_state
103	RESTORE_GP64_STACK
104	PTR_ADDU	sp, FRAMESZ
105	cfi_adjust_cfa_offset (-FRAMESZ)
106	ret
107
108	/* Something bad happened -- no child created */
109L(error):
110	cfi_restore_state
111#ifdef __PIC__
112	PTR_LA		t9,__syscall_error
113	RESTORE_GP64_STACK
114	PTR_ADDU	sp, FRAMESZ
115	cfi_adjust_cfa_offset (-FRAMESZ)
116	jr		t9
117#else
118	RESTORE_GP64_STACK
119	PTR_ADDU	sp, FRAMESZ
120	cfi_adjust_cfa_offset (-FRAMESZ)
121	j		__syscall_error
122#endif
123	END(__clone)
124
125/* Load up the arguments to the function.  Put this block of code in
126   its own function so that we can terminate the stack trace with our
127   debug info.  */
128
129ENTRY(__thread_start)
130L(thread_start):
131	cfi_undefined ($31)
132	/* cp is already loaded.  */
133	SAVE_GP (GPOFF)
134	/* The stackframe has been created on entry of clone().  */
135
136	/* Restore the arg for user's function.  */
137	PTR_L		t9,0(sp)	/* Function pointer.  */
138	PTR_L		a0,PTRSIZE(sp)	/* Argument pointer.  */
139
140	/* Call the user's function.  */
141	jal		t9
142
143	move		a0,v0
144	li		v0,__NR_exit
145	syscall
146
147	END(__thread_start)
148
149libc_hidden_def (__clone)
150weak_alias (__clone, clone)
151