2 * Copyright 2002-2016, The Haiku Team. All rights reserved.
3 * Distributed under the terms of the MIT License.
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.
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>
15 #include <commpage_defs.h>
16 #include <thread_types.h>
18 #include "tracing_config.h"
20 #include "asm_offsets.h"
21 #include "syscall_numbers.h"
22 #include "syscall_table.h"
25 #define LOCK_THREAD_TIME() \
26 lea THREAD_time_lock(%edi), %eax; \
28 call acquire_spinlock;
29 /* leave spinlock address on stack for UNLOCK_THREAD_TIME() */
31 #define UNLOCK_THREAD_TIME() \
32 /* spinlock address still on stack from */ \
33 /* LOCK_THREAD_TIME() */ \
34 call release_spinlock; \
37 #define UPDATE_THREAD_USER_TIME_COMMON() \
38 movl %eax, %ebx; /* save for later */ \
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); \
47 /* thread->last_time = now; */ \
48 movl %ebx, THREAD_last_time(%edi); \
49 movl %ecx, (THREAD_last_time + 4)(%edi); \
51 /* thread->in_kernel = true; */ \
52 movb $1, THREAD_in_kernel(%edi);
54 #define UPDATE_THREAD_USER_TIME() \
57 UPDATE_THREAD_USER_TIME_COMMON() \
60 #define UPDATE_THREAD_USER_TIME_PUSH_TIME() \
67 /* recover the system time, note that */ \
68 /* LOCK_THREAD_TIME() leaves an address on the stack */ \
72 UPDATE_THREAD_USER_TIME_COMMON() \
76 #define UPDATE_THREAD_KERNEL_TIME() \
81 movl %eax, %ebx; /* save for later */ \
84 /* thread->kernel_time += now - thread->last_time; */ \
85 sub THREAD_last_time(%edi), %eax; \
86 sbb (THREAD_last_time + 4)(%edi), %edx; \
87 add %eax, THREAD_kernel_time(%edi); \
88 adc %edx, (THREAD_kernel_time + 4)(%edi); \
90 /* thread->last_time = now; */ \
91 movl %ebx, THREAD_last_time(%edi); \
92 movl %ecx, (THREAD_last_time + 4)(%edi); \
94 /* thread->in_kernel = false; */ \
95 movb $0, THREAD_in_kernel(%edi); \
97 UNLOCK_THREAD_TIME() \
99 #define PUSH_IFRAME_BOTTOM(iframeType) \
107 #define PUSH_IFRAME_BOTTOM_SYSCALL() \
112 PUSH_IFRAME_BOTTOM(IFRAME_TYPE_SYSCALL)
114 #define POP_IFRAME_AND_RETURN() \
115 /* skip iframe type */ \
119 addl $4, %esp; /* we skip %fs, as this contains the CPU \
120 dependent TLS segment */ \
125 addl $16,%esp; /* ignore the vector, error code, and \
126 original eax/edx values */ \
129 #define STOP_USER_DEBUGGING() \
130 testl $(THREAD_FLAGS_BREAKPOINTS_INSTALLED \
131 | THREAD_FLAGS_SINGLE_STEP), THREAD_flags(%edi); \
133 call x86_exit_user_debug_at_kernel_entry; \
136 #define COPY_SYSCALL_PARAMETERS() \
137 /* make room for the syscall params */ \
140 /* get the address of the syscall parameters */ \
141 movl IFRAME_user_sp(%ebp), %esi; \
143 cmp $KERNEL_BASE, %esi; /* must not be a kernel address */ \
144 jae bad_syscall_params; \
146 /* set the fault handler */ \
147 movl $bad_syscall_params, THREAD_fault_handler(%edi); \
149 /* target address is our stack */ \
152 /* number of syscall parameter words */ \
153 movl SYSCALL_INFO_parameter_size(%edx), %ecx; \
160 /* restore pointers and clear fault handler */ \
161 movl %edx, %esi; /* syscall info pointer */ \
162 movl %gs:0, %edi; /* thread pointer */ \
163 movl $0, THREAD_fault_handler(%edi)
166 # define TRACE_PRE_SYSCALL() \
167 movl %esp, %eax; /* syscall parameters */ \
169 movl IFRAME_orig_eax(%ebp), %eax; /* syscall number */ \
171 call trace_pre_syscall; \
174 # define TRACE_POST_SYSCALL() \
175 testl $THREAD_FLAGS_64_BIT_SYSCALL_RETURN, THREAD_flags(%edi); \
179 push %edx; /* syscall return value */ \
181 movl IFRAME_orig_eax(%ebp), %eax; /* syscall number */ \
183 call trace_post_syscall; \
186 # define TRACE_PRE_SYSCALL()
187 # define TRACE_POST_SYSCALL()
193 #define TRAP_ERRC(name, vector) \
202 #define TRAP(name, vector) \
222 FUNCTION(double_fault):
223 pushl $-1; // user-ss
224 pushl $-1; // user-esp
226 pushl $KERNEL_CODE_SELECTOR // cs
228 pushl $0; // error-code
233 PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
235 movl %esp, %ebp // frame pointer is the iframe
238 call x86_double_fault_exception
240 // Well, there's no returning from a double fault, but maybe a real hacker
241 // can repair things in KDL.
242 POP_IFRAME_AND_RETURN()
243 FUNCTION_END(double_fault)
247 TRAP_ERRC(trap10, 10)
248 TRAP_ERRC(trap11, 11)
249 TRAP_ERRC(trap12, 12)
250 TRAP_ERRC(trap13, 13)
251 TRAP_ERRC(trap14, 14)
254 TRAP_ERRC(trap17, 17)
258 // legacy or ioapic interrupts
276 // additional ioapic interrupts
286 // configurable msi or msi-x interrupts
329 //TRAP(trap98, 98) // performance testing interrupt
330 //TRAP(trap99, 99) // syscall interrupt
483 // smp / apic local interrupts
492 FUNCTION(trap14_double_fault):
497 PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
499 movl %esp, %ebp // frame pointer is the iframe
502 call x86_page_fault_exception_double_fault
504 POP_IFRAME_AND_RETURN()
505 FUNCTION_END(trap14_double_fault)
509 STATIC_FUNCTION(int_bottom):
510 PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
512 movl $KERNEL_TLS_SELECTOR, %edx
515 movl %esp, %ebp // frame pointer is the iframe
517 // Set the RF (resume flag) in EFLAGS. This prevents an instruction
518 // breakpoint on the instruction we're returning to to trigger a debug
520 orl $0x10000, IFRAME_flags(%ebp);
522 cmpw $USER_CODE_SELECTOR, IFRAME_cs(%ebp) // user mode
525 // We need to recheck user mode using the thread's in_kernel flag, since
526 // sysexit introduces a raced condition: It doesn't reenable interrupts,
527 // so that we have to do it in the instruction before, thus opening a
528 // window for an interrupt while still being in the kernel, but having set
529 // up everything for userland already.
530 movl %gs:0, %edi // thread pointer
531 cmpb $0, THREAD_in_kernel(%edi)
534 // disable interrupts -- the handler will enable them, if necessary
538 movl IFRAME_vector(%ebp), %eax
539 call *gInterruptHandlerTable(, %eax, 4)
541 POP_IFRAME_AND_RETURN()
542 FUNCTION_END(int_bottom)
545 STATIC_FUNCTION(int_bottom_user):
546 movl $KERNEL_DATA_SELECTOR, %eax
551 // disable breakpoints, if installed
552 movl %gs:0, %edi // thread pointer
553 cli // disable interrupts
554 STOP_USER_DEBUGGING()
556 // update the thread's user time
557 UPDATE_THREAD_USER_TIME()
559 // leave interrupts disabled -- the handler will enable them, if
563 movl IFRAME_vector(%ebp), %eax
564 call *gInterruptHandlerTable(, %eax, 4)
566 // Don't do any kernel exit work, if we actually came from the kernel (but
567 // were already/still prepared for userland), since the iframe in this case
568 // will be a kernel iframe and e.g. trying to set up a signal stack will not
569 // be a very healthy endeavor.
570 cmpw $USER_CODE_SELECTOR, IFRAME_cs(%ebp)
573 testl $(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
574 | THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
575 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
580 cli // disable interrupts
582 // update the thread's kernel time and return
583 UPDATE_THREAD_KERNEL_TIME()
584 POP_IFRAME_AND_RETURN()
585 FUNCTION_END(int_bottom_user)
588 // test interrupt handler for performance measurements
597 // push error, vector, orig_edx, orig_eax, and other registers
598 PUSH_IFRAME_BOTTOM_SYSCALL()
602 POP_IFRAME_AND_RETURN()
606 STATIC_FUNCTION(handle_syscall):
607 movl $KERNEL_TLS_SELECTOR, %edx
610 // save %eax, the number of the syscall
613 movl $KERNEL_DATA_SELECTOR, %eax
618 lea 4(%esp), %ebp // skipping the return address, the stack
619 // frame pointer is the iframe
620 movl %gs:0, %edi // thread pointer
622 // disable breakpoints, if installed
623 cli // disable interrupts
624 STOP_USER_DEBUGGING()
626 // update the thread's user time
627 UPDATE_THREAD_USER_TIME_PUSH_TIME()
628 // leave the time on the stack (needed for post syscall debugging)
630 sti // enable interrupts
632 cmp $SYSCALL_COUNT, %esi // check syscall number
633 jae bad_syscall_number
634 movl $kSyscallInfos, %eax // get syscall info
635 lea (%eax, %esi, SYSCALL_INFO_sizeof), %edx
637 // copy parameters onto this stack
638 COPY_SYSCALL_PARAMETERS()
640 // pre syscall debugging
642 testl $THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%edi)
643 jnz do_pre_syscall_debug
644 pre_syscall_debug_done:
646 // call the syscall function
647 call *SYSCALL_INFO_function(%esi)
649 // overwrite the values of %eax and %edx on the stack (the syscall return
651 movl %edx, IFRAME_dx(%ebp)
652 movl %eax, IFRAME_ax(%ebp)
656 testl $(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
657 | THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
658 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP \
659 | THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
660 | THREAD_FLAGS_RESTART_SYSCALL | THREAD_FLAGS_SYSCALL_RESTARTED) \
662 jnz post_syscall_work
664 cli // disable interrupts
666 // update the thread's kernel time
667 UPDATE_THREAD_KERNEL_TIME()
669 lea -4(%ebp), %esp // remove all parameters from the stack
672 FUNCTION_END(handle_syscall)
674 STATIC_FUNCTION(do_pre_syscall_debug):
675 movl %esp, %eax // syscall parameters
677 movl IFRAME_orig_eax(%ebp), %eax // syscall number
679 call user_debug_pre_syscall
681 jmp pre_syscall_debug_done
682 FUNCTION_END(do_pre_syscall_debug)
684 STATIC_FUNCTION(post_syscall_work):
685 // clear the 64 bit return value and syscall restarted bits
686 testl $(THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
687 | THREAD_FLAGS_SYSCALL_RESTARTED), THREAD_flags(%edi)
690 movl THREAD_flags(%edi), %eax
692 andl $~(THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
693 | THREAD_FLAGS_SYSCALL_RESTARTED), %edx
695 cmpxchgl %edx, THREAD_flags(%edi)
699 // post syscall debugging
700 testl $THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%edi)
702 pushl -8(%ebp) // syscall start time
704 movl IFRAME_dx(%ebp), %edx // syscall return value
705 movl IFRAME_ax(%ebp), %eax
708 lea 16(%esp), %eax // syscall parameters
710 movl IFRAME_orig_eax(%ebp), %eax // syscall number
712 call user_debug_post_syscall
715 FUNCTION_END(post_syscall_work)
718 STATIC_FUNCTION(kernel_exit_work):
719 // if no signals are pending and the thread shall not be debugged or stopped
720 // for a core dump, we can use the quick kernel exit function
721 testl $(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \
722 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
724 jnz kernel_exit_handle_signals
725 cli // disable interrupts
726 call thread_at_kernel_exit_no_signals
727 kernel_exit_work_done:
730 // TODO: this only needs to be done for syscalls!
731 testl $THREAD_FLAGS_RESTART_SYSCALL, THREAD_flags(%edi)
734 call x86_restart_syscall
738 // install breakpoints, if defined
739 testl $THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%edi)
742 call x86_init_user_debug_at_kernel_exit
744 POP_IFRAME_AND_RETURN()
745 FUNCTION_END(kernel_exit_work)
747 STATIC_FUNCTION(kernel_exit_handle_signals):
748 // make sure interrupts are enabled (they are, when coming from a syscall
749 // but otherwise they might be disabled)
751 call thread_at_kernel_exit // also disables interrupts
752 jmp kernel_exit_work_done
753 FUNCTION_END(kernel_exit_handle_signals)
755 STATIC_FUNCTION(bad_syscall_params):
756 // clear the fault handler and exit normally
758 movl $0, THREAD_fault_handler(%edi)
760 FUNCTION_END(bad_syscall_params)
763 /*! Handler called by the sysenter instruction
766 FUNCTION(x86_sysenter):
769 movl $KERNEL_TLS_SELECTOR, %edx
775 pushl $USER_DATA_SELECTOR // user_ss
776 pushl %ecx // user_esp
778 orl $(1 << 9), (%esp) // set the IF (interrupts) bit
779 pushl $USER_CODE_SELECTOR // user cs
782 movl THREAD_team(%edx), %edx
783 movl TEAM_commpage_address(%edx), %edx
784 addl 4 * COMMPAGE_ENTRY_X86_SYSCALL(%edx), %edx
785 addl $4, %edx // sysenter is at offset 2, 2 bytes long
788 PUSH_IFRAME_BOTTOM_SYSCALL()
792 // pop the bottom of the iframe
793 lea 4(%ebp), %esp // skip iframe type
796 addl $4, %esp /* we skip %fs, as this contains the CPU
797 dependent TLS segment */
803 // ecx already contains the user esp -- load edx with the return address
806 // pop eflags, which also reenables interrupts
807 addl $24, %esp // skip, orig_eax/edx, vector, error_code, eip, cs
811 FUNCTION_END(x86_sysenter)
814 /*! \fn void x86_return_to_userland(iframe* frame)
815 \brief Returns to the userland environment given by \a frame.
817 Before returning to userland all potentially necessary kernel exit work is
820 \a frame must point to a location somewhere on the caller's stack (e.g. a
822 The function must be called with interrupts disabled.
824 \param frame The iframe defining the userland environment.
826 FUNCTION(x86_return_to_userland):
827 // get the iframe* parameter
831 // check, if any kernel exit work has to be done
833 testl $(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
834 | THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
835 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
839 // update the thread's kernel time and return
840 UPDATE_THREAD_KERNEL_TIME()
841 POP_IFRAME_AND_RETURN()
842 FUNCTION_END(x86_return_to_userland)