xref: /haiku/src/system/kernel/arch/x86/32/interrupts.S (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
1/*
2 * Copyright 2002-2016, The Haiku Team. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
6 * Copyright 2002, Michael Noisternig. All rights reserved.
7 * Distributed under the terms of the NewOS License.
8 */
9
10#include <arch/user_debugger.h>
11#include <arch/x86/arch_cpu.h>
12#include <arch/x86/arch_kernel.h>
13#include <arch/x86/descriptors.h>
14#include <asm_defs.h>
15#include <commpage_defs.h>
16#include <thread_types.h>
17
18#include "tracing_config.h"
19
20#include "asm_offsets.h"
21#include "syscall_numbers.h"
22#include "syscall_table.h"
23
24
25#define LOCK_THREAD_TIME()									\
26	lea		THREAD_time_lock(%edi), %eax;					\
27	pushl	%eax;											\
28	call	acquire_spinlock;
29		/* leave spinlock address on stack for UNLOCK_THREAD_TIME() */
30
31#define UNLOCK_THREAD_TIME()								\
32	/* spinlock address still on stack from */				\
33	/* LOCK_THREAD_TIME() */								\
34	call	release_spinlock;								\
35	addl	$4, %esp;
36
37#define UPDATE_THREAD_USER_TIME_COMMON()					\
38	movl	%eax, %ebx;				/* save for later */	\
39	movl	%edx, %ecx;										\
40															\
41	/* thread->user_time += now - thread->last_time; */		\
42	sub		THREAD_last_time(%edi), %eax;					\
43	sbb		(THREAD_last_time + 4)(%edi), %edx;				\
44	add		%eax, THREAD_user_time(%edi);					\
45	adc		%edx, (THREAD_user_time + 4)(%edi);				\
46															\
47	/* thread->last_time = now; */							\
48	movl	%ebx, THREAD_last_time(%edi);					\
49	movl	%ecx, (THREAD_last_time + 4)(%edi);				\
50															\
51	/* thread->in_kernel = true; */							\
52	movb	$1, THREAD_in_kernel(%edi);
53
54#define UPDATE_THREAD_USER_TIME()							\
55	LOCK_THREAD_TIME()										\
56	call	system_time;									\
57	UPDATE_THREAD_USER_TIME_COMMON()						\
58	UNLOCK_THREAD_TIME()
59
60#define UPDATE_THREAD_KERNEL_TIME()							\
61	LOCK_THREAD_TIME()										\
62															\
63	call	system_time;									\
64															\
65	movl	%eax, %ebx;				/* save for later */	\
66	movl	%edx, %ecx;										\
67															\
68	/* thread->kernel_time += now - thread->last_time; */	\
69	sub		THREAD_last_time(%edi), %eax;					\
70	sbb		(THREAD_last_time + 4)(%edi), %edx;				\
71	add		%eax, THREAD_kernel_time(%edi);					\
72	adc		%edx, (THREAD_kernel_time + 4)(%edi);			\
73															\
74	/* thread->last_time = now; */							\
75	movl	%ebx, THREAD_last_time(%edi);					\
76	movl	%ecx, (THREAD_last_time + 4)(%edi);				\
77															\
78	/* thread->in_kernel = false; */						\
79	movb	$0, THREAD_in_kernel(%edi);						\
80															\
81	UNLOCK_THREAD_TIME()									\
82
83#define PUSH_IFRAME_BOTTOM(iframeType)	\
84	pusha;								\
85	push	%ds;						\
86	push	%es;						\
87	push	%fs;						\
88	push	%gs;						\
89	pushl	$iframeType
90
91#define PUSH_IFRAME_BOTTOM_SYSCALL()	\
92	pushl	$0;							\
93	pushl	$99;						\
94	pushl	%edx;						\
95	pushl	%eax;						\
96	PUSH_IFRAME_BOTTOM(IFRAME_TYPE_SYSCALL)
97
98#define POP_IFRAME_AND_RETURN()										\
99	/* skip iframe type */											\
100	lea		4(%ebp), %esp;											\
101																	\
102	pop		%gs;													\
103	addl	$4, %esp;	/* we skip %fs, as this contains the CPU	\
104						   dependent TLS segment */					\
105	pop		%es;													\
106	pop		%ds;													\
107																	\
108	popa;															\
109	addl	$16,%esp;	/* ignore the vector, error code, and		\
110						   original eax/edx values */				\
111	iret
112
113#define STOP_USER_DEBUGGING()											\
114	testl	$(THREAD_FLAGS_BREAKPOINTS_INSTALLED						\
115				| THREAD_FLAGS_SINGLE_STEP), THREAD_flags(%edi);		\
116	jz		1f;															\
117	call	x86_exit_user_debug_at_kernel_entry;						\
118  1:
119
120#define COPY_SYSCALL_PARAMETERS()										\
121	/* make room for the syscall params */								\
122	subl	$80, %esp;													\
123																		\
124	/* get the address of the syscall parameters */						\
125	movl	IFRAME_user_sp(%ebp), %esi;									\
126	addl	$4, %esi;													\
127	cmp		$KERNEL_BASE, %esi;		/* must not be a kernel address */	\
128	jae		bad_syscall_params;											\
129																		\
130	/* set the fault handler */											\
131	movl	$bad_syscall_params, THREAD_fault_handler(%edi);			\
132																		\
133	/* target address is our stack */									\
134	movl	%esp, %edi;													\
135																		\
136	/* number of syscall parameter words */								\
137	movl	SYSCALL_INFO_parameter_size(%edx), %ecx;					\
138	shrl	$2, %ecx;													\
139																		\
140	/* copy */															\
141	cld;																\
142	rep movsl;															\
143																		\
144	/* restore pointers and clear fault handler */						\
145	movl	%edx, %esi;				/* syscall info pointer */			\
146	movl	%gs:0, %edi;			/* thread pointer */				\
147	movl	$0, THREAD_fault_handler(%edi)
148
149#if SYSCALL_TRACING
150#	define TRACE_PRE_SYSCALL()	\
151		movl	%esp, %eax;						/* syscall parameters */	\
152		push	%eax;														\
153		movl	IFRAME_orig_eax(%ebp), %eax;	/* syscall number */		\
154		push	%eax;														\
155		call	trace_pre_syscall;											\
156		addl	$8, %esp;
157
158#	define TRACE_POST_SYSCALL()	\
159		testl	$THREAD_FLAGS_64_BIT_SYSCALL_RETURN, THREAD_flags(%edi);	\
160		jnz		1f;															\
161		xor		%edx, %edx;													\
1621:																			\
163		push	%edx;							/* syscall return value */	\
164		push	%eax;														\
165		movl	IFRAME_orig_eax(%ebp), %eax;	/* syscall number */		\
166		push	%eax;														\
167		call	trace_post_syscall;											\
168		addl	$12, %esp
169#else
170#	define TRACE_PRE_SYSCALL()
171#	define TRACE_POST_SYSCALL()
172#endif
173
174
175.text
176
177#define TRAP_ERRC(name, vector) \
178.align 8; \
179FUNCTION(name): \
180	pushl	$vector; \
181	pushl	%edx; \
182	pushl	%eax; \
183	jmp		int_bottom;	\
184FUNCTION_END(name)
185
186#define TRAP(name, vector) \
187.align 8; \
188FUNCTION(name): \
189	pushl	$0; \
190	pushl	$vector; \
191	pushl	%edx; \
192	pushl	%eax; \
193	jmp		int_bottom; \
194FUNCTION_END(name)
195
196TRAP(trap0, 0)
197TRAP(trap1, 1)
198TRAP(trap2, 2)
199TRAP(trap3, 3)
200TRAP(trap4, 4)
201TRAP(trap5, 5)
202TRAP(trap6, 6)
203TRAP(trap7, 7)
204
205.align 8;
206FUNCTION(double_fault):
207	pushl	$-1;	// user-ss
208	pushl	$-1;	// user-esp
209	pushl	$-1;	// flags
210	pushl	$KERNEL_CODE_SELECTOR	// cs
211	pushl	$-1;	// eip
212	pushl	$0;		// error-code
213	pushl	$8;
214	pushl	$-1;
215	pushl	$-1;
216
217	PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
218
219	movl	%esp, %ebp		// frame pointer is the iframe
220
221	pushl	%ebp
222	call	x86_double_fault_exception
223
224	// Well, there's no returning from a double fault, but maybe a real hacker
225	// can repair things in KDL.
226	POP_IFRAME_AND_RETURN()
227FUNCTION_END(double_fault)
228
229
230TRAP(trap9, 9)
231TRAP_ERRC(trap10, 10)
232TRAP_ERRC(trap11, 11)
233TRAP_ERRC(trap12, 12)
234TRAP_ERRC(trap13, 13)
235TRAP_ERRC(trap14, 14)
236/*TRAP(trap15, 15)*/
237TRAP(trap16, 16)
238TRAP_ERRC(trap17, 17)
239TRAP(trap18, 18)
240TRAP(trap19, 19)
241
242// legacy or ioapic interrupts
243TRAP(trap32, 32)
244TRAP(trap33, 33)
245TRAP(trap34, 34)
246TRAP(trap35, 35)
247TRAP(trap36, 36)
248TRAP(trap37, 37)
249TRAP(trap38, 38)
250TRAP(trap39, 39)
251TRAP(trap40, 40)
252TRAP(trap41, 41)
253TRAP(trap42, 42)
254TRAP(trap43, 43)
255TRAP(trap44, 44)
256TRAP(trap45, 45)
257TRAP(trap46, 46)
258TRAP(trap47, 47)
259
260// additional ioapic interrupts
261TRAP(trap48, 48)
262TRAP(trap49, 49)
263TRAP(trap50, 50)
264TRAP(trap51, 51)
265TRAP(trap52, 52)
266TRAP(trap53, 53)
267TRAP(trap54, 54)
268TRAP(trap55, 55)
269
270// configurable msi or msi-x interrupts
271TRAP(trap56, 56)
272TRAP(trap57, 57)
273TRAP(trap58, 58)
274TRAP(trap59, 59)
275TRAP(trap60, 60)
276TRAP(trap61, 61)
277TRAP(trap62, 62)
278TRAP(trap63, 63)
279TRAP(trap64, 64)
280TRAP(trap65, 65)
281TRAP(trap66, 66)
282TRAP(trap67, 67)
283TRAP(trap68, 68)
284TRAP(trap69, 69)
285TRAP(trap70, 70)
286TRAP(trap71, 71)
287TRAP(trap72, 72)
288TRAP(trap73, 73)
289TRAP(trap74, 74)
290TRAP(trap75, 75)
291TRAP(trap76, 76)
292TRAP(trap77, 77)
293TRAP(trap78, 78)
294TRAP(trap79, 79)
295TRAP(trap80, 80)
296TRAP(trap81, 81)
297TRAP(trap82, 82)
298TRAP(trap83, 83)
299TRAP(trap84, 84)
300TRAP(trap85, 85)
301TRAP(trap86, 86)
302TRAP(trap87, 87)
303TRAP(trap88, 88)
304TRAP(trap89, 89)
305TRAP(trap90, 90)
306TRAP(trap91, 91)
307TRAP(trap92, 92)
308TRAP(trap93, 93)
309TRAP(trap94, 94)
310TRAP(trap95, 95)
311TRAP(trap96, 96)
312TRAP(trap97, 97)
313//TRAP(trap98, 98) // performance testing interrupt
314//TRAP(trap99, 99) // syscall interrupt
315TRAP(trap100, 100)
316TRAP(trap101, 101)
317TRAP(trap102, 102)
318TRAP(trap103, 103)
319TRAP(trap104, 104)
320TRAP(trap105, 105)
321TRAP(trap106, 106)
322TRAP(trap107, 107)
323TRAP(trap108, 108)
324TRAP(trap109, 109)
325TRAP(trap110, 110)
326TRAP(trap111, 111)
327TRAP(trap112, 112)
328TRAP(trap113, 113)
329TRAP(trap114, 114)
330TRAP(trap115, 115)
331TRAP(trap116, 116)
332TRAP(trap117, 117)
333TRAP(trap118, 118)
334TRAP(trap119, 119)
335TRAP(trap120, 120)
336TRAP(trap121, 121)
337TRAP(trap122, 122)
338TRAP(trap123, 123)
339TRAP(trap124, 124)
340TRAP(trap125, 125)
341TRAP(trap126, 126)
342TRAP(trap127, 127)
343TRAP(trap128, 128)
344TRAP(trap129, 129)
345TRAP(trap130, 130)
346TRAP(trap131, 131)
347TRAP(trap132, 132)
348TRAP(trap133, 133)
349TRAP(trap134, 134)
350TRAP(trap135, 135)
351TRAP(trap136, 136)
352TRAP(trap137, 137)
353TRAP(trap138, 138)
354TRAP(trap139, 139)
355TRAP(trap140, 140)
356TRAP(trap141, 141)
357TRAP(trap142, 142)
358TRAP(trap143, 143)
359TRAP(trap144, 144)
360TRAP(trap145, 145)
361TRAP(trap146, 146)
362TRAP(trap147, 147)
363TRAP(trap148, 148)
364TRAP(trap149, 149)
365TRAP(trap150, 150)
366TRAP(trap151, 151)
367TRAP(trap152, 152)
368TRAP(trap153, 153)
369TRAP(trap154, 154)
370TRAP(trap155, 155)
371TRAP(trap156, 156)
372TRAP(trap157, 157)
373TRAP(trap158, 158)
374TRAP(trap159, 159)
375TRAP(trap160, 160)
376TRAP(trap161, 161)
377TRAP(trap162, 162)
378TRAP(trap163, 163)
379TRAP(trap164, 164)
380TRAP(trap165, 165)
381TRAP(trap166, 166)
382TRAP(trap167, 167)
383TRAP(trap168, 168)
384TRAP(trap169, 169)
385TRAP(trap170, 170)
386TRAP(trap171, 171)
387TRAP(trap172, 172)
388TRAP(trap173, 173)
389TRAP(trap174, 174)
390TRAP(trap175, 175)
391TRAP(trap176, 176)
392TRAP(trap177, 177)
393TRAP(trap178, 178)
394TRAP(trap179, 179)
395TRAP(trap180, 180)
396TRAP(trap181, 181)
397TRAP(trap182, 182)
398TRAP(trap183, 183)
399TRAP(trap184, 184)
400TRAP(trap185, 185)
401TRAP(trap186, 186)
402TRAP(trap187, 187)
403TRAP(trap188, 188)
404TRAP(trap189, 189)
405TRAP(trap190, 190)
406TRAP(trap191, 191)
407TRAP(trap192, 192)
408TRAP(trap193, 193)
409TRAP(trap194, 194)
410TRAP(trap195, 195)
411TRAP(trap196, 196)
412TRAP(trap197, 197)
413TRAP(trap198, 198)
414TRAP(trap199, 199)
415TRAP(trap200, 200)
416TRAP(trap201, 201)
417TRAP(trap202, 202)
418TRAP(trap203, 203)
419TRAP(trap204, 204)
420TRAP(trap205, 205)
421TRAP(trap206, 206)
422TRAP(trap207, 207)
423TRAP(trap208, 208)
424TRAP(trap209, 209)
425TRAP(trap210, 210)
426TRAP(trap211, 211)
427TRAP(trap212, 212)
428TRAP(trap213, 213)
429TRAP(trap214, 214)
430TRAP(trap215, 215)
431TRAP(trap216, 216)
432TRAP(trap217, 217)
433TRAP(trap218, 218)
434TRAP(trap219, 219)
435TRAP(trap220, 220)
436TRAP(trap221, 221)
437TRAP(trap222, 222)
438TRAP(trap223, 223)
439TRAP(trap224, 224)
440TRAP(trap225, 225)
441TRAP(trap226, 226)
442TRAP(trap227, 227)
443TRAP(trap228, 228)
444TRAP(trap229, 229)
445TRAP(trap230, 230)
446TRAP(trap231, 231)
447TRAP(trap232, 232)
448TRAP(trap233, 233)
449TRAP(trap234, 234)
450TRAP(trap235, 235)
451TRAP(trap236, 236)
452TRAP(trap237, 237)
453TRAP(trap238, 238)
454TRAP(trap239, 239)
455TRAP(trap240, 240)
456TRAP(trap241, 241)
457TRAP(trap242, 242)
458TRAP(trap243, 243)
459TRAP(trap244, 244)
460TRAP(trap245, 245)
461TRAP(trap246, 246)
462TRAP(trap247, 247)
463TRAP(trap248, 248)
464TRAP(trap249, 249)
465TRAP(trap250, 250)
466
467// smp / apic local interrupts
468TRAP(trap251, 251)
469TRAP(trap252, 252)
470TRAP(trap253, 253)
471TRAP(trap254, 254)
472TRAP(trap255, 255)
473
474
475.align 8;
476FUNCTION(trap14_double_fault):
477	pushl	$14
478	pushl	$-1
479	pushl	$-1
480
481	PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
482
483	movl	%esp, %ebp		// frame pointer is the iframe
484
485	pushl	%ebp
486	call	x86_page_fault_exception_double_fault
487
488	POP_IFRAME_AND_RETURN()
489FUNCTION_END(trap14_double_fault)
490
491
492.align 16
493STATIC_FUNCTION(int_bottom):
494	PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
495
496	movl	$KERNEL_TLS_SELECTOR, %edx
497	movw	%dx, %gs
498
499	movl	%esp, %ebp		// frame pointer is the iframe
500
501	// Set the RF (resume flag) in EFLAGS. This prevents an instruction
502	// breakpoint on the instruction we're returning to to trigger a debug
503	// exception.
504	orl		$0x10000, IFRAME_flags(%ebp);
505
506	cmpw	$USER_CODE_SELECTOR, IFRAME_cs(%ebp)	// user mode
507	je		int_bottom_user
508
509	// We need to recheck user mode using the thread's in_kernel flag, since
510	// sysexit introduces a raced condition: It doesn't reenable interrupts,
511	// so that we have to do it in the instruction before, thus opening a
512	// window for an interrupt while still being in the kernel, but having set
513	// up everything for userland already.
514	movl	%gs:0, %edi						// thread pointer
515	cmpb	$0, THREAD_in_kernel(%edi)
516	je		int_bottom_user
517
518	// disable interrupts -- the handler will enable them, if necessary
519	cli
520
521	pushl	%ebp
522	movl	IFRAME_vector(%ebp), %eax
523	call	*gInterruptHandlerTable(, %eax, 4)
524
525	POP_IFRAME_AND_RETURN()
526FUNCTION_END(int_bottom)
527
528
529STATIC_FUNCTION(int_bottom_user):
530	movl	$KERNEL_DATA_SELECTOR, %eax
531	cld
532	movl	%eax,%ds
533	movl	%eax,%es
534
535	// disable breakpoints, if installed
536	movl	%gs:0, %edi				// thread pointer
537	cli								// disable interrupts
538	STOP_USER_DEBUGGING()
539
540	// update the thread's user time
541	UPDATE_THREAD_USER_TIME()
542
543	// leave interrupts disabled -- the handler will enable them, if
544	// necessary
545
546	pushl	%ebp
547	movl	IFRAME_vector(%ebp), %eax
548	call	*gInterruptHandlerTable(, %eax, 4)
549
550	// Don't do any kernel exit work, if we actually came from the kernel (but
551	// were already/still prepared for userland), since the iframe in this case
552	// will be a kernel iframe and e.g. trying to set up a signal stack will not
553	// be a very healthy endeavor.
554	cmpw	$USER_CODE_SELECTOR, IFRAME_cs(%ebp)
555	jne		1f
556
557	testl	$(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
558			| THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
559			| THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
560			, THREAD_flags(%edi)
561	jnz		kernel_exit_work
5621:
563
564	cli								// disable interrupts
565
566	// update the thread's kernel time and return
567	UPDATE_THREAD_KERNEL_TIME()
568	POP_IFRAME_AND_RETURN()
569FUNCTION_END(int_bottom_user)
570
571
572// test interrupt handler for performance measurements
573.align 16
574FUNCTION(trap98):
575	iret
576FUNCTION_END(trap98)
577
578
579.align 16
580FUNCTION(trap99):
581	// push error, vector, orig_edx, orig_eax, and other registers
582	PUSH_IFRAME_BOTTOM_SYSCALL()
583
584	call	handle_syscall
585
586	POP_IFRAME_AND_RETURN()
587FUNCTION_END(trap99)
588
589
590STATIC_FUNCTION(handle_syscall):
591	movl	$KERNEL_TLS_SELECTOR, %edx
592	movw	%dx, %gs
593
594	// save %eax, the number of the syscall
595	movl	%eax, %esi
596
597	movl	$KERNEL_DATA_SELECTOR, %eax
598	cld
599	movl	%eax,%ds
600	movl	%eax,%es
601
602	lea		4(%esp), %ebp			// skipping the return address, the stack
603									// frame pointer is the iframe
604	movl	%gs:0, %edi				// thread pointer
605
606	// disable breakpoints, if installed
607	cli								// disable interrupts
608	STOP_USER_DEBUGGING()
609
610	// update the thread's user time
611	UPDATE_THREAD_USER_TIME()
612
613	sti								// enable interrupts
614
615	cmp		$SYSCALL_COUNT, %esi	// check syscall number
616	jae		bad_syscall_number
617	movl	$kSyscallInfos, %eax	// get syscall info
618	lea		(%eax, %esi, SYSCALL_INFO_sizeof), %edx
619
620	// copy parameters onto this stack
621	COPY_SYSCALL_PARAMETERS()
622
623	// pre syscall debugging
624	TRACE_PRE_SYSCALL()
625	testl	$THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%edi)
626	jnz		do_pre_syscall_debug
627  pre_syscall_debug_done:
628
629	// call the syscall function
630	call	*SYSCALL_INFO_function(%esi)
631
632	// overwrite the values of %eax and %edx on the stack (the syscall return value)
633	testl	$THREAD_FLAGS_64_BIT_SYSCALL_RETURN, THREAD_flags(%edi)
634	jz		1f
635	movl	%edx, IFRAME_dx(%ebp)
636  1:
637	movl	%eax, IFRAME_ax(%ebp)
638
639	TRACE_POST_SYSCALL()
640
641	testl	$(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
642			| THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
643			| THREAD_FLAGS_TRAP_FOR_CORE_DUMP \
644			| THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
645			| THREAD_FLAGS_RESTART_SYSCALL | THREAD_FLAGS_SYSCALL_RESTARTED) \
646			, THREAD_flags(%edi)
647	jnz		post_syscall_work
648
649	cli								// disable interrupts
650
651	// update the thread's kernel time
652	UPDATE_THREAD_KERNEL_TIME()
653
654	lea		-4(%ebp), %esp			// remove all parameters from the stack
655
656	ret
657FUNCTION_END(handle_syscall)
658
659  STATIC_FUNCTION(do_pre_syscall_debug):
660	movl	%esp, %eax				// syscall parameters
661	push	%eax
662	movl	IFRAME_orig_eax(%ebp), %eax		// syscall number
663	push	%eax
664	call	user_debug_pre_syscall
665	addl	$8, %esp
666	jmp		pre_syscall_debug_done
667  FUNCTION_END(do_pre_syscall_debug)
668
669  STATIC_FUNCTION(post_syscall_work):
670	// post syscall debugging
671	testl	$THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%edi)
672	jz		2f
673	xor		%edx, %edx
674	testl	$THREAD_FLAGS_64_BIT_SYSCALL_RETURN, THREAD_flags(%edi)
675	jz		1f
676	movl	IFRAME_dx(%ebp), %edx	// syscall return value
677  1:
678	movl	IFRAME_ax(%ebp), %eax
679	push	%edx
680	push	%eax
681	lea		16(%esp), %eax			// syscall parameters
682	push	%eax
683	movl	IFRAME_orig_eax(%ebp), %eax		// syscall number
684	push	%eax
685	call	user_debug_post_syscall
686	addl	$24, %esp
687
688  2:
689	// clear the 64 bit return value and syscall restarted bits
690	testl	$(THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
691				| THREAD_FLAGS_SYSCALL_RESTARTED), THREAD_flags(%edi)
692	jz		2f
693  1:
694	movl	THREAD_flags(%edi), %eax
695	movl	%eax, %edx
696	andl	$~(THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
697				| THREAD_FLAGS_SYSCALL_RESTARTED), %edx
698	lock
699	cmpxchgl	%edx, THREAD_flags(%edi)
700	jnz		1b
701  2:
702  FUNCTION_END(post_syscall_work)
703
704  bad_syscall_number:
705  STATIC_FUNCTION(kernel_exit_work):
706	// if no signals are pending and the thread shall not be debugged or stopped
707	// for a core dump, we can use the quick kernel exit function
708	testl	$(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \
709			| THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
710			, THREAD_flags(%edi)
711	jnz		kernel_exit_handle_signals
712	cli								// disable interrupts
713	call	thread_at_kernel_exit_no_signals
714  kernel_exit_work_done:
715
716	// syscall restart
717	// TODO: this only needs to be done for syscalls!
718	testl	$THREAD_FLAGS_RESTART_SYSCALL, THREAD_flags(%edi)
719	jz		1f
720	push	%ebp
721	call	x86_restart_syscall
722	addl	$4, %esp
723  1:
724
725	// install breakpoints, if defined
726	testl	$THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%edi)
727	jz		1f
728	push	%ebp
729	call	x86_init_user_debug_at_kernel_exit
730  1:
731	POP_IFRAME_AND_RETURN()
732  FUNCTION_END(kernel_exit_work)
733
734  STATIC_FUNCTION(kernel_exit_handle_signals):
735	// make sure interrupts are enabled (they are, when coming from a syscall
736	// but otherwise they might be disabled)
737	sti
738	call	thread_at_kernel_exit	// also disables interrupts
739	jmp		kernel_exit_work_done
740  FUNCTION_END(kernel_exit_handle_signals)
741
742  STATIC_FUNCTION(bad_syscall_params):
743	// clear the fault handler and exit normally
744	movl	%gs:0, %edi
745	movl	$0, THREAD_fault_handler(%edi)
746	jmp		kernel_exit_work
747  FUNCTION_END(bad_syscall_params)
748
749
750/*!	Handler called by the sysenter instruction
751	ecx - user esp
752*/
753FUNCTION(x86_sysenter):
754	// get the thread
755	push	%gs
756	movl	$KERNEL_TLS_SELECTOR, %edx
757	movw	%dx, %gs
758	movl	%gs:0, %edx
759	pop		%gs
760
761	// push the iframe
762	pushl	$USER_DATA_SELECTOR		// user_ss
763	pushl	%ecx					// user_esp
764	pushfl							// eflags
765	orl		$(1 << 9), (%esp)		// set the IF (interrupts) bit
766	pushl	$USER_CODE_SELECTOR		// user cs
767
768	// user_eip
769	movl	THREAD_team(%edx), %edx
770	movl	TEAM_commpage_address(%edx), %edx
771	addl	4 * COMMPAGE_ENTRY_X86_SYSCALL(%edx), %edx
772	addl	$4, %edx				// sysenter is at offset 2, 2 bytes long
773	pushl	%edx
774
775	PUSH_IFRAME_BOTTOM_SYSCALL()
776
777	call	handle_syscall
778
779	// pop the bottom of the iframe
780	lea		4(%ebp), %esp	// skip iframe type
781
782	pop		%gs
783	addl	$4, %esp	/* we skip %fs, as this contains the CPU
784						   dependent TLS segment */
785	pop		%es
786	pop		%ds
787
788	popa
789
790	// ecx already contains the user esp -- load edx with the return address
791	movl	16(%esp), %edx
792
793	// pop eflags, which also reenables interrupts
794	addl	$24, %esp	// skip, orig_eax/edx, vector, error_code, eip, cs
795	popfl
796
797	sysexit
798FUNCTION_END(x86_sysenter)
799
800
801/*!	\fn void x86_return_to_userland(iframe* frame)
802	\brief Returns to the userland environment given by \a frame.
803
804	Before returning to userland all potentially necessary kernel exit work is
805	done.
806
807	\a frame must point to a location somewhere on the caller's stack (e.g. a
808	local variable).
809	The function must be called with interrupts disabled.
810
811	\param frame The iframe defining the userland environment.
812*/
813FUNCTION(x86_return_to_userland):
814	// get the iframe* parameter
815	movl	4(%esp), %ebp
816	movl	%ebp, %esp
817
818	// check, if any kernel exit work has to be done
819	movl	%gs:0, %edi
820	testl	$(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
821			| THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
822			| THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
823			, THREAD_flags(%edi)
824	jnz		kernel_exit_work
825
826	// update the thread's kernel time and return
827	UPDATE_THREAD_KERNEL_TIME()
828	POP_IFRAME_AND_RETURN()
829FUNCTION_END(x86_return_to_userland)
830