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