1/*
2 * Copyright 2018, J��r��me Duval, jerome.duval@gmail.com.
3 * Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include <asm_defs.h>
9
10#include <thread_types.h>
11
12#include <arch/x86/descriptors.h>
13#include <arch/x86/arch_altcodepatch.h>
14#include <arch/x86/arch_cpu.h>
15#include <arch/x86/arch_kernel.h>
16#define COMMPAGE_COMPAT
17#include <commpage_defs.h>
18
19#include "asm_offsets.h"
20#include "syscall_numbers.h"
21#include "syscall_table.h"
22
23
24// Push the remainder of the interrupt frame onto the stack.
25#define PUSH_IFRAME_BOTTOM(iframeType)	\
26	push	%rax;	/* orig_rax */		\
27	push	%rax;						\
28	push	%rbx;						\
29	push	%rcx;						\
30	push	%rdx;						\
31	push	%rdi;						\
32	push	%rsi;						\
33	push	%rbp;						\
34	push	%r8;						\
35	push	%r9;						\
36	push	%r10;						\
37	push	%r11;						\
38	push	%r12;						\
39	push	%r13;						\
40	push	%r14;						\
41	push	%r15;						\
42	pushq	$0;							\
43	push	$iframeType;
44
45
46// Restore the interrupt frame.
47#define RESTORE_IFRAME()				\
48	add		$16, %rsp;					\
49	pop		%r15;						\
50	pop		%r14;						\
51	pop		%r13;						\
52	pop		%r12;						\
53	pop		%r11;						\
54	pop		%r10;						\
55	pop		%r9;						\
56	pop		%r8;						\
57	pop		%rbp;						\
58	pop		%rsi;						\
59	pop		%rdi;						\
60	pop		%rdx;						\
61	pop		%rcx;						\
62	pop		%rbx;						\
63	pop		%rax;						\
64	addq	$24, %rsp;
65
66
67// The macros below require R12 to contain the current thread pointer. R12 is
68// callee-save so will be preserved through all function calls and only needs
69// to be obtained once. R13 is used to store the system call start time, will
70// also be preserved.
71
72#define LOCK_THREAD_TIME()										\
73	leaq	THREAD_time_lock(%r12), %rdi;						\
74	call	acquire_spinlock;
75
76#define UNLOCK_THREAD_TIME()									\
77	leaq	THREAD_time_lock(%r12), %rdi;						\
78	call	release_spinlock;									\
79
80#define UPDATE_THREAD_USER_TIME()								\
81	LOCK_THREAD_TIME()											\
82																\
83	call	system_time;										\
84																\
85	/* Preserve system_time for post syscall debug */			\
86	movq	%rax, %r13;											\
87																\
88	/* thread->user_time += now - thread->last_time; */			\
89	subq	THREAD_last_time(%r12), %rax;						\
90	addq	%rax, THREAD_user_time(%r12);						\
91																\
92	/* thread->last_time = now; */								\
93	movq	%r13, THREAD_last_time(%r12);						\
94																\
95	/* thread->in_kernel = true; */								\
96	movb	$1, THREAD_in_kernel(%r12);							\
97																\
98	UNLOCK_THREAD_TIME()
99
100#define UPDATE_THREAD_KERNEL_TIME()								\
101	LOCK_THREAD_TIME()											\
102																\
103	call	system_time;										\
104	movq	%rax, %r13;											\
105																\
106	/* thread->kernel_time += now - thread->last_time; */		\
107	subq	THREAD_last_time(%r12), %rax;						\
108	addq	%rax, THREAD_kernel_time(%r12);						\
109																\
110	/* thread->last_time = now; */								\
111	movq	%r13, THREAD_last_time(%r12);						\
112																\
113	/* thread->in_kernel = false; */							\
114	movb	$0, THREAD_in_kernel(%r12);							\
115																\
116	UNLOCK_THREAD_TIME()
117
118#define STOP_USER_DEBUGGING()									\
119	testl	$(THREAD_FLAGS_BREAKPOINTS_INSTALLED				\
120			| THREAD_FLAGS_SINGLE_STEP), THREAD_flags(%r12);	\
121	jz		1f;													\
122	call	x86_exit_user_debug_at_kernel_entry;				\
123  1:
124
125#define CLEAR_FPU_STATE() \
126	pxor %xmm0, %xmm0; \
127	pxor %xmm1, %xmm1; \
128	pxor %xmm2, %xmm2; \
129	pxor %xmm3, %xmm3; \
130	pxor %xmm4, %xmm4; \
131	pxor %xmm5, %xmm5; \
132	pxor %xmm6, %xmm6; \
133	pxor %xmm7, %xmm7; \
134	pxor %xmm8, %xmm8; \
135	pxor %xmm9, %xmm9; \
136	pxor %xmm10, %xmm10; \
137	pxor %xmm11, %xmm11; \
138	pxor %xmm12, %xmm12; \
139	pxor %xmm13, %xmm13; \
140	pxor %xmm14, %xmm14; \
141	pxor %xmm15, %xmm15
142
143
144// SYSCALL entry point.
145FUNCTION(x86_64_syscall32_entry):
146	// TODO: implement for AMD SYSCALL
147	sysret
148FUNCTION_END(x86_64_syscall32_entry)
149
150
151// SYSENTER entry point.
152//	ecx - user esp
153FUNCTION(x86_64_sysenter32_entry):
154	swapgs
155
156	// Set up an iframe on the stack (ECX = saved ESP).
157	push	$USER_DATA_SELECTOR			// ss
158	// zero extend %ecx
159	movl	%ecx, %ecx
160	push	%rcx						// rsp
161	pushfq								// flags
162	orl		$(1 << 9), (%rsp)		// set the IF (interrupts) bit
163	push	$USER32_CODE_SELECTOR		// cs
164
165	movq	%gs:0, %rdx
166	movq	THREAD_team(%rdx), %rdx
167	movq	TEAM_commpage_address(%rdx), %rdx
168	ASM_STAC
169	add		4 * COMMPAGE_ENTRY_X86_SYSCALL(%rdx), %rdx
170	ASM_CLAC
171	add		$4, %rdx				// sysenter is at offset 2, 2 bytes long
172	push	%rdx						// ip
173
174	push	$0							// error_code
175	push	$99							// vector
176	PUSH_IFRAME_BOTTOM(IFRAME_TYPE_SYSCALL)
177
178	cld
179
180	// Frame pointer is the iframe.
181	movq	%rsp, %rbp
182	andq	$~15, %rsp
183
184	// Preserve call number (R14 is callee-save), get thread pointer.
185	movq	%rax, %r14
186	movq	%gs:0, %r12
187
188	STOP_USER_DEBUGGING()
189	UPDATE_THREAD_USER_TIME()
190
191	// No longer need interrupts disabled.
192	sti
193
194	// Check whether the syscall number is valid.
195	cmpq	$SYSCALL_COUNT, %r14
196	jae		.Lsyscall_return
197
198	// Get the system call table entry. Note I'm hardcoding the shift because
199	// sizeof(syscall_info) is 16 and scale factors of 16 aren't supported,
200	// so can't just do leaq kSyscallInfos(, %rax, SYSCALL_INFO_sizeof).
201	movq	%r14, %rax
202	shlq	$4, %rax
203	leaq	kSyscallCompatInfos(, %rax, 1), %rax
204
205	// Restore the arguments from the stack.
206	movq	SYSCALL_INFO_parameter_size(%rax), %rcx
207
208	// Get the address to copy from.
209	movq	IFRAME_user_sp(%rbp), %rsi
210	addq	$4, %rsi
211	movabs	$(USER_BASE + USER_SIZE), %rdx
212	cmp		%rdx, %rsi
213	jae		.Lbad_syscall_args
214
215	// Make space on the stack for the double size.
216	shlq	$1, %rcx
217	cmpq	$48, %rcx
218	ja		.Lprepare_stack
219	movq	$48, %rcx
220.Lprepare_stack:
221	subq	%rcx, %rsp
222	andq	$~15, %rsp
223	movq	%rsp, %rdi
224
225	// Get the extended system call table entry.
226	movq	%r14, %r15
227	imulq	$ EXTENDED_SYSCALL_INFO_sizeof, %r15
228	leaq	kExtendedSyscallCompatInfos(, %r15, 1), %r15
229	xor		%rcx, %rcx
230	movl	EXTENDED_SYSCALL_INFO_parameter_count(%r15), %ecx
231	leaq	EXTENDED_SYSCALL_INFO_parameters(%r15), %r15
232
233	// Set a fault handler.
234	movq	$.Lbad_syscall_args, THREAD_fault_handler(%r12)
235
236	ASM_STAC
237
238	jmp 	2f
239	// Copy them by doublewords.
2401:
241	// Advance to next parameter
242	addq	$ SYSCALL_PARAMETER_INFO_sizeof, %r15
243	subq	$1, %rcx
2442:
245	cmpq	$0, %rcx
246	je		4f
247	movsd
248	cmpl	$0x8, SYSCALL_PARAMETER_INFO_used_size(%r15)
249	je		3f
250	movl	$0,	(%rdi)
251	addq	$4, %rdi
252	jmp		1b
2533:
254	// Copy the next doubleword
255	movsd
256	jmp		1b
2574:
258	ASM_CLAC
259	movq	$0, THREAD_fault_handler(%r12)
260
261.Lperform_syscall:
262	testl	$THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%r12)
263	jnz		.Lpre_syscall_debug
264
265.Lpre_syscall_debug_done:
266	// arguments on the stack, copy in the registers
267	pop		%rdi
268	pop		%rsi
269	pop		%rdx
270	pop		%rcx
271	pop		%r8
272	pop		%r9
273
274	// TODO: pre-syscall tracing
275
276	// Call the function and save its return value.
277	call	*SYSCALL_INFO_function(%rax)
278	movq	%rax, %rdx
279	movq	%rax, IFRAME_ax(%rbp)
280	shrq	$32, %rdx
281	movq	%rdx, IFRAME_dx(%rbp)
282
283	// TODO: post-syscall tracing
284
285.Lsyscall_return:
286	// Restore the original stack pointer and return.
287	movq	%rbp, %rsp
288
289	testl	$(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
290			| THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
291			| THREAD_FLAGS_TRAP_FOR_CORE_DUMP \
292			| THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
293			| THREAD_FLAGS_RESTART_SYSCALL | THREAD_FLAGS_SYSCALL_RESTARTED) \
294			, THREAD_flags(%r12)
295	jnz		.Lpost_syscall_work
296
297	cli
298
299	UPDATE_THREAD_KERNEL_TIME()
300
301	// If we've just restored a signal frame, use the IRET path.
302	cmpq	$SYSCALL_RESTORE_SIGNAL_FRAME, %r14
303	je		.Lrestore_fpu
304
305	CLEAR_FPU_STATE()
306
307	// Restore the iframe and RCX/RDX for SYSRET.
308	RESTORE_IFRAME()
309	pop		%rdx
310	addq	$8, %rsp
311	andl  $~0x200,(%rsp)
312	popfq
313	pop		%rcx
314
315	// Restore previous GS base and return.
316	swapgs
317	sti
318	sysexit
319
320
321.Lpre_syscall_debug:
322	// preserve registers
323	push	%rdi
324	push	%rsi
325
326	// user_debug_pre_syscall expects a pointer to a block of arguments, need
327	// to push the register arguments onto the stack.
328	movq	%r14, %rdi				// syscall number
329	movq	0x10(%rsp), %rsi
330	push	%rax
331	call	user_debug_pre_syscall
332	pop		%rax
333
334	// restore registers
335	pop		%rsi
336	pop		%rdi
337	jmp		.Lpre_syscall_debug_done
338
339.Lpost_syscall_work:
340	// Clear the restarted flag.
341	testl	$(THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
342				| THREAD_FLAGS_SYSCALL_RESTARTED), THREAD_flags(%r12)
343	jz		2f
3441:
345	movl	THREAD_flags(%r12), %eax
346	movl	%eax, %edx
347	andl	$~(THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
348				| THREAD_FLAGS_SYSCALL_RESTARTED), %edx
349	lock
350	cmpxchgl	%edx, THREAD_flags(%r12)
351	jnz		1b
3522:
353	testl	$THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%r12)
354	jz		1f
355
356	// Post-syscall debugging. Same as above, need a block of arguments.
357	// TODO: restore arguments from the stack
358	push	IFRAME_r9(%rbp)
359	push	IFRAME_r8(%rbp)
360	push	IFRAME_r10(%rbp)
361	push	IFRAME_dx(%rbp)
362	push	IFRAME_si(%rbp)
363	push	IFRAME_di(%rbp)
364	movq	%r14, %rdi				// syscall number
365	movq	%rsp, %rsi
366	movq	IFRAME_ax(%rbp), %rdx	// return value
367	movq	%r13, %rcx				// start time, preserved earlier
368	call	user_debug_post_syscall
369	addq	$48, %rsp
3701:
371	// Do we need to handle signals?
372	testl	$(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \
373			| THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
374			, THREAD_flags(%r12)
375	jnz		.Lpost_syscall_handle_signals
376	cli
377	call	thread_at_kernel_exit_no_signals
378
379.Lpost_syscall_work_done:
380	// Handle syscall restarting.
381	testl	$THREAD_FLAGS_RESTART_SYSCALL, THREAD_flags(%r12)
382	jz		1f
383	movq	%rsp, %rdi
384	call	x86_restart_syscall
3851:
386	// Install breakpoints, if defined.
387	testl	$THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%r12)
388	jz		1f
389	movq	%rbp, %rdi
390	call	x86_init_user_debug_at_kernel_exit
3911:
392	// On this return path it is possible that the frame has been modified,
393	// for example to execute a signal handler. In this case it is safer to
394	// return via IRET.
395	CLEAR_FPU_STATE()
396	jmp .Liret
397
398.Lrestore_fpu:
399	movq	IFRAME_fpu(%rbp), %rax
400	fxrstorq	(%rax)
401.Liret:
402	// Restore the saved registers.
403	RESTORE_IFRAME()
404
405	// Restore the previous GS base and return.
406	swapgs
407	iretq
408
409.Lpost_syscall_handle_signals:
410	call	thread_at_kernel_exit
411	jmp		.Lpost_syscall_work_done
412
413.Lbad_syscall_args:
414	movq	$0, THREAD_fault_handler(%r12)
415	movq	%rbp, %rsp
416	jmp		.Lsyscall_return
417FUNCTION_END(x86_64_sysenter32_entry)
418
419
420/* thread exit stub */
421// TODO: build with the x86 compiler
422FUNCTION(x86_sysenter32_userspace_thread_exit):
423	.byte	0x50		// push %eax
424	mov		$SYSCALL_EXIT_THREAD, %eax
425	.byte	0x89,0xe1	// mov %esp, %ecx
426	sysenter
427FUNCTION_END(x86_sysenter32_userspace_thread_exit)
428SYMBOL(x86_sysenter32_userspace_thread_exit_end):
429
430