summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4/brand/common/brand_solaris.s
blob: c216d786a3d4dbd0f986b3a8625db76f11d5586d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * This is an assembly file that gets #include-ed into the brand-specific
 * assembly files (e.g. sn1_brand_asm.s) for Solaris-derived brands.
 * We can't make these into functions since in the trap context there's
 * no easy place to save the extra parameters that would be required, so
 * each brand module needs its own copy of this code.  We #include this and
 * use brand-specific #defines to replace the XXX_brand_... definitions.
 */ 

#include <sys/asm_linkage.h>
#include <sys/machthread.h>
#include <sys/privregs.h>
#include "assym.h"

#ifdef _ASM	/* The remainder of this file is only for assembly files */

#if defined(sun4v)

#define	GLOBALS_SWAP(reg)				\
	rdpr	%gl, reg;				\
	wrpr	reg, 1, %gl

/*
 * The GLOBALS_RESTORE macro can only be one instruction since it's
 * used in a delay slot.
 */
#define	GLOBALS_RESTORE(reg)				\
	wrpr	reg, 0, %gl

#else /* !sun4v */

#define	GLOBALS_SWAP(reg)				\
	rdpr	%pstate, reg;				\
	wrpr	reg, PSTATE_AG, %pstate

/*
 * The GLOBALS_RESTORE macro can only be one instruction since it's
 * used in a delay slot.
 */
#define	GLOBALS_RESTORE(reg)				\
	wrpr	reg, %g0, %pstate

#endif /* !sun4v */

/*
 * Input parameters:
 * %g1: return point
 * %g2: pointer to our cpu structure
 */
ENTRY(XXX_brand_syscall32_callback)
	/*
	 * If the trapping thread has the address mask bit clear, then it's
	 * a 64-bit process, and has no business calling 32-bit syscalls.
	 */
	rdpr	%tstate, %g3;		/* %tstate.am is the trapping */
	andcc	%g3, TSTATE_AM, %g3;	/*   threads address mask bit */
	bne,pt	%xcc, _entry;
	nop;
	jmp	%g1;			/* 64 bit process, bail out */
	nop;
SET_SIZE(XXX_brand_syscall32_callback)

/*
 * Input parameters:
 * %g1: return point
 * %g2: pointer to our cpu structure
 */
ENTRY(XXX_brand_syscall_callback)
	/*
	 * If the trapping thread has the address mask bit set, then it's
	 * a 32-bit process, and has no business calling 64-bit syscalls.
	 */
	rdpr	%tstate, %g3;		/* %tstate.am is the trapping */
	andcc	%g3, TSTATE_AM, %g3;	/*   threads address mask bit */
	be,pt	%xcc, _entry;
	nop;
	jmp	%g1;			/* 32 bit process, bail out */
	nop;
SET_SIZE(XXX_brand_syscall_callback)

ENTRY(XXX_brand_syscall_callback_common)
_entry:
	/*
	 * Input parameters:
	 * %g1: return point
	 * %g2: pointer to our cpu structure
	 *
	 * Note that we're free to use any %g? registers as long as
	 * we are are executing with alternate globals.  If we're
	 * executing with user globals we need to backup any registers
	 * that we want to use so that we can restore them when we're
	 * done.
	 *
	 * Save some locals in the CPU tmp area to give us a little
	 * room to work.
	 */
	stn	%l0, [%g2 + CPU_TMP1];
	stn	%l1, [%g2 + CPU_TMP2];

#if defined(sun4v)
	/*
	 * On sun4v save our input parameters (which are stored in the
	 * alternate globals) since we'll need to switch between alternate
	 * globals and normal globals, and on sun4v the alternate globals
	 * are not preserved across these types of switches.
	 */
	stn	%l2, [%g2 + CPU_TMP3];
	stn	%l3, [%g2 + CPU_TMP4];

	mov	%g1, %l2;		/* save %g1 in %l2 */
	mov	%g2, %l3;		/* save %g2 in %l3 */
#endif /* sun4v */

	/*
	 * Switch from the alternate to user globals to grab the syscall
	 * number.
	 */
	GLOBALS_SWAP(%l0);		/* switch to normal globals */

	/*
	 * If the system call number is >= 1024, then it is a native
	 * syscall that doesn't need emulation.
	 */
	cmp	%g1, 1024;		/* is this a native syscall? */
	bl,a	_indirect_check;	/* probably not, continue checking */
	mov	%g1, %l1;		/* delay slot - grab syscall number */

	/*
	 * This is a native syscall, probably from the emulation library.
	 * Subtract 1024 from the syscall number and let it go through.
	 */
	sub	%g1, 1024, %g1;		/* convert magic num to real syscall */
	ba	_exit;			/* jump back into syscall path */
	GLOBALS_RESTORE(%l0);		/* delay slot - */
					/* switch back to alternate globals */

_indirect_check:
	/*
	 * If the system call number is 0 (SYS_syscall), then this might be
	 * an indirect syscall, in which case the actual syscall number
	 * would be stored in %o0, in which case we need to redo the
	 * the whole >= 1024 check.
	 */
	brnz,pt %g1, _emulation_check;	/* is this an indirect syscall? */
	nop;				/* if not, goto the emulation check */

	/*
	 * Indirect syscalls are only supported for 32 bit processes so
	 * consult the tstate address mask again.
	 */
	rdpr	%tstate, %l1;		/* %tstate.am is the trapping */
	andcc	%l1, TSTATE_AM, %l1;	/*   threads address mask bit */
	be,a,pn	%xcc, _exit;
	GLOBALS_RESTORE(%l0);		/* delay slot -	*/
					/* switch back to alternate globals */

	/*
	 * The caller is 32 bit and this an indirect system call.
	 */
	cmp	%o0, 1024;		/* is this a native syscall? */
	bl,a	_emulation_check;	/* no, goto the emulation check */
	mov	%o0, %l1;		/* delay slot - grab syscall number */

	/*
	 * This is native indirect syscall, probably from the emulation
	 * library.  Subtract 1024 from the syscall number and let it go
	 * through.
	 */
	sub	%o0, 1024, %o0;		/* convert magic num to real syscall */
	ba	_exit;			/* jump back into syscall path */
	GLOBALS_RESTORE(%l0);		/* delay slot - */
					/* switch back to alternate globals */

_emulation_check:
	GLOBALS_RESTORE(%l0);		/* switch back to alternate globals */

	/*
	 * Check to see if we want to interpose on this system call.  If
	 * not, we jump back into the normal syscall path and pretend
	 * nothing happened.  %l1 contains the syscall we're invoking.
	 */
	set	XXX_emulation_table, %g3;
	ldn	[%g3], %g3;
	add	%g3, %l1, %g3;
	ldub	[%g3], %g3;
	brz	%g3, _exit;
	nop;

	/*
	 * Find the address of the userspace handler.
	 * cpu->cpu_thread->t_procp->p_brand_data->spd_handler.
	 */
#if defined(sun4v)
	/* restore the alternate global registers after incrementing %gl */
	mov	%l3, %g2;
#endif /* sun4v */
	ldn	[%g2 + CPU_THREAD], %g3;	/* get thread ptr */
	ldn	[%g3 + T_PROCP], %g4;		/* get proc ptr */
	ldn	[%g4 + P_BRAND_DATA], %g5;	/* get brand data ptr */
	ldn	[%g5 + SPD_HANDLER], %g5;	/* get userland brnd hdlr ptr */
	brz	%g5, _exit;			/* has it been set? */
	nop;

	/*
	 * Make sure this isn't an agent lwp.  We can't do syscall
	 * interposition for system calls made by a agent lwp.  See
	 * the block comments in the top of the brand emulation library
	 * for more information.
	 */
	ldn	[%g4 + P_AGENTTP], %g4;		/* get agent thread ptr */
	cmp	%g3, %g4;			/* is this an agent thread? */
	be,pn	%xcc, _exit;			/* if so don't emulate */
	nop;

	/*
	 * Now the magic happens.  Grab the trap return address and then
	 * reset it to point to the user space handler.  When we execute
	 * the 'done' instruction, we will jump into our handler instead of
	 * the user's code.  We also stick the old return address in %g5,
	 * so we can return to the proper instruction in the user's code.
	 * Note: we also pass back the base address of the syscall
	 * emulation table.  This is a performance hack to avoid having to
	 * look it up on every call.
	 */
	rdpr	%tnpc, %l1;		/* save old tnpc */
	wrpr	%g0, %g5, %tnpc;	/* setup tnpc */
	GLOBALS_SWAP(%l0);		/* switch to normal globals */
	mov	%l1, %g5;		/* pass tnpc to user code in %g5 */
	GLOBALS_RESTORE(%l0);		/* switch back to alternate globals */

	/* Update the address we're going to return to */
#if defined(sun4v)
	set	fast_trap_done_chk_intr, %l2;
#else /* !sun4v */
	set	fast_trap_done_chk_intr, %g1;
#endif /* !sun4v */

_exit:
	/*
	 * Restore registers before returning.
	 *
	 * Note that %g2 should be loaded with the CPU struct addr and
	 * %g1 should be loaded the address we're going to return to.
	 */
#if defined(sun4v)
	/* restore the alternate global registers after incrementing %gl */
	mov	%l2, %g1;		/* restore %g1 from %l2 */
	mov	%l3, %g2;		/* restore %g2 from %l3 */

	ldn	[%g2 + CPU_TMP4], %l3;	/* restore locals */
	ldn	[%g2 + CPU_TMP3], %l2;
#endif /* sun4v */

	ldn	[%g2 + CPU_TMP2], %l1;	/* restore locals */
	ldn	[%g2 + CPU_TMP1], %l0;

	jmp	%g1;
	nop;
SET_SIZE(XXX_brand_syscall_callback_common)

#endif	/* _ASM */