1 | /* The clone3 syscall wrapper. Linux/mips version. |
2 | Copyright (C) 2023-2024 Free Software Foundation, Inc. |
3 | |
4 | This file is part of the GNU C Library. |
5 | |
6 | The GNU C Library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Lesser General Public |
8 | License as published by the Free Software Foundation; either |
9 | version 2.1 of the License, or (at your option) any later version. |
10 | |
11 | The GNU C Library is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | Lesser General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Lesser General Public |
17 | License along with the GNU C Library; if not, see |
18 | <https://www.gnu.org/licenses/>. */ |
19 | |
20 | #include <sys/asm.h> |
21 | #include <sysdep.h> |
22 | #define _ERRNO_H 1 |
23 | #include <bits/errno.h> |
24 | |
25 | /* The userland implementation is: |
26 | int clone3 (struct clone_args *cl_args, size_t size, |
27 | int (*func)(void *arg), void *arg); |
28 | |
29 | the kernel entry is: |
30 | int clone3 (struct clone_args *cl_args, size_t size); |
31 | |
32 | The parameters are passed in registers from userland: |
33 | a0/$4: cl_args |
34 | a1/$5: size |
35 | a2/$6: func |
36 | a3/$7: arg */ |
37 | |
38 | .text |
39 | .set nomips16 |
40 | #define FRAMESZ ((NARGSAVE*SZREG)+ALSZ)&ALMASK |
41 | GPOFF= FRAMESZ-(1*SZREG) |
42 | NESTED(__clone3, SZREG, sp) |
43 | #ifdef __PIC__ |
44 | SETUP_GP |
45 | #endif |
46 | #if FRAMESZ |
47 | PTR_SUBU sp, FRAMESZ |
48 | cfi_adjust_cfa_offset (FRAMESZ) |
49 | #endif |
50 | SETUP_GP64_STACK (GPOFF, __clone3) |
51 | #ifdef __PIC__ |
52 | SAVE_GP (GPOFF) |
53 | #endif |
54 | #ifdef PROF |
55 | .set noat |
56 | move $1,ra |
57 | jal _mcount |
58 | .set at |
59 | #endif |
60 | |
61 | /* Sanity check args. */ |
62 | li v0, EINVAL |
63 | beqz a0, L(error) /* No NULL cl_args pointer. */ |
64 | beqz a2, L(error) /* No NULL function pointer. */ |
65 | |
66 | #if _MIPS_SIM == _ABIO32 |
67 | /* Both stack and stack_size on clone_args are defined as uint64_t, and |
68 | there is no need to handle values larger than to 32 bits for o32. */ |
69 | # if __BYTE_ORDER == __BIG_ENDIAN |
70 | # define CL_STACKPOINTER_OFFSET 44 |
71 | # define CL_STACKSIZE_OFFSET 52 |
72 | # else |
73 | # define CL_STACKPOINTER_OFFSET 40 |
74 | # define CL_STACKSIZE_OFFSET 48 |
75 | # endif |
76 | |
77 | /* For o32 we need to setup a minimal stack frame to allow cprestore |
78 | on __thread_start_clone3. Also there is no guarantee by kABI that |
79 | $8 will be preserved after syscall execution (so we need to save it |
80 | on the provided stack). */ |
81 | lw t0, CL_STACKPOINTER_OFFSET(a0) /* Load the stack pointer. */ |
82 | lw t1, CL_STACKSIZE_OFFSET(a0) /* Load the stack_size. */ |
83 | addiu t1, -32 /* Update the stack size. */ |
84 | addu t2, t1, t0 /* Calculate the thread stack. */ |
85 | sw a3, 0(t2) /* Save argument pointer. */ |
86 | sw t1, CL_STACKSIZE_OFFSET(a0) /* Save the new stack size. */ |
87 | #else |
88 | move $8, a3 /* a3 is set to 0/1 for syscall success/error |
89 | while a4/$8 is returned unmodified. */ |
90 | #endif |
91 | |
92 | /* Do the system call, the kernel expects: |
93 | v0: system call number |
94 | a0: cl_args |
95 | a1: size */ |
96 | li v0, __NR_clone3 |
97 | cfi_endproc |
98 | syscall |
99 | |
100 | bnez a3, L(error) |
101 | beqz v0, L(thread_start_clone3) |
102 | |
103 | /* Successful return from the parent */ |
104 | cfi_startproc |
105 | #if FRAMESZ |
106 | cfi_adjust_cfa_offset (FRAMESZ) |
107 | #endif |
108 | SETUP_GP64_STACK_CFI (GPOFF) |
109 | cfi_remember_state |
110 | RESTORE_GP64_STACK |
111 | #if FRAMESZ |
112 | PTR_ADDU sp, FRAMESZ |
113 | cfi_adjust_cfa_offset (-FRAMESZ) |
114 | #endif |
115 | ret |
116 | |
117 | L(error): |
118 | cfi_restore_state |
119 | #ifdef __PIC__ |
120 | PTR_LA t9, __syscall_error |
121 | RESTORE_GP64_STACK |
122 | PTR_ADDU sp, FRAMESZ |
123 | cfi_adjust_cfa_offset (-FRAMESZ) |
124 | jr t9 |
125 | #else |
126 | RESTORE_GP64_STACK |
127 | PTR_ADDU sp, FRAMESZ |
128 | cfi_adjust_cfa_offset (-FRAMESZ) |
129 | j __syscall_error |
130 | #endif |
131 | END (__clone3) |
132 | |
133 | /* Load up the arguments to the function. Put this block of code in |
134 | its own function so that we can terminate the stack trace with our |
135 | debug info. */ |
136 | |
137 | ENTRY(__thread_start_clone3) |
138 | L(thread_start_clone3): |
139 | cfi_undefined ($31) |
140 | /* cp is already loaded. */ |
141 | SAVE_GP (GPOFF) |
142 | /* The stackframe has been created on entry of clone3. */ |
143 | |
144 | /* Restore the arg for user's function. */ |
145 | move t9, a2 /* Function pointer. */ |
146 | #if _MIPS_SIM == _ABIO32 |
147 | PTR_L a0, 0(sp) |
148 | #else |
149 | move a0, $8 /* Argument pointer. */ |
150 | #endif |
151 | |
152 | /* Call the user's function. */ |
153 | jal t9 |
154 | |
155 | move a0, v0 |
156 | li v0, __NR_exit |
157 | syscall |
158 | END(__thread_start_clone3) |
159 | |
160 | libc_hidden_def (__clone3) |
161 | weak_alias (__clone3, clone3) |
162 | |