s10_brand_asm.s revision 11460:3e8510aaac0f
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 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#if defined(lint)
27
28#include <sys/systm.h>
29
30#else	/* lint */
31
32#include <sys/asm_linkage.h>
33#include <sys/privregs.h>
34#include <sys/segments.h>
35#include <s10_offsets.h>
36#include "assym.h"
37
38#endif	/* lint */
39
40#ifdef	lint
41
42void
43s10_brand_sysenter_callback(void)
44{
45}
46
47void
48s10_brand_syscall_callback(void)
49{
50}
51
52#if defined(__amd64)
53void
54s10_brand_syscall32_callback(void)
55{
56}
57#endif	/* amd64 */
58
59void
60s10_brand_int91_callback(void)
61{
62}
63
64#else	/* lint */
65
66#if defined(__amd64)
67/*
68 * When our syscall interposition callback entry point gets invoked the
69 * stack looks like this:
70 *         --------------------------------------
71 *      40 | user %gs				|
72 *      32 | callback pointer			|
73 *      24 | saved stack pointer		|
74 *    | 16 | lwp pointer			|
75 *    v  8 | user return address		|
76 *       0 | BRAND_CALLBACK()'s return addr 	|
77 *         --------------------------------------
78 */
79
80#define	V_COUNT	6
81#define	V_END		(CLONGSIZE * 6)
82#define	V_SSP		(CLONGSIZE * 3)
83#define	V_LWP		(CLONGSIZE * 2)
84#define	V_URET_ADDR	(CLONGSIZE * 1)
85#define	V_CB_ADDR	(CLONGSIZE * 0)
86
87#define	SP_REG		%rsp
88#define	SYSCALL_REG	%rax
89
90#else	/* !__amd64 */
91/*
92 * When our syscall interposition callback entry point gets invoked the
93 * stack looks like this:
94 *         --------------------------------------
95 *    | 24 | 'scratch space'			|
96 *    | 20 | user's %ebx			|
97 *    | 16 | user's %gs selector		|
98 *    | 12 | kernel's %gs selector		|
99 *    |  8 | lwp pointer			|
100 *    v  4 | user return address		|
101 *       0 | callback wrapper return addr	|
102 *         --------------------------------------
103 */
104
105#define	V_COUNT	7
106#define	V_END		(CLONGSIZE * 7)
107#define	V_U_GS		(CLONGSIZE * 4)
108#define	V_K_GS		(CLONGSIZE * 3)
109#define	V_LWP		(CLONGSIZE * 2)
110#define	V_URET_ADDR	(CLONGSIZE * 1)
111#define	V_CB_ADDR	(CLONGSIZE * 0)
112
113#define	SP_REG		%esp
114#define	SYSCALL_REG	%eax
115
116#endif	/* !__amd64 */
117
118/*
119 * The following macros allow us to access to variables/parameters passed
120 * in on the stack.  They take the following variables:
121 *	sp	- a register with the current stack pointer value
122 *	pcnt	- the number of words currently pushed onto the stack
123 *	var	- the variable to lookup
124 *	reg	- a register to read the variable into, or
125 *		  a register to write to the variable
126 */
127#define V_OFFSET(pcnt, var)						 \
128	(var + (pcnt * CLONGSIZE))
129
130#define GET_V(sp, pcnt, var, reg)					 \
131	mov	V_OFFSET(pcnt, var)(sp), reg
132
133#define SET_V(sp, pcnt, var, reg)					 \
134	mov	reg, V_OFFSET(pcnt, var)(sp)
135
136#define GET_PROCP(sp, pcnt, reg)					 \
137	GET_V(sp, pcnt, V_LWP, reg)		/* get lwp pointer */	;\
138	mov	LWP_PROCP(reg), reg		/* get proc pointer */
139
140#define GET_P_BRAND_DATA(sp, pcnt, reg)					 \
141	GET_PROCP(sp, pcnt, reg)					;\
142	mov	P_BRAND_DATA(reg), reg		/* get p_brand_data */
143
144/*
145 * Each of the following macros returns to the standard syscall codepath if
146 * it detects that this process is not able, or intended, to emulate this
147 * system call.  They all assume that the routine provides a 'bail-out'
148 * label of '9'.
149 */
150
151/*
152 * See if this process has a user-space hdlr registered for it.  For the
153 * s10 brand, the per-process brand data holds the address of the handler.
154 * As shown in the stack diagrams below, the callback code leaves that data
155 * at these offsets.  So check if s10_proc_data_t->spd_handler is non-NULL.
156 */
157#define	CHECK_FOR_HANDLER(scr)						 \
158	GET_P_BRAND_DATA(SP_REG, 1, scr)	/* get p_brand_data */	;\
159	cmp	$0, scr							;\
160	je	9f							;\
161	cmp	$0, SPD_HANDLER(scr)		/* check spd_handler */ ;\
162	je	9f
163
164/*
165 * If the system call number is >= 1024, then it is coming from the
166 * emulation support library.  As such we should handle it natively instead
167 * of sending it back to the emulation library.
168 */
169#define	CHECK_FOR_NATIVE(reg)		 \
170	cmp	$1024, reg		;\
171	jl	1f			;\
172	sub	$1024, reg		;\
173	jmp	9f			;\
1741:
175
176/*
177 * Check to see if we want to interpose on this system call.  If not, we
178 * jump back into the normal syscall path and pretend nothing happened.
179 */
180#define CHECK_FOR_INTERPOSITION(sysr, scr, scr_low)		 \
181	cmp	$NSYSCALL, sysr	/* is 0 <= syscall <= MAX? */	;\
182	ja	9f		/* no, take normal err path */	;\
183	lea	s10_emulation_table, scr			;\
184	mov	(scr), scr					;\
185	add	sysr, scr					;\
186	movb	(scr), scr_low					;\
187	cmpb	$0, scr_low					;\
188	je	9f
189
190#define	CALLBACK_PROLOGUE(call, scr, scr_low)			 \
191	push	scr		/* Save scratch register */	;\
192	CHECK_FOR_HANDLER(scr)					;\
193	CHECK_FOR_NATIVE(call)					;\
194	CHECK_FOR_INTERPOSITION(call, scr, scr_low)
195
196/*
197 * Rather than returning to the instruction after the syscall, we need to
198 * transfer control into the brand library's handler table at
199 * table_addr + (16 * syscall_num), thus encoding the system call number in the
200 * instruction pointer.  The CALC_TABLE_ADDR macro performs that calculation.
201 *
202 * This macro assumes the syscall number is in SYSCALL_REG and it clobbers
203 * that register.  It leaves the calculated handler table return address in
204 * the scratch reg.
205 */
206#define CALC_TABLE_ADDR(scr)						 \
207	GET_P_BRAND_DATA(SP_REG, 1, scr) /* get p_brand_data ptr */	;\
208	mov	SPD_HANDLER(scr), scr	/* get p_brand_data->spd_handler */ ;\
209	shl	$4, SYSCALL_REG		/* syscall_num * 16 */		;\
210	add	SYSCALL_REG, scr	/* leave return addr in scr reg. */
211
212/*
213 * To 'return' to our user-space handler, we just need to place its address
214 * into 'retreg'.  The original return address is passed in SYSCALL_REG.
215 */
216#define SETUP_RET_DATA(scr, retreg)					 \
217	CALC_TABLE_ADDR(scr)					 	;\
218	mov	retreg, SYSCALL_REG /* save orig return addr in %rax */	;\
219	mov	scr, retreg	/* save new return addr in ret reg */	;\
220	pop	scr		/* restore scratch register */
221
222/*
223 * The callback routines:
224 */
225
226#if defined(__amd64)
227
228/*
229 * syscall handler for 32-bit user processes:
230 *	%rax - syscall number
231 *	%ecx - the address of the instruction after the syscall
232 */
233ENTRY(s10_brand_syscall32_callback)
234
235	CALLBACK_PROLOGUE(%rax, %r15, %r15b)
236
237	SETUP_RET_DATA(%r15, %rcx)
238	GET_V(%rsp, 0, V_SSP, %rsp)	/* restore user's stack pointer	*/
239	jmp	nopop_sys_syscall32_swapgs_sysretl
2409:
241	popq	%r15
242	retq
243SET_SIZE(s10_brand_syscall32_callback)
244
245/*
246 * syscall handler for 64-bit user processes:
247 *     %rax - syscall number
248 *     %rcx - user space %rip
249 */
250ENTRY(s10_brand_syscall_callback)
251
252	CALLBACK_PROLOGUE(%rax, %r15, %r15b)
253
254	SETUP_RET_DATA(%r15, %rcx)
255	GET_V(%rsp, 0, V_SSP, %rsp)	/* restore user's stack pointer	*/
256	jmp	nopop_sys_syscall_swapgs_sysretq
2579:
258	popq	%r15
259	retq
260
261SET_SIZE(s10_brand_syscall_callback)
262
263/*
264 * %eax - syscall number
265 * %ecx - user space %esp
266 * %edx - user space return address
267 */
268ENTRY(s10_brand_sysenter_callback)
269
270	CALLBACK_PROLOGUE(%rax, %r15, %r15b)
271
272	SETUP_RET_DATA(%r15, %rdx)
273	jmp	sys_sysenter_swapgs_sysexit
2749:
275	popq	%r15
276	ret
277SET_SIZE(s10_brand_sysenter_callback)
278
279/*
280 * The saved stack pointer points at the state saved when we took
281 * the interrupt:
282 *	   --------------------------------------
283 *    | 32 | user's %ss				|
284 *    | 24 | user's %esp			|
285 *    | 16 | EFLAGS register			|
286 *    v  8 | user's %cs				|
287 *       0 | user's %eip			|
288 *	   --------------------------------------
289 */
290#define	V_U_EIP		(CLONGSIZE * 0)
291
292ENTRY(s10_brand_int91_callback)
293
294	CALLBACK_PROLOGUE(%rax, %r15, %r15b)
295
296	/*
297	 * To 'return' to our user-space handler we need to update the user's
298	 * %eip pointer in the saved interrupt state.  The interrupt state was
299	 * pushed onto our stack automatically when the interrupt occured; see
300	 * the comments above.  The original return address is passed in %rax.
301	 */
302	CALC_TABLE_ADDR(%r15)
303	GET_V(%rsp, 1, V_SSP, %rax)	/* get saved stack pointer */
304	SET_V(%rax, 0, V_U_EIP, %r15)	/* save new return addr in %eip */
305	GET_V(%rsp, 1, V_URET_ADDR, %rax) /* %rax has orig. return addr. */
306
307	popq	%r15			/* Restore scratch register	*/
308	movq	V_SSP(%rsp), %rsp	/* Remove callback stuff from stack */
309	jmp	sys_sysint_swapgs_iret
3109:
311	popq	%r15
312	retq
313SET_SIZE(s10_brand_int91_callback)
314
315#else	/* !__amd64 */
316
317/*
318 * lcall handler for 32-bit OS
319 *     %eax - syscall number
320 *
321 * Above the stack contents common to all callbacks is the
322 * int/lcall-specific state:
323 *	   --------------------------------------
324 *    | 44 | user's %ss				|
325 *    | 40 | user's %esp			|
326 *    | 36 | EFLAGS register			|
327 *    v 32 | user's %cs				|
328 *      28 | user's %eip			|
329 *	   --------------------------------------
330 */
331#define	V_U_SS		(V_END + (CLONGSIZE * 4))
332#define	V_U_ESP		(V_END + (CLONGSIZE * 3))
333#define	V_EFLAGS	(V_END + (CLONGSIZE * 2))
334#define	V_U_CS		(V_END + (CLONGSIZE * 1))
335#define	V_U_EIP		(V_END + (CLONGSIZE * 0))
336
337ENTRY(s10_brand_syscall_callback)
338
339	CALLBACK_PROLOGUE(%eax, %ebx, %bl)
340
341	/*
342  	 * To 'return' to our user-space handler, we need to replace the
343	 * iret target address.
344	 * The original return address is passed in %eax.
345	 */
346	CALC_TABLE_ADDR(%ebx)		/* new return addr is in %ebx */
347	SET_V(%esp, 1, V_U_EIP, %ebx)	/* set iret target address to hdlr */
348	GET_V(%esp, 1, V_URET_ADDR, %eax) /* save orig return addr in %eax */
349
350	GET_V(%esp, 1, V_U_GS, %ebx)	/* grab the the user %gs	*/
351	movw	%bx, %gs		/* restore the user %gs	*/
352
353	popl	%ebx			/* Restore scratch register	*/
354	addl	$V_END, %esp	/* Remove all callback stuff from stack	*/
355	jmp	nopop_sys_rtt_syscall
3569:
357	popl	%ebx
358	ret
359SET_SIZE(s10_brand_syscall_callback)
360
361/*
362 * %eax - syscall number
363 * %ecx - user space %esp
364 * %edx - user space return address
365 */
366ENTRY(s10_brand_sysenter_callback)
367
368	CALLBACK_PROLOGUE(%eax, %ebx, %bl)
369
370	/*
371  	 * To 'return' to our user-space handler, we just need to place its
372	 * address into %edx.
373	 * The original return address is passed in %eax.
374	 */
375	movl    %edx, %ebx		/* save orig return addr in tmp reg */
376	CALC_TABLE_ADDR(%edx)		/* new return addr is in %edx */
377	movl    %ebx, %eax		/* save orig return addr in %eax */
378
379	GET_V(%esp, 1, V_U_GS, %ebx)	/* grab the the user %gs	*/
380	movw	%bx, %gs		/* restore the user %gs	*/
381
382	popl	%ebx			/* restore scratch register	*/
383	sysexit
3849:
385	popl	%ebx
386	ret
387SET_SIZE(s10_brand_sysenter_callback)
388
389#endif	/* !__amd64 */
390#endif	/* lint */
391