xref: /netbsd/sys/arch/amd64/amd64/linux32_syscall.c (revision 6550d01e)
1 /*	$NetBSD: linux32_syscall.c,v 1.30 2010/12/20 00:25:24 matt Exp $ */
2 
3 #include <sys/cdefs.h>
4 __KERNEL_RCSID(0, "$NetBSD: linux32_syscall.c,v 1.30 2010/12/20 00:25:24 matt Exp $");
5 
6 #include <sys/param.h>
7 #include <sys/systm.h>
8 #include <sys/proc.h>
9 #include <sys/signal.h>
10 #include <sys/syscall.h>
11 #include <sys/syscallvar.h>
12 
13 #include <machine/cpu.h>
14 #include <machine/psl.h>
15 #include <machine/userret.h>
16 
17 #include <compat/linux32/linux32_syscall.h>
18 #include <compat/linux32/common/linux32_errno.h>
19 
20 void linux32_syscall_intern(struct proc *);
21 void linux32_syscall(struct trapframe *);
22 
23 void
24 linux32_syscall_intern(struct proc *p)
25 {
26 
27 	p->p_md.md_syscall = linux32_syscall;
28 }
29 
30 void
31 linux32_syscall(struct trapframe *frame)
32 {
33 	const struct sysent *callp;
34 	struct proc *p;
35 	struct lwp *l;
36 	int error;
37 	size_t narg;
38 	register32_t code, args[6];
39 	register_t rval[2];
40 	int i;
41 	register_t args64[6];
42 
43 	l = curlwp;
44 	p = l->l_proc;
45 
46 	code = frame->tf_rax;
47 
48 	LWP_CACHE_CREDS(l, p);
49 
50 	callp = p->p_emul->e_sysent;
51 
52 	code &= (LINUX32_SYS_NSYSENT - 1);
53 	callp += code;
54 
55 	/*
56 	 * Linux passes the args in ebx, ecx, edx, esi, edi, ebp, in
57 	 * increasing order.
58 	 */
59 	args[0] = frame->tf_rbx & 0xffffffff;
60 	args[1] = frame->tf_rcx & 0xffffffff;
61 	args[2] = frame->tf_rdx & 0xffffffff;
62 	args[3] = frame->tf_rsi & 0xffffffff;
63 	args[4] = frame->tf_rdi & 0xffffffff;
64 	args[5] = frame->tf_rbp & 0xffffffff;
65 
66 	if (__predict_false(p->p_trace_enabled)) {
67 		narg = callp->sy_narg;
68 		if (__predict_false(narg > __arraycount(args)))
69 			panic("impossible syscall narg, code %d, narg %zd",
70 			    code, narg);
71 		for (i = 0; i < narg; i++)
72 			args64[i] = args[i] & 0xffffffff;
73 		if ((error = trace_enter(code, args64, narg)) != 0)
74 			goto out;
75 	}
76 
77 	rval[0] = 0;
78 	rval[1] = 0;
79 
80 	error = sy_call(callp, l, args, rval);
81 out:
82 	switch (error) {
83 	case 0:
84 		frame->tf_rax = rval[0];
85 		frame->tf_rflags &= ~PSL_C;	/* carry bit */
86 		break;
87 	case ERESTART:
88 		/*
89 		 * The offset to adjust the PC by depends on whether we entered
90 		 * the kernel through the trap or call gate.  We pushed the
91 		 * size of the instruction into tf_err on entry.
92 		 */
93 		frame->tf_rip -= frame->tf_err;
94 		break;
95 	case EJUSTRETURN:
96 		/* nothing to do */
97 		break;
98 	default:
99 		error = native_to_linux32_errno[error];
100 		frame->tf_rax = error;
101 		frame->tf_rflags |= PSL_C;	/* carry bit */
102 		break;
103 	}
104 
105 	if (__predict_false(p->p_trace_enabled))
106 		trace_exit(code, rval, error);
107 	userret(l);
108 }
109