2 * Copyright (c) 2000-2007 Apple 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.
58 #include <platforms.h>
60 #include <mach_kgdb.h>
62 #include <stat_time.h>
63 #include <mach_assert.h>
65 #include <sys/errno.h>
67 #include <i386/cpuid.h>
68 #include <i386/eflags.h>
69 #include <i386/lapic.h>
70 #include <i386/rtclock.h>
71 #include <i386/proc_reg.h>
72 #include <i386/trap.h>
74 #include <mach/exception_types.h>
75 #include <config_dtrace.h>
77 #define _ARCH_I386_ASM_HELP_H_ /* Prevent inclusion of user header */
78 #include <mach/i386/syscall_sw.h>
87 * PTmap is recursive pagemap at top of virtual address space.
88 * Within PTmap, the page directory can be found (third indirection).
90 .globl _PTmap,_PTD,_PTDpde
91 .set _PTmap,(PTDPTDI << PDESHIFT)
92 .set _PTD,_PTmap + (PTDPTDI * NBPG)
93 .set _PTDpde,_PTD + (PTDPTDI * PDESIZE)
96 * APTmap, APTD is the alternate recursive pagemap.
97 * It's used when modifying another process's page tables.
99 .globl _APTmap,_APTD,_APTDpde
100 .set _APTmap,(APTDPTDI << PDESHIFT)
101 .set _APTD,_APTmap + (APTDPTDI * NBPG)
102 .set _APTDpde,_PTD + (APTDPTDI * PDESIZE)
105 /* Under Mach-O, etext is a variable which contains
106 * the last text address
108 #define ETEXT_ADDR (EXT(etext))
110 /* Under ELF and other non-Mach-O formats, the address of
111 * etext represents the last text address
113 #define ETEXT_ADDR $ EXT(etext)
116 #define CX(addr,reg) addr(,reg,4)
119 * The following macros make calls into C code.
120 * They dynamically align the stack to 16 bytes.
121 * Arguments are moved (not pushed) onto the correctly aligned stack.
122 * NOTE: EDI is destroyed in the process, and hence cannot
123 * be directly used as a parameter. Users of this macro must
124 * independently preserve EDI (a non-volatile) if the routine is
125 * intended to be called from C, for instance.
130 andl $0xFFFFFFF0, %esp ;\
134 #define CCALL1(fn, arg1) \
137 andl $0xFFFFFFF0, %esp ;\
138 movl arg1, 0(%esp) ;\
142 #define CCALL2(fn, arg1, arg2) \
145 andl $0xFFFFFFF0, %esp ;\
146 movl arg2, 4(%esp) ;\
147 movl arg1, 0(%esp) ;\
152 * CCALL5 is used for callee functions with 3 arguments but
153 * where arg2 (a3:a2) and arg3 (a5:a4) are 64-bit values.
155 #define CCALL5(fn, a1, a2, a3, a4, a5) \
158 andl $0xFFFFFFF0, %esp ;\
175 #define RECOVERY_SECTION .section __VECTORS, __recover
177 #define RECOVERY_SECTION .text
178 #define RECOVERY_SECTION .text
181 #define RECOVER_TABLE_START \
183 .globl EXT(recover_table) ;\
184 LEXT(recover_table) ;\
187 #define RECOVER(addr) \
194 #define RECOVER_TABLE_END \
196 .globl EXT(recover_table_end) ;\
197 LEXT(recover_table_end) ;\
198 .long 0 /* workaround see comment below */ ;\
202 * the .long 0 is to work around a linker bug (insert radar# here)
203 * basically recover_table_end has zero size and bumps up right against saved_esp in acpi_wakeup.s
204 * recover_table_end is in __RECOVER,__vectors and saved_esp is in __SLEEP,__data, but they're right next to each
205 * other and so the linker combines them and incorrectly relocates everything referencing recover_table_end to point
206 * into the SLEEP section
210 * Allocate recovery and table.
222 movl %eax,TIMER_HIGHCHK(%ecx)
223 movl %edx,TIMER_LOW(%ecx)
224 movl %eax,TIMER_HIGH(%ecx)
229 0: movl TIMER_HIGH(%ecx),%edx
230 movl TIMER_LOW(%ecx),%eax
231 cmpl TIMER_HIGHCHK(%ecx),%edx
237 #define TIME_TRAP_UENTRY
238 #define TIME_TRAP_UEXIT
239 #define TIME_INT_ENTRY
240 #define TIME_INT_EXIT
248 * Nanotime returned in %edx:%eax.
249 * Computed from tsc based on the scale factor
250 * and an implicit 32 bit shift.
252 * Uses %eax, %ebx, %ecx, %edx, %esi, %edi.
255 mov %gs:CPU_NANOTIME,%edi ; \
256 RTC_NANOTIME_READ_FAST()
260 * Add 64-bit delta in register dreg : areg to timer pointed to by register treg.
262 #define TIMER_UPDATE(treg,dreg,areg,offset) \
263 addl (TIMER_LOW+(offset))(treg),areg /* add low bits */ ;\
264 adcl dreg,(TIMER_HIGH+(offset))(treg) /* add carry high bits */ ;\
265 movl areg,(TIMER_LOW+(offset))(treg) /* store updated low bit */ ;\
266 movl (TIMER_HIGH+(offset))(treg),dreg /* copy high bits */ ;\
267 movl dreg,(TIMER_HIGHCHK+(offset))(treg) /* to high check */
270 * Add time delta to old timer and start new.
272 #define TIMER_EVENT(old,new) \
273 NANOTIME /* edx:eax nanosecs */ ; \
274 movl %eax,%esi /* save timestamp */ ; \
275 movl %edx,%edi /* save timestamp */ ; \
276 movl %gs:CPU_ACTIVE_THREAD,%ecx /* get current thread */ ; \
277 subl (old##_TIMER)+TIMER_TSTAMP(%ecx),%eax /* compute elapsed time */ ; \
278 sbbl (old##_TIMER)+TIMER_TSTAMP+4(%ecx),%edx /* compute elapsed time */ ; \
279 TIMER_UPDATE(%ecx,%edx,%eax,old##_TIMER) /* update timer */ ; \
280 movl %esi,(new##_TIMER)+TIMER_TSTAMP(%ecx) /* set timestamp */ ; \
281 movl %edi,(new##_TIMER)+TIMER_TSTAMP+4(%ecx) /* set timestamp */ ; \
282 leal (new##_TIMER)(%ecx), %ecx /* compute new timer pointer */ ; \
283 movl %gs:CPU_PROCESSOR,%ebx /* get current processor */ ; \
284 movl %ecx,THREAD_TIMER(%ebx) /* set current timer */ ; \
285 movl %esi,%eax /* restore timestamp */ ; \
286 movl %edi,%edx /* restore timestamp */ ; \
287 subl (old##_STATE)+TIMER_TSTAMP(%ebx),%eax /* compute elapsed time */ ; \
288 sbbl (old##_STATE)+TIMER_TSTAMP+4(%ebx),%edx /* compute elapsed time */ ; \
289 TIMER_UPDATE(%ebx,%edx,%eax,old##_STATE) /* update timer */ ; \
290 leal (new##_STATE)(%ebx),%ecx /* compute new state pointer */ ; \
291 movl %ecx,CURRENT_STATE(%ebx) /* set current state */ ; \
292 movl %esi,TIMER_TSTAMP(%ecx) /* set timestamp */ ; \
293 movl %edi,TIMER_TSTAMP+4(%ecx) /* set timestamp */
296 * Update time on user trap entry.
297 * Uses %eax,%ebx,%ecx,%edx,%esi,%edi.
299 #define TIME_TRAP_UENTRY TIMER_EVENT(USER,SYSTEM)
302 * update time on user trap exit.
303 * Uses %eax,%ebx,%ecx,%edx,%esi,%edi.
305 #define TIME_TRAP_UEXIT TIMER_EVENT(SYSTEM,USER)
308 * update time on interrupt entry.
309 * Uses %eax,%ebx,%ecx,%edx,%esi,%edi.
310 * Saves processor state info on stack.
312 #define TIME_INT_ENTRY \
313 NANOTIME /* edx:eax nanosecs */ ; \
314 movl %eax,%gs:CPU_INT_EVENT_TIME /* save in cpu data */ ; \
315 movl %edx,%gs:CPU_INT_EVENT_TIME+4 /* save in cpu data */ ; \
316 movl %eax,%esi /* save timestamp */ ; \
317 movl %edx,%edi /* save timestamp */ ; \
318 movl %gs:CPU_PROCESSOR,%ebx /* get current processor */ ; \
319 movl THREAD_TIMER(%ebx),%ecx /* get current timer */ ; \
320 subl TIMER_TSTAMP(%ecx),%eax /* compute elapsed time */ ; \
321 sbbl TIMER_TSTAMP+4(%ecx),%edx /* compute elapsed time */ ; \
322 TIMER_UPDATE(%ecx,%edx,%eax,0) /* update timer */ ; \
323 movl KERNEL_TIMER(%ebx),%ecx /* point to kernel timer */ ; \
324 movl %esi,TIMER_TSTAMP(%ecx) /* set timestamp */ ; \
325 movl %edi,TIMER_TSTAMP+4(%ecx) /* set timestamp */ ; \
326 movl %esi,%eax /* restore timestamp */ ; \
327 movl %edi,%edx /* restore timestamp */ ; \
328 movl CURRENT_STATE(%ebx),%ecx /* get current state */ ; \
329 pushl %ecx /* save state */ ; \
330 subl TIMER_TSTAMP(%ecx),%eax /* compute elapsed time */ ; \
331 sbbl TIMER_TSTAMP+4(%ecx),%edx /* compute elapsed time */ ; \
332 TIMER_UPDATE(%ecx,%edx,%eax,0) /* update timer */ ; \
333 leal IDLE_STATE(%ebx),%eax /* get idle state */ ; \
334 cmpl %eax,%ecx /* compare current state */ ; \
335 je 0f /* skip if equal */ ; \
336 leal SYSTEM_STATE(%ebx),%ecx /* get system state */ ; \
337 movl %ecx,CURRENT_STATE(%ebx) /* set current state */ ; \
338 0: movl %esi,TIMER_TSTAMP(%ecx) /* set timestamp */ ; \
339 movl %edi,TIMER_TSTAMP+4(%ecx) /* set timestamp */
342 * update time on interrupt exit.
343 * Uses %eax,%ebx,%ecx,%edx,%esi,%edi.
344 * Restores processor state info from stack.
346 #define TIME_INT_EXIT \
347 NANOTIME /* edx:eax nanosecs */ ; \
348 movl %eax,%gs:CPU_INT_EVENT_TIME /* save in cpu data */ ; \
349 movl %edx,%gs:CPU_INT_EVENT_TIME+4 /* save in cpu data */ ; \
350 movl %eax,%esi /* save timestamp */ ; \
351 movl %edx,%edi /* save timestamp */ ; \
352 movl %gs:CPU_PROCESSOR,%ebx /* get current processor */ ; \
353 movl KERNEL_TIMER(%ebx),%ecx /* point to kernel timer */ ; \
354 subl TIMER_TSTAMP(%ecx),%eax /* compute elapsed time */ ; \
355 sbbl TIMER_TSTAMP+4(%ecx),%edx /* compute elapsed time */ ; \
356 TIMER_UPDATE(%ecx,%edx,%eax,0) /* update timer */ ; \
357 movl THREAD_TIMER(%ebx),%ecx /* interrupted timer */ ; \
358 movl %esi,TIMER_TSTAMP(%ecx) /* set timestamp */ ; \
359 movl %edi,TIMER_TSTAMP+4(%ecx) /* set timestamp */ ; \
360 movl %esi,%eax /* restore timestamp */ ; \
361 movl %edi,%edx /* restore timestamp */ ; \
362 movl CURRENT_STATE(%ebx),%ecx /* get current state */ ; \
363 subl TIMER_TSTAMP(%ecx),%eax /* compute elapsed time */ ; \
364 sbbl TIMER_TSTAMP+4(%ecx),%edx /* compute elapsed time */ ; \
365 TIMER_UPDATE(%ecx,%edx,%eax,0) /* update timer */ ; \
366 popl %ecx /* restore state */ ; \
367 movl %ecx,CURRENT_STATE(%ebx) /* set current state */ ; \
368 movl %esi,TIMER_TSTAMP(%ecx) /* set timestamp */ ; \
369 movl %edi,TIMER_TSTAMP+4(%ecx) /* set timestamp */
371 #endif /* STAT_TIME */
378 * Traditional, not ANSI.
382 .globl label/**/count ;\
385 .globl label/**/limit ;\
389 addl $1,%ss:label/**/count ;\
390 cmpl $0,label/**/limit ;\
394 movl %ss:label/**/count,%eax ;\
395 cmpl %eax,%ss:label/**/limit ;\
408 * Last-ditch debug code to handle faults that might result
409 * from entering kernel (from collocated server) on an invalid
410 * stack. On collocated entry, there's no hardware-initiated
411 * stack switch, so a valid stack must be in place when an
412 * exception occurs, or we may double-fault.
414 * In case of a double-fault, our only recourse is to switch
415 * hardware "tasks", so that we avoid using the current stack.
417 * The idea here is just to get the processor into the debugger,
418 * post-haste. No attempt is made to fix up whatever error got
419 * us here, so presumably continuing from the debugger will
420 * simply land us here again -- at best.
424 * Note that the per-fault entry points are not currently
425 * functional. The only way to make them work would be to
426 * set up separate TSS's for each fault type, which doesn't
427 * currently seem worthwhile. (The offset part of a task
428 * gate is always ignored.) So all faults that task switch
429 * currently resume at db_task_start.
432 * Double fault (Murphy's point) - error code (0) on stack
434 Entry(db_task_dbl_fault)
436 movl $(T_DOUBLE_FAULT),%ebx
439 * Segment not present - error code on stack
441 Entry(db_task_seg_np)
443 movl $(T_SEGMENT_NOT_PRESENT),%ebx
446 * Stack fault - error code on (current) stack
448 Entry(db_task_stk_fault)
450 movl $(T_STACK_FAULT),%ebx
453 * General protection fault - error code on stack
455 Entry(db_task_gen_prot)
457 movl $(T_GENERAL_PROTECTION),%ebx
461 * The entry point where execution resumes after last-ditch debugger task
466 subl $(ISS32_SIZE),%edx
467 movl %edx,%esp /* allocate x86_saved_state on stack */
468 movl %eax,R32_ERR(%esp)
469 movl %ebx,R32_TRAPNO(%esp)
472 movl CX(EXT(master_dbtss),%edx),%edx
473 movl TSS_LINK(%edx),%eax
474 pushl %eax /* pass along selector of previous TSS */
475 call EXT(db_tss_to_frame)
476 popl %eax /* get rid of TSS selector */
477 call EXT(db_trap_from_asm)
482 iret /* ha, ha, ha... */
483 #endif /* MACH_KDB */
486 * Called as a function, makes the current thread
487 * return from the kernel as if from an exception.
488 * We will consult with DTrace if this is a
489 * newly created thread and we need to fire a probe.
492 .globl EXT(thread_exception_return)
493 .globl EXT(thread_bootstrap_return)
494 LEXT(thread_bootstrap_return)
496 call EXT(dtrace_thread_bootstrap)
499 LEXT(thread_exception_return)
501 movl %gs:CPU_KERNEL_STACK,%ecx
503 movl (%ecx),%esp /* switch back to PCB stack */
504 xorl %ecx,%ecx /* don't check if we're in the PFZ */
505 jmp EXT(return_from_trap)
507 Entry(call_continuation)
508 movl S_ARG0,%eax /* get continuation */
509 movl S_ARG1,%edx /* continuation param */
510 movl S_ARG2,%ecx /* wait result */
511 movl %gs:CPU_KERNEL_STACK,%esp /* pop the stack */
512 xorl %ebp,%ebp /* zero frame pointer */
513 subl $8,%esp /* align the stack */
516 call *%eax /* call continuation */
518 movl %gs:CPU_ACTIVE_THREAD,%eax
520 call EXT(thread_terminate)
524 /*******************************************************************************************************
526 * All 64 bit task 'exceptions' enter lo_alltraps:
527 * esp -> x86_saved_state_t
529 * The rest of the state is set up as:
530 * cr3 -> kernel directory
531 * esp -> low based stack
534 * ss/ds/es -> KERNEL_DS
536 * interrupts disabled
537 * direction flag cleared
540 movl R32_CS(%esp),%eax /* assume 32-bit state */
541 cmpl $(SS_64),SS_FLAVOR(%esp)/* 64-bit? */
543 movl R64_CS(%esp),%eax /* 64-bit user mode */
550 movl %gs:CPU_ACTIVE_THREAD,%ecx
551 movl ACT_TASK(%ecx),%ebx
553 /* Check for active vtimers in the current task */
554 cmpl $0,TASK_VTIMERS(%ebx)
557 /* Set a pending AST */
558 orl $(AST_BSD),%gs:CPU_PENDING_AST
560 /* Set a thread AST (atomic) */
562 orl $(AST_BSD),ACT_AST(%ecx)
565 movl %gs:CPU_KERNEL_STACK,%ebx
566 xchgl %ebx,%esp /* switch to kernel stack */
569 CCALL1(user_trap, %ebx) /* call user trap routine */
570 cli /* hold off intrs - critical section */
571 popl %esp /* switch back to PCB stack */
572 xorl %ecx,%ecx /* don't check if we're in the PFZ */
575 * Return from trap or system call, checking for ASTs.
576 * On lowbase PCB stack with intrs disabled
578 LEXT(return_from_trap)
579 movl %gs:CPU_PENDING_AST, %eax
581 je EXT(return_to_user) /* branch if no AST */
583 LEXT(return_from_trap_with_ast)
584 movl %gs:CPU_KERNEL_STACK, %ebx
585 xchgl %ebx, %esp /* switch to kernel stack */
587 testl %ecx, %ecx /* see if we need to check for an EIP in the PFZ */
588 je 2f /* no, go handle the AST */
589 cmpl $(SS_64), SS_FLAVOR(%ebx) /* are we a 64-bit task? */
591 /* no... 32-bit user mode */
592 movl R32_EIP(%ebx), %eax
593 pushl %ebx /* save PCB stack */
594 xorl %ebp, %ebp /* clear frame pointer */
595 CCALL1(commpage_is_in_pfz32, %eax)
596 popl %ebx /* retrieve pointer to PCB stack */
598 je 2f /* not in the PFZ... go service AST */
599 movl %eax, R32_EBX(%ebx) /* let the PFZ know we've pended an AST */
600 xchgl %ebx, %esp /* switch back to PCB stack */
601 jmp EXT(return_to_user)
602 1: /* 64-bit user mode */
603 movl R64_RIP(%ebx), %ecx
604 movl R64_RIP+4(%ebx), %eax
605 pushl %ebx /* save PCB stack */
606 xorl %ebp, %ebp /* clear frame pointer */
607 CCALL2(commpage_is_in_pfz64, %ecx, %eax)
608 popl %ebx /* retrieve pointer to PCB stack */
610 je 2f /* not in the PFZ... go service AST */
611 movl %eax, R64_RBX(%ebx) /* let the PFZ know we've pended an AST */
612 xchgl %ebx, %esp /* switch back to PCB stack */
613 jmp EXT(return_to_user)
615 STI /* interrupts always enabled on return to user mode */
616 pushl %ebx /* save PCB stack */
617 xorl %ebp, %ebp /* Clear framepointer */
618 CCALL1(i386_astintr, $0) /* take the AST */
621 popl %esp /* switch back to PCB stack (w/exc link) */
623 xorl %ecx, %ecx /* don't check if we're in the PFZ */
624 jmp EXT(return_from_trap) /* and check again (rare) */
630 cmpl $0, %gs:CPU_IS64BIT
631 je EXT(lo_ret_to_user)
632 jmp EXT(lo64_ret_to_user)
637 * Trap from kernel mode. No need to switch stacks.
638 * Interrupts must be off here - we will set them to state at time of trap
639 * as soon as it's safe for us to do so and not recurse doing preemption
642 movl %esp, %eax /* saved state addr */
643 pushl R32_EIP(%esp) /* Simulate a CALL from fault point */
644 pushl %ebp /* Extend framepointer chain */
646 CCALL1(kernel_trap, %eax) /* Call kernel trap handler */
651 movl %gs:CPU_PENDING_AST,%eax /* get pending asts */
652 testl $ AST_URGENT,%eax /* any urgent preemption? */
653 je ret_to_kernel /* no, nothing to do */
654 cmpl $ T_PREEMPT,R32_TRAPNO(%esp)
655 je ret_to_kernel /* T_PREEMPT handled in kernel_trap() */
656 testl $ EFL_IF,R32_EFLAGS(%esp) /* interrupts disabled? */
658 cmpl $0,%gs:CPU_PREEMPTION_LEVEL /* preemption disabled? */
660 movl %gs:CPU_KERNEL_STACK,%eax
663 and EXT(kernel_stack_mask),%ecx
664 testl %ecx,%ecx /* are we on the kernel stack? */
665 jne ret_to_kernel /* no, skip it */
667 CCALL1(i386_astintr, $1) /* take the AST */
670 cmpl $0, %gs:CPU_IS64BIT
671 je EXT(lo_ret_to_kernel)
672 jmp EXT(lo64_ret_to_kernel)
676 /*******************************************************************************************************
678 * All interrupts on all tasks enter here with:
679 * esp-> -> x86_saved_state_t
681 * cr3 -> kernel directory
682 * esp -> low based stack
685 * ss/ds/es -> KERNEL_DS
687 * interrupts disabled
688 * direction flag cleared
692 * test whether already on interrupt stack
694 movl %gs:CPU_INT_STACK_TOP,%ecx
697 leal -INTSTACK_SIZE(%ecx),%edx
701 xchgl %ecx,%esp /* switch to interrupt stack */
703 movl %cr0,%eax /* get cr0 */
704 orl $(CR0_TS),%eax /* or in TS bit */
705 movl %eax,%cr0 /* set cr0 */
707 subl $8, %esp /* for 16-byte stack alignment */
708 pushl %ecx /* save pointer to old stack */
709 movl %ecx,%gs:CPU_INT_STATE /* save intr state */
711 TIME_INT_ENTRY /* do timing */
713 movl %gs:CPU_ACTIVE_THREAD,%ecx
714 movl ACT_TASK(%ecx),%ebx
716 /* Check for active vtimers in the current task */
717 cmpl $0,TASK_VTIMERS(%ebx)
720 /* Set a pending AST */
721 orl $(AST_BSD),%gs:CPU_PENDING_AST
723 /* Set a thread AST (atomic) */
725 orl $(AST_BSD),ACT_AST(%ecx)
728 incl %gs:CPU_PREEMPTION_LEVEL
729 incl %gs:CPU_INTERRUPT_LEVEL
731 movl %gs:CPU_INT_STATE, %eax
732 CCALL1(interrupt, %eax) /* call generic interrupt routine */
734 cli /* just in case we returned with intrs enabled */
736 movl %eax,%gs:CPU_INT_STATE /* clear intr state pointer */
738 decl %gs:CPU_INTERRUPT_LEVEL
739 decl %gs:CPU_PREEMPTION_LEVEL
741 TIME_INT_EXIT /* do timing */
743 movl %gs:CPU_ACTIVE_THREAD,%eax
744 movl ACT_PCB(%eax),%eax /* get act`s PCB */
745 movl PCB_FPS(%eax),%eax /* get pcb's ims.ifps */
746 cmpl $0,%eax /* Is there a context */
747 je 1f /* Branch if not */
748 movl FP_VALID(%eax),%eax /* Load fp_valid */
749 cmpl $0,%eax /* Check if valid */
750 jne 1f /* Branch if valid */
754 movl %cr0,%eax /* get cr0 */
755 orl $(CR0_TS),%eax /* or in TS bit */
756 movl %eax,%cr0 /* set cr0 */
758 popl %esp /* switch back to old stack */
760 /* Load interrupted code segment into %eax */
761 movl R32_CS(%esp),%eax /* assume 32-bit state */
762 cmpl $(SS_64),SS_FLAVOR(%esp)/* 64-bit? */
764 movl R64_CS(%esp),%eax /* 64-bit user mode */
766 testb $3,%al /* user mode, */
767 jnz ast_from_interrupt_user /* go handle potential ASTs */
769 * we only want to handle preemption requests if
770 * the interrupt fell in the kernel context
771 * and preemption isn't disabled
773 movl %gs:CPU_PENDING_AST,%eax
774 testl $ AST_URGENT,%eax /* any urgent requests? */
775 je ret_to_kernel /* no, nothing to do */
777 cmpl $0,%gs:CPU_PREEMPTION_LEVEL /* preemption disabled? */
778 jne ret_to_kernel /* yes, skip it */
780 movl %gs:CPU_KERNEL_STACK,%eax
783 and EXT(kernel_stack_mask),%ecx
784 testl %ecx,%ecx /* are we on the kernel stack? */
785 jne ret_to_kernel /* no, skip it */
788 * Take an AST from kernel space. We don't need (and don't want)
789 * to do as much as the case where the interrupt came from user
792 CCALL1(i386_astintr, $1)
798 * nested int - simple path, can't preempt etc on way out
801 incl %gs:CPU_PREEMPTION_LEVEL
802 incl %gs:CPU_INTERRUPT_LEVEL
804 movl %esp, %edx /* x86_saved_state */
805 CCALL1(interrupt, %edx)
807 decl %gs:CPU_INTERRUPT_LEVEL
808 decl %gs:CPU_PREEMPTION_LEVEL
813 * Take an AST from an interrupted user
815 ast_from_interrupt_user:
816 movl %gs:CPU_PENDING_AST,%eax
817 testl %eax,%eax /* pending ASTs? */
818 je EXT(ret_to_user) /* no, nothing to do */
822 movl $1, %ecx /* check if we're in the PFZ */
823 jmp EXT(return_from_trap_with_ast) /* return */
826 /*******************************************************************************************************
829 * System call entries via INTR_GATE or sysenter:
831 * esp -> x86_saved_state32_t
832 * cr3 -> kernel directory
833 * esp -> low based stack
836 * ss/ds/es -> KERNEL_DS
838 * interrupts disabled
839 * direction flag cleared
844 * We can be here either for a mach syscall or a unix syscall,
845 * as indicated by the sign of the code:
847 movl R32_EAX(%esp),%eax
849 js EXT(lo_mach_scall) /* < 0 => mach */
855 movl %gs:CPU_ACTIVE_THREAD,%ecx /* get current thread */
856 movl ACT_TASK(%ecx),%ebx /* point to current task */
857 addl $1,TASK_SYSCALLS_UNIX(%ebx) /* increment call count */
859 /* Check for active vtimers in the current task */
860 cmpl $0,TASK_VTIMERS(%ebx)
863 /* Set a pending AST */
864 orl $(AST_BSD),%gs:CPU_PENDING_AST
866 /* Set a thread AST (atomic) */
868 orl $(AST_BSD),ACT_AST(%ecx)
871 movl %gs:CPU_KERNEL_STACK,%ebx
872 xchgl %ebx,%esp /* switch to kernel stack */
876 CCALL1(unix_syscall, %ebx)
878 * always returns through thread_exception_return
885 movl %gs:CPU_ACTIVE_THREAD,%ecx /* get current thread */
886 movl ACT_TASK(%ecx),%ebx /* point to current task */
887 addl $1,TASK_SYSCALLS_MACH(%ebx) /* increment call count */
889 /* Check for active vtimers in the current task */
890 cmpl $0,TASK_VTIMERS(%ebx)
893 /* Set a pending AST */
894 orl $(AST_BSD),%gs:CPU_PENDING_AST
896 /* Set a thread AST (atomic) */
898 orl $(AST_BSD),ACT_AST(%ecx)
901 movl %gs:CPU_KERNEL_STACK,%ebx
902 xchgl %ebx,%esp /* switch to kernel stack */
906 CCALL1(mach_call_munger, %ebx)
908 * always returns through thread_exception_return
915 movl %gs:CPU_ACTIVE_THREAD,%ecx /* get current thread */
916 movl ACT_TASK(%ecx),%ebx /* point to current task */
918 /* Check for active vtimers in the current task */
919 cmpl $0,TASK_VTIMERS(%ebx)
922 /* Set a pending AST */
923 orl $(AST_BSD),%gs:CPU_PENDING_AST
925 /* Set a thread AST (atomic) */
927 orl $(AST_BSD),ACT_AST(%ecx)
930 movl %gs:CPU_KERNEL_STACK,%ebx
931 xchgl %ebx,%esp /* switch to kernel stack */
935 CCALL1(machdep_syscall, %ebx)
937 * always returns through thread_exception_return
944 movl %gs:CPU_ACTIVE_THREAD,%ecx /* get current thread */
945 movl ACT_TASK(%ecx),%ebx /* point to current task */
947 /* Check for active vtimers in the current task */
948 cmpl $0,TASK_VTIMERS(%ebx)
951 /* Set a pending AST */
952 orl $(AST_BSD),%gs:CPU_PENDING_AST
954 /* Set a thread AST (atomic) */
956 orl $(AST_BSD),ACT_AST(%ecx)
959 movl %gs:CPU_KERNEL_STACK,%ebx // Get the address of the kernel stack
960 xchgl %ebx,%esp // Switch to it, saving the previous
962 CCALL1(diagCall, %ebx) // Call diagnostics
964 cmpl $0,%eax // What kind of return is this?
966 cli // Disable interruptions just in case they were enabled
967 popl %esp // Get back the original stack
968 jmp EXT(return_to_user) // Normal return, do not check asts...
970 CCALL5(i386_exception, $EXC_SYSCALL, $0x6000, $0, $1, $0)
971 // pass what would be the diag syscall
972 // error return - cause an exception
977 /*******************************************************************************************************
980 * System call entries via syscall only:
982 * esp -> x86_saved_state64_t
983 * cr3 -> kernel directory
984 * esp -> low based stack
987 * ss/ds/es -> KERNEL_DS
989 * interrupts disabled
990 * direction flag cleared
997 * We can be here either for a mach, unix machdep or diag syscall,
998 * as indicated by the syscall class:
1000 movl R64_RAX(%esp), %eax /* syscall number/class */
1002 andl $(SYSCALL_CLASS_MASK), %ebx /* syscall class */
1003 cmpl $(SYSCALL_CLASS_MACH<<SYSCALL_CLASS_SHIFT), %ebx
1004 je EXT(lo64_mach_scall)
1005 cmpl $(SYSCALL_CLASS_UNIX<<SYSCALL_CLASS_SHIFT), %ebx
1006 je EXT(lo64_unix_scall)
1007 cmpl $(SYSCALL_CLASS_MDEP<<SYSCALL_CLASS_SHIFT), %ebx
1008 je EXT(lo64_mdep_scall)
1009 cmpl $(SYSCALL_CLASS_DIAG<<SYSCALL_CLASS_SHIFT), %ebx
1010 je EXT(lo64_diag_scall)
1012 movl %gs:CPU_KERNEL_STACK,%ebx
1013 xchgl %ebx,%esp /* switch to kernel stack */
1017 /* Syscall class unknown */
1018 CCALL5(i386_exception, $(EXC_SYSCALL), %eax, $0, $1, $0)
1022 Entry(lo64_unix_scall)
1023 movl %gs:CPU_ACTIVE_THREAD,%ecx /* get current thread */
1024 movl ACT_TASK(%ecx),%ebx /* point to current task */
1025 addl $1,TASK_SYSCALLS_UNIX(%ebx) /* increment call count */
1027 /* Check for active vtimers in the current task */
1028 cmpl $0,TASK_VTIMERS(%ebx)
1031 /* Set a pending AST */
1032 orl $(AST_BSD),%gs:CPU_PENDING_AST
1034 /* Set a thread AST (atomic) */
1036 orl $(AST_BSD),ACT_AST(%ecx)
1039 movl %gs:CPU_KERNEL_STACK,%ebx
1040 xchgl %ebx,%esp /* switch to kernel stack */
1044 CCALL1(unix_syscall64, %ebx)
1046 * always returns through thread_exception_return
1050 Entry(lo64_mach_scall)
1051 movl %gs:CPU_ACTIVE_THREAD,%ecx /* get current thread */
1052 movl ACT_TASK(%ecx),%ebx /* point to current task */
1053 addl $1,TASK_SYSCALLS_MACH(%ebx) /* increment call count */
1055 /* Check for active vtimers in the current task */
1056 cmpl $0,TASK_VTIMERS(%ebx)
1059 /* Set a pending AST */
1060 orl $(AST_BSD),%gs:CPU_PENDING_AST
1063 orl $(AST_BSD),ACT_AST(%ecx)
1066 movl %gs:CPU_KERNEL_STACK,%ebx
1067 xchgl %ebx,%esp /* switch to kernel stack */
1071 CCALL1(mach_call_munger64, %ebx)
1073 * always returns through thread_exception_return
1078 Entry(lo64_mdep_scall)
1079 movl %gs:CPU_ACTIVE_THREAD,%ecx /* get current thread */
1080 movl ACT_TASK(%ecx),%ebx /* point to current task */
1082 /* Check for active vtimers in the current task */
1083 cmpl $0,TASK_VTIMERS(%ebx)
1086 /* Set a pending AST */
1087 orl $(AST_BSD),%gs:CPU_PENDING_AST
1089 /* Set a thread AST (atomic) */
1091 orl $(AST_BSD),ACT_AST(%ecx)
1094 movl %gs:CPU_KERNEL_STACK,%ebx
1095 xchgl %ebx,%esp /* switch to kernel stack */
1099 CCALL1(machdep_syscall64, %ebx)
1101 * always returns through thread_exception_return
1105 Entry(lo64_diag_scall)
1106 movl %gs:CPU_ACTIVE_THREAD,%ecx /* get current thread */
1107 movl ACT_TASK(%ecx),%ebx /* point to current task */
1109 /* Check for active vtimers in the current task */
1110 cmpl $0,TASK_VTIMERS(%ebx)
1113 /* Set a pending AST */
1114 orl $(AST_BSD),%gs:CPU_PENDING_AST
1116 /* Set a thread AST (atomic) */
1118 orl $(AST_BSD),ACT_AST(%ecx)
1121 movl %gs:CPU_KERNEL_STACK,%ebx // Get the address of the kernel stack
1122 xchgl %ebx,%esp // Switch to it, saving the previous
1124 CCALL1(diagCall64, %ebx) // Call diagnostics
1126 cmpl $0,%eax // What kind of return is this?
1128 cli // Disable interruptions just in case they were enabled
1129 popl %esp // Get back the original stack
1130 jmp EXT(return_to_user) // Normal return, do not check asts...
1132 CCALL5(i386_exception, $EXC_SYSCALL, $0x6000, $0, $1, $0)
1133 // pass what would be the diag syscall
1134 // error return - cause an exception
1144 * Copy from user/kernel address space.
1145 * arg0: window offset or kernel address
1146 * arg1: kernel address
1149 Entry(copyinphys_user)
1150 movl $(USER_WINDOW_SEL),%ecx /* user data segment access through kernel window */
1153 Entry(copyinphys_kern)
1154 movl $(PHYS_WINDOW_SEL),%ecx /* physical access through kernel window */
1159 movl $(USER_WINDOW_SEL),%ecx /* user data segment access through kernel window */
1166 pushl %edi /* save registers */
1168 movl 8+S_ARG0,%esi /* get source - window offset or kernel address */
1169 movl 8+S_ARG1,%edi /* get destination - kernel address */
1170 movl 8+S_ARG2,%edx /* get count */
1173 movl %edx,%ecx /* move by longwords first */
1176 RECOVER(copyin_fail)
1178 movsl /* move longwords */
1179 movl %edx,%ecx /* now move remaining bytes */
1182 RECOVER(copyin_fail)
1185 xorl %eax,%eax /* return 0 for success */
1187 mov %ss,%cx /* restore kernel data and extended segments */
1191 popl %edi /* restore registers */
1193 ret /* and return */
1196 movl $(EFAULT),%eax /* return error for failure */
1197 jmp copyin_ret /* pop frame and return */
1202 * Copy string from user/kern address space.
1203 * arg0: window offset or kernel address
1204 * arg1: kernel address
1205 * arg2: max byte count
1206 * arg3: actual byte count (OUT)
1208 Entry(copyinstr_kern)
1210 jmp copyinstr_common
1212 Entry(copyinstr_user)
1213 movl $(USER_WINDOW_SEL),%ecx /* user data segment access through kernel window */
1219 pushl %edi /* save registers */
1221 movl 8+S_ARG0,%esi /* get source - window offset or kernel address */
1222 movl 8+S_ARG1,%edi /* get destination - kernel address */
1223 movl 8+S_ARG2,%edx /* get count */
1225 xorl %eax,%eax /* set to 0 here so that the high 24 bits */
1226 /* are 0 for the cmpl against 0 */
1229 RECOVER(copystr_fail) /* copy bytes... */
1232 testl %edi,%edi /* if kernel address is ... */
1233 jz 3f /* not NULL */
1234 movb %al,(%edi) /* copy the byte */
1237 testl %eax,%eax /* did we just stuff the 0-byte? */
1238 jz 4f /* yes, return 0 status already in %eax */
1239 decl %edx /* decrement #bytes left in buffer */
1240 jnz 2b /* buffer not full so copy in another byte */
1241 movl $(ENAMETOOLONG),%eax /* buffer full but no 0-byte: ENAMETOOLONG */
1243 movl 8+S_ARG3,%edi /* get OUT len ptr */
1245 jz copystr_ret /* if null, just return */
1247 movl %esi,(%edi) /* else set OUT arg to xfer len */
1249 popl %edi /* restore registers */
1251 ret /* and return */
1254 movl $(EFAULT),%eax /* return error for failure */
1255 jmp copystr_ret /* pop frame and return */
1259 * Copy to user/kern address space.
1260 * arg0: kernel address
1261 * arg1: window offset or kernel address
1264 ENTRY(copyoutphys_user)
1265 movl $(USER_WINDOW_SEL),%ecx /* user data segment access through kernel window */
1268 ENTRY(copyoutphys_kern)
1269 movl $(PHYS_WINDOW_SEL),%ecx /* physical access through kernel window */
1274 movl $(USER_WINDOW_SEL),%ecx /* user data segment access through kernel window */
1281 pushl %edi /* save registers */
1283 movl 8+S_ARG0,%esi /* get source - kernel address */
1284 movl 8+S_ARG1,%edi /* get destination - window offset or kernel address */
1285 movl 8+S_ARG2,%edx /* get count */
1288 movl %edx,%ecx /* move by longwords first */
1291 RECOVER(copyout_fail)
1294 movl %edx,%ecx /* now move remaining bytes */
1297 RECOVER(copyout_fail)
1300 xorl %eax,%eax /* return 0 for success */
1302 mov %ss,%cx /* restore kernel segment */
1306 popl %edi /* restore registers */
1308 ret /* and return */
1311 movl $(EFAULT),%eax /* return error for failure */
1312 jmp copyout_ret /* pop frame and return */
1315 * io register must not be used on slaves (no AT bus)
1317 #define ILL_ON_SLAVE
1325 #define PUSH_FRAME FRAME
1326 #define POP_FRAME EMARF
1328 #else /* MACH_ASSERT */
1336 #endif /* MACH_ASSERT */
1339 * void loutb(unsigned byte *io_port,
1340 * unsigned byte *data,
1341 * unsigned int count)
1343 * Output an array of bytes to an IO port.
1349 movl %esi,%eax /* save register */
1350 movl ARG0,%edx /* get io port number */
1351 movl ARG1,%esi /* get data address */
1352 movl ARG2,%ecx /* get count */
1356 movl %eax,%esi /* restore register */
1362 * void loutw(unsigned short *io_port,
1363 * unsigned short *data,
1364 * unsigned int count)
1366 * Output an array of shorts to an IO port.
1372 movl %esi,%eax /* save register */
1373 movl ARG0,%edx /* get io port number */
1374 movl ARG1,%esi /* get data address */
1375 movl ARG2,%ecx /* get count */
1379 movl %eax,%esi /* restore register */
1384 * void loutw(unsigned short io_port,
1385 * unsigned int *data,
1386 * unsigned int count)
1388 * Output an array of longs to an IO port.
1394 movl %esi,%eax /* save register */
1395 movl ARG0,%edx /* get io port number */
1396 movl ARG1,%esi /* get data address */
1397 movl ARG2,%ecx /* get count */
1401 movl %eax,%esi /* restore register */
1407 * void linb(unsigned char *io_port,
1408 * unsigned char *data,
1409 * unsigned int count)
1411 * Input an array of bytes from an IO port.
1417 movl %edi,%eax /* save register */
1418 movl ARG0,%edx /* get io port number */
1419 movl ARG1,%edi /* get data address */
1420 movl ARG2,%ecx /* get count */
1424 movl %eax,%edi /* restore register */
1430 * void linw(unsigned short *io_port,
1431 * unsigned short *data,
1432 * unsigned int count)
1434 * Input an array of shorts from an IO port.
1440 movl %edi,%eax /* save register */
1441 movl ARG0,%edx /* get io port number */
1442 movl ARG1,%edi /* get data address */
1443 movl ARG2,%ecx /* get count */
1447 movl %eax,%edi /* restore register */
1453 * void linl(unsigned short io_port,
1454 * unsigned int *data,
1455 * unsigned int count)
1457 * Input an array of longs from an IO port.
1463 movl %edi,%eax /* save register */
1464 movl ARG0,%edx /* get io port number */
1465 movl ARG1,%edi /* get data address */
1466 movl ARG2,%ecx /* get count */
1470 movl %eax,%edi /* restore register */
1475 * int rdmsr_carefully(uint32_t msr, uint32_t *lo, uint32_t *hi)
1477 ENTRY(rdmsr_carefully)
1494 * Done with recovery table.
1534 lidt null_idtr /* disable the interrupt handler */
1535 xor %ecx,%ecx /* generate a divide by zero */
1536 div %ecx,%eax /* reboot now */
1537 ret /* this will "never" be executed */
1539 #endif /* SYMMETRY */
1543 * setbit(int bitno, int *s) - set bit in bit string
1546 movl S_ARG0, %ecx /* bit number */
1547 movl S_ARG1, %eax /* address */
1548 btsl %ecx, (%eax) /* set bit */
1552 * clrbit(int bitno, int *s) - clear bit in bit string
1555 movl S_ARG0, %ecx /* bit number */
1556 movl S_ARG1, %eax /* address */
1557 btrl %ecx, (%eax) /* clear bit */
1561 * ffsbit(int *s) - find first set bit in bit string
1564 movl S_ARG0, %ecx /* address */
1565 movl $0, %edx /* base offset */
1567 bsfl (%ecx), %eax /* check argument bits */
1568 jnz 1f /* found bit, return */
1569 addl $4, %ecx /* increment address */
1570 addl $32, %edx /* increment offset */
1571 jmp 0b /* try again */
1573 addl %edx, %eax /* return offset */
1577 * testbit(int nr, volatile void *array)
1579 * Test to see if the bit is set within the bit string
1583 movl S_ARG0,%eax /* Get the bit to test */
1584 movl S_ARG1,%ecx /* get the array string */
1597 * jail: set the EIP to "jail" to block a kernel thread.
1598 * Useful to debug synchronization problems on MPs.
1605 * div_scale(unsigned int dividend,
1606 * unsigned int divisor,
1607 * unsigned int *scale)
1609 * This function returns (dividend << *scale) //divisor where *scale
1610 * is the largest possible value before overflow. This is used in
1611 * computation where precision must be achieved in order to avoid
1612 * floating point usage.
1616 * while (((dividend >> *scale) >= divisor))
1618 * *scale = 32 - *scale;
1619 * return ((dividend << *scale) / divisor);
1623 xorl %ecx, %ecx /* *scale = 0 */
1625 movl ARG0, %edx /* get dividend */
1627 cmpl ARG1, %edx /* if (divisor > dividend) */
1628 jle 1f /* goto 1f */
1629 addl $1, %ecx /* (*scale)++ */
1630 shrdl $1, %edx, %eax /* dividend >> 1 */
1631 shrl $1, %edx /* dividend >> 1 */
1632 jmp 0b /* goto 0b */
1634 divl ARG1 /* (dividend << (32 - *scale)) / divisor */
1635 movl ARG2, %edx /* get scale */
1636 movl $32, (%edx) /* *scale = 32 */
1637 subl %ecx, (%edx) /* *scale -= %ecx */
1643 * mul_scale(unsigned int multiplicand,
1644 * unsigned int multiplier,
1645 * unsigned int *scale)
1647 * This function returns ((multiplicand * multiplier) >> *scale) where
1648 * scale is the largest possible value before overflow. This is used in
1649 * computation where precision must be achieved in order to avoid
1650 * floating point usage.
1654 * while (overflow((multiplicand * multiplier) >> *scale))
1656 * return ((multiplicand * multiplier) >> *scale);
1660 xorl %ecx, %ecx /* *scale = 0 */
1661 movl ARG0, %eax /* get multiplicand */
1662 mull ARG1 /* multiplicand * multiplier */
1664 cmpl $0, %edx /* if (!overflow()) */
1666 addl $1, %ecx /* (*scale)++ */
1667 shrdl $1, %edx, %eax /* (multiplicand * multiplier) >> 1 */
1668 shrl $1, %edx /* (multiplicand * multiplier) >> 1 */
1671 movl ARG2, %edx /* get scale */
1672 movl %ecx, (%edx) /* set *scale */
1679 * Double-fault exception handler task. The last gasp...
1681 Entry(df_task_start)
1682 CCALL1(panic_double_fault32, $(T_DOUBLE_FAULT))
1687 * machine-check handler task. The last gasp...
1689 Entry(mc_task_start)
1690 CCALL1(panic_machine_check32, $(T_MACHINE_CHECK))
1694 * Compatibility mode's last gasp...
1698 CCALL1(panic_double_fault64, %eax)
1703 CCALL1(panic_machine_check64, %eax)