]>
Commit | Line | Data |
---|---|---|
0c530ab8 | 1 | /* |
39037602 | 2 | * Copyright (c) 2000-2016 Apple Inc. All rights reserved. |
0c530ab8 | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0a7de745 | 5 | * |
2d21ac55 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. | |
0a7de745 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
2d21ac55 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 | |
0c530ab8 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
0a7de745 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
0c530ab8 A |
27 | */ |
28 | #include <kern/task.h> | |
29 | #include <kern/thread.h> | |
30 | #include <kern/assert.h> | |
31 | #include <kern/clock.h> | |
32 | #include <kern/locks.h> | |
33 | #include <kern/sched_prim.h> | |
b0d623f7 | 34 | #include <kern/debug.h> |
0c530ab8 A |
35 | #include <mach/machine/thread_status.h> |
36 | #include <mach/thread_act.h> | |
37 | ||
38 | #include <sys/kernel.h> | |
39 | #include <sys/vm.h> | |
40 | #include <sys/proc_internal.h> | |
41 | #include <sys/syscall.h> | |
42 | #include <sys/systm.h> | |
43 | #include <sys/user.h> | |
44 | #include <sys/errno.h> | |
0c530ab8 A |
45 | #include <sys/kdebug.h> |
46 | #include <sys/sysent.h> | |
47 | #include <sys/sysproto.h> | |
48 | #include <sys/kauth.h> | |
49 | #include <sys/systm.h> | |
cb323159 | 50 | #include <sys/bitstring.h> |
0c530ab8 | 51 | |
b0d623f7 | 52 | #include <security/audit/audit.h> |
0c530ab8 A |
53 | |
54 | #include <i386/seg.h> | |
55 | #include <i386/machine_routines.h> | |
56 | #include <mach/i386/syscall_sw.h> | |
57 | ||
6d2010ae A |
58 | #include <machine/pal_routines.h> |
59 | ||
cb323159 A |
60 | #if CONFIG_MACF |
61 | #include <security/mac_framework.h> | |
62 | #endif | |
63 | ||
2d21ac55 A |
64 | #if CONFIG_DTRACE |
65 | extern int32_t dtrace_systrace_syscall(struct proc *, void *, int *); | |
66 | extern void dtrace_systrace_syscall_return(unsigned short, int, int *); | |
67 | #endif | |
68 | ||
0c530ab8 A |
69 | extern void unix_syscall(x86_saved_state_t *); |
70 | extern void unix_syscall64(x86_saved_state_t *); | |
0c530ab8 | 71 | extern void *find_user_regs(thread_t); |
0c530ab8 | 72 | |
b0d623f7 A |
73 | /* dynamically generated at build time based on syscalls.master */ |
74 | extern const char *syscallnames[]; | |
75 | ||
3e170ce0 | 76 | #define code_is_kdebug_trace(code) (((code) == SYS_kdebug_trace) || \ |
0a7de745 A |
77 | ((code) == SYS_kdebug_trace64) || \ |
78 | ((code) == SYS_kdebug_trace_string)) | |
a1c7dba1 | 79 | |
0c530ab8 A |
80 | /* |
81 | * Function: unix_syscall | |
82 | * | |
83 | * Inputs: regs - pointer to i386 save area | |
84 | * | |
85 | * Outputs: none | |
86 | */ | |
39037602 | 87 | __attribute__((noreturn)) |
0c530ab8 A |
88 | void |
89 | unix_syscall(x86_saved_state_t *state) | |
90 | { | |
0a7de745 A |
91 | thread_t thread; |
92 | void *vt; | |
cb323159 | 93 | unsigned int code, syscode; |
f427ee49 | 94 | const struct sysent *callp; |
0a7de745 A |
95 | |
96 | int error; | |
97 | vm_offset_t params; | |
98 | struct proc *p; | |
99 | struct uthread *uthread; | |
100 | x86_saved_state32_t *regs; | |
101 | boolean_t is_vfork; | |
102 | pid_t pid; | |
0c530ab8 A |
103 | |
104 | assert(is_saved_state32(state)); | |
105 | regs = saved_state32(state); | |
2d21ac55 | 106 | #if DEBUG |
0a7de745 | 107 | if (regs->eax == 0x800) { |
0c530ab8 | 108 | thread_exception_return(); |
0a7de745 | 109 | } |
2d21ac55 | 110 | #endif |
0c530ab8 A |
111 | thread = current_thread(); |
112 | uthread = get_bsdthread_info(thread); | |
113 | ||
3e170ce0 | 114 | uthread_reset_proc_refcount(uthread); |
3e170ce0 | 115 | |
0c530ab8 | 116 | /* Get the approriate proc; may be different from task's for vfork() */ |
6d2010ae | 117 | is_vfork = uthread->uu_flag & UT_VFORK; |
0a7de745 | 118 | if (__improbable(is_vfork != 0)) { |
0c530ab8 | 119 | p = current_proc(); |
0a7de745 | 120 | } else { |
6d2010ae | 121 | p = (struct proc *)get_bsdtask_info(current_task()); |
0a7de745 | 122 | } |
0c530ab8 | 123 | |
cb323159 A |
124 | code = regs->eax & I386_SYSCALL_NUMBER_MASK; |
125 | syscode = (code < nsysent) ? code : SYS_invalid; | |
b0d623f7 | 126 | DEBUG_KPRINT_SYSCALL_UNIX("unix_syscall: code=%d(%s) eip=%u\n", |
cb323159 | 127 | code, syscallnames[syscode], (uint32_t)regs->eip); |
0a7de745 | 128 | params = (vm_offset_t) (regs->uesp + sizeof(int)); |
2d21ac55 A |
129 | |
130 | regs->efl &= ~(EFL_CF); | |
131 | ||
cb323159 | 132 | callp = &sysent[syscode]; |
0c530ab8 | 133 | |
6d2010ae | 134 | if (__improbable(callp == sysent)) { |
0c530ab8 | 135 | code = fuword(params); |
2d21ac55 | 136 | params += sizeof(int); |
cb323159 A |
137 | syscode = (code < nsysent) ? code : SYS_invalid; |
138 | callp = &sysent[syscode]; | |
0c530ab8 | 139 | } |
2d21ac55 | 140 | |
0c530ab8 A |
141 | vt = (void *)uthread->uu_arg; |
142 | ||
2d21ac55 | 143 | if (callp->sy_arg_bytes != 0) { |
fe8ab488 | 144 | #if CONFIG_REQUIRES_U32_MUNGING |
0a7de745 | 145 | sy_munge_t *mungerp; |
fe8ab488 A |
146 | #else |
147 | #error U32 syscalls on x86_64 kernel requires munging | |
148 | #endif | |
0a7de745 | 149 | uint32_t nargs; |
0c530ab8 | 150 | |
0a7de745 | 151 | assert((unsigned) callp->sy_arg_bytes <= sizeof(uthread->uu_arg)); |
39236c6e A |
152 | nargs = callp->sy_arg_bytes; |
153 | error = copyin((user_addr_t) params, (char *) vt, nargs); | |
154 | if (error) { | |
155 | regs->eax = error; | |
156 | regs->efl |= EFL_CF; | |
157 | thread_exception_return(); | |
158 | /* NOTREACHED */ | |
0c530ab8 | 159 | } |
2d21ac55 | 160 | |
a1c7dba1 | 161 | if (__probable(!code_is_kdebug_trace(code))) { |
cb323159 A |
162 | uint32_t *uip = vt; |
163 | KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, | |
164 | uip[0], uip[1], uip[2], uip[3]); | |
0c530ab8 | 165 | } |
fe8ab488 A |
166 | |
167 | #if CONFIG_REQUIRES_U32_MUNGING | |
0c530ab8 A |
168 | mungerp = callp->sy_arg_munge32; |
169 | ||
0a7de745 | 170 | if (mungerp != NULL) { |
fe8ab488 | 171 | (*mungerp)(vt); |
0a7de745 | 172 | } |
fe8ab488 | 173 | #endif |
0a7de745 | 174 | } else { |
cb323159 | 175 | KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START); |
0a7de745 | 176 | } |
2d21ac55 | 177 | |
0c530ab8 A |
178 | /* |
179 | * Delayed binding of thread credential to process credential, if we | |
180 | * are not running with an explicitly set thread credential. | |
181 | */ | |
2d21ac55 | 182 | kauth_cred_uthread_update(uthread, p); |
0c530ab8 A |
183 | |
184 | uthread->uu_rval[0] = 0; | |
fe8ab488 | 185 | uthread->uu_rval[1] = 0; |
2d21ac55 | 186 | uthread->uu_flag |= UT_NOTCANCELPT; |
fe8ab488 | 187 | uthread->syscall_code = code; |
743345f9 | 188 | pid = proc_pid(p); |
0c530ab8 | 189 | |
2d21ac55 | 190 | #ifdef JOE_DEBUG |
0a7de745 A |
191 | uthread->uu_iocount = 0; |
192 | uthread->uu_vpindex = 0; | |
2d21ac55 | 193 | #endif |
0c530ab8 | 194 | |
cb323159 A |
195 | #if CONFIG_MACF |
196 | if (__improbable(p->syscall_filter_mask != NULL && !bitstr_test(p->syscall_filter_mask, syscode))) { | |
197 | error = mac_proc_check_syscall_unix(p, syscode); | |
198 | if (error) { | |
199 | goto skip_syscall; | |
200 | } | |
201 | } | |
202 | #endif /* CONFIG_MACF */ | |
203 | ||
0c530ab8 A |
204 | AUDIT_SYSCALL_ENTER(code, p, uthread); |
205 | error = (*(callp->sy_call))((void *) p, (void *) vt, &(uthread->uu_rval[0])); | |
fe8ab488 | 206 | AUDIT_SYSCALL_EXIT(code, p, uthread, error); |
2d21ac55 | 207 | |
cb323159 A |
208 | #if CONFIG_MACF |
209 | skip_syscall: | |
210 | #endif /* CONFIG_MACF */ | |
211 | ||
2d21ac55 | 212 | #ifdef JOE_DEBUG |
0a7de745 A |
213 | if (uthread->uu_iocount) { |
214 | printf("system call returned with uu_iocount != 0\n"); | |
215 | } | |
2d21ac55 A |
216 | #endif |
217 | #if CONFIG_DTRACE | |
218 | uthread->t_dtrace_errno = error; | |
219 | #endif /* CONFIG_DTRACE */ | |
220 | ||
6d2010ae | 221 | if (__improbable(error == ERESTART)) { |
0c530ab8 A |
222 | /* |
223 | * Move the user's pc back to repeat the syscall: | |
224 | * 5 bytes for a sysenter, or 2 for an int 8x. | |
225 | * The SYSENTER_TF_CS covers single-stepping over a sysenter | |
226 | * - see debug trap handler in idt.s/idt64.s | |
227 | */ | |
b0d623f7 | 228 | |
6d2010ae | 229 | pal_syscall_restart(thread, state); |
0a7de745 | 230 | } else if (error != EJUSTRETURN) { |
6d2010ae | 231 | if (__improbable(error)) { |
0a7de745 A |
232 | regs->eax = error; |
233 | regs->efl |= EFL_CF; /* carry bit */ | |
0c530ab8 | 234 | } else { /* (not error) */ |
0a7de745 A |
235 | /* |
236 | * We split retval across two registers, in case the | |
237 | * syscall had a 64-bit return value, in which case | |
238 | * eax/edx matches the function call ABI. | |
239 | */ | |
240 | regs->eax = uthread->uu_rval[0]; | |
241 | regs->edx = uthread->uu_rval[1]; | |
242 | } | |
0c530ab8 A |
243 | } |
244 | ||
b0d623f7 A |
245 | DEBUG_KPRINT_SYSCALL_UNIX( |
246 | "unix_syscall: error=%d retval=(%u,%u)\n", | |
247 | error, regs->eax, regs->edx); | |
248 | ||
2d21ac55 | 249 | uthread->uu_flag &= ~UT_NOTCANCELPT; |
0a7de745 | 250 | uthread->syscall_code = 0; |
6d2010ae | 251 | |
5ba3f43e A |
252 | #if DEBUG || DEVELOPMENT |
253 | kern_allocation_name_t | |
254 | prior __assert_only = thread_set_allocation_name(NULL); | |
255 | assertf(prior == NULL, "thread_set_allocation_name(\"%s\") not cleared", kern_allocation_get_name(prior)); | |
256 | #endif /* DEBUG || DEVELOPMENT */ | |
257 | ||
6d2010ae | 258 | if (__improbable(uthread->uu_lowpri_window)) { |
0a7de745 | 259 | /* |
0c530ab8 A |
260 | * task is marked as a low priority I/O type |
261 | * and the I/O we issued while in this system call | |
262 | * collided with normal I/O operations... we'll | |
263 | * delay in order to mitigate the impact of this | |
264 | * task on the normal operation of the system | |
265 | */ | |
39236c6e | 266 | throttle_lowpri_io(1); |
0c530ab8 | 267 | } |
0a7de745 | 268 | if (__probable(!code_is_kdebug_trace(code))) { |
cb323159 A |
269 | KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, |
270 | error, uthread->uu_rval[0], uthread->uu_rval[1], pid); | |
0a7de745 | 271 | } |
b0d623f7 | 272 | |
6d2010ae A |
273 | if (__improbable(!is_vfork && callp->sy_call == (sy_call_t *)execve && !error)) { |
274 | pal_execve_return(thread); | |
275 | } | |
0c530ab8 | 276 | |
3e170ce0 A |
277 | #if PROC_REF_DEBUG |
278 | if (__improbable(uthread_get_proc_refcount(uthread) != 0)) { | |
279 | panic("system call returned with uu_proc_refcount != 0"); | |
280 | } | |
281 | #endif | |
282 | ||
0c530ab8 A |
283 | thread_exception_return(); |
284 | /* NOTREACHED */ | |
285 | } | |
286 | ||
39037602 | 287 | __attribute__((noreturn)) |
0c530ab8 A |
288 | void |
289 | unix_syscall64(x86_saved_state_t *state) | |
290 | { | |
0a7de745 A |
291 | thread_t thread; |
292 | void *vt; | |
cb323159 | 293 | unsigned int code, syscode; |
f427ee49 | 294 | const struct sysent *callp; |
0a7de745 A |
295 | int args_in_regs; |
296 | boolean_t args_start_at_rdi; | |
297 | int error; | |
298 | struct proc *p; | |
299 | struct uthread *uthread; | |
0c530ab8 | 300 | x86_saved_state64_t *regs; |
0a7de745 | 301 | pid_t pid; |
0c530ab8 A |
302 | |
303 | assert(is_saved_state64(state)); | |
304 | regs = saved_state64(state); | |
0a7de745 A |
305 | #if DEBUG |
306 | if (regs->rax == 0x2000800) { | |
0c530ab8 | 307 | thread_exception_return(); |
0a7de745 | 308 | } |
6d2010ae | 309 | #endif |
0c530ab8 A |
310 | thread = current_thread(); |
311 | uthread = get_bsdthread_info(thread); | |
312 | ||
3e170ce0 | 313 | uthread_reset_proc_refcount(uthread); |
3e170ce0 | 314 | |
0c530ab8 | 315 | /* Get the approriate proc; may be different from task's for vfork() */ |
0a7de745 | 316 | if (__probable(!(uthread->uu_flag & UT_VFORK))) { |
0c530ab8 | 317 | p = (struct proc *)get_bsdtask_info(current_task()); |
0a7de745 | 318 | } else { |
0c530ab8 | 319 | p = current_proc(); |
0a7de745 | 320 | } |
0c530ab8 A |
321 | |
322 | /* Verify that we are not being called from a task without a proc */ | |
6d2010ae | 323 | if (__improbable(p == NULL)) { |
0c530ab8 A |
324 | regs->rax = EPERM; |
325 | regs->isf.rflags |= EFL_CF; | |
326 | task_terminate_internal(current_task()); | |
327 | thread_exception_return(); | |
328 | /* NOTREACHED */ | |
329 | } | |
0c530ab8 | 330 | |
cb323159 A |
331 | code = regs->rax & SYSCALL_NUMBER_MASK; |
332 | syscode = (code < nsysent) ? code : SYS_invalid; | |
b0d623f7 A |
333 | DEBUG_KPRINT_SYSCALL_UNIX( |
334 | "unix_syscall64: code=%d(%s) rip=%llx\n", | |
cb323159 A |
335 | code, syscallnames[syscode], regs->isf.rip); |
336 | callp = &sysent[syscode]; | |
fe8ab488 A |
337 | |
338 | vt = (void *)uthread->uu_arg; | |
0c530ab8 | 339 | |
6d2010ae | 340 | if (__improbable(callp == sysent)) { |
0a7de745 | 341 | /* |
0c530ab8 A |
342 | * indirect system call... system call number |
343 | * passed as 'arg0' | |
344 | */ | |
cb323159 A |
345 | code = regs->rdi; |
346 | syscode = (code < nsysent) ? code : SYS_invalid; | |
347 | callp = &sysent[syscode]; | |
fe8ab488 | 348 | args_start_at_rdi = FALSE; |
0c530ab8 | 349 | args_in_regs = 5; |
fe8ab488 A |
350 | } else { |
351 | args_start_at_rdi = TRUE; | |
352 | args_in_regs = 6; | |
0c530ab8 A |
353 | } |
354 | ||
355 | if (callp->sy_narg != 0) { | |
fe8ab488 A |
356 | assert(callp->sy_narg <= 8); /* size of uu_arg */ |
357 | ||
358 | args_in_regs = MIN(args_in_regs, callp->sy_narg); | |
359 | memcpy(vt, args_start_at_rdi ? ®s->rdi : ®s->rsi, args_in_regs * sizeof(syscall_arg_t)); | |
360 | ||
a1c7dba1 | 361 | if (!code_is_kdebug_trace(code)) { |
cb323159 | 362 | uint64_t *uip = vt; |
0c530ab8 | 363 | |
cb323159 A |
364 | KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, |
365 | uip[0], uip[1], uip[2], uip[3]); | |
0c530ab8 | 366 | } |
0c530ab8 | 367 | |
6d2010ae | 368 | if (__improbable(callp->sy_narg > args_in_regs)) { |
2d21ac55 | 369 | int copyin_count; |
0c530ab8 | 370 | |
fe8ab488 | 371 | copyin_count = (callp->sy_narg - args_in_regs) * sizeof(syscall_arg_t); |
0c530ab8 | 372 | |
fe8ab488 | 373 | error = copyin((user_addr_t)(regs->isf.rsp + sizeof(user_addr_t)), (char *)&uthread->uu_arg[args_in_regs], copyin_count); |
0c530ab8 | 374 | if (error) { |
2d21ac55 | 375 | regs->rax = error; |
0c530ab8 A |
376 | regs->isf.rflags |= EFL_CF; |
377 | thread_exception_return(); | |
378 | /* NOTREACHED */ | |
379 | } | |
380 | } | |
0a7de745 | 381 | } else { |
cb323159 | 382 | KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START); |
0a7de745 | 383 | } |
0c530ab8 A |
384 | |
385 | /* | |
386 | * Delayed binding of thread credential to process credential, if we | |
387 | * are not running with an explicitly set thread credential. | |
388 | */ | |
2d21ac55 | 389 | kauth_cred_uthread_update(uthread, p); |
0c530ab8 A |
390 | |
391 | uthread->uu_rval[0] = 0; | |
392 | uthread->uu_rval[1] = 0; | |
2d21ac55 | 393 | uthread->uu_flag |= UT_NOTCANCELPT; |
fe8ab488 | 394 | uthread->syscall_code = code; |
743345f9 | 395 | pid = proc_pid(p); |
0c530ab8 | 396 | |
6d2010ae | 397 | #ifdef JOE_DEBUG |
0a7de745 A |
398 | uthread->uu_iocount = 0; |
399 | uthread->uu_vpindex = 0; | |
6d2010ae | 400 | #endif |
0c530ab8 | 401 | |
cb323159 A |
402 | #if CONFIG_MACF |
403 | if (__improbable(p->syscall_filter_mask != NULL && !bitstr_test(p->syscall_filter_mask, syscode))) { | |
404 | error = mac_proc_check_syscall_unix(p, syscode); | |
405 | if (error) { | |
406 | goto skip_syscall; | |
407 | } | |
408 | } | |
409 | #endif /* CONFIG_MACF */ | |
410 | ||
0c530ab8 | 411 | AUDIT_SYSCALL_ENTER(code, p, uthread); |
fe8ab488 A |
412 | error = (*(callp->sy_call))((void *) p, vt, &(uthread->uu_rval[0])); |
413 | AUDIT_SYSCALL_EXIT(code, p, uthread, error); | |
2d21ac55 | 414 | |
cb323159 A |
415 | #if CONFIG_MACF |
416 | skip_syscall: | |
417 | #endif /* CONFIG_MACF */ | |
418 | ||
6d2010ae | 419 | #ifdef JOE_DEBUG |
0a7de745 A |
420 | if (uthread->uu_iocount) { |
421 | printf("system call returned with uu_iocount != 0\n"); | |
422 | } | |
6d2010ae A |
423 | #endif |
424 | ||
2d21ac55 A |
425 | #if CONFIG_DTRACE |
426 | uthread->t_dtrace_errno = error; | |
427 | #endif /* CONFIG_DTRACE */ | |
0a7de745 | 428 | |
6d2010ae | 429 | if (__improbable(error == ERESTART)) { |
0c530ab8 A |
430 | /* |
431 | * all system calls come through via the syscall instruction | |
432 | * in 64 bit mode... its 2 bytes in length | |
433 | * move the user's pc back to repeat the syscall: | |
434 | */ | |
6d2010ae | 435 | pal_syscall_restart( thread, state ); |
0a7de745 | 436 | } else if (error != EJUSTRETURN) { |
6d2010ae | 437 | if (__improbable(error)) { |
2d21ac55 | 438 | regs->rax = error; |
0a7de745 | 439 | regs->isf.rflags |= EFL_CF; /* carry bit */ |
0c530ab8 | 440 | } else { /* (not error) */ |
0c530ab8 A |
441 | switch (callp->sy_return_type) { |
442 | case _SYSCALL_RET_INT_T: | |
443 | regs->rax = uthread->uu_rval[0]; | |
444 | regs->rdx = uthread->uu_rval[1]; | |
445 | break; | |
446 | case _SYSCALL_RET_UINT_T: | |
447 | regs->rax = ((u_int)uthread->uu_rval[0]); | |
448 | regs->rdx = ((u_int)uthread->uu_rval[1]); | |
449 | break; | |
450 | case _SYSCALL_RET_OFF_T: | |
451 | case _SYSCALL_RET_ADDR_T: | |
452 | case _SYSCALL_RET_SIZE_T: | |
453 | case _SYSCALL_RET_SSIZE_T: | |
d1ecb069 | 454 | case _SYSCALL_RET_UINT64_T: |
0a7de745 | 455 | regs->rax = *((uint64_t *)(&uthread->uu_rval[0])); |
0c530ab8 A |
456 | regs->rdx = 0; |
457 | break; | |
458 | case _SYSCALL_RET_NONE: | |
459 | break; | |
460 | default: | |
461 | panic("unix_syscall: unknown return type"); | |
462 | break; | |
463 | } | |
464 | regs->isf.rflags &= ~EFL_CF; | |
0a7de745 | 465 | } |
0c530ab8 A |
466 | } |
467 | ||
b0d623f7 A |
468 | DEBUG_KPRINT_SYSCALL_UNIX( |
469 | "unix_syscall64: error=%d retval=(%llu,%llu)\n", | |
470 | error, regs->rax, regs->rdx); | |
0a7de745 | 471 | |
2d21ac55 | 472 | uthread->uu_flag &= ~UT_NOTCANCELPT; |
0a7de745 | 473 | uthread->syscall_code = 0; |
0c530ab8 | 474 | |
5ba3f43e A |
475 | #if DEBUG || DEVELOPMENT |
476 | kern_allocation_name_t | |
477 | prior __assert_only = thread_set_allocation_name(NULL); | |
478 | assertf(prior == NULL, "thread_set_allocation_name(\"%s\") not cleared", kern_allocation_get_name(prior)); | |
479 | #endif /* DEBUG || DEVELOPMENT */ | |
480 | ||
6d2010ae | 481 | if (__improbable(uthread->uu_lowpri_window)) { |
0a7de745 | 482 | /* |
0c530ab8 A |
483 | * task is marked as a low priority I/O type |
484 | * and the I/O we issued while in this system call | |
485 | * collided with normal I/O operations... we'll | |
486 | * delay in order to mitigate the impact of this | |
487 | * task on the normal operation of the system | |
488 | */ | |
39236c6e | 489 | throttle_lowpri_io(1); |
0c530ab8 | 490 | } |
0a7de745 | 491 | if (__probable(!code_is_kdebug_trace(code))) { |
cb323159 A |
492 | KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, |
493 | error, uthread->uu_rval[0], uthread->uu_rval[1], pid); | |
0a7de745 | 494 | } |
0c530ab8 | 495 | |
3e170ce0 A |
496 | #if PROC_REF_DEBUG |
497 | if (__improbable(uthread_get_proc_refcount(uthread))) { | |
498 | panic("system call returned with uu_proc_refcount != 0"); | |
499 | } | |
500 | #endif | |
501 | ||
0c530ab8 A |
502 | thread_exception_return(); |
503 | /* NOTREACHED */ | |
504 | } | |
505 | ||
506 | ||
507 | void | |
508 | unix_syscall_return(int error) | |
509 | { | |
0a7de745 A |
510 | thread_t thread; |
511 | struct uthread *uthread; | |
0c530ab8 A |
512 | struct proc *p; |
513 | unsigned int code; | |
f427ee49 | 514 | const struct sysent *callp; |
0c530ab8 A |
515 | |
516 | thread = current_thread(); | |
517 | uthread = get_bsdthread_info(thread); | |
518 | ||
6d2010ae | 519 | pal_register_cache_state(thread, DIRTY); |
b0d623f7 | 520 | |
0c530ab8 A |
521 | p = current_proc(); |
522 | ||
523 | if (proc_is64bit(p)) { | |
2d21ac55 | 524 | x86_saved_state64_t *regs; |
0c530ab8 A |
525 | |
526 | regs = saved_state64(find_user_regs(thread)); | |
527 | ||
fe8ab488 | 528 | code = uthread->syscall_code; |
39037602 | 529 | callp = (code >= nsysent) ? &sysent[SYS_invalid] : &sysent[code]; |
0c530ab8 | 530 | |
2d21ac55 | 531 | #if CONFIG_DTRACE |
0a7de745 | 532 | if (callp->sy_call == dtrace_systrace_syscall) { |
2d21ac55 | 533 | dtrace_systrace_syscall_return( code, error, uthread->uu_rval ); |
0a7de745 | 534 | } |
2d21ac55 | 535 | #endif /* CONFIG_DTRACE */ |
b0d623f7 | 536 | AUDIT_SYSCALL_EXIT(code, p, uthread, error); |
0c530ab8 A |
537 | |
538 | if (error == ERESTART) { | |
2d21ac55 | 539 | /* |
6d2010ae | 540 | * repeat the syscall |
0c530ab8 | 541 | */ |
0a7de745 A |
542 | pal_syscall_restart( thread, find_user_regs(thread)); |
543 | } else if (error != EJUSTRETURN) { | |
2d21ac55 A |
544 | if (error) { |
545 | regs->rax = error; | |
0a7de745 | 546 | regs->isf.rflags |= EFL_CF; /* carry bit */ |
0c530ab8 | 547 | } else { /* (not error) */ |
2d21ac55 | 548 | switch (callp->sy_return_type) { |
0c530ab8 | 549 | case _SYSCALL_RET_INT_T: |
2d21ac55 | 550 | regs->rax = uthread->uu_rval[0]; |
0c530ab8 A |
551 | regs->rdx = uthread->uu_rval[1]; |
552 | break; | |
553 | case _SYSCALL_RET_UINT_T: | |
2d21ac55 | 554 | regs->rax = ((u_int)uthread->uu_rval[0]); |
0c530ab8 A |
555 | regs->rdx = ((u_int)uthread->uu_rval[1]); |
556 | break; | |
557 | case _SYSCALL_RET_OFF_T: | |
558 | case _SYSCALL_RET_ADDR_T: | |
559 | case _SYSCALL_RET_SIZE_T: | |
560 | case _SYSCALL_RET_SSIZE_T: | |
d1ecb069 | 561 | case _SYSCALL_RET_UINT64_T: |
2d21ac55 | 562 | regs->rax = *((uint64_t *)(&uthread->uu_rval[0])); |
0c530ab8 A |
563 | regs->rdx = 0; |
564 | break; | |
565 | case _SYSCALL_RET_NONE: | |
2d21ac55 | 566 | break; |
0c530ab8 | 567 | default: |
2d21ac55 | 568 | panic("unix_syscall: unknown return type"); |
0c530ab8 A |
569 | break; |
570 | } | |
571 | regs->isf.rflags &= ~EFL_CF; | |
0a7de745 | 572 | } |
0c530ab8 | 573 | } |
b0d623f7 A |
574 | DEBUG_KPRINT_SYSCALL_UNIX( |
575 | "unix_syscall_return: error=%d retval=(%llu,%llu)\n", | |
576 | error, regs->rax, regs->rdx); | |
0c530ab8 | 577 | } else { |
0a7de745 | 578 | x86_saved_state32_t *regs; |
0c530ab8 A |
579 | |
580 | regs = saved_state32(find_user_regs(thread)); | |
581 | ||
2d21ac55 | 582 | regs->efl &= ~(EFL_CF); |
fe8ab488 A |
583 | |
584 | code = uthread->syscall_code; | |
39037602 | 585 | callp = (code >= nsysent) ? &sysent[SYS_invalid] : &sysent[code]; |
2d21ac55 A |
586 | |
587 | #if CONFIG_DTRACE | |
0a7de745 | 588 | if (callp->sy_call == dtrace_systrace_syscall) { |
2d21ac55 | 589 | dtrace_systrace_syscall_return( code, error, uthread->uu_rval ); |
0a7de745 | 590 | } |
2d21ac55 | 591 | #endif /* CONFIG_DTRACE */ |
b0d623f7 | 592 | AUDIT_SYSCALL_EXIT(code, p, uthread, error); |
0c530ab8 | 593 | |
0c530ab8 | 594 | if (error == ERESTART) { |
0a7de745 A |
595 | pal_syscall_restart( thread, find_user_regs(thread)); |
596 | } else if (error != EJUSTRETURN) { | |
2d21ac55 A |
597 | if (error) { |
598 | regs->eax = error; | |
0a7de745 | 599 | regs->efl |= EFL_CF; /* carry bit */ |
0c530ab8 | 600 | } else { /* (not error) */ |
2d21ac55 | 601 | regs->eax = uthread->uu_rval[0]; |
0c530ab8 | 602 | regs->edx = uthread->uu_rval[1]; |
0a7de745 | 603 | } |
0c530ab8 | 604 | } |
b0d623f7 A |
605 | DEBUG_KPRINT_SYSCALL_UNIX( |
606 | "unix_syscall_return: error=%d retval=(%u,%u)\n", | |
607 | error, regs->eax, regs->edx); | |
0c530ab8 | 608 | } |
0c530ab8 | 609 | |
0c530ab8 | 610 | |
2d21ac55 | 611 | uthread->uu_flag &= ~UT_NOTCANCELPT; |
0c530ab8 | 612 | |
5ba3f43e A |
613 | #if DEBUG || DEVELOPMENT |
614 | kern_allocation_name_t | |
615 | prior __assert_only = thread_set_allocation_name(NULL); | |
616 | assertf(prior == NULL, "thread_set_allocation_name(\"%s\") not cleared", kern_allocation_get_name(prior)); | |
617 | #endif /* DEBUG || DEVELOPMENT */ | |
618 | ||
593a1d5f | 619 | if (uthread->uu_lowpri_window) { |
0a7de745 | 620 | /* |
0c530ab8 A |
621 | * task is marked as a low priority I/O type |
622 | * and the I/O we issued while in this system call | |
623 | * collided with normal I/O operations... we'll | |
624 | * delay in order to mitigate the impact of this | |
625 | * task on the normal operation of the system | |
626 | */ | |
39236c6e | 627 | throttle_lowpri_io(1); |
0c530ab8 | 628 | } |
0a7de745 | 629 | if (!code_is_kdebug_trace(code)) { |
cb323159 A |
630 | KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, |
631 | error, uthread->uu_rval[0], uthread->uu_rval[1], p->p_pid); | |
0a7de745 | 632 | } |
0c530ab8 A |
633 | |
634 | thread_exception_return(); | |
635 | /* NOTREACHED */ | |
636 | } |