1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26#include <brand_misc.h>
27
28/*
29 * Each JMP must occupy 16 bytes
30 */
31#define	JMP	\
32	pushl	$_CONST(. - brand_handler_table); \
33	jmp	brand_handler;	\
34	.align	16;
35
36#define	JMP4	JMP; JMP; JMP; JMP
37#define JMP16	JMP4; JMP4; JMP4; JMP4
38#define JMP64	JMP16; JMP16; JMP16; JMP16
39#define JMP256	JMP64; JMP64; JMP64; JMP64
40
41#if defined(lint)
42
43void
44brand_handler_table(void)
45{}
46
47void
48brand_handler(void)
49{
50}
51
52#else	/* lint */
53
54	/*
55	 * On entry to this table, %eax will hold the return address. The
56	 * location where we enter the table is a function of the system
57	 * call number. The table needs the same alignment as the individual
58	 * entries.
59	 */
60	.align	16
61	ENTRY_NP(brand_handler_table)
62	JMP256
63	SET_SIZE(brand_handler_table)
64
65#define	PIC_SETUP(r)					\
66	call	9f;					\
679:							\
68	popl	r;					\
69	addl	$_GLOBAL_OFFSET_TABLE_ + [. - 9b], r
70
71	/*
72	 * %eax - userland return address
73	 * stack contains:
74	 *    |    --------------------------------------
75	 *    v  4 | syscall arguments			|
76	 *  %esp+0 | syscall number			|
77	 *         --------------------------------------
78	 */
79	ENTRY_NP(brand_handler)
80	pushl	%ebp				/* allocate a stack frame */
81	movl	%esp, %ebp
82
83	/* Save registers at the time of the syscall. */
84	movl	$0, EH_LOCALS_GREG(TRAPNO)(%ebp)
85	movl	$0, EH_LOCALS_GREG(ERR)(%ebp)
86	movl	%ebx, EH_LOCALS_GREG(EBX)(%ebp)
87	movl	%ecx, EH_LOCALS_GREG(ECX)(%ebp)
88	movl	%edx, EH_LOCALS_GREG(EDX)(%ebp)
89	movl	%edi, EH_LOCALS_GREG(EDI)(%ebp)
90	movl	%esi, EH_LOCALS_GREG(ESI)(%ebp)
91	mov	%cs, EH_LOCALS_GREG(CS)(%ebp)
92	mov	%ds, EH_LOCALS_GREG(DS)(%ebp)
93	mov	%es, EH_LOCALS_GREG(ES)(%ebp)
94	mov	%fs, EH_LOCALS_GREG(FS)(%ebp)
95	mov	%gs, EH_LOCALS_GREG(GS)(%ebp)
96	pushfl					/* save syscall flags */
97	popl	%ecx
98	movl	%ecx, EH_LOCALS_GREG(EFL)(%ebp)
99	movl	EH_ARGS_OFFSET(0)(%ebp), %ecx	/* save syscall ebp */
100	movl	%ecx, EH_LOCALS_GREG(EBP)(%ebp)
101	movl	%ebp, %ecx			/* save syscall esp */
102	addl	$CPTRSIZE, %ecx
103	movl	%ecx, EH_LOCALS_GREG(ESP)(%ebp)
104
105	/*
106	 * The kernel drops us into the middle of the brand_handle_table
107	 * above that then pushes that table offset onto the stack, and calls
108	 * into brand_handler. That offset indicates the system call number
109	 * while %eax holds the return address for the system call. We replace
110	 * the value on the stack with the return address, and use the value to
111	 * compute the system call number by dividing by the table entry size.
112	 */
113	xchgl	CPTRSIZE(%ebp), %eax	/* swap JMP table offset and ret addr */
114	shrl	$4, %eax		/* table_offset/size = syscall num */
115	movl	%eax, EH_LOCALS_GREG(EAX)(%ebp)	/* save syscall num */
116
117	/*
118	 * Finish setting up our stack frame.  We would normally do this
119	 * upon entry to this function, but in this case we delayed it
120	 * because a "sub" operation can modify flags and we wanted to
121	 * save the flags into the gregset_t above before they get modified.
122	 *
123	 * Our stack frame format is documented in brand_misc.h.
124	 */
125	subl	$EH_LOCALS_SIZE, %esp
126
127	/* Look up the system call's entry in the sysent table */
128	PIC_SETUP(%ecx)
129	movl	brand_sysent_table@GOT(%ecx), %edx   /* %edx = sysent_table */
130	shll	$3, %eax	/* each entry is 8 bytes */
131	add	%eax, %edx	/* %edx = sysent entry address */
132
133	/*
134	 * Get the return value flag and the number of arguments from the
135	 * sysent table.
136	 */
137	movl	CPTRSIZE(%edx), %ecx		/* number of args + rv flag */
138	andl	$RV_MASK, %ecx			/* strip out number of args */
139	movl	%ecx, EH_LOCALS_RVFLAG(%ebp)	/* save rv flag */
140	movl	CPTRSIZE(%edx), %ecx		/* number of args + rv flag */
141	andl	$NARGS_MASK, %ecx		/* strip out rv flag */
142
143	/*
144	 * Setup arguments for our emulation call.  Our input arguments,
145	 * 0 to N, will become emulation call arguments 1 to N+1.
146	 * %ecx == number of arguments.
147	 */
148	movl	%ebp, %esi			/* args are at 12(%ebp) */
149	addl	$EH_ARGS_OFFSET(3), %esi
150	movl	%esp, %edi			/* copy args to 4(%esp) */
151	addl	$EH_ARGS_OFFSET(1), %edi
152	rep;	smovl				/* copy: (%esi) -> (%edi) */
153						/* copy: %ecx 32-bit words */
154	movl	EH_LOCALS_GREG(ESI)(%ebp), %esi	/* restore %esi */
155	movl	EH_LOCALS_GREG(EDI)(%ebp), %edi	/* restore %edi */
156
157	/*
158	 * The first parameter to the emulation callback function is a
159	 * pointer to a sysret_t structure.
160	 */
161	movl	%ebp, %ecx
162	addl	$EH_LOCALS_SYSRET, %ecx
163	movl	%ecx, EH_ARGS_OFFSET(0)(%esp)	/* arg0 == sysret_t ptr */
164
165	/* invoke the emulation routine */
166	ALTENTRY(brand_handler_savepc)
167	call	*(%edx)				/* call emulation routine */
168
169	/* restore scratch registers */
170	movl	EH_LOCALS_GREG(ECX)(%ebp), %ecx	/* restore %ecx */
171	movl	EH_LOCALS_GREG(EDX)(%ebp), %edx	/* restore %edx */
172
173	/* Check for syscall emulation success or failure */
174	cmpl	$0, %eax			/* check for an error */
175	je	success
176	stc					/* failure, set carry flag */
177	jmp	return				/* return, %rax == errno */
178
179success:
180	/* There is always at least one return value. */
181	movl	EH_LOCALS_SYSRET1(%ebp), %eax	/* %eax == sys_rval1 */
182	cmpl	$RV_DEFAULT, EH_LOCALS_RVFLAG(%ebp) /* check rv flag */
183	je	clear_carry
184	mov	EH_LOCALS_SYSRET2(%ebp), %edx	/* %edx == sys_rval2 */
185clear_carry:
186	clc					/* success, clear carry flag */
187
188return:
189	movl	%ebp, %esp			/* restore stack */
190	popl	%ebp
191	ret					/* ret to instr after syscall */
192	SET_SIZE(brand_handler)
193
194
195#endif	/* lint */
196