]>
Commit | Line | Data |
---|---|---|
f1a1da6c | 1 | /* |
214d78a2 | 2 | * Copyright (c) 2000-2017 Apple Inc. All rights reserved. |
f1a1da6c A |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
a0619f9c | 5 | * |
f1a1da6c A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
a0619f9c | 14 | * |
f1a1da6c A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
a0619f9c | 17 | * |
f1a1da6c A |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
a0619f9c | 25 | * |
f1a1da6c A |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ | |
28 | /* Copyright (c) 1995-2005 Apple Computer, Inc. All Rights Reserved */ | |
29 | /* | |
30 | * pthread_synch.c | |
31 | */ | |
32 | ||
964d3577 A |
33 | #pragma mark - Front Matter |
34 | ||
214d78a2 A |
35 | #define _PTHREAD_CONDATTR_T |
36 | #define _PTHREAD_COND_T | |
f1a1da6c A |
37 | #define _PTHREAD_MUTEXATTR_T |
38 | #define _PTHREAD_MUTEX_T | |
39 | #define _PTHREAD_RWLOCKATTR_T | |
40 | #define _PTHREAD_RWLOCK_T | |
41 | ||
42 | #undef pthread_mutexattr_t | |
43 | #undef pthread_mutex_t | |
44 | #undef pthread_condattr_t | |
45 | #undef pthread_cond_t | |
46 | #undef pthread_rwlockattr_t | |
47 | #undef pthread_rwlock_t | |
48 | ||
2546420a | 49 | #include <sys/cdefs.h> |
0691f650 | 50 | #include <os/log.h> |
2546420a A |
51 | |
52 | // <rdar://problem/26158937> panic() should be marked noreturn | |
53 | extern void panic(const char *string, ...) __printflike(1,2) __dead2; | |
54 | ||
f1a1da6c A |
55 | #include <sys/param.h> |
56 | #include <sys/queue.h> | |
57 | #include <sys/resourcevar.h> | |
58 | //#include <sys/proc_internal.h> | |
59 | #include <sys/kauth.h> | |
60 | #include <sys/systm.h> | |
61 | #include <sys/timeb.h> | |
62 | #include <sys/times.h> | |
63 | #include <sys/acct.h> | |
64 | #include <sys/kernel.h> | |
65 | #include <sys/wait.h> | |
66 | #include <sys/signalvar.h> | |
67 | #include <sys/sysctl.h> | |
68 | #include <sys/syslog.h> | |
69 | #include <sys/stat.h> | |
70 | #include <sys/lock.h> | |
71 | #include <sys/kdebug.h> | |
72 | //#include <sys/sysproto.h> | |
73 | #include <sys/vm.h> | |
74 | #include <sys/user.h> /* for coredump */ | |
75 | #include <sys/proc_info.h> /* for fill_procworkqueue */ | |
76 | ||
f1a1da6c A |
77 | #include <mach/mach_port.h> |
78 | #include <mach/mach_types.h> | |
79 | #include <mach/semaphore.h> | |
80 | #include <mach/sync_policy.h> | |
81 | #include <mach/task.h> | |
82 | #include <mach/vm_prot.h> | |
83 | #include <kern/kern_types.h> | |
84 | #include <kern/task.h> | |
85 | #include <kern/clock.h> | |
86 | #include <mach/kern_return.h> | |
87 | #include <kern/thread.h> | |
a0619f9c | 88 | #include <kern/zalloc.h> |
f1a1da6c A |
89 | #include <kern/sched_prim.h> /* for thread_exception_return */ |
90 | #include <kern/processor.h> | |
91 | #include <kern/assert.h> | |
92 | #include <mach/mach_vm.h> | |
93 | #include <mach/mach_param.h> | |
94 | #include <mach/thread_status.h> | |
95 | #include <mach/thread_policy.h> | |
96 | #include <mach/message.h> | |
97 | #include <mach/port.h> | |
98 | //#include <vm/vm_protos.h> | |
99 | #include <vm/vm_fault.h> | |
100 | #include <vm/vm_map.h> | |
101 | #include <mach/thread_act.h> /* for thread_resume */ | |
102 | #include <machine/machine_routines.h> | |
964d3577 | 103 | #include <mach/shared_region.h> |
f1a1da6c A |
104 | |
105 | #include <libkern/OSAtomic.h> | |
2546420a | 106 | #include <libkern/libkern.h> |
f1a1da6c | 107 | |
f1a1da6c A |
108 | #include "kern_internal.h" |
109 | ||
214d78a2 A |
110 | #ifndef WQ_SETUP_EXIT_THREAD |
111 | #define WQ_SETUP_EXIT_THREAD 8 | |
112 | #endif | |
f1a1da6c | 113 | |
2546420a A |
114 | // XXX: Ditto for thread tags from kern/thread.h |
115 | #define THREAD_TAG_MAINTHREAD 0x1 | |
116 | #define THREAD_TAG_PTHREAD 0x10 | |
117 | #define THREAD_TAG_WORKQUEUE 0x20 | |
118 | ||
f1a1da6c A |
119 | lck_grp_attr_t *pthread_lck_grp_attr; |
120 | lck_grp_t *pthread_lck_grp; | |
121 | lck_attr_t *pthread_lck_attr; | |
122 | ||
f1a1da6c A |
123 | #define C_32_STK_ALIGN 16 |
124 | #define C_64_STK_ALIGN 16 | |
964d3577 | 125 | |
214d78a2 A |
126 | // WORKQ use the largest alignment any platform needs |
127 | #define C_WORKQ_STK_ALIGN 16 | |
128 | ||
c6e5f90c A |
129 | #if defined(__arm64__) |
130 | /* Pull the pthread_t into the same page as the top of the stack so we dirty one less page. | |
131 | * <rdar://problem/19941744> The _pthread struct at the top of the stack shouldn't be page-aligned | |
132 | */ | |
133 | #define PTHREAD_T_OFFSET (12*1024) | |
134 | #else | |
964d3577 | 135 | #define PTHREAD_T_OFFSET 0 |
c6e5f90c | 136 | #endif |
f1a1da6c A |
137 | |
138 | /* | |
a0619f9c | 139 | * Flags filed passed to bsdthread_create and back in pthread_start |
f1a1da6c A |
140 | 31 <---------------------------------> 0 |
141 | _________________________________________ | |
142 | | flags(8) | policy(8) | importance(16) | | |
143 | ----------------------------------------- | |
144 | */ | |
145 | ||
214d78a2 | 146 | #define PTHREAD_START_CUSTOM 0x01000000 // <rdar://problem/34501401> |
2546420a | 147 | #define PTHREAD_START_SETSCHED 0x02000000 |
214d78a2 | 148 | // was PTHREAD_START_DETACHED 0x04000000 |
2546420a A |
149 | #define PTHREAD_START_QOSCLASS 0x08000000 |
150 | #define PTHREAD_START_TSD_BASE_SET 0x10000000 | |
214d78a2 | 151 | #define PTHREAD_START_SUSPENDED 0x20000000 |
2546420a | 152 | #define PTHREAD_START_QOSCLASS_MASK 0x00ffffff |
f1a1da6c A |
153 | #define PTHREAD_START_POLICY_BITSHIFT 16 |
154 | #define PTHREAD_START_POLICY_MASK 0xff | |
155 | #define PTHREAD_START_IMPORTANCE_MASK 0xffff | |
156 | ||
157 | #define SCHED_OTHER POLICY_TIMESHARE | |
158 | #define SCHED_FIFO POLICY_FIFO | |
159 | #define SCHED_RR POLICY_RR | |
160 | ||
964d3577 A |
161 | #define BASEPRI_DEFAULT 31 |
162 | ||
2546420a A |
163 | uint32_t pthread_debug_tracing = 1; |
164 | ||
76b7b9a2 A |
165 | static uint32_t pthread_mutex_default_policy; |
166 | ||
167 | SYSCTL_INT(_kern, OID_AUTO, pthread_mutex_default_policy, CTLFLAG_RW | CTLFLAG_LOCKED, | |
168 | &pthread_mutex_default_policy, 0, ""); | |
169 | ||
964d3577 A |
170 | #pragma mark - Process/Thread Setup/Teardown syscalls |
171 | ||
2546420a A |
172 | static mach_vm_offset_t |
173 | stack_addr_hint(proc_t p, vm_map_t vmap) | |
174 | { | |
964d3577 | 175 | mach_vm_offset_t stackaddr; |
2546420a A |
176 | mach_vm_offset_t aslr_offset; |
177 | bool proc64bit = proc_is64bit(p); | |
c6e5f90c | 178 | bool proc64bit_data = proc_is64bit_data(p); |
2546420a A |
179 | |
180 | // We can't safely take random values % something unless its a power-of-two | |
181 | _Static_assert(powerof2(PTH_DEFAULT_STACKSIZE), "PTH_DEFAULT_STACKSIZE is a power-of-two"); | |
182 | ||
964d3577 | 183 | #if defined(__i386__) || defined(__x86_64__) |
c6e5f90c | 184 | (void)proc64bit_data; |
2546420a A |
185 | if (proc64bit) { |
186 | // Matches vm_map_get_max_aslr_slide_pages's image shift in xnu | |
187 | aslr_offset = random() % (1 << 28); // about 512 stacks | |
188 | } else { | |
189 | // Actually bigger than the image shift, we've got ~256MB to work with | |
190 | aslr_offset = random() % (16 * PTH_DEFAULT_STACKSIZE); | |
191 | } | |
192 | aslr_offset = vm_map_trunc_page_mask(aslr_offset, vm_map_page_mask(vmap)); | |
193 | if (proc64bit) { | |
964d3577 | 194 | // Above nanomalloc range (see NANOZONE_SIGNATURE) |
2546420a | 195 | stackaddr = 0x700000000000 + aslr_offset; |
964d3577 | 196 | } else { |
2546420a | 197 | stackaddr = SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386 + aslr_offset; |
964d3577 A |
198 | } |
199 | #elif defined(__arm__) || defined(__arm64__) | |
a0619f9c A |
200 | user_addr_t main_thread_stack_top = 0; |
201 | if (pthread_kern->proc_get_user_stack) { | |
202 | main_thread_stack_top = pthread_kern->proc_get_user_stack(p); | |
203 | } | |
204 | if (proc64bit && main_thread_stack_top) { | |
205 | // The main thread stack position is randomly slid by xnu (c.f. | |
206 | // load_main() in mach_loader.c), so basing pthread stack allocations | |
207 | // where the main thread stack ends is already ASLRd and doing so | |
208 | // avoids creating a gap in the process address space that may cause | |
209 | // extra PTE memory usage. rdar://problem/33328206 | |
210 | stackaddr = vm_map_trunc_page_mask((vm_map_offset_t)main_thread_stack_top, | |
211 | vm_map_page_mask(vmap)); | |
964d3577 | 212 | } else { |
a0619f9c A |
213 | // vm_map_get_max_aslr_slide_pages ensures 1MB of slide, we do better |
214 | aslr_offset = random() % ((proc64bit ? 4 : 2) * PTH_DEFAULT_STACKSIZE); | |
215 | aslr_offset = vm_map_trunc_page_mask((vm_map_offset_t)aslr_offset, | |
216 | vm_map_page_mask(vmap)); | |
217 | if (proc64bit) { | |
218 | // 64 stacks below shared region | |
219 | stackaddr = SHARED_REGION_BASE_ARM64 - 64 * PTH_DEFAULT_STACKSIZE - aslr_offset; | |
220 | } else { | |
221 | // If you try to slide down from this point, you risk ending up in memory consumed by malloc | |
c6e5f90c A |
222 | if (proc64bit_data) { |
223 | stackaddr = SHARED_REGION_BASE_ARM64_32; | |
224 | } else { | |
225 | stackaddr = SHARED_REGION_BASE_ARM; | |
226 | } | |
227 | ||
228 | stackaddr -= 32 * PTH_DEFAULT_STACKSIZE + aslr_offset; | |
a0619f9c | 229 | } |
964d3577 A |
230 | } |
231 | #else | |
232 | #error Need to define a stack address hint for this architecture | |
233 | #endif | |
234 | return stackaddr; | |
235 | } | |
236 | ||
214d78a2 A |
237 | static bool |
238 | _pthread_priority_to_policy(pthread_priority_t priority, | |
239 | thread_qos_policy_data_t *data) | |
240 | { | |
241 | data->qos_tier = _pthread_priority_thread_qos(priority); | |
242 | data->tier_importance = _pthread_priority_relpri(priority); | |
243 | if (data->qos_tier == THREAD_QOS_UNSPECIFIED || data->tier_importance > 0 || | |
244 | data->tier_importance < THREAD_QOS_MIN_TIER_IMPORTANCE) { | |
245 | return false; | |
246 | } | |
247 | return true; | |
248 | } | |
249 | ||
964d3577 A |
250 | /** |
251 | * bsdthread_create system call. Used by pthread_create. | |
252 | */ | |
f1a1da6c | 253 | int |
214d78a2 A |
254 | _bsdthread_create(struct proc *p, |
255 | __unused user_addr_t user_func, __unused user_addr_t user_funcarg, | |
256 | user_addr_t user_stack, user_addr_t user_pthread, uint32_t flags, | |
257 | user_addr_t *retval) | |
f1a1da6c A |
258 | { |
259 | kern_return_t kret; | |
260 | void * sright; | |
261 | int error = 0; | |
2546420a | 262 | mach_vm_offset_t th_tsd_base; |
f1a1da6c A |
263 | mach_port_name_t th_thport; |
264 | thread_t th; | |
f1a1da6c A |
265 | task_t ctask = current_task(); |
266 | unsigned int policy, importance; | |
2546420a | 267 | uint32_t tsd_offset; |
214d78a2 | 268 | bool start_suspended = (flags & PTHREAD_START_SUSPENDED); |
f1a1da6c A |
269 | |
270 | if (pthread_kern->proc_get_register(p) == 0) { | |
271 | return EINVAL; | |
272 | } | |
273 | ||
214d78a2 | 274 | PTHREAD_TRACE(pthread_thread_create | DBG_FUNC_START, flags, 0, 0, 0); |
f1a1da6c | 275 | |
f1a1da6c A |
276 | kret = pthread_kern->thread_create(ctask, &th); |
277 | if (kret != KERN_SUCCESS) | |
278 | return(ENOMEM); | |
279 | thread_reference(th); | |
280 | ||
2546420a A |
281 | pthread_kern->thread_set_tag(th, THREAD_TAG_PTHREAD); |
282 | ||
f1a1da6c A |
283 | sright = (void *)pthread_kern->convert_thread_to_port(th); |
284 | th_thport = pthread_kern->ipc_port_copyout_send(sright, pthread_kern->task_get_ipcspace(ctask)); | |
a0619f9c A |
285 | if (!MACH_PORT_VALID(th_thport)) { |
286 | error = EMFILE; // userland will convert this into a crash | |
287 | goto out; | |
288 | } | |
f1a1da6c | 289 | |
964d3577 | 290 | if ((flags & PTHREAD_START_CUSTOM) == 0) { |
214d78a2 A |
291 | error = EINVAL; |
292 | goto out; | |
f1a1da6c | 293 | } |
2546420a | 294 | |
214d78a2 A |
295 | PTHREAD_TRACE(pthread_thread_create|DBG_FUNC_NONE, 0, 0, 0, 3); |
296 | ||
2546420a A |
297 | tsd_offset = pthread_kern->proc_get_pthread_tsd_offset(p); |
298 | if (tsd_offset) { | |
214d78a2 | 299 | th_tsd_base = user_pthread + tsd_offset; |
2546420a A |
300 | kret = pthread_kern->thread_set_tsd_base(th, th_tsd_base); |
301 | if (kret == KERN_SUCCESS) { | |
302 | flags |= PTHREAD_START_TSD_BASE_SET; | |
303 | } | |
304 | } | |
214d78a2 A |
305 | /* |
306 | * Strip PTHREAD_START_SUSPENDED so that libpthread can observe the kernel | |
307 | * supports this flag (after the fact). | |
308 | */ | |
309 | flags &= ~PTHREAD_START_SUSPENDED; | |
2546420a | 310 | |
f1a1da6c | 311 | /* |
214d78a2 | 312 | * Set up registers & function call. |
f1a1da6c | 313 | */ |
214d78a2 A |
314 | #if defined(__i386__) || defined(__x86_64__) |
315 | if (proc_is64bit_data(p)) { | |
316 | x86_thread_state64_t state = { | |
317 | .rip = (uint64_t)pthread_kern->proc_get_threadstart(p), | |
318 | .rdi = (uint64_t)user_pthread, | |
319 | .rsi = (uint64_t)th_thport, | |
320 | .rdx = (uint64_t)user_func, /* golang wants this */ | |
321 | .rcx = (uint64_t)user_funcarg, /* golang wants this */ | |
322 | .r8 = (uint64_t)user_stack, /* golang wants this */ | |
323 | .r9 = (uint64_t)flags, | |
324 | ||
c6e5f90c | 325 | .rsp = (uint64_t)user_stack, |
964d3577 | 326 | }; |
f1a1da6c | 327 | |
214d78a2 | 328 | (void)pthread_kern->thread_set_wq_state64(th, (thread_state_t)&state); |
f1a1da6c | 329 | } else { |
214d78a2 A |
330 | x86_thread_state32_t state = { |
331 | .eip = (uint32_t)pthread_kern->proc_get_threadstart(p), | |
332 | .eax = (uint32_t)user_pthread, | |
333 | .ebx = (uint32_t)th_thport, | |
334 | .ecx = (uint32_t)user_func, /* golang wants this */ | |
335 | .edx = (uint32_t)user_funcarg, /* golang wants this */ | |
336 | .edi = (uint32_t)user_stack, /* golang wants this */ | |
337 | .esi = (uint32_t)flags, | |
338 | ||
c6e5f90c A |
339 | .esp = (uint32_t)user_stack, |
340 | }; | |
341 | ||
342 | (void)pthread_kern->thread_set_wq_state32(th, (thread_state_t)&state); | |
343 | } | |
344 | #elif defined(__arm__) || defined(__arm64__) | |
345 | if (proc_is64bit_data(p)) { | |
346 | #ifdef __arm64__ | |
347 | arm_thread_state64_t state = { | |
348 | .pc = (uint64_t)pthread_kern->proc_get_threadstart(p), | |
349 | .x[0] = (uint64_t)user_pthread, | |
350 | .x[1] = (uint64_t)th_thport, | |
351 | .x[2] = (uint64_t)user_func, /* golang wants this */ | |
352 | .x[3] = (uint64_t)user_funcarg, /* golang wants this */ | |
353 | .x[4] = (uint64_t)user_stack, /* golang wants this */ | |
354 | .x[5] = (uint64_t)flags, | |
355 | ||
356 | .sp = (uint64_t)user_stack, | |
357 | }; | |
358 | ||
359 | (void)pthread_kern->thread_set_wq_state64(th, (thread_state_t)&state); | |
360 | #else | |
361 | panic("Shouldn't have a 64-bit thread on a 32-bit kernel..."); | |
362 | #endif // defined(__arm64__) | |
363 | } else { | |
364 | arm_thread_state_t state = { | |
365 | .pc = (uint32_t)pthread_kern->proc_get_threadstart(p), | |
366 | .r[0] = (uint32_t)user_pthread, | |
367 | .r[1] = (uint32_t)th_thport, | |
368 | .r[2] = (uint32_t)user_func, /* golang wants this */ | |
369 | .r[3] = (uint32_t)user_funcarg, /* golang wants this */ | |
370 | .r[4] = (uint32_t)user_stack, /* golang wants this */ | |
371 | .r[5] = (uint32_t)flags, | |
372 | ||
373 | .sp = (uint32_t)user_stack, | |
964d3577 | 374 | }; |
f1a1da6c | 375 | |
214d78a2 | 376 | (void)pthread_kern->thread_set_wq_state32(th, (thread_state_t)&state); |
f1a1da6c | 377 | } |
f1a1da6c A |
378 | #else |
379 | #error bsdthread_create not defined for this architecture | |
380 | #endif | |
381 | ||
214d78a2 | 382 | if (flags & PTHREAD_START_SETSCHED) { |
f1a1da6c A |
383 | /* Set scheduling parameters if needed */ |
384 | thread_extended_policy_data_t extinfo; | |
385 | thread_precedence_policy_data_t precedinfo; | |
386 | ||
387 | importance = (flags & PTHREAD_START_IMPORTANCE_MASK); | |
388 | policy = (flags >> PTHREAD_START_POLICY_BITSHIFT) & PTHREAD_START_POLICY_MASK; | |
389 | ||
390 | if (policy == SCHED_OTHER) { | |
391 | extinfo.timeshare = 1; | |
392 | } else { | |
393 | extinfo.timeshare = 0; | |
394 | } | |
395 | ||
396 | thread_policy_set(th, THREAD_EXTENDED_POLICY, (thread_policy_t)&extinfo, THREAD_EXTENDED_POLICY_COUNT); | |
397 | ||
f1a1da6c A |
398 | precedinfo.importance = (importance - BASEPRI_DEFAULT); |
399 | thread_policy_set(th, THREAD_PRECEDENCE_POLICY, (thread_policy_t)&precedinfo, THREAD_PRECEDENCE_POLICY_COUNT); | |
214d78a2 | 400 | } else if (flags & PTHREAD_START_QOSCLASS) { |
f1a1da6c | 401 | /* Set thread QoS class if requested. */ |
f1a1da6c | 402 | thread_qos_policy_data_t qos; |
f1a1da6c | 403 | |
214d78a2 A |
404 | if (!_pthread_priority_to_policy(flags & PTHREAD_START_QOSCLASS_MASK, &qos)) { |
405 | error = EINVAL; | |
406 | goto out; | |
407 | } | |
408 | pthread_kern->thread_policy_set_internal(th, THREAD_QOS_POLICY, | |
409 | (thread_policy_t)&qos, THREAD_QOS_POLICY_COUNT); | |
f1a1da6c A |
410 | } |
411 | ||
a0619f9c A |
412 | if (pthread_kern->proc_get_mach_thread_self_tsd_offset) { |
413 | uint64_t mach_thread_self_offset = | |
414 | pthread_kern->proc_get_mach_thread_self_tsd_offset(p); | |
415 | if (mach_thread_self_offset && tsd_offset) { | |
416 | bool proc64bit = proc_is64bit(p); | |
417 | if (proc64bit) { | |
418 | uint64_t th_thport_tsd = (uint64_t)th_thport; | |
214d78a2 | 419 | error = copyout(&th_thport_tsd, user_pthread + tsd_offset + |
a0619f9c A |
420 | mach_thread_self_offset, sizeof(th_thport_tsd)); |
421 | } else { | |
422 | uint32_t th_thport_tsd = (uint32_t)th_thport; | |
214d78a2 | 423 | error = copyout(&th_thport_tsd, user_pthread + tsd_offset + |
a0619f9c A |
424 | mach_thread_self_offset, sizeof(th_thport_tsd)); |
425 | } | |
426 | if (error) { | |
214d78a2 | 427 | goto out; |
a0619f9c A |
428 | } |
429 | } | |
430 | } | |
431 | ||
214d78a2 A |
432 | if (!start_suspended) { |
433 | kret = pthread_kern->thread_resume(th); | |
434 | if (kret != KERN_SUCCESS) { | |
435 | error = EINVAL; | |
436 | goto out; | |
437 | } | |
f1a1da6c A |
438 | } |
439 | thread_deallocate(th); /* drop the creator reference */ | |
440 | ||
214d78a2 | 441 | PTHREAD_TRACE(pthread_thread_create|DBG_FUNC_END, error, user_pthread, 0, 0); |
f1a1da6c | 442 | |
214d78a2 | 443 | *retval = user_pthread; |
f1a1da6c A |
444 | return(0); |
445 | ||
f1a1da6c A |
446 | out: |
447 | (void)pthread_kern->mach_port_deallocate(pthread_kern->task_get_ipcspace(ctask), th_thport); | |
a0619f9c A |
448 | if (pthread_kern->thread_will_park_or_terminate) { |
449 | pthread_kern->thread_will_park_or_terminate(th); | |
450 | } | |
f1a1da6c A |
451 | (void)thread_terminate(th); |
452 | (void)thread_deallocate(th); | |
453 | return(error); | |
454 | } | |
455 | ||
964d3577 A |
456 | /** |
457 | * bsdthread_terminate system call. Used by pthread_terminate | |
458 | */ | |
f1a1da6c A |
459 | int |
460 | _bsdthread_terminate(__unused struct proc *p, | |
461 | user_addr_t stackaddr, | |
462 | size_t size, | |
463 | uint32_t kthport, | |
464 | uint32_t sem, | |
465 | __unused int32_t *retval) | |
466 | { | |
467 | mach_vm_offset_t freeaddr; | |
468 | mach_vm_size_t freesize; | |
469 | kern_return_t kret; | |
2546420a | 470 | thread_t th = current_thread(); |
f1a1da6c A |
471 | |
472 | freeaddr = (mach_vm_offset_t)stackaddr; | |
473 | freesize = size; | |
474 | ||
214d78a2 | 475 | PTHREAD_TRACE(pthread_thread_terminate|DBG_FUNC_START, freeaddr, freesize, kthport, 0xff); |
f1a1da6c A |
476 | |
477 | if ((freesize != (mach_vm_size_t)0) && (freeaddr != (mach_vm_offset_t)0)) { | |
2546420a A |
478 | if (pthread_kern->thread_get_tag(th) & THREAD_TAG_MAINTHREAD){ |
479 | vm_map_t user_map = pthread_kern->current_map(); | |
480 | freesize = vm_map_trunc_page_mask((vm_map_offset_t)freesize - 1, vm_map_page_mask(user_map)); | |
481 | kret = mach_vm_behavior_set(user_map, freeaddr, freesize, VM_BEHAVIOR_REUSABLE); | |
214d78a2 A |
482 | #if MACH_ASSERT |
483 | if (kret != KERN_SUCCESS && kret != KERN_INVALID_ADDRESS) { | |
484 | os_log_error(OS_LOG_DEFAULT, "unable to make thread stack reusable (kr: %d)", kret); | |
485 | } | |
486 | #endif | |
2546420a A |
487 | kret = kret ? kret : mach_vm_protect(user_map, freeaddr, freesize, FALSE, VM_PROT_NONE); |
488 | assert(kret == KERN_SUCCESS || kret == KERN_INVALID_ADDRESS); | |
489 | } else { | |
490 | kret = mach_vm_deallocate(pthread_kern->current_map(), freeaddr, freesize); | |
491 | if (kret != KERN_SUCCESS) { | |
214d78a2 | 492 | PTHREAD_TRACE(pthread_thread_terminate|DBG_FUNC_END, kret, 0, 0, 0); |
2546420a | 493 | } |
f1a1da6c A |
494 | } |
495 | } | |
a0619f9c A |
496 | |
497 | if (pthread_kern->thread_will_park_or_terminate) { | |
498 | pthread_kern->thread_will_park_or_terminate(th); | |
499 | } | |
500 | (void)thread_terminate(th); | |
f1a1da6c | 501 | if (sem != MACH_PORT_NULL) { |
214d78a2 | 502 | kret = pthread_kern->semaphore_signal_internal_trap(sem); |
f1a1da6c | 503 | if (kret != KERN_SUCCESS) { |
214d78a2 | 504 | PTHREAD_TRACE(pthread_thread_terminate|DBG_FUNC_END, kret, 0, 0, 0); |
f1a1da6c A |
505 | } |
506 | } | |
a0619f9c | 507 | |
f1a1da6c A |
508 | if (kthport != MACH_PORT_NULL) { |
509 | pthread_kern->mach_port_deallocate(pthread_kern->task_get_ipcspace(current_task()), kthport); | |
510 | } | |
511 | ||
214d78a2 | 512 | PTHREAD_TRACE(pthread_thread_terminate|DBG_FUNC_END, 0, 0, 0, 0); |
f1a1da6c A |
513 | |
514 | pthread_kern->thread_exception_return(); | |
214d78a2 | 515 | __builtin_unreachable(); |
f1a1da6c A |
516 | } |
517 | ||
964d3577 A |
518 | /** |
519 | * bsdthread_register system call. Performs per-process setup. Responsible for | |
520 | * returning capabilitiy bits to userspace and receiving userspace function addresses. | |
521 | */ | |
f1a1da6c A |
522 | int |
523 | _bsdthread_register(struct proc *p, | |
524 | user_addr_t threadstart, | |
525 | user_addr_t wqthread, | |
526 | int pthsize, | |
527 | user_addr_t pthread_init_data, | |
2546420a | 528 | user_addr_t pthread_init_data_size, |
f1a1da6c A |
529 | uint64_t dispatchqueue_offset, |
530 | int32_t *retval) | |
531 | { | |
a0619f9c A |
532 | struct _pthread_registration_data data = {}; |
533 | uint32_t max_tsd_offset; | |
534 | kern_return_t kr; | |
535 | size_t pthread_init_sz = 0; | |
2546420a | 536 | |
f1a1da6c A |
537 | /* syscall randomizer test can pass bogus values */ |
538 | if (pthsize < 0 || pthsize > MAX_PTHREAD_SIZE) { | |
539 | return(EINVAL); | |
540 | } | |
a0619f9c A |
541 | /* |
542 | * if we have pthread_init_data, then we use that and target_concptr | |
543 | * (which is an offset) get data. | |
544 | */ | |
545 | if (pthread_init_data != 0) { | |
546 | if (pthread_init_data_size < sizeof(data.version)) { | |
547 | return EINVAL; | |
548 | } | |
549 | pthread_init_sz = MIN(sizeof(data), (size_t)pthread_init_data_size); | |
550 | int ret = copyin(pthread_init_data, &data, pthread_init_sz); | |
551 | if (ret) { | |
552 | return ret; | |
553 | } | |
554 | if (data.version != (size_t)pthread_init_data_size) { | |
555 | return EINVAL; | |
556 | } | |
557 | } else { | |
558 | data.dispatch_queue_offset = dispatchqueue_offset; | |
559 | } | |
560 | ||
561 | /* We have to do this before proc_get_register so that it resets after fork */ | |
562 | mach_vm_offset_t stackaddr = stack_addr_hint(p, pthread_kern->current_map()); | |
563 | pthread_kern->proc_set_stack_addr_hint(p, (user_addr_t)stackaddr); | |
564 | ||
565 | /* prevent multiple registrations */ | |
566 | if (pthread_kern->proc_get_register(p) != 0) { | |
567 | return(EINVAL); | |
568 | } | |
569 | ||
f1a1da6c A |
570 | pthread_kern->proc_set_threadstart(p, threadstart); |
571 | pthread_kern->proc_set_wqthread(p, wqthread); | |
572 | pthread_kern->proc_set_pthsize(p, pthsize); | |
573 | pthread_kern->proc_set_register(p); | |
574 | ||
a0619f9c A |
575 | uint32_t tsd_slot_sz = proc_is64bit(p) ? sizeof(uint64_t) : sizeof(uint32_t); |
576 | if ((uint32_t)pthsize >= tsd_slot_sz && | |
577 | data.tsd_offset <= (uint32_t)(pthsize - tsd_slot_sz)) { | |
578 | max_tsd_offset = ((uint32_t)pthsize - data.tsd_offset - tsd_slot_sz); | |
579 | } else { | |
580 | data.tsd_offset = 0; | |
581 | max_tsd_offset = 0; | |
582 | } | |
583 | pthread_kern->proc_set_pthread_tsd_offset(p, data.tsd_offset); | |
f1a1da6c | 584 | |
a0619f9c A |
585 | if (data.dispatch_queue_offset > max_tsd_offset) { |
586 | data.dispatch_queue_offset = 0; | |
587 | } | |
588 | pthread_kern->proc_set_dispatchqueue_offset(p, data.dispatch_queue_offset); | |
f1a1da6c | 589 | |
a0619f9c A |
590 | if (pthread_kern->proc_set_return_to_kernel_offset) { |
591 | if (data.return_to_kernel_offset > max_tsd_offset) { | |
592 | data.return_to_kernel_offset = 0; | |
f1a1da6c | 593 | } |
a0619f9c A |
594 | pthread_kern->proc_set_return_to_kernel_offset(p, |
595 | data.return_to_kernel_offset); | |
596 | } | |
f1a1da6c | 597 | |
a0619f9c A |
598 | if (pthread_kern->proc_set_mach_thread_self_tsd_offset) { |
599 | if (data.mach_thread_self_offset > max_tsd_offset) { | |
600 | data.mach_thread_self_offset = 0; | |
2546420a | 601 | } |
a0619f9c A |
602 | pthread_kern->proc_set_mach_thread_self_tsd_offset(p, |
603 | data.mach_thread_self_offset); | |
604 | } | |
f1a1da6c | 605 | |
a0619f9c | 606 | if (pthread_init_data != 0) { |
f1a1da6c | 607 | /* Outgoing data that userspace expects as a reply */ |
2546420a | 608 | data.version = sizeof(struct _pthread_registration_data); |
214d78a2 A |
609 | data.main_qos = _pthread_unspecified_priority(); |
610 | ||
f1a1da6c A |
611 | if (pthread_kern->qos_main_thread_active()) { |
612 | mach_msg_type_number_t nqos = THREAD_QOS_POLICY_COUNT; | |
a0619f9c | 613 | thread_qos_policy_data_t qos; |
f1a1da6c A |
614 | boolean_t gd = FALSE; |
615 | ||
214d78a2 A |
616 | kr = pthread_kern->thread_policy_get(current_thread(), |
617 | THREAD_QOS_POLICY, (thread_policy_t)&qos, &nqos, &gd); | |
f1a1da6c | 618 | if (kr != KERN_SUCCESS || qos.qos_tier == THREAD_QOS_UNSPECIFIED) { |
214d78a2 A |
619 | /* |
620 | * Unspecified threads means the kernel wants us | |
621 | * to impose legacy upon the thread. | |
622 | */ | |
f1a1da6c A |
623 | qos.qos_tier = THREAD_QOS_LEGACY; |
624 | qos.tier_importance = 0; | |
625 | ||
214d78a2 A |
626 | kr = pthread_kern->thread_policy_set_internal(current_thread(), |
627 | THREAD_QOS_POLICY, (thread_policy_t)&qos, | |
628 | THREAD_QOS_POLICY_COUNT); | |
f1a1da6c A |
629 | } |
630 | ||
631 | if (kr == KERN_SUCCESS) { | |
214d78a2 A |
632 | data.main_qos = _pthread_priority_make_from_thread_qos( |
633 | qos.qos_tier, 0, 0); | |
f1a1da6c | 634 | } |
f1a1da6c A |
635 | } |
636 | ||
214d78a2 | 637 | data.stack_addr_hint = stackaddr; |
76b7b9a2 A |
638 | data.mutex_default_policy = pthread_mutex_default_policy; |
639 | ||
f1a1da6c A |
640 | kr = copyout(&data, pthread_init_data, pthread_init_sz); |
641 | if (kr != KERN_SUCCESS) { | |
642 | return EINVAL; | |
643 | } | |
f1a1da6c A |
644 | } |
645 | ||
646 | /* return the supported feature set as the return value. */ | |
647 | *retval = PTHREAD_FEATURE_SUPPORTED; | |
648 | ||
649 | return(0); | |
650 | } | |
651 | ||
214d78a2 A |
652 | |
653 | #pragma mark - Workqueue Thread Support | |
654 | ||
655 | static mach_vm_size_t | |
656 | workq_thread_allocsize(proc_t p, vm_map_t wq_map, | |
657 | mach_vm_size_t *guardsize_out) | |
658 | { | |
659 | mach_vm_size_t guardsize = vm_map_page_size(wq_map); | |
660 | mach_vm_size_t pthread_size = vm_map_round_page_mask( | |
661 | pthread_kern->proc_get_pthsize(p) + PTHREAD_T_OFFSET, | |
662 | vm_map_page_mask(wq_map)); | |
663 | if (guardsize_out) *guardsize_out = guardsize; | |
664 | return guardsize + PTH_DEFAULT_STACKSIZE + pthread_size; | |
665 | } | |
964d3577 | 666 | |
f1a1da6c | 667 | int |
214d78a2 | 668 | workq_create_threadstack(proc_t p, vm_map_t vmap, mach_vm_offset_t *out_addr) |
f1a1da6c | 669 | { |
214d78a2 A |
670 | mach_vm_offset_t stackaddr = pthread_kern->proc_get_stack_addr_hint(p); |
671 | mach_vm_size_t guardsize, th_allocsize; | |
672 | kern_return_t kret; | |
f1a1da6c | 673 | |
214d78a2 A |
674 | th_allocsize = workq_thread_allocsize(p, vmap, &guardsize); |
675 | kret = mach_vm_map(vmap, &stackaddr, th_allocsize, page_size - 1, | |
676 | VM_MAKE_TAG(VM_MEMORY_STACK) | VM_FLAGS_ANYWHERE, NULL, 0, FALSE, | |
677 | VM_PROT_DEFAULT, VM_PROT_ALL, VM_INHERIT_DEFAULT); | |
f1a1da6c | 678 | |
214d78a2 A |
679 | if (kret != KERN_SUCCESS) { |
680 | kret = mach_vm_allocate(vmap, &stackaddr, th_allocsize, | |
681 | VM_MAKE_TAG(VM_MEMORY_STACK) | VM_FLAGS_ANYWHERE); | |
f1a1da6c A |
682 | } |
683 | ||
214d78a2 A |
684 | if (kret != KERN_SUCCESS) { |
685 | goto fail; | |
f1a1da6c A |
686 | } |
687 | ||
214d78a2 A |
688 | /* |
689 | * The guard page is at the lowest address | |
690 | * The stack base is the highest address | |
691 | */ | |
692 | kret = mach_vm_protect(vmap, stackaddr, guardsize, FALSE, VM_PROT_NONE); | |
693 | if (kret != KERN_SUCCESS) { | |
694 | goto fail_vm_deallocate; | |
f1a1da6c A |
695 | } |
696 | ||
214d78a2 A |
697 | if (out_addr) { |
698 | *out_addr = stackaddr; | |
f1a1da6c | 699 | } |
214d78a2 | 700 | return 0; |
f1a1da6c | 701 | |
214d78a2 A |
702 | fail_vm_deallocate: |
703 | (void)mach_vm_deallocate(vmap, stackaddr, th_allocsize); | |
704 | fail: | |
705 | return kret; | |
f1a1da6c A |
706 | } |
707 | ||
214d78a2 A |
708 | int |
709 | workq_destroy_threadstack(proc_t p, vm_map_t vmap, mach_vm_offset_t stackaddr) | |
f1a1da6c | 710 | { |
214d78a2 A |
711 | return mach_vm_deallocate(vmap, stackaddr, |
712 | workq_thread_allocsize(p, vmap, NULL)); | |
f1a1da6c A |
713 | } |
714 | ||
214d78a2 A |
715 | void |
716 | workq_markfree_threadstack(proc_t OS_UNUSED p, thread_t OS_UNUSED th, | |
717 | vm_map_t vmap, user_addr_t stackaddr) | |
718 | { | |
719 | // Keep this in sync with workq_setup_thread() | |
720 | const vm_size_t guardsize = vm_map_page_size(vmap); | |
721 | const user_addr_t freeaddr = (user_addr_t)stackaddr + guardsize; | |
722 | const vm_map_offset_t freesize = vm_map_trunc_page_mask( | |
723 | (PTH_DEFAULT_STACKSIZE + guardsize + PTHREAD_T_OFFSET) - 1, | |
724 | vm_map_page_mask(vmap)) - guardsize; | |
725 | ||
726 | __assert_only kern_return_t kr = mach_vm_behavior_set(vmap, freeaddr, | |
727 | freesize, VM_BEHAVIOR_REUSABLE); | |
728 | #if MACH_ASSERT | |
729 | if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) { | |
730 | os_log_error(OS_LOG_DEFAULT, "unable to make thread stack reusable (kr: %d)", kr); | |
a0619f9c | 731 | } |
214d78a2 A |
732 | #endif |
733 | } | |
a0619f9c | 734 | |
214d78a2 A |
735 | struct workq_thread_addrs { |
736 | user_addr_t self; | |
737 | user_addr_t stack_bottom; | |
738 | user_addr_t stack_top; | |
739 | }; | |
a0619f9c | 740 | |
214d78a2 A |
741 | static inline void |
742 | workq_thread_set_top_addr(struct workq_thread_addrs *th_addrs, user_addr_t addr) | |
743 | { | |
744 | th_addrs->stack_top = (addr & -C_WORKQ_STK_ALIGN); | |
745 | } | |
a0619f9c | 746 | |
214d78a2 A |
747 | static void |
748 | workq_thread_get_addrs(vm_map_t map, user_addr_t stackaddr, | |
749 | struct workq_thread_addrs *th_addrs) | |
750 | { | |
751 | const vm_size_t guardsize = vm_map_page_size(map); | |
a0619f9c | 752 | |
214d78a2 A |
753 | th_addrs->self = (user_addr_t)(stackaddr + PTH_DEFAULT_STACKSIZE + |
754 | guardsize + PTHREAD_T_OFFSET); | |
755 | workq_thread_set_top_addr(th_addrs, th_addrs->self); | |
756 | th_addrs->stack_bottom = (user_addr_t)(stackaddr + guardsize); | |
a0619f9c A |
757 | } |
758 | ||
214d78a2 A |
759 | static inline void |
760 | workq_set_register_state(proc_t p, thread_t th, | |
761 | struct workq_thread_addrs *addrs, mach_port_name_t kport, | |
762 | user_addr_t kevent_list, uint32_t upcall_flags, int kevent_count) | |
f1a1da6c | 763 | { |
214d78a2 A |
764 | user_addr_t wqstart_fnptr = pthread_kern->proc_get_wqthread(p); |
765 | if (!wqstart_fnptr) { | |
766 | panic("workqueue thread start function pointer is NULL"); | |
767 | } | |
f1a1da6c | 768 | |
214d78a2 A |
769 | #if defined(__i386__) || defined(__x86_64__) |
770 | if (proc_is64bit_data(p) == 0) { | |
771 | x86_thread_state32_t state = { | |
772 | .eip = (unsigned int)wqstart_fnptr, | |
773 | .eax = /* arg0 */ (unsigned int)addrs->self, | |
774 | .ebx = /* arg1 */ (unsigned int)kport, | |
775 | .ecx = /* arg2 */ (unsigned int)addrs->stack_bottom, | |
776 | .edx = /* arg3 */ (unsigned int)kevent_list, | |
777 | .edi = /* arg4 */ (unsigned int)upcall_flags, | |
778 | .esi = /* arg5 */ (unsigned int)kevent_count, | |
f1a1da6c | 779 | |
214d78a2 A |
780 | .esp = (int)((vm_offset_t)addrs->stack_top), |
781 | }; | |
782 | ||
783 | int error = pthread_kern->thread_set_wq_state32(th, (thread_state_t)&state); | |
784 | if (error != KERN_SUCCESS) { | |
785 | panic(__func__ ": thread_set_wq_state failed: %d", error); | |
2546420a | 786 | } |
214d78a2 A |
787 | } else { |
788 | x86_thread_state64_t state64 = { | |
789 | // x86-64 already passes all the arguments in registers, so we just put them in their final place here | |
790 | .rip = (uint64_t)wqstart_fnptr, | |
791 | .rdi = (uint64_t)addrs->self, | |
792 | .rsi = (uint64_t)kport, | |
793 | .rdx = (uint64_t)addrs->stack_bottom, | |
794 | .rcx = (uint64_t)kevent_list, | |
795 | .r8 = (uint64_t)upcall_flags, | |
796 | .r9 = (uint64_t)kevent_count, | |
2546420a | 797 | |
214d78a2 A |
798 | .rsp = (uint64_t)(addrs->stack_top) |
799 | }; | |
2546420a | 800 | |
214d78a2 A |
801 | int error = pthread_kern->thread_set_wq_state64(th, (thread_state_t)&state64); |
802 | if (error != KERN_SUCCESS) { | |
803 | panic(__func__ ": thread_set_wq_state failed: %d", error); | |
2546420a A |
804 | } |
805 | } | |
c6e5f90c A |
806 | #elif defined(__arm__) || defined(__arm64__) |
807 | if (!proc_is64bit_data(p)) { | |
808 | arm_thread_state_t state = { | |
809 | .pc = (int)wqstart_fnptr, | |
810 | .r[0] = (unsigned int)addrs->self, | |
811 | .r[1] = (unsigned int)kport, | |
812 | .r[2] = (unsigned int)addrs->stack_bottom, | |
813 | .r[3] = (unsigned int)kevent_list, | |
814 | // will be pushed onto the stack as arg4/5 | |
815 | .r[4] = (unsigned int)upcall_flags, | |
816 | .r[5] = (unsigned int)kevent_count, | |
817 | ||
818 | .sp = (int)(addrs->stack_top) | |
819 | }; | |
820 | ||
821 | int error = pthread_kern->thread_set_wq_state32(th, (thread_state_t)&state); | |
822 | if (error != KERN_SUCCESS) { | |
823 | panic(__func__ ": thread_set_wq_state failed: %d", error); | |
824 | } | |
825 | } else { | |
826 | #if defined(__arm64__) | |
827 | arm_thread_state64_t state = { | |
828 | .pc = (uint64_t)wqstart_fnptr, | |
829 | .x[0] = (uint64_t)addrs->self, | |
830 | .x[1] = (uint64_t)kport, | |
831 | .x[2] = (uint64_t)addrs->stack_bottom, | |
832 | .x[3] = (uint64_t)kevent_list, | |
833 | .x[4] = (uint64_t)upcall_flags, | |
834 | .x[5] = (uint64_t)kevent_count, | |
835 | ||
836 | .sp = (uint64_t)((vm_offset_t)addrs->stack_top), | |
837 | }; | |
838 | ||
839 | int error = pthread_kern->thread_set_wq_state64(th, (thread_state_t)&state); | |
840 | if (error != KERN_SUCCESS) { | |
841 | panic(__func__ ": thread_set_wq_state failed: %d", error); | |
842 | } | |
843 | #else /* defined(__arm64__) */ | |
844 | panic("Shouldn't have a 64-bit thread on a 32-bit kernel..."); | |
845 | #endif /* defined(__arm64__) */ | |
846 | } | |
214d78a2 A |
847 | #else |
848 | #error setup_wqthread not defined for this architecture | |
849 | #endif | |
850 | } | |
2546420a | 851 | |
c6e5f90c A |
852 | static inline int |
853 | workq_kevent(proc_t p, struct workq_thread_addrs *th_addrs, | |
214d78a2 A |
854 | user_addr_t eventlist, int nevents, int kevent_flags, |
855 | user_addr_t *kevent_list_out, int *kevent_count_out) | |
856 | { | |
214d78a2 | 857 | int ret; |
f1a1da6c | 858 | |
c6e5f90c A |
859 | user_addr_t kevent_list = th_addrs->self - |
860 | WQ_KEVENT_LIST_LEN * sizeof(struct kevent_qos_s); | |
861 | user_addr_t data_buf = kevent_list - WQ_KEVENT_DATA_SIZE; | |
862 | user_size_t data_available = WQ_KEVENT_DATA_SIZE; | |
f1a1da6c | 863 | |
c6e5f90c A |
864 | ret = pthread_kern->kevent_workq_internal(p, eventlist, nevents, |
865 | kevent_list, WQ_KEVENT_LIST_LEN, | |
866 | data_buf, &data_available, | |
867 | kevent_flags, kevent_count_out); | |
f1a1da6c | 868 | |
214d78a2 | 869 | // squash any errors into just empty output |
c6e5f90c | 870 | if (ret != 0 || *kevent_count_out == -1) { |
214d78a2 A |
871 | *kevent_list_out = NULL; |
872 | *kevent_count_out = 0; | |
873 | return ret; | |
874 | } | |
f1a1da6c | 875 | |
c6e5f90c | 876 | workq_thread_set_top_addr(th_addrs, data_buf + data_available); |
214d78a2 A |
877 | *kevent_list_out = kevent_list; |
878 | return ret; | |
879 | } | |
f1a1da6c | 880 | |
964d3577 A |
881 | /** |
882 | * configures initial thread stack/registers to jump into: | |
2546420a | 883 | * _pthread_wqthread(pthread_t self, mach_port_t kport, void *stackaddr, void *keventlist, int upcall_flags, int nkevents); |
964d3577 A |
884 | * to get there we jump through assembily stubs in pthread_asm.s. Those |
885 | * routines setup a stack frame, using the current stack pointer, and marshall | |
886 | * arguments from registers to the stack as required by the ABI. | |
887 | * | |
888 | * One odd thing we do here is to start the pthread_t 4k below what would be the | |
889 | * top of the stack otherwise. This is because usually only the first 4k of the | |
890 | * pthread_t will be used and so we want to put it on the same 16k page as the | |
891 | * top of the stack to save memory. | |
892 | * | |
893 | * When we are done the stack will look like: | |
894 | * |-----------| th_stackaddr + th_allocsize | |
895 | * |pthread_t | th_stackaddr + DEFAULT_STACKSIZE + guardsize + PTHREAD_STACK_OFFSET | |
a0619f9c A |
896 | * |kevent list| optionally - at most WQ_KEVENT_LIST_LEN events |
897 | * |kevent data| optionally - at most WQ_KEVENT_DATA_SIZE bytes | |
c6e5f90c | 898 | * |stack gap | bottom aligned to 16 bytes |
964d3577 A |
899 | * | STACK | |
900 | * | ⇓ | | |
901 | * | | | |
902 | * |guard page | guardsize | |
903 | * |-----------| th_stackaddr | |
904 | */ | |
214d78a2 | 905 | __attribute__((noreturn,noinline)) |
2546420a | 906 | void |
214d78a2 A |
907 | workq_setup_thread(proc_t p, thread_t th, vm_map_t map, user_addr_t stackaddr, |
908 | mach_port_name_t kport, int th_qos __unused, int setup_flags, int upcall_flags) | |
f1a1da6c | 909 | { |
214d78a2 A |
910 | struct workq_thread_addrs th_addrs; |
911 | bool first_use = (setup_flags & WQ_SETUP_FIRST_USE); | |
912 | user_addr_t kevent_list = NULL; | |
913 | int kevent_count = 0; | |
f1a1da6c | 914 | |
214d78a2 | 915 | workq_thread_get_addrs(map, stackaddr, &th_addrs); |
2546420a | 916 | |
214d78a2 | 917 | if (first_use) { |
a0619f9c A |
918 | uint32_t tsd_offset = pthread_kern->proc_get_pthread_tsd_offset(p); |
919 | if (tsd_offset) { | |
214d78a2 A |
920 | mach_vm_offset_t th_tsd_base = th_addrs.self + tsd_offset; |
921 | kern_return_t kret = pthread_kern->thread_set_tsd_base(th, | |
922 | th_tsd_base); | |
a0619f9c A |
923 | if (kret == KERN_SUCCESS) { |
924 | upcall_flags |= WQ_FLAG_THREAD_TSD_BASE_SET; | |
925 | } | |
2546420a | 926 | } |
2546420a | 927 | |
2546420a | 928 | /* |
214d78a2 A |
929 | * Pre-fault the first page of the new thread's stack and the page that will |
930 | * contain the pthread_t structure. | |
931 | */ | |
932 | vm_map_offset_t mask = vm_map_page_mask(map); | |
933 | vm_map_offset_t th_page = vm_map_trunc_page_mask(th_addrs.self, mask); | |
934 | vm_map_offset_t stk_page = vm_map_trunc_page_mask(th_addrs.stack_top - 1, mask); | |
935 | if (th_page != stk_page) { | |
936 | vm_fault(map, stk_page, VM_PROT_READ | VM_PROT_WRITE, FALSE, THREAD_UNINT, NULL, 0); | |
2546420a | 937 | } |
214d78a2 | 938 | vm_fault(map, th_page, VM_PROT_READ | VM_PROT_WRITE, FALSE, THREAD_UNINT, NULL, 0); |
2546420a | 939 | } |
f1a1da6c | 940 | |
214d78a2 A |
941 | if (setup_flags & WQ_SETUP_EXIT_THREAD) { |
942 | kevent_count = WORKQ_EXIT_THREAD_NKEVENT; | |
943 | } else if (upcall_flags & WQ_FLAG_THREAD_KEVENT) { | |
a0619f9c | 944 | unsigned int flags = KEVENT_FLAG_STACK_DATA | KEVENT_FLAG_IMMEDIATE; |
c6e5f90c | 945 | workq_kevent(p, &th_addrs, NULL, 0, flags, &kevent_list, &kevent_count); |
964d3577 A |
946 | } |
947 | ||
214d78a2 A |
948 | workq_set_register_state(p, th, &th_addrs, kport, |
949 | kevent_list, upcall_flags, kevent_count); | |
964d3577 | 950 | |
214d78a2 A |
951 | if (first_use) { |
952 | pthread_kern->thread_bootstrap_return(); | |
964d3577 | 953 | } else { |
214d78a2 | 954 | pthread_kern->unix_syscall_return(EJUSTRETURN); |
f1a1da6c | 955 | } |
214d78a2 | 956 | __builtin_unreachable(); |
964d3577 | 957 | } |
964d3577 | 958 | |
a0619f9c | 959 | int |
214d78a2 A |
960 | workq_handle_stack_events(proc_t p, thread_t th, vm_map_t map, |
961 | user_addr_t stackaddr, mach_port_name_t kport, | |
962 | user_addr_t events, int nevents, int upcall_flags) | |
f1a1da6c | 963 | { |
214d78a2 A |
964 | struct workq_thread_addrs th_addrs; |
965 | user_addr_t kevent_list = NULL; | |
966 | int kevent_count = 0, error; | |
967 | __assert_only kern_return_t kr; | |
f1a1da6c | 968 | |
214d78a2 | 969 | workq_thread_get_addrs(map, stackaddr, &th_addrs); |
2546420a | 970 | |
214d78a2 A |
971 | unsigned int flags = KEVENT_FLAG_STACK_DATA | KEVENT_FLAG_IMMEDIATE | |
972 | KEVENT_FLAG_PARKING; | |
c6e5f90c | 973 | error = workq_kevent(p, &th_addrs, events, nevents, flags, |
214d78a2 | 974 | &kevent_list, &kevent_count); |
2546420a | 975 | |
214d78a2 A |
976 | if (error || kevent_count == 0) { |
977 | return error; | |
2546420a A |
978 | } |
979 | ||
214d78a2 A |
980 | kr = pthread_kern->thread_set_voucher_name(MACH_PORT_NULL); |
981 | assert(kr == KERN_SUCCESS); | |
2546420a | 982 | |
214d78a2 A |
983 | workq_set_register_state(p, th, &th_addrs, kport, |
984 | kevent_list, upcall_flags, kevent_count); | |
2546420a | 985 | |
214d78a2 A |
986 | pthread_kern->unix_syscall_return(EJUSTRETURN); |
987 | __builtin_unreachable(); | |
2546420a A |
988 | } |
989 | ||
a0619f9c | 990 | int |
f1a1da6c A |
991 | _thread_selfid(__unused struct proc *p, uint64_t *retval) |
992 | { | |
993 | thread_t thread = current_thread(); | |
994 | *retval = thread_tid(thread); | |
995 | return KERN_SUCCESS; | |
996 | } | |
997 | ||
998 | void | |
999 | _pthread_init(void) | |
1000 | { | |
1001 | pthread_lck_grp_attr = lck_grp_attr_alloc_init(); | |
1002 | pthread_lck_grp = lck_grp_alloc_init("pthread", pthread_lck_grp_attr); | |
a0619f9c | 1003 | |
f1a1da6c A |
1004 | /* |
1005 | * allocate the lock attribute for pthread synchronizers | |
1006 | */ | |
1007 | pthread_lck_attr = lck_attr_alloc_init(); | |
f1a1da6c | 1008 | pthread_list_mlock = lck_mtx_alloc_init(pthread_lck_grp, pthread_lck_attr); |
a0619f9c | 1009 | |
f1a1da6c A |
1010 | pth_global_hashinit(); |
1011 | psynch_thcall = thread_call_allocate(psynch_wq_cleanup, NULL); | |
1012 | psynch_zoneinit(); | |
1013 | ||
76b7b9a2 A |
1014 | int policy_bootarg; |
1015 | if (PE_parse_boot_argn("pthread_mutex_default_policy", &policy_bootarg, sizeof(policy_bootarg))) { | |
1016 | pthread_mutex_default_policy = policy_bootarg; | |
1017 | } | |
1018 | ||
76b7b9a2 | 1019 | sysctl_register_oid(&sysctl__kern_pthread_mutex_default_policy); |
f1a1da6c | 1020 | } |