xref: /illumos-gate/usr/src/lib/brand/shared/brand/amd64/handler.S (revision 9b6224883056ca9db111541974efeb6a4de0c074)
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#include <brand_misc.h>
26
27/*
28 * Each JMP must occupy 16 bytes
29 */
30#define	JMP	\
31	pushq	$_CONST(. - brand_handler_table); \
32	jmp	brand_handler;	\
33	.align	16;
34
35#define	JMP4	JMP; JMP; JMP; JMP
36#define JMP16	JMP4; JMP4; JMP4; JMP4
37#define JMP64	JMP16; JMP16; JMP16; JMP16
38#define JMP256	JMP64; JMP64; JMP64; JMP64
39
40#if defined(lint)
41
42void
43brand_handler_table(void)
44{}
45
46void
47brand_handler(void)
48{
49}
50
51#else	/* lint */
52
53	/*
54	 * On entry to this table, %rax will hold the return address. The
55	 * location where we enter the table is a function of the system
56	 * call number. The table needs the same alignment as the individual
57	 * entries.
58	 */
59	.align	16
60	ENTRY_NP(brand_handler_table)
61	JMP256
62	SET_SIZE(brand_handler_table)
63
64	/*
65	 * %rax - userland return address
66	 * stack contains:
67	 *    |    --------------------------------------
68	 *    v  8 | syscall arguments			|
69	 *  %rsp+0 | syscall number			|
70	 *         --------------------------------------
71	 */
72	ENTRY_NP(brand_handler)
73	pushq	%rbp			/* allocate stack frame */
74	movq	%rsp, %rbp
75
76	/* Save registers at the time of the syscall. */
77	movq	$0, EH_LOCALS_GREG(REG_TRAPNO)(%rbp)
78	movq	$0, EH_LOCALS_GREG(REG_ERR)(%rbp)
79	movq	%r15, EH_LOCALS_GREG(REG_R15)(%rbp)
80	movq	%r14, EH_LOCALS_GREG(REG_R14)(%rbp)
81	movq	%r13, EH_LOCALS_GREG(REG_R13)(%rbp)
82	movq	%r12, EH_LOCALS_GREG(REG_R12)(%rbp)
83	movq	%r11, EH_LOCALS_GREG(REG_R11)(%rbp)
84	movq	%r10, EH_LOCALS_GREG(REG_R10)(%rbp)
85	movq	%r9, EH_LOCALS_GREG(REG_R9)(%rbp)
86	movq	%r8, EH_LOCALS_GREG(REG_R8)(%rbp)
87	movq	%rdi, EH_LOCALS_GREG(REG_RDI)(%rbp)
88	movq	%rsi, EH_LOCALS_GREG(REG_RSI)(%rbp)
89	movq	%rbx, EH_LOCALS_GREG(REG_RBX)(%rbp)
90	movq	%rcx, EH_LOCALS_GREG(REG_RCX)(%rbp)
91	movq	%rdx, EH_LOCALS_GREG(REG_RDX)(%rbp)
92	xorq	%rcx, %rcx
93	movw	%cs, %cx
94	movq	%rcx, EH_LOCALS_GREG(REG_CS)(%rbp)
95	movw	%ds, %cx
96	movq	%rcx, EH_LOCALS_GREG(REG_DS)(%rbp)
97	movw	%es, %cx
98	movq	%rcx, EH_LOCALS_GREG(REG_ES)(%rbp)
99	movw	%fs, %cx
100	movq	%rcx, EH_LOCALS_GREG(REG_FS)(%rbp)
101	movw	%gs, %cx
102	movq	%rcx, EH_LOCALS_GREG(REG_GS)(%rbp)
103	movw	%ss, %cx
104	movq	%rcx, EH_LOCALS_GREG(REG_SS)(%rbp)
105	pushfq					/* save syscall flags */
106	popq	%r12
107	movq	%r12, EH_LOCALS_GREG(REG_RFL)(%rbp)
108	movq	EH_ARGS_OFFSET(0)(%rbp), %r12	/* save syscall rbp */
109	movq	%r12, EH_LOCALS_GREG(REG_RBP)(%rbp)
110	movq	%rbp, %r12			/* save syscall rsp */
111	addq	$CPTRSIZE, %r12
112	movq	%r12, EH_LOCALS_GREG(REG_RSP)(%rbp)
113	movq	%fs:0, %r12			/* save syscall fsbase */
114	movq	%r12, EH_LOCALS_GREG(REG_FSBASE)(%rbp)
115	movq	$0, EH_LOCALS_GREG(REG_GSBASE)(%rbp)
116
117	/*
118	 * The kernel drops us into the middle of the brand_handle_table
119	 * above that then pushes that table offset onto the stack, and calls
120	 * into brand_handler. That offset indicates the system call number
121	 * while %rax holds the return address for the system call. We replace
122	 * the value on the stack with the return address, and use the value to
123	 * compute the system call number by dividing by the table entry size.
124	 */
125	xchgq	CPTRSIZE(%rbp), %rax	/* swap JMP table offset and ret addr */
126	shrq	$4, %rax		/* table_offset/size = syscall num */
127	movq	%rax, EH_LOCALS_GREG(REG_RAX)(%rbp) /* save syscall num */
128
129	/*
130	 * Finish setting up our stack frame.  We would normally do this
131	 * upon entry to this function, but in this case we delayed it
132	 * because a "sub" operation can modify flags and we wanted to
133	 * save the flags into the gregset_t above before they get modified.
134	 *
135	 * Our stack frame format is documented in brand_misc.h.
136	 */
137	subq	$EH_LOCALS_SIZE, %rsp
138
139	/* Look up the system call's entry in the sysent table */
140	movq	brand_sysent_table@GOTPCREL(%rip), %r11 /* %r11 = sysent_tbl */
141	shlq	$4, %rax		/* each entry is 16 bytes */
142	addq	%rax, %r11		/* %r11 = sysent entry address */
143
144	/*
145	 * Get the return value flag and the number of arguments from the
146	 * sysent table.
147	 */
148	movq	CPTRSIZE(%r11), %r12		/* number of args + rv flag */
149	andq	$RV_MASK, %r12			/* strip out number of args */
150	movq	%r12, EH_LOCALS_RVFLAG(%rbp)	/* save rv flag */
151
152	/*
153	 * Setup arguments for our emulation call.  Our input arguments,
154	 * 0 to N, will become emulation call arguments 1 to N+1.
155	 *
156	 * Note: Syscall argument passing is different from function call
157	 * argument passing on amd64.  For function calls, the fourth arg
158	 * is passed via %rcx, but for system calls the 4th argument is
159	 * passed via %r10.  This is because in amd64, the syscall
160	 * instruction puts lower 32 bit of %rflags in %r11 and puts the
161	 * %rip value to %rcx.
162	 */
163	movq	EH_ARGS_OFFSET(4)(%rbp), %r12		/* copy 8th arg */
164	movq	%r12, EH_ARGS_OFFSET(2)(%rsp)
165	movq	EH_ARGS_OFFSET(3)(%rbp), %r12		/* copy 7th arg */
166	movq	%r12, EH_ARGS_OFFSET(1)(%rsp)
167	movq	%r9, EH_ARGS_OFFSET(0)(%rsp)
168	movq	%r8, %r9
169	movq	%r10, %r8
170	movq	%rdx, %rcx
171	movq	%rsi, %rdx
172	movq	%rdi, %rsi
173
174	/*
175	 * The first parameter to the emulation callback function is a
176	 * pointer to a sysret_t structure.
177	 */
178	movq	%rbp, %rdi
179	addq	$EH_LOCALS_SYSRET, %rdi		/* arg0 == sysret_t ptr */
180
181	/* invoke the emulation routine */
182	ALTENTRY(brand_handler_savepc)
183	call	*(%r11)
184
185	/* restore scratch and parameter registers */
186	movq	EH_LOCALS_GREG(REG_R12)(%rbp), %r12	/* restore %r12 */
187	movq	EH_LOCALS_GREG(REG_R11)(%rbp), %r11	/* restore %r11 */
188	movq	EH_LOCALS_GREG(REG_R10)(%rbp), %r10	/* restore %r10 */
189	movq	EH_LOCALS_GREG(REG_R9)(%rbp), %r9	/* restore %r9 */
190	movq	EH_LOCALS_GREG(REG_R8)(%rbp), %r8	/* restore %r8 */
191	movq	EH_LOCALS_GREG(REG_RCX)(%rbp), %rcx	/* restore %rcx */
192	movq	EH_LOCALS_GREG(REG_RDX)(%rbp), %rdx	/* restore %rdx */
193	movq	EH_LOCALS_GREG(REG_RSI)(%rbp), %rsi	/* restore %rsi */
194	movq	EH_LOCALS_GREG(REG_RDI)(%rbp), %rdi	/* restore %rdi */
195
196	/* Check for syscall emulation success or failure */
197	cmpq	$0, %rax
198	je	success
199	stc					/* failure, set carry flag */
200	jmp	return				/* return, %rax == errno */
201
202success:
203	/* There is always at least one return value. */
204	movq	EH_LOCALS_SYSRET1(%rbp), %rax	/* %rax == sys_rval1 */
205	cmpq	$RV_DEFAULT, EH_LOCALS_RVFLAG(%rbp) /* check rv flag */
206	je	clear_carry
207	mov	EH_LOCALS_SYSRET2(%rbp), %rdx	/* %rdx == sys_rval2 */
208clear_carry:
209	clc					/* success, clear carry flag */
210
211return:
212	movq	%rbp, %rsp			/* restore stack */
213	popq	%rbp
214	ret					/* ret to instr after syscall */
215	SET_SIZE(brand_handler)
216
217
218#endif	/* lint */
219