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