2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * Mach Operating System
33 * Copyright (c) 1991,1990 Carnegie Mellon University
34 * All Rights Reserved.
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
46 * Carnegie Mellon requests users of this software to return to
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
61 #include <i386/eflags.h>
62 #include <i386/trap.h>
64 #define HI_DATA(lo_addr) ( (EXT(lo_addr) - EXT(hi_remap_data)) + HIGH_IDT_BASE )
65 #define HI_TEXT(lo_text) ( (EXT(lo_text) - EXT(hi_remap_text)) + HIGH_MEM_BASE )
68 * Interrupt descriptor table and code vectors for it.
70 #define IDT_BASE_ENTRY(vec,seg,type) \
72 .long EXT(vec) - EXT(hi_remap_text) + HIGH_MEM_BASE ;\
78 #define IDT_BASE_ENTRY_INT(vec,seg,type) \
80 .long vec - EXT(hi_remap_text) + HIGH_MEM_BASE ;\
86 #define IDT_BASE_ENTRY_TG(vec,seg,type) \
94 #define IDT_ENTRY(vec,type) IDT_BASE_ENTRY(vec,KERNEL_CS,type)
95 #define IDT_ENTRY_INT(vec,type) IDT_BASE_ENTRY_INT(vec,KERNEL_CS,type)
98 * No error code. Clear error code and push trap number.
100 #define EXCEPTION(n,name) \
101 IDT_ENTRY(name,K_INTR_GATE);\
106 movl $ EXT(lo_alltraps),%ebx ;\
111 * Interrupt from user. Clear error code and push trap number.
113 #define EXCEP_USR(n,name) \
114 IDT_ENTRY(name,U_INTR_GATE);\
119 movl $ EXT(lo_alltraps),%ebx ;\
124 * Special interrupt code.
126 #define EXCEP_SPC(n,name) \
127 IDT_ENTRY(name,K_INTR_GATE)
130 * Special interrupt code from user.
132 #define EXCEP_SPC_USR(n,name) \
133 IDT_ENTRY(name,U_INTR_GATE)
137 * Extra-special interrupt code. Note that no offset may be
138 * specified in a task gate descriptor, so name is ignored.
140 #define EXCEP_TASK(n,name) \
141 IDT_BASE_ENTRY_TG(0,DEBUG_TSS,K_TASK_GATE)
143 /* Double-fault fatal handler */
144 #define DF_FATAL_TASK(n,name) \
145 IDT_BASE_ENTRY_TG(0,DF_TSS,K_TASK_GATE)
147 /* machine-check handler */
148 #define MC_FATAL_TASK(n,name) \
149 IDT_BASE_ENTRY_TG(0,MC_TSS,K_TASK_GATE)
152 * Error code has been pushed. Push trap number.
154 #define EXCEP_ERR(n,name) \
155 IDT_ENTRY(name,K_INTR_GATE);\
159 movl $ EXT(lo_alltraps),%ebx ;\
166 #define INTERRUPT(n) \
167 IDT_ENTRY_INT(L_ ## n,K_INTR_GATE) ;\
173 movl $ EXT(lo_allintrs),%ebx ;\
184 EXCEPTION(0x00,t_zero_div)
185 EXCEP_SPC(0x01,hi_debug)
186 INTERRUPT(0x02) /* NMI */
187 EXCEP_USR(0x03,t_int3)
188 EXCEP_USR(0x04,t_into)
189 EXCEP_USR(0x05,t_bounds)
190 EXCEPTION(0x06,t_invop)
191 EXCEPTION(0x07,t_nofpu)
193 EXCEP_TASK(0x08,db_task_dbl_fault)
195 DF_FATAL_TASK(0x08,df_task_start)
197 EXCEPTION(0x09,a_fpu_over)
198 EXCEPTION(0x0a,a_inv_tss)
199 EXCEP_SPC(0x0b,hi_segnp)
201 EXCEP_TASK(0x0c,db_task_stk_fault)
203 EXCEP_ERR(0x0c,t_stack_fault)
205 EXCEP_SPC(0x0d,hi_gen_prot)
206 EXCEP_SPC(0x0e,hi_page_fault)
207 EXCEPTION(0x0f,t_trap_0f)
208 EXCEPTION(0x10,t_fpu_err)
209 EXCEPTION(0x11,t_trap_11)
210 MC_FATAL_TASK(0x12,mc_task_start)
211 EXCEPTION(0x13,t_sse_err)
212 EXCEPTION(0x14,t_trap_14)
213 EXCEPTION(0x15,t_trap_15)
214 EXCEPTION(0x16,t_trap_16)
215 EXCEPTION(0x17,t_trap_17)
216 EXCEPTION(0x18,t_trap_18)
217 EXCEPTION(0x19,t_trap_19)
218 EXCEPTION(0x1a,t_trap_1a)
219 EXCEPTION(0x1b,t_trap_1b)
220 EXCEPTION(0x1c,t_trap_1c)
221 EXCEPTION(0x1d,t_trap_1d)
222 EXCEPTION(0x1e,t_trap_1e)
223 EXCEPTION(0x1f,t_trap_1f)
327 EXCEP_SPC_USR(0x80,hi_unix_scall)
328 EXCEP_SPC_USR(0x81,hi_mach_scall)
329 EXCEP_SPC_USR(0x82,hi_mdep_scall)
330 EXCEP_SPC_USR(0x83,hi_diag_scall)
462 EXCEPTION(0xff,t_preempt)
473 /*******************************************************************************************************
475 * Trap/interrupt entry points.
477 * All traps must create the following save area on the PCB "stack":
486 * cr2 if page fault - otherwise unused
496 * user esp - if from user
497 * user ss - if from user
501 Entry(hi_ret_to_user)
503 movl %gs:CPU_ACTIVE_THREAD,%ecx
504 subl ACT_PCB_ISS(%ecx),%ebx
505 movl $(WINDOWS_CLEAN),ACT_COPYIO_STATE(%ecx)
507 movl ACT_PCB_IDS(%ecx),%eax /* get debug state struct */
508 cmpl $0,%eax /* is there a debug state */
509 je 1f /* branch if not */
510 movl DS_DR0(%eax), %ecx /* Load the 32 bit debug registers */
512 movl DS_DR1(%eax), %ecx
514 movl DS_DR2(%eax), %ecx
516 movl DS_DR3(%eax), %ecx
518 movl DS_DR7(%eax), %eax
520 addl %gs:CPU_HI_ISS,%ebx /* rebase PCB save area to high addr */
521 movl %gs:CPU_TASK_CR3,%ecx
522 movl %ecx,%gs:CPU_ACTIVE_CR3
523 movl %ebx,%esp /* switch to hi based PCB stack */
524 movl %ecx,%cr3 /* switch to user's address space */
526 cmpl $0,%eax /* is dr7 set to something? */
527 je 2f /* branch if not */
528 movl %eax,%db7 /* Set dr7 */
531 Entry(hi_ret_to_kernel)
533 popl %eax /* ignore flavor of saved state */
535 popl %gs /* restore segment registers */
543 popa /* restore general registers */
544 addl $8,%esp /* discard trap number and error code */
546 cmpl $(SYSENTER_CS),4(%esp) /* test for fast entry/exit */
549 iret /* return from interrupt */
551 popl %edx /* user return eip */
552 popl %ecx /* pop and toss cs */
553 andl $(~EFL_IF),(%esp) /* clear intrs enabled, see sti below */
554 popf /* flags - carry denotes failure */
555 popl %ecx /* user return esp */
556 sti /* interrupts enabled after sysexit */
559 /*******************************************************************************************************/
563 pushl %eax /* save system call number */
564 pushl $0 /* clear trap number slot */
565 pusha /* save the general registers */
566 movl $ EXT(lo_unix_scall),%ebx
571 pushl %eax /* save system call number */
572 pushl $0 /* clear trap number slot */
573 pusha /* save the general registers */
574 movl $ EXT(lo_mach_scall),%ebx
579 pushl %eax /* save system call number */
580 pushl $0 /* clear trap number slot */
581 pusha /* save the general registers */
582 movl $ EXT(lo_mdep_scall),%ebx
587 pushl %eax // Save sselector
588 pushl $0 // Clear trap number slot
589 pusha // save the general registers
590 movl $EXT(lo_diag_scall),%ebx // Get the function down low to transfer to
591 jmp enter_lohandler // Leap to it...
595 * sysenter entry point
596 * Requires user code to set up:
597 * edx: user instruction pointer (return address)
598 * ecx: user stack pointer
599 * on which is pushed stub ret addr and saved ebx
600 * Return to user-space is made using sysexit.
601 * Note: sysenter/sysexit cannot be used for calls returning a value in edx,
602 * or requiring ecx to be preserved.
605 movl (%esp), %esp /* switch from intr stack to pcb */
607 * Push values on to the PCB stack
608 * to cons up the saved state.
610 pushl $(USER_DS) /* ss */
611 pushl %ecx /* uesp */
614 * Clear, among others, the Nested Task (NT) flags bit;
615 * This is cleared by INT, but not by sysenter, which only
616 * clears RF, VM and IF.
620 pushl $(SYSENTER_CS) /* cs */
623 pushl %eax /* err/eax - syscall code */
624 pushl $0 /* clear trap number slot */
625 pusha /* save the general registers */
626 orl $(EFL_IF),R_EFLAGS-R_EDI(%esp) /* (edi was last reg pushed) */
627 movl $ EXT(lo_sysenter),%ebx
634 pushl $(SS_32) /* 32-bit state flavor */
638 mov %eax,%es /* switch to kernel data seg */
639 mov $(CPU_DATA_GS),%eax
641 cld /* clear direction flag */
643 * Switch to kernel's address space if necessary
645 movl HI_DATA(lo_kernel_cr3),%ecx
650 movl %ecx,%gs:CPU_ACTIVE_CR3
654 movl %esp,%edx /* came from user mode */
655 subl %gs:CPU_HI_ISS,%edx
656 movl %gs:CPU_ACTIVE_THREAD,%ecx
657 addl ACT_PCB_ISS(%ecx),%edx /* rebase the high stack to a low address */
659 cmpl $0, ACT_PCB_IDS(%ecx) /* Is there a debug register state? */
661 movl $0, %ecx /* If so, reset DR7 (the control) */
664 movl R_TRAPNO(%esp),%ecx // Get the interrupt vector
665 addl $1,%gs:hwIntCnt(,%ecx,4) // Bump the count
670 * Page fault traps save cr2.
673 pushl $(T_PAGE_FAULT) /* mark a page fault trap */
674 pusha /* save the general registers */
675 movl %cr2,%eax /* get the faulting address */
676 movl %eax,R_CR2-R_EDI(%esp) /* save in esp save slot */
678 movl $ EXT(lo_alltraps),%ebx
684 * Debug trap. Check for single-stepping across system call into
685 * kernel. If this is the case, taking the debug trap has turned
686 * off single-stepping - save the flags register with the trace
692 /* trap came from kernel mode */
693 cmpl $(HI_TEXT(hi_mach_scall)),(%esp)
695 addl $12,%esp /* remove eip/cs/eflags from debug_trap */
696 jmp EXT(hi_mach_scall) /* continue system call entry */
698 cmpl $(HI_TEXT(hi_mdep_scall)),(%esp)
700 addl $12,%esp /* remove eip/cs/eflags from debug_trap */
701 jmp EXT(hi_mdep_scall) /* continue system call entry */
703 cmpl $(HI_TEXT(hi_unix_scall)),(%esp)
705 addl $12,%esp /* remove eip/cs/eflags from debug_trap */
706 jmp EXT(hi_unix_scall) /* continue system call entry */
708 cmpl $(HI_TEXT(hi_sysenter)),(%esp)
711 * eip/cs/flags have been pushed on intr stack
712 * We have to switch to pcb stack and copy eflags.
713 * Note: setting the cs selector to SYSENTER_TF_CS
714 * will cause the return to user path to take the iret path so
715 * that eflags (containing the trap bit) is set atomically.
716 * In unix_syscall this is tested so that we'll rewind the pc
717 * to account for with sysenter or int entry.
719 addl $8,%esp /* remove eip/cs */
720 pushl %ecx /* save %ecx */
721 movl 8(%esp),%ecx /* top of intr stack -> pcb stack */
722 xchgl %ecx,%esp /* switch to pcb stack */
723 pushl $(USER_DS) /* ss */
724 pushl %ss:(%ecx) /* %ecx into uesp slot */
725 pushl %ss:4(%ecx) /* eflags */
726 movl %ss:(%ecx),%ecx /* restore %ecx */
727 pushl $(SYSENTER_TF_CS) /* cs - not SYSENTER_CS for iret path */
728 jmp hi_sysenter_2 /* continue sysenter entry */
731 pushl $(T_DEBUG) /* handle as user trap */
732 pusha /* save the general registers */
733 movl $ EXT(lo_alltraps),%ebx
739 * General protection or segment-not-present fault.
740 * Check for a GP/NP fault in the kernel_return
741 * sequence; if there, report it as a GP/NP fault on the user's instruction.
743 * esp-> 0: trap code (NP or GP)
744 * 4: segment number in error
748 * 20 old registers (trap is from kernel)
751 pushl $(T_GENERAL_PROTECTION) /* indicate fault type */
752 jmp trap_check_kernel_exit /* check for kernel exit sequence */
755 pushl $(T_SEGMENT_NOT_PRESENT)
756 /* indicate fault type */
757 trap_check_kernel_exit:
760 /* trap was from kernel mode, so */
761 /* check for the kernel exit sequence */
762 cmpl $(HI_TEXT(ret_iret)),8(%esp) /* on IRET? */
764 cmpl $(HI_TEXT(ret_popl_ds)),8(%esp) /* popping DS? */
766 cmpl $(HI_TEXT(ret_popl_es)),8(%esp) /* popping ES? */
768 cmpl $(HI_TEXT(ret_popl_fs)),8(%esp) /* popping FS? */
770 cmpl $(HI_TEXT(ret_popl_gs)),8(%esp) /* popping GS? */
773 pusha /* save the general registers */
774 movl $ EXT(lo_alltraps),%ebx
779 * GP/NP fault on IRET: CS or SS is in error.
780 * All registers contain the user's values.
795 movl %eax,8(%esp) /* save eax (we don`t need saved eip) */
796 popl %eax /* get trap number */
797 movl %eax,12-4(%esp) /* put in user trap number */
798 popl %eax /* get error code */
799 movl %eax,16-8(%esp) /* put in user errcode */
800 popl %eax /* restore eax */
801 /* now treat as fault from user */
802 pusha /* save the general registers */
803 movl $ EXT(lo_alltraps),%ebx
807 * Fault restoring a segment register. The user's registers are still
808 * saved on the stack. The offending segment register has not been
812 popl %eax /* get trap number */
813 popl %edx /* get error code */
814 addl $12,%esp /* pop stack to user regs */
815 jmp push_es /* (DS on top of stack) */
817 popl %eax /* get trap number */
818 popl %edx /* get error code */
819 addl $12,%esp /* pop stack to user regs */
820 jmp push_fs /* (ES on top of stack) */
822 popl %eax /* get trap number */
823 popl %edx /* get error code */
824 addl $12,%esp /* pop stack to user regs */
825 jmp push_gs /* (FS on top of stack) */
827 popl %eax /* get trap number */
828 popl %edx /* get error code */
829 addl $12,%esp /* pop stack to user regs */
830 jmp push_none /* (GS on top of stack) */
833 pushl %es /* restore es, */
835 pushl %fs /* restore fs, */
837 pushl %gs /* restore gs. */
839 movl %eax,R_TRAPNO(%esp) /* set trap number */
840 movl %edx,R_ERR(%esp) /* set error code */
841 /* now treat as fault from user */
842 /* except that segment registers are */
844 movl $ EXT(lo_alltraps),%ebx
851 Entry(lo_ret_to_user)
853 1: .long HI_TEXT(hi_ret_to_user)
855 Entry(lo_ret_to_kernel)
857 1: .long HI_TEXT(hi_ret_to_kernel)
859 Entry(hi_remap_etext)