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