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
30void
31sn1_brand_syscall32_callback(void)
32{
33}
34
35void
36sn1_brand_syscall_callback(void)
37{
38}
39
40#else	/* !lint */
41
42#include <sys/asm_linkage.h>
43#include <sys/machthread.h>
44#include <sys/privregs.h>
45#include <sn1_offsets.h>
46#include "assym.h"
47
48#if defined(sun4v)
49
50#define GLOBALS_SWAP(reg)				\
51	rdpr	%gl, reg				;\
52	wrpr	reg, 1, %gl
53
54/*
55 * The GLOBALS_RESTORE macro can only be one instruction since it's
56 * used in a delay slot.
57 */
58#define GLOBALS_RESTORE(reg)				\
59	wrpr	reg, 0, %gl
60
61#else /* !sun4v */
62
63#define GLOBALS_SWAP(reg)				\
64	rdpr	%pstate, reg				;\
65	wrpr	reg, PSTATE_AG, %pstate
66
67/*
68 * The GLOBALS_RESTORE macro can only be one instruction since it's
69 * used in a delay slot.
70 */
71#define GLOBALS_RESTORE(reg)				\
72	wrpr	reg, %g0, %pstate
73
74#endif /* !sun4v */
75
76	/*
77	 * Input parameters:
78	 * %g1: return point
79	 * %g2: pointer to our cpu structure
80	 */
81	ENTRY(sn1_brand_syscall32_callback)
82	/*
83	 * If the trapping thread has the address mask bit clear, then it's
84	 * a 64-bit process, and has no business calling 32-bit syscalls.
85	 */
86	rdpr	%tstate, %g3		! %tstate.am is the trapping
87	andcc	%g3, TSTATE_AM, %g3	!   threads address mask bit
88	bne,pt	%xcc, _entry
89	nop
90	jmp	%g1			! 64 bit process, bail out
91	nop
92	SET_SIZE(sn1_brand_syscall32_callback)
93
94	/*
95	 * Input parameters:
96	 * %g1: return point
97	 * %g2: pointer to our cpu structure
98	 */
99	ENTRY(sn1_brand_syscall_callback)
100	/*
101	 * If the trapping thread has the address mask bit set, then it's
102	 * a 32-bit process, and has no business calling 64-bit syscalls.
103	 */
104	rdpr	%tstate, %g3		! %tstate.am is the trapping
105	andcc	%g3, TSTATE_AM, %g3	!   threads address mask bit
106	be,pt	%xcc, _entry
107	nop
108	jmp	%g1			! 32 bit process, bail out
109	nop
110	SET_SIZE(sn1_brand_syscall_callback)
111
112	ENTRY(sn1_brand_syscall_callback_common)
113_entry:
114	/*
115	 * Input parameters:
116	 * %g1: return point
117	 * %g2: pointer to our cpu structure
118	 *
119	 * Note that we're free to use any %g? registers as long as
120	 * we are are executing with alternate globals.  If we're
121	 * executing with user globals we need to backup any registers
122	 * that we want to use so that we can restore them when we're
123	 * done.
124	 *
125	 * Save some locals in the CPU tmp area to give us a little
126	 * room to work.
127	 */
128	stn	%l0, [%g2 + CPU_TMP1]
129	stn	%l1, [%g2 + CPU_TMP2]
130
131#if defined(sun4v)
132	/*
133	 * On sun4v save our input parameters (which are stored in the
134	 * alternate globals) since we'll need to switch between alternate
135	 * globals and normal globals, and on sun4v the alternate globals
136	 * are not preserved across these types of switches.
137	 */
138	stn	%l2, [%g2 + CPU_TMP3]
139	stn	%l3, [%g2 + CPU_TMP4]
140
141	mov	%g1, %l2		! save %g1 in %l2
142	mov	%g2, %l3		! save %g2 in %l3
143#endif /* sun4v */
144
145	/*
146	 * Switch from the alternate to user globals to grab the syscall
147	 * number.
148	 */
149	GLOBALS_SWAP(%l0)		! switch to normal globals
150
151	/*
152	 * If the system call number is >= 1024, then it is a native
153	 * syscall that doesn't need emulation.
154	 */
155	cmp	%g1, 1024		! is this a native syscall?
156	bl,a	_indirect_check		! probably not, continue checking
157	mov	%g1, %l1		! delay slot - grab syscall number
158
159	/*
160	 * This is a native syscall, probably from the emulation library.
161	 * Subtract 1024 from the syscall number and let it go through.
162	 */
163	sub	%g1, 1024, %g1		! convert magic num to real syscall
164	ba	_exit			! jump back into syscall path
165	GLOBALS_RESTORE(%l0)		! delay slot -
166					! switch back to alternate globals
167
168_indirect_check:
169	/*
170	 * If the system call number is 0 (SYS_syscall), then this might be
171	 * an indirect syscall, in which case the actual syscall number
172	 * would be stored in %o0, in which case we need to redo the
173	 * the whole >= 1024 check.
174	 */
175	brnz,pt %g1, _emulation_check	! is this an indirect syscall?
176	nop				! if not, goto the emulation check
177
178	/*
179	 * Indirect syscalls are only supported for 32 bit processes so
180	 * consult the tstate address mask again.
181	 */
182	rdpr	%tstate, %l1		! %tstate.am is the trapping
183	andcc	%l1, TSTATE_AM, %l1	!   threads address mask bit
184	be,a,pn	%xcc, _exit
185	GLOBALS_RESTORE(%l0)		! delay slot -
186					! switch back to alternate globals
187
188	/*
189	 * The caller is 32 bit and this an indirect system call.
190	 */
191	cmp	%o0, 1024		! is this a native syscall?
192	bl,a	_emulation_check	! no, goto the emulation check
193	mov	%o0, %l1		! delay slot - grab syscall number
194
195	/*
196	 * This is native indirect syscall, probably from the emulation library.
197	 * Subtract 1024 from the syscall number and let it go through.
198	 */
199	sub	%o0, 1024, %o0		! convert magic num to real syscall
200	ba	_exit			! jump back into syscall path
201	GLOBALS_RESTORE(%l0)		! delay slot -
202					! switch back to alternate globals
203
204_emulation_check:
205	GLOBALS_RESTORE(%l0)		! switch back to alternate globals
206
207	/*
208	 * Check to see if we want to interpose on this system call.  If
209	 * not, we jump back into the normal syscall path and pretend
210	 * nothing happened.  %l1 contains the syscall we're invoking.
211	 */
212	set	sn1_emulation_table, %g3
213	ldn	[%g3], %g3
214	add	%g3, %l1, %g3
215	ldub	[%g3], %g3
216	brz	%g3, _exit
217	nop
218
219	/*
220	 * Find the address of the userspace handler.
221	 * cpu->cpu_thread->t_procp->p_brand_data->spd_handler.
222	 */
223#if defined(sun4v)
224	! restore the alternate global registers after incrementing %gl
225	mov	%l3, %g2
226#endif /* sun4v */
227	ldn	[%g2 + CPU_THREAD], %g3		! get thread ptr
228	ldn	[%g3 + T_PROCP], %g4		! get proc ptr
229	ldn	[%g4 + P_BRAND_DATA], %g5	! get brand data ptr
230	ldn	[%g5 + SPD_HANDLER], %g5	! get userland brand handler ptr
231	brz	%g5, _exit			! has it been set?
232	nop
233
234	/*
235	 * Make sure this isn't an agent lwp.  We can't do syscall
236	 * interposition for system calls made by a agent lwp.  See
237	 * the block comments in the top of the brand emulation library
238	 * for more information.
239	 */
240	ldn	[%g4 + P_AGENTTP], %g4		! get agent thread ptr
241	cmp	%g3, %g4			! is this an agent thread?
242	be,pn	%xcc, _exit			! if so don't emulate
243	nop
244
245	/*
246	 * Now the magic happens.  Grab the trap return address and then
247	 * reset it to point to the user space handler.  When we execute
248	 * the 'done' instruction, we will jump into our handler instead of
249	 * the user's code.  We also stick the old return address in %g5,
250	 * so we can return to the proper instruction in the user's code.
251	 * Note: we also pass back the base address of the syscall
252	 * emulation table.  This is a performance hack to avoid having to
253	 * look it up on every call.
254	 */
255	rdpr	%tnpc, %l1		! save old tnpc
256	wrpr	%g0, %g5, %tnpc		! setup tnpc
257	GLOBALS_SWAP(%l0)		! switch to normal globals
258	mov	%l1, %g5		! pass tnpc to user code in %g5
259	GLOBALS_RESTORE(%l0)		! switch back to alternate globals
260
261	/* Update the address we're going to return to */
262#if defined(sun4v)
263	set	fast_trap_done_chk_intr, %l2
264#else /* !sun4v */
265	set	fast_trap_done_chk_intr, %g1
266#endif /* !sun4v */
267
268_exit:
269	/*
270	 * Restore registers before returning.
271	 *
272	 * Note that %g2 should be loaded with the CPU struct addr and
273	 * %g1 should be loaded the address we're going to return to.
274	 */
275#if defined(sun4v)
276	! restore the alternate global registers after incrementing %gl
277	mov	%l2, %g1		! restore %g1 from %l2
278	mov	%l3, %g2		! restore %g2 from %l3
279
280	ldn	[%g2 + CPU_TMP4], %l3	! restore locals
281	ldn	[%g2 + CPU_TMP3], %l2
282#endif /* sun4v */
283
284	ldn	[%g2 + CPU_TMP2], %l1	! restore locals
285	ldn	[%g2 + CPU_TMP1], %l0
286
287	jmp	%g1
288	nop
289	SET_SIZE(sn1_brand_syscall_callback_common)
290#endif	/* !lint */
291