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