summaryrefslogtreecommitdiff
path: root/usr/src/uts/intel/brand/common/brand_asm.h
blob: 175b98eb7d62553433dfc4db0aeaf9a399f57059 (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
/*
 * 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) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 */

#ifndef _COMMON_BRAND_ASM_H
#define	_COMMON_BRAND_ASM_H

#ifdef  __cplusplus
extern "C" {
#endif

#ifndef	lint

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

#endif	/* lint */

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

#if defined(__amd64)
/*
 * Common to all 64-bit callbacks:
 *
 * We're running on the kernel's %gs.
 *
 * We return directly to userland, bypassing the _update_sregs logic, so
 * the routine must NOT do anything that could cause a context switch.
 *
 * %rax - syscall number
 *
 * When called, all general registers, except for %r15, are as they were when
 * the user process made the system call.  %r15 is available to the callback as
 * a scratch register.  If the callback returns to the kernel path, %r15 does
 * not have to be restored to the user value.  If the callback returns to the
 * userlevel emulation code, the callback should restore %r15 if the emulation
 * depends on the original userlevel value.
 *
 * 64-BIT INTERPOSITION STACK
 * On entry to the callback the stack looks like this:
 *         --------------------------------------
 *      32 | callback pointer			|
 *      24 | saved stack pointer		|
 *    | 16 | lwp pointer			|
 *    v  8 | user return address		|
 *       0 | BRAND_CALLBACK()'s return addr	|
 *         --------------------------------------
 */

#define	V_COUNT	5
#define	V_END		(CLONGSIZE * 5)
#define	V_SSP		(CLONGSIZE * 3)
#define	V_LWP		(CLONGSIZE * 2)
#define	V_URET_ADDR	(CLONGSIZE * 1)
#define	V_CB_ADDR	(CLONGSIZE * 0)

#define	SP_REG		%rsp
#define	SCR_REG		%r15
#define	SCR_REGB	%r15b
#define	SYSCALL_REG	%rax

/*
 * 64-BIT INT STACK
 * For int callbacks (e.g. int91) the saved stack pointer (V_SSP) points at
 * the state saved when we took the interrupt:
 *	   --------------------------------------
 *    | 32 | user's %ss				|
 *    | 24 | user's %esp			|
 *    | 16 | EFLAGS register			|
 *    v  8 | user's %cs				|
 *       0 | user's %eip (user return address)	|
 *	   --------------------------------------
 */
#define	V_U_EIP		(CLONGSIZE * 0)

#else	/* !__amd64 */
/*
 * 32-BIT INTERPOSITION STACK
 * When our syscall interposition callback entry point gets invoked the
 * stack looks like this:
 *         --------------------------------------
 *    | 16 | 'scratch space'			|
 *    | 12 | user's %ebx			|
 *    |  8 | user's %gs selector		|
 *    v  4 | lwp pointer			|
 *       0 | callback wrapper return addr	|
 *         --------------------------------------
 */

#define	V_COUNT	5
#define	V_END		(CLONGSIZE * 5)
#define	V_U_EBX		(CLONGSIZE * 3)
#define	V_LWP		(CLONGSIZE * 1)
#define	V_CB_ADDR	(CLONGSIZE * 0)

#define	SP_REG		%esp
#define	SCR_REG		%ebx
#define	SCR_REGB	%bl
#define	SYSCALL_REG	%eax

/*
 * 32-BIT INT STACK
 * For the lcall handler for 32-bit OS (i.e. xxx_brand_syscall_callback)
 * above the stack contents common to all callbacks is the int/lcall-specific
 * state:
 *	   --------------------------------------
 *    | 36 | user's %ss				|
 *    | 32 | user's %esp			|
 *    | 28 | EFLAGS register			|
 *    v 24 | user's %cs				|
 *      20 | user's %eip (user return address)	|
 *	   --------------------------------------
 */
#define	V_U_EIP		(V_END + (CLONGSIZE * 0))

#endif	/* !__amd64 */

/*
 * The following macros allow us to access to variables/parameters passed
 * in on the stack.  They take the following variables:
 *	sp	- a register with the current stack pointer value
 *	pcnt	- the number of words currently pushed onto the stack
 *	var	- the variable to lookup
 *	reg	- a register to read the variable into, or
 *		  a register to write to the variable
 */
#define	V_OFFSET(pcnt, var)						\
	(var + (pcnt * CLONGSIZE))

#define	GET_V(sp, pcnt, var, reg)					\
	mov	V_OFFSET(pcnt, var)(sp), reg

#define	SET_V(sp, pcnt, var, reg)					\
	mov	reg, V_OFFSET(pcnt, var)(sp)

#define	GET_PROCP(sp, pcnt, reg)					\
	GET_V(sp, pcnt, V_LWP, reg);		/* get lwp pointer */	\
	mov	LWP_PROCP(reg), reg		/* get proc pointer */

#define	GET_P_BRAND_DATA(sp, pcnt, reg)					\
	GET_PROCP(sp, pcnt, reg);					\
	mov	P_BRAND_DATA(reg), reg		/* get p_brand_data */

/*
 * Each of the following macros returns to the standard syscall codepath if
 * it detects that this process is not able, or intended, to emulate this
 * system call.  They all assume that the routine provides a 'bail-out'
 * label of '9'.
 */

/*
 * See if this process has a user-space handler registered for it.  For the
 * brand, the per-process brand data holds the address of the handler.
 * As shown in the stack diagrams above, the callback code leaves the lwp
 * pointer at well-defined offsets, so check if proc_data_t->X_handler is
 * non-NULL.  For each brand, the handler parameter refers to the brand's
 * user-space handler variable name.
 */
#define	CHECK_FOR_HANDLER(scr, handler)					\
	GET_P_BRAND_DATA(SP_REG, 0, scr);	/* get p_brand_data */	\
	cmp	$0, scr;						\
	je	9f;							\
	cmpq	$0, handler(scr);		/* check handler */	\
	je	9f

/*
 * If the system call number is >= 1024, then it is coming from the
 * emulation support library.  As such we should handle it natively instead
 * of sending it back to the emulation library.
 */
#define	CHECK_FOR_NATIVE(reg)		\
	cmp	$1024, reg;		\
	jl	1f;			\
	sub	$1024, reg;		\
	jmp	9f;			\
1:

/*
 * 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.
 * This macro is usable for brands which have the same number of syscalls
 * as the base OS.
 */
#define	CHECK_FOR_INTERPOSITION(emul_table, call, scr, scr_low)		\
	cmp	$NSYSCALL, call;	/* is 0 <= syscall <= MAX? */	\
	ja	9f;			/* no, take normal ret path */	\
	lea	emul_table, scr;					\
	/*CSTYLED*/							\
	mov	(scr), scr;						\
	add	call, scr;						\
	/*CSTYLED*/							\
	movb	(scr), scr_low;						\
	cmpb	$0, scr_low;						\
	je	9f			/* no, take normal ret path */

#define	CALLBACK_PROLOGUE(emul_table, handler, call, scr, scr_low)	\
	CHECK_FOR_HANDLER(scr, handler);				\
	CHECK_FOR_NATIVE(call);						\
	CHECK_FOR_INTERPOSITION(emul_table, call, scr, scr_low)

/*
 * Rather than returning to the instruction after the syscall, we need to
 * transfer control into the brand library's handler table at
 * table_addr + (16 * syscall_num), thus encoding the system call number in the
 * instruction pointer.  The CALC_TABLE_ADDR macro performs that calculation.
 *
 * This macro assumes the syscall number is in SYSCALL_REG and it clobbers
 * that register.  It leaves the calculated handler table return address in
 * the scratch reg.
 */
#define	CALC_TABLE_ADDR(scr, handler)					\
	GET_P_BRAND_DATA(SP_REG, 0, scr); /* get p_brand_data ptr */	\
	mov	handler(scr), scr;	/* get p_brand_data->XX_handler */ \
	shl	$4, SYSCALL_REG;	/* syscall_num * 16 */		\
	add	SYSCALL_REG, scr	/* leave return addr in scr reg. */

#endif	/* _ASM */

#ifdef  __cplusplus
}
#endif

#endif	/* _COMMON_BRAND_ASM_H */