]>
Commit | Line | Data |
---|---|---|
0c530ab8 | 1 | /* |
b0d623f7 | 2 | * Copyright (c) 2000-2008 Apple Inc. All rights reserved. |
0c530ab8 | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0c530ab8 | 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. | |
0c530ab8 | 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. | |
17 | * | |
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. | |
0c530ab8 | 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> | |
50 | ||
b0d623f7 | 51 | #include <security/audit/audit.h> |
0c530ab8 A |
52 | |
53 | #include <i386/seg.h> | |
54 | #include <i386/machine_routines.h> | |
55 | #include <mach/i386/syscall_sw.h> | |
56 | ||
2d21ac55 A |
57 | #if CONFIG_DTRACE |
58 | extern int32_t dtrace_systrace_syscall(struct proc *, void *, int *); | |
59 | extern void dtrace_systrace_syscall_return(unsigned short, int, int *); | |
60 | #endif | |
61 | ||
0c530ab8 A |
62 | extern void unix_syscall(x86_saved_state_t *); |
63 | extern void unix_syscall64(x86_saved_state_t *); | |
0c530ab8 | 64 | extern void *find_user_regs(thread_t); |
0c530ab8 | 65 | |
2d21ac55 A |
66 | extern void x86_toggle_sysenter_arg_store(thread_t thread, boolean_t valid); |
67 | extern boolean_t x86_sysenter_arg_store_isvalid(thread_t thread); | |
b0d623f7 A |
68 | |
69 | /* dynamically generated at build time based on syscalls.master */ | |
70 | extern const char *syscallnames[]; | |
71 | ||
0c530ab8 A |
72 | /* |
73 | * Function: unix_syscall | |
74 | * | |
75 | * Inputs: regs - pointer to i386 save area | |
76 | * | |
77 | * Outputs: none | |
78 | */ | |
79 | void | |
80 | unix_syscall(x86_saved_state_t *state) | |
81 | { | |
2d21ac55 A |
82 | thread_t thread; |
83 | void *vt; | |
84 | unsigned int code; | |
85 | struct sysent *callp; | |
86 | ||
87 | int error; | |
88 | vm_offset_t params; | |
89 | struct proc *p; | |
90 | struct uthread *uthread; | |
0c530ab8 | 91 | x86_saved_state32_t *regs; |
2d21ac55 | 92 | boolean_t args_in_uthread; |
0c530ab8 A |
93 | |
94 | assert(is_saved_state32(state)); | |
95 | regs = saved_state32(state); | |
2d21ac55 | 96 | #if DEBUG |
0c530ab8 A |
97 | if (regs->eax == 0x800) |
98 | thread_exception_return(); | |
2d21ac55 | 99 | #endif |
0c530ab8 A |
100 | thread = current_thread(); |
101 | uthread = get_bsdthread_info(thread); | |
102 | ||
b0d623f7 | 103 | |
0c530ab8 A |
104 | /* Get the approriate proc; may be different from task's for vfork() */ |
105 | if (!(uthread->uu_flag & UT_VFORK)) | |
106 | p = (struct proc *)get_bsdtask_info(current_task()); | |
107 | else | |
108 | p = current_proc(); | |
109 | ||
110 | /* Verify that we are not being called from a task without a proc */ | |
111 | if (p == NULL) { | |
112 | regs->eax = EPERM; | |
113 | regs->efl |= EFL_CF; | |
114 | task_terminate_internal(current_task()); | |
115 | thread_exception_return(); | |
116 | /* NOTREACHED */ | |
117 | } | |
118 | ||
2d21ac55 | 119 | code = regs->eax & I386_SYSCALL_NUMBER_MASK; |
b0d623f7 A |
120 | DEBUG_KPRINT_SYSCALL_UNIX("unix_syscall: code=%d(%s) eip=%u\n", |
121 | code, syscallnames[code >= NUM_SYSENT ? 63 : code], (uint32_t)regs->eip); | |
2d21ac55 | 122 | args_in_uthread = ((regs->eax & I386_SYSCALL_ARG_BYTES_MASK) != 0) && x86_sysenter_arg_store_isvalid(thread); |
b0d623f7 | 123 | params = (vm_offset_t) (regs->uesp + sizeof (int)); |
2d21ac55 A |
124 | |
125 | regs->efl &= ~(EFL_CF); | |
126 | ||
127 | callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; | |
0c530ab8 A |
128 | |
129 | if (callp == sysent) { | |
130 | code = fuword(params); | |
2d21ac55 A |
131 | params += sizeof(int); |
132 | callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; | |
0c530ab8 | 133 | } |
2d21ac55 | 134 | |
0c530ab8 A |
135 | vt = (void *)uthread->uu_arg; |
136 | ||
2d21ac55 | 137 | if (callp->sy_arg_bytes != 0) { |
0c530ab8 A |
138 | sy_munge_t *mungerp; |
139 | ||
2d21ac55 A |
140 | assert((unsigned) callp->sy_arg_bytes <= sizeof (uthread->uu_arg)); |
141 | if (!args_in_uthread) | |
142 | { | |
143 | uint32_t nargs; | |
144 | nargs = callp->sy_arg_bytes; | |
145 | error = copyin((user_addr_t) params, (char *) vt, nargs); | |
146 | if (error) { | |
147 | regs->eax = error; | |
148 | regs->efl |= EFL_CF; | |
149 | thread_exception_return(); | |
150 | /* NOTREACHED */ | |
151 | } | |
0c530ab8 | 152 | } |
2d21ac55 | 153 | |
0c530ab8 | 154 | if (code != 180) { |
2d21ac55 | 155 | int *ip = (int *)vt; |
0c530ab8 A |
156 | |
157 | KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, | |
2d21ac55 | 158 | *ip, *(ip+1), *(ip+2), *(ip+3), 0); |
0c530ab8 A |
159 | } |
160 | mungerp = callp->sy_arg_munge32; | |
161 | ||
162 | /* | |
163 | * If non-NULL, then call the syscall argument munger to | |
b0d623f7 | 164 | * copy in arguments (see xnu/bsd/dev/{i386|x86_64}/munge.s); the |
0c530ab8 A |
165 | * first argument is NULL because we are munging in place |
166 | * after a copyin because the ABI currently doesn't use | |
167 | * registers to pass system call arguments. | |
168 | */ | |
169 | if (mungerp != NULL) | |
170 | (*mungerp)(NULL, vt); | |
171 | } else | |
2d21ac55 A |
172 | KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, |
173 | 0, 0, 0, 0, 0); | |
174 | ||
0c530ab8 A |
175 | /* |
176 | * Delayed binding of thread credential to process credential, if we | |
177 | * are not running with an explicitly set thread credential. | |
178 | */ | |
2d21ac55 | 179 | kauth_cred_uthread_update(uthread, p); |
0c530ab8 A |
180 | |
181 | uthread->uu_rval[0] = 0; | |
182 | uthread->uu_rval[1] = regs->edx; | |
2d21ac55 | 183 | uthread->uu_flag |= UT_NOTCANCELPT; |
0c530ab8 | 184 | |
0c530ab8 | 185 | |
2d21ac55 A |
186 | #ifdef JOE_DEBUG |
187 | uthread->uu_iocount = 0; | |
188 | uthread->uu_vpindex = 0; | |
189 | #endif | |
0c530ab8 A |
190 | |
191 | AUDIT_SYSCALL_ENTER(code, p, uthread); | |
192 | error = (*(callp->sy_call))((void *) p, (void *) vt, &(uthread->uu_rval[0])); | |
2d21ac55 | 193 | AUDIT_SYSCALL_EXIT(code, p, uthread, error); |
b0d623f7 A |
194 | #if CONFIG_MACF |
195 | mac_thread_userret(code, error, thread); | |
196 | #endif | |
2d21ac55 A |
197 | |
198 | #ifdef JOE_DEBUG | |
199 | if (uthread->uu_iocount) | |
b0d623f7 | 200 | printf("system call returned with uu_iocount != 0\n"); |
2d21ac55 A |
201 | #endif |
202 | #if CONFIG_DTRACE | |
203 | uthread->t_dtrace_errno = error; | |
204 | #endif /* CONFIG_DTRACE */ | |
205 | ||
0c530ab8 A |
206 | if (error == ERESTART) { |
207 | /* | |
208 | * Move the user's pc back to repeat the syscall: | |
209 | * 5 bytes for a sysenter, or 2 for an int 8x. | |
210 | * The SYSENTER_TF_CS covers single-stepping over a sysenter | |
211 | * - see debug trap handler in idt.s/idt64.s | |
212 | */ | |
b0d623f7 | 213 | |
2d21ac55 | 214 | if (regs->cs == SYSENTER_CS || regs->cs == SYSENTER_TF_CS) { |
0c530ab8 | 215 | regs->eip -= 5; |
2d21ac55 | 216 | } |
0c530ab8 A |
217 | else |
218 | regs->eip -= 2; | |
219 | } | |
220 | else if (error != EJUSTRETURN) { | |
221 | if (error) { | |
222 | regs->eax = error; | |
223 | regs->efl |= EFL_CF; /* carry bit */ | |
224 | } else { /* (not error) */ | |
225 | regs->eax = uthread->uu_rval[0]; | |
226 | regs->edx = uthread->uu_rval[1]; | |
0c530ab8 A |
227 | } |
228 | } | |
229 | ||
b0d623f7 A |
230 | DEBUG_KPRINT_SYSCALL_UNIX( |
231 | "unix_syscall: error=%d retval=(%u,%u)\n", | |
232 | error, regs->eax, regs->edx); | |
233 | ||
2d21ac55 A |
234 | uthread->uu_flag &= ~UT_NOTCANCELPT; |
235 | #if DEBUG | |
0c530ab8 | 236 | /* |
2d21ac55 | 237 | * if we're holding the funnel panic |
0c530ab8 | 238 | */ |
2d21ac55 A |
239 | syscall_exit_funnelcheck(); |
240 | #endif /* DEBUG */ | |
593a1d5f | 241 | if (uthread->uu_lowpri_window) { |
0c530ab8 A |
242 | /* |
243 | * task is marked as a low priority I/O type | |
244 | * and the I/O we issued while in this system call | |
245 | * collided with normal I/O operations... we'll | |
246 | * delay in order to mitigate the impact of this | |
247 | * task on the normal operation of the system | |
248 | */ | |
593a1d5f | 249 | throttle_lowpri_io(TRUE); |
0c530ab8 A |
250 | } |
251 | if (code != 180) | |
252 | KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, | |
b0d623f7 A |
253 | error, uthread->uu_rval[0], uthread->uu_rval[1], p->p_pid, 0); |
254 | ||
0c530ab8 A |
255 | |
256 | thread_exception_return(); | |
257 | /* NOTREACHED */ | |
258 | } | |
259 | ||
260 | ||
261 | void | |
262 | unix_syscall64(x86_saved_state_t *state) | |
263 | { | |
264 | thread_t thread; | |
265 | unsigned int code; | |
266 | struct sysent *callp; | |
267 | void *uargp; | |
268 | int args_in_regs; | |
269 | int error; | |
0c530ab8 A |
270 | struct proc *p; |
271 | struct uthread *uthread; | |
0c530ab8 A |
272 | x86_saved_state64_t *regs; |
273 | ||
274 | assert(is_saved_state64(state)); | |
275 | regs = saved_state64(state); | |
276 | ||
277 | if (regs->rax == 0x2000800) | |
278 | thread_exception_return(); | |
279 | ||
280 | thread = current_thread(); | |
281 | uthread = get_bsdthread_info(thread); | |
282 | ||
283 | /* Get the approriate proc; may be different from task's for vfork() */ | |
284 | if (!(uthread->uu_flag & UT_VFORK)) | |
285 | p = (struct proc *)get_bsdtask_info(current_task()); | |
286 | else | |
287 | p = current_proc(); | |
288 | ||
289 | /* Verify that we are not being called from a task without a proc */ | |
290 | if (p == NULL) { | |
291 | regs->rax = EPERM; | |
292 | regs->isf.rflags |= EFL_CF; | |
293 | task_terminate_internal(current_task()); | |
294 | thread_exception_return(); | |
295 | /* NOTREACHED */ | |
296 | } | |
297 | args_in_regs = 6; | |
298 | ||
299 | code = regs->rax & SYSCALL_NUMBER_MASK; | |
b0d623f7 A |
300 | DEBUG_KPRINT_SYSCALL_UNIX( |
301 | "unix_syscall64: code=%d(%s) rip=%llx\n", | |
302 | code, syscallnames[code >= NUM_SYSENT ? 63 : code], regs->isf.rip); | |
2d21ac55 | 303 | callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; |
0c530ab8 A |
304 | uargp = (void *)(®s->rdi); |
305 | ||
306 | if (callp == sysent) { | |
307 | /* | |
308 | * indirect system call... system call number | |
309 | * passed as 'arg0' | |
310 | */ | |
2d21ac55 A |
311 | code = regs->rdi; |
312 | callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; | |
0c530ab8 A |
313 | uargp = (void *)(®s->rsi); |
314 | args_in_regs = 5; | |
315 | } | |
316 | ||
317 | if (callp->sy_narg != 0) { | |
318 | if (code != 180) { | |
2d21ac55 | 319 | uint64_t *ip = (uint64_t *)uargp; |
0c530ab8 A |
320 | |
321 | KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, | |
2d21ac55 | 322 | (int)(*ip), (int)(*(ip+1)), (int)(*(ip+2)), (int)(*(ip+3)), 0); |
0c530ab8 | 323 | } |
2d21ac55 | 324 | assert(callp->sy_narg <= 8); |
0c530ab8 A |
325 | |
326 | if (callp->sy_narg > args_in_regs) { | |
2d21ac55 | 327 | int copyin_count; |
0c530ab8 | 328 | |
2d21ac55 | 329 | copyin_count = (callp->sy_narg - args_in_regs) * sizeof(uint64_t); |
0c530ab8 | 330 | |
2d21ac55 | 331 | error = copyin((user_addr_t)(regs->isf.rsp + sizeof(user_addr_t)), (char *)®s->v_arg6, copyin_count); |
0c530ab8 | 332 | if (error) { |
2d21ac55 | 333 | regs->rax = error; |
0c530ab8 A |
334 | regs->isf.rflags |= EFL_CF; |
335 | thread_exception_return(); | |
336 | /* NOTREACHED */ | |
337 | } | |
338 | } | |
339 | /* | |
340 | * XXX Turn 64 bit unsafe calls into nosys() | |
341 | */ | |
2d21ac55 A |
342 | if (callp->sy_flags & UNSAFE_64BIT) { |
343 | callp = &sysent[63]; | |
0c530ab8 A |
344 | goto unsafe; |
345 | } | |
0c530ab8 A |
346 | } else |
347 | KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, | |
348 | 0, 0, 0, 0, 0); | |
349 | unsafe: | |
350 | ||
351 | /* | |
352 | * Delayed binding of thread credential to process credential, if we | |
353 | * are not running with an explicitly set thread credential. | |
354 | */ | |
2d21ac55 | 355 | kauth_cred_uthread_update(uthread, p); |
0c530ab8 A |
356 | |
357 | uthread->uu_rval[0] = 0; | |
358 | uthread->uu_rval[1] = 0; | |
359 | ||
0c530ab8 | 360 | |
2d21ac55 | 361 | uthread->uu_flag |= UT_NOTCANCELPT; |
0c530ab8 | 362 | |
0c530ab8 A |
363 | |
364 | AUDIT_SYSCALL_ENTER(code, p, uthread); | |
365 | error = (*(callp->sy_call))((void *) p, uargp, &(uthread->uu_rval[0])); | |
2d21ac55 A |
366 | AUDIT_SYSCALL_EXIT(code, p, uthread, error); |
367 | ||
368 | #if CONFIG_DTRACE | |
369 | uthread->t_dtrace_errno = error; | |
370 | #endif /* CONFIG_DTRACE */ | |
0c530ab8 A |
371 | |
372 | if (error == ERESTART) { | |
373 | /* | |
374 | * all system calls come through via the syscall instruction | |
375 | * in 64 bit mode... its 2 bytes in length | |
376 | * move the user's pc back to repeat the syscall: | |
377 | */ | |
378 | regs->isf.rip -= 2; | |
379 | } | |
380 | else if (error != EJUSTRETURN) { | |
381 | if (error) { | |
2d21ac55 | 382 | regs->rax = error; |
0c530ab8 A |
383 | regs->isf.rflags |= EFL_CF; /* carry bit */ |
384 | } else { /* (not error) */ | |
385 | ||
386 | switch (callp->sy_return_type) { | |
387 | case _SYSCALL_RET_INT_T: | |
388 | regs->rax = uthread->uu_rval[0]; | |
389 | regs->rdx = uthread->uu_rval[1]; | |
390 | break; | |
391 | case _SYSCALL_RET_UINT_T: | |
392 | regs->rax = ((u_int)uthread->uu_rval[0]); | |
393 | regs->rdx = ((u_int)uthread->uu_rval[1]); | |
394 | break; | |
395 | case _SYSCALL_RET_OFF_T: | |
396 | case _SYSCALL_RET_ADDR_T: | |
397 | case _SYSCALL_RET_SIZE_T: | |
398 | case _SYSCALL_RET_SSIZE_T: | |
d1ecb069 | 399 | case _SYSCALL_RET_UINT64_T: |
0c530ab8 A |
400 | regs->rax = *((uint64_t *)(&uthread->uu_rval[0])); |
401 | regs->rdx = 0; | |
402 | break; | |
403 | case _SYSCALL_RET_NONE: | |
404 | break; | |
405 | default: | |
406 | panic("unix_syscall: unknown return type"); | |
407 | break; | |
408 | } | |
409 | regs->isf.rflags &= ~EFL_CF; | |
410 | } | |
411 | } | |
412 | ||
b0d623f7 A |
413 | DEBUG_KPRINT_SYSCALL_UNIX( |
414 | "unix_syscall64: error=%d retval=(%llu,%llu)\n", | |
415 | error, regs->rax, regs->rdx); | |
416 | ||
2d21ac55 | 417 | uthread->uu_flag &= ~UT_NOTCANCELPT; |
0c530ab8 A |
418 | |
419 | /* | |
2d21ac55 | 420 | * if we're holding the funnel panic |
0c530ab8 | 421 | */ |
2d21ac55 | 422 | syscall_exit_funnelcheck(); |
0c530ab8 | 423 | |
593a1d5f | 424 | if (uthread->uu_lowpri_window) { |
0c530ab8 A |
425 | /* |
426 | * task is marked as a low priority I/O type | |
427 | * and the I/O we issued while in this system call | |
428 | * collided with normal I/O operations... we'll | |
429 | * delay in order to mitigate the impact of this | |
430 | * task on the normal operation of the system | |
431 | */ | |
593a1d5f | 432 | throttle_lowpri_io(TRUE); |
0c530ab8 A |
433 | } |
434 | if (code != 180) | |
435 | KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, | |
b0d623f7 | 436 | error, uthread->uu_rval[0], uthread->uu_rval[1], p->p_pid, 0); |
0c530ab8 A |
437 | |
438 | thread_exception_return(); | |
439 | /* NOTREACHED */ | |
440 | } | |
441 | ||
442 | ||
443 | void | |
444 | unix_syscall_return(int error) | |
445 | { | |
446 | thread_t thread; | |
447 | struct uthread *uthread; | |
448 | struct proc *p; | |
449 | unsigned int code; | |
450 | vm_offset_t params; | |
451 | struct sysent *callp; | |
0c530ab8 A |
452 | |
453 | thread = current_thread(); | |
454 | uthread = get_bsdthread_info(thread); | |
455 | ||
b0d623f7 | 456 | |
0c530ab8 A |
457 | p = current_proc(); |
458 | ||
459 | if (proc_is64bit(p)) { | |
2d21ac55 | 460 | x86_saved_state64_t *regs; |
0c530ab8 A |
461 | |
462 | regs = saved_state64(find_user_regs(thread)); | |
463 | ||
2d21ac55 | 464 | /* reconstruct code for tracing before blasting rax */ |
0c530ab8 | 465 | code = regs->rax & SYSCALL_NUMBER_MASK; |
2d21ac55 | 466 | callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; |
0c530ab8 A |
467 | |
468 | if (callp == sysent) | |
2d21ac55 | 469 | /* |
0c530ab8 A |
470 | * indirect system call... system call number |
471 | * passed as 'arg0' | |
472 | */ | |
2d21ac55 A |
473 | code = regs->rdi; |
474 | ||
475 | #if CONFIG_DTRACE | |
476 | if (callp->sy_call == dtrace_systrace_syscall) | |
477 | dtrace_systrace_syscall_return( code, error, uthread->uu_rval ); | |
478 | #endif /* CONFIG_DTRACE */ | |
b0d623f7 | 479 | AUDIT_SYSCALL_EXIT(code, p, uthread, error); |
0c530ab8 A |
480 | |
481 | if (error == ERESTART) { | |
2d21ac55 | 482 | /* |
0c530ab8 A |
483 | * all system calls come through via the syscall instruction |
484 | * in 64 bit mode... its 2 bytes in length | |
485 | * move the user's pc back to repeat the syscall: | |
486 | */ | |
2d21ac55 | 487 | regs->isf.rip -= 2; |
0c530ab8 A |
488 | } |
489 | else if (error != EJUSTRETURN) { | |
2d21ac55 A |
490 | if (error) { |
491 | regs->rax = error; | |
0c530ab8 A |
492 | regs->isf.rflags |= EFL_CF; /* carry bit */ |
493 | } else { /* (not error) */ | |
494 | ||
2d21ac55 | 495 | switch (callp->sy_return_type) { |
0c530ab8 | 496 | case _SYSCALL_RET_INT_T: |
2d21ac55 | 497 | regs->rax = uthread->uu_rval[0]; |
0c530ab8 A |
498 | regs->rdx = uthread->uu_rval[1]; |
499 | break; | |
500 | case _SYSCALL_RET_UINT_T: | |
2d21ac55 | 501 | regs->rax = ((u_int)uthread->uu_rval[0]); |
0c530ab8 A |
502 | regs->rdx = ((u_int)uthread->uu_rval[1]); |
503 | break; | |
504 | case _SYSCALL_RET_OFF_T: | |
505 | case _SYSCALL_RET_ADDR_T: | |
506 | case _SYSCALL_RET_SIZE_T: | |
507 | case _SYSCALL_RET_SSIZE_T: | |
d1ecb069 | 508 | case _SYSCALL_RET_UINT64_T: |
2d21ac55 | 509 | regs->rax = *((uint64_t *)(&uthread->uu_rval[0])); |
0c530ab8 A |
510 | regs->rdx = 0; |
511 | break; | |
512 | case _SYSCALL_RET_NONE: | |
2d21ac55 | 513 | break; |
0c530ab8 | 514 | default: |
2d21ac55 | 515 | panic("unix_syscall: unknown return type"); |
0c530ab8 A |
516 | break; |
517 | } | |
518 | regs->isf.rflags &= ~EFL_CF; | |
519 | } | |
520 | } | |
b0d623f7 A |
521 | DEBUG_KPRINT_SYSCALL_UNIX( |
522 | "unix_syscall_return: error=%d retval=(%llu,%llu)\n", | |
523 | error, regs->rax, regs->rdx); | |
0c530ab8 | 524 | } else { |
2d21ac55 | 525 | x86_saved_state32_t *regs; |
0c530ab8 A |
526 | |
527 | regs = saved_state32(find_user_regs(thread)); | |
528 | ||
2d21ac55 | 529 | regs->efl &= ~(EFL_CF); |
0c530ab8 | 530 | /* reconstruct code for tracing before blasting eax */ |
2d21ac55 A |
531 | code = regs->eax & I386_SYSCALL_NUMBER_MASK; |
532 | callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; | |
533 | ||
534 | #if CONFIG_DTRACE | |
535 | if (callp->sy_call == dtrace_systrace_syscall) | |
536 | dtrace_systrace_syscall_return( code, error, uthread->uu_rval ); | |
537 | #endif /* CONFIG_DTRACE */ | |
b0d623f7 | 538 | AUDIT_SYSCALL_EXIT(code, p, uthread, error); |
0c530ab8 A |
539 | |
540 | if (callp == sysent) { | |
b0d623f7 | 541 | params = (vm_offset_t) (regs->uesp + sizeof (int)); |
2d21ac55 | 542 | code = fuword(params); |
0c530ab8 A |
543 | } |
544 | if (error == ERESTART) { | |
2d21ac55 | 545 | regs->eip -= ((regs->cs & 0xffff) == SYSENTER_CS) ? 5 : 2; |
0c530ab8 A |
546 | } |
547 | else if (error != EJUSTRETURN) { | |
2d21ac55 A |
548 | if (error) { |
549 | regs->eax = error; | |
0c530ab8 A |
550 | regs->efl |= EFL_CF; /* carry bit */ |
551 | } else { /* (not error) */ | |
2d21ac55 | 552 | regs->eax = uthread->uu_rval[0]; |
0c530ab8 | 553 | regs->edx = uthread->uu_rval[1]; |
0c530ab8 A |
554 | } |
555 | } | |
b0d623f7 A |
556 | DEBUG_KPRINT_SYSCALL_UNIX( |
557 | "unix_syscall_return: error=%d retval=(%u,%u)\n", | |
558 | error, regs->eax, regs->edx); | |
0c530ab8 | 559 | } |
0c530ab8 | 560 | |
0c530ab8 | 561 | |
2d21ac55 | 562 | uthread->uu_flag &= ~UT_NOTCANCELPT; |
0c530ab8 A |
563 | |
564 | /* | |
2d21ac55 | 565 | * if we're holding the funnel panic |
0c530ab8 | 566 | */ |
2d21ac55 | 567 | syscall_exit_funnelcheck(); |
0c530ab8 | 568 | |
593a1d5f | 569 | if (uthread->uu_lowpri_window) { |
0c530ab8 A |
570 | /* |
571 | * task is marked as a low priority I/O type | |
572 | * and the I/O we issued while in this system call | |
573 | * collided with normal I/O operations... we'll | |
574 | * delay in order to mitigate the impact of this | |
575 | * task on the normal operation of the system | |
576 | */ | |
593a1d5f | 577 | throttle_lowpri_io(TRUE); |
0c530ab8 A |
578 | } |
579 | if (code != 180) | |
580 | KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, | |
b0d623f7 | 581 | error, uthread->uu_rval[0], uthread->uu_rval[1], p->p_pid, 0); |
0c530ab8 A |
582 | |
583 | thread_exception_return(); | |
584 | /* NOTREACHED */ | |
585 | } | |
586 | ||
587 | void | |
588 | munge_wwwlww( | |
589 | __unused const void *in32, | |
590 | void *out64) | |
591 | { | |
592 | uint32_t *arg32; | |
593 | uint64_t *arg64; | |
594 | ||
595 | /* we convert in place in out64 */ | |
596 | arg32 = (uint32_t *) out64; | |
597 | arg64 = (uint64_t *) out64; | |
598 | ||
599 | arg64[5] = arg32[6]; /* wwwlwW */ | |
600 | arg64[4] = arg32[5]; /* wwwlWw */ | |
601 | arg32[7] = arg32[4]; /* wwwLww (hi) */ | |
602 | arg32[6] = arg32[3]; /* wwwLww (lo) */ | |
603 | arg64[2] = arg32[2]; /* wwWlww */ | |
604 | arg64[1] = arg32[1]; /* wWwlww */ | |
605 | arg64[0] = arg32[0]; /* Wwwlww */ | |
606 | } | |
2d21ac55 A |
607 | |
608 | ||
609 | void | |
610 | munge_wwlwww( | |
611 | __unused const void *in32, | |
612 | void *out64) | |
613 | { | |
614 | uint32_t *arg32; | |
615 | uint64_t *arg64; | |
616 | ||
617 | /* we convert in place in out64 */ | |
618 | arg32 = (uint32_t *) out64; | |
619 | arg64 = (uint64_t *) out64; | |
620 | ||
621 | arg64[5] = arg32[6]; /* wwlwwW */ | |
622 | arg64[4] = arg32[5]; /* wwlwWw */ | |
623 | arg64[3] = arg32[4]; /* wwlWww */ | |
624 | arg32[5] = arg32[3]; /* wwLwww (hi) */ | |
625 | arg32[4] = arg32[2]; /* wwLwww (lo) */ | |
626 | arg64[1] = arg32[1]; /* wWlwww */ | |
627 | arg64[0] = arg32[0]; /* Wwlwww */ | |
628 | } | |
629 |