/* * from: vector.s, 386BSD 0.1 unknown origin * $Id: apic_vector.s,v 1.24 1997/12/08 22:59:34 fsmp Exp $ */ #include #include #include "i386/isa/intr_machdep.h" #ifdef FAST_SIMPLELOCK #define GET_FAST_INTR_LOCK \ pushl $_fast_intr_lock ; /* address of lock */ \ call _s_lock ; /* MP-safe */ \ addl $4,%esp #define REL_FAST_INTR_LOCK \ pushl $_fast_intr_lock ; /* address of lock */ \ call _s_unlock ; /* MP-safe */ \ addl $4,%esp #else /* FAST_SIMPLELOCK */ #define GET_FAST_INTR_LOCK \ call _get_isrlock #define REL_FAST_INTR_LOCK \ pushl $_mp_lock ; /* GIANT_LOCK */ \ call _MPrellock ; \ add $4, %esp #endif /* FAST_SIMPLELOCK */ /* convert an absolute IRQ# into a bitmask */ #define IRQ_BIT(irq_num) (1 << (irq_num)) /* make an index into the IO APIC from the IRQ# */ #define REDTBL_IDX(irq_num) (0x10 + ((irq_num) * 2)) /* * Macros for interrupt interrupt entry, call to handler, and exit. */ #ifdef FAST_WITHOUTCPL /* */ #define FAST_INTR(irq_num, vec_name) \ .text ; \ SUPERALIGN_TEXT ; \ IDTVEC(vec_name) ; \ pushl %eax ; /* save only call-used registers */ \ pushl %ecx ; \ pushl %edx ; \ pushl %ds ; \ MAYBE_PUSHL_ES ; \ movl $KDSEL,%eax ; \ movl %ax,%ds ; \ MAYBE_MOVW_AX_ES ; \ FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \ pushl _intr_unit + (irq_num) * 4 ; \ GET_FAST_INTR_LOCK ; \ call *_intr_handler + (irq_num) * 4 ; /* do the work ASAP */ \ REL_FAST_INTR_LOCK ; \ addl $4, %esp ; \ movl $0, lapic_eoi ; \ lock ; \ incl _cnt+V_INTR ; /* book-keeping can wait */ \ movl _intr_countp + (irq_num) * 4, %eax ; \ lock ; \ incl (%eax) ; \ MEXITCOUNT ; \ MAYBE_POPL_ES ; \ popl %ds ; \ popl %edx ; \ popl %ecx ; \ popl %eax ; \ iret #else /* FAST_WITHOUTCPL */ #define FAST_INTR(irq_num, vec_name) \ .text ; \ SUPERALIGN_TEXT ; \ IDTVEC(vec_name) ; \ pushl %eax ; /* save only call-used registers */ \ pushl %ecx ; \ pushl %edx ; \ pushl %ds ; \ MAYBE_PUSHL_ES ; \ movl $KDSEL, %eax ; \ movl %ax, %ds ; \ MAYBE_MOVW_AX_ES ; \ FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \ GET_FAST_INTR_LOCK ; \ pushl _intr_unit + (irq_num) * 4 ; \ call *_intr_handler + (irq_num) * 4 ; /* do the work ASAP */ \ addl $4, %esp ; \ movl $0, lapic_eoi ; \ lock ; \ incl _cnt+V_INTR ; /* book-keeping can wait */ \ movl _intr_countp + (irq_num) * 4,%eax ; \ lock ; \ incl (%eax) ; \ movl _cpl, %eax ; /* unmasking pending HWIs or SWIs? */ \ notl %eax ; \ andl _ipending, %eax ; \ jne 2f ; /* yes, maybe handle them */ \ 1: ; \ MEXITCOUNT ; \ REL_FAST_INTR_LOCK ; \ MAYBE_POPL_ES ; \ popl %ds ; \ popl %edx ; \ popl %ecx ; \ popl %eax ; \ iret ; \ ; \ ALIGN_TEXT ; \ 2: ; \ cmpb $3, _intr_nesting_level ; /* enough stack? */ \ jae 1b ; /* no, return */ \ movl _cpl, %eax ; \ /* XXX next line is probably unnecessary now. */ \ movl $HWI_MASK|SWI_MASK, _cpl ; /* limit nesting ... */ \ lock ; \ incb _intr_nesting_level ; /* ... really limit it ... */ \ sti ; /* to do this as early as possible */ \ MAYBE_POPL_ES ; /* discard most of thin frame ... */ \ popl %ecx ; /* ... original %ds ... */ \ popl %edx ; \ xchgl %eax, 4(%esp) ; /* orig %eax; save cpl */ \ pushal ; /* build fat frame (grrr) ... */ \ pushl %ecx ; /* ... actually %ds ... */ \ pushl %es ; \ movl $KDSEL, %eax ; \ movl %ax, %es ; \ movl (2+8+0)*4(%esp), %ecx ; /* %ecx from thin frame ... */ \ movl %ecx, (2+6)*4(%esp) ; /* ... to fat frame ... */ \ movl (2+8+1)*4(%esp), %eax ; /* ... cpl from thin frame */ \ pushl %eax ; \ subl $4, %esp ; /* junk for unit number */ \ MEXITCOUNT ; \ jmp _doreti #endif /** FAST_WITHOUTCPL */ /* * */ #define PUSH_FRAME \ pushl $0 ; /* dummy error code */ \ pushl $0 ; /* dummy trap type */ \ pushal ; \ pushl %ds ; /* save data and extra segments ... */ \ pushl %es #define POP_FRAME \ popl %es ; \ popl %ds ; \ popal ; \ addl $4+4,%esp /* * Test to see whether we are handling an edge or level triggered INT. * Level-triggered INTs must still be masked as we don't clear the source, * and the EOI cycle would cause redundant INTs to occur. */ #define MASK_LEVEL_IRQ(irq_num) \ testl $IRQ_BIT(irq_num), _apic_pin_trigger ; \ jz 8f ; /* edge, don't mask */ \ IMASK_LOCK ; /* into critical reg */ \ orl $IRQ_BIT(irq_num), _apic_imen ; /* set the mask bit */ \ movl _ioapic, %ecx ; /* ioapic[0] addr */ \ movl $REDTBL_IDX(irq_num), (%ecx) ; /* write the index */ \ movl IOAPIC_WINDOW(%ecx), %eax ; /* current value */ \ orl $IOART_INTMASK, %eax ; /* set the mask */ \ movl %eax, IOAPIC_WINDOW(%ecx) ; /* new value */ \ IMASK_UNLOCK ; \ 8: /* * Test to see if the source is currntly masked, clear if so. */ #define UNMASK_IRQ(irq_num) \ IMASK_LOCK ; /* into critical reg */ \ testl $IRQ_BIT(irq_num), _apic_imen ; \ jne 7f ; /* bit set, masked */ \ testl $IRQ_BIT(irq_num), _apic_pin_trigger ; \ jz 9f ; /* edge, don't EOI */ \ movl $0, lapic_eoi ; /* should be safe */ \ jmp 9f ; /* skip unmasking */ \ 7: \ andl $~IRQ_BIT(irq_num), _apic_imen ;/* clear mask bit */ \ movl _ioapic,%ecx ; /* ioapic[0]addr */ \ movl $REDTBL_IDX(irq_num),(%ecx) ; /* write the index */ \ movl IOAPIC_WINDOW(%ecx),%eax ; /* current value */ \ andl $~IOART_INTMASK,%eax ; /* clear the mask */ \ movl %eax,IOAPIC_WINDOW(%ecx) ; /* new value */ \ 9: ; \ IMASK_UNLOCK #ifdef INTR_SIMPLELOCK #define ENLOCK #define DELOCK #define LATELOCK call _get_isrlock #else #define ENLOCK \ ISR_TRYLOCK ; /* XXX this is going away... */ \ testl %eax, %eax ; /* did we get it? */ \ jz 1f #define DELOCK ISR_RELLOCK #define LATELOCK #endif #ifdef CPL_AND_CML #define INTR(irq_num, vec_name) \ .text ; \ SUPERALIGN_TEXT ; \ /* _XintrNN: entry point used by IDT/HWIs & splz_unpend via _vec[]. */ \ IDTVEC(vec_name) ; \ PUSH_FRAME ; \ movl $KDSEL, %eax ; /* reload with kernel's data segment */ \ movl %ax, %ds ; \ movl %ax, %es ; \ ; \ lock ; /* MP-safe */ \ btsl $(irq_num), iactive ; /* lazy masking */ \ jc 1f ; /* already active */ \ ; \ ENLOCK ; \ ; \ AVCPL_LOCK ; /* MP-safe */ \ testl $IRQ_BIT(irq_num), _cpl ; \ jne 2f ; /* this INT masked */ \ testl $IRQ_BIT(irq_num), _cml ; \ jne 2f ; /* this INT masked */ \ orl $IRQ_BIT(irq_num), _cil ; \ AVCPL_UNLOCK ; \ ; \ ;;; movl $0, lapic_eoi ; /* XXX too soon? */ \ incb _intr_nesting_level ; \ ; \ /* entry point used by doreti_unpend for HWIs. */ \ __CONCAT(Xresume,irq_num): ; \ FAKE_MCOUNT(12*4(%esp)) ; /* XXX avoid dbl cnt */ \ lock ; incl _cnt+V_INTR ; /* tally interrupts */ \ movl _intr_countp + (irq_num) * 4, %eax ; \ lock ; incl (%eax) ; \ ; \ AVCPL_LOCK ; /* MP-safe */ \ movl _cml, %eax ; \ pushl %eax ; \ orl _intr_mask + (irq_num) * 4, %eax ; \ movl %eax, _cml ; \ AVCPL_UNLOCK ; \ ; \ pushl _intr_unit + (irq_num) * 4 ; \ incl _inside_intr ; \ sti ; \ call *_intr_handler + (irq_num) * 4 ; \ cli ; \ decl _inside_intr ; \ ; \ lock ; andl $~IRQ_BIT(irq_num), iactive ; \ lock ; andl $~IRQ_BIT(irq_num), _cil ; \ UNMASK_IRQ(irq_num) ; \ sti ; /* doreti repeats cli/sti */ \ MEXITCOUNT ; \ LATELOCK ; \ jmp _doreti ; \ ; \ ALIGN_TEXT ; \ 1: ; /* active or locked */ \ MASK_LEVEL_IRQ(irq_num) ; \ movl $0, lapic_eoi ; /* do the EOI */ \ ; \ AVCPL_LOCK ; /* MP-safe */ \ orl $IRQ_BIT(irq_num), _ipending ; \ AVCPL_UNLOCK ; \ ; \ POP_FRAME ; \ iret ; \ ; \ ALIGN_TEXT ; \ 2: ; /* masked by cpl|cml */ \ AVCPL_UNLOCK ; \ DELOCK ; /* XXX this is going away... */ \ jmp 1b #else /* CPL_AND_CML */ #define INTR(irq_num, vec_name) \ .text ; \ SUPERALIGN_TEXT ; \ /* _XintrNN: entry point used by IDT/HWIs & splz_unpend via _vec[]. */ \ IDTVEC(vec_name) ; \ PUSH_FRAME ; \ movl $KDSEL, %eax ; /* reload with kernel's data segment */ \ movl %ax, %ds ; \ movl %ax, %es ; \ ; \ lock ; /* MP-safe */ \ btsl $(irq_num), iactive ; /* lazy masking */ \ jc 1f ; /* already active */ \ ; \ ISR_TRYLOCK ; /* XXX this is going away... */ \ testl %eax, %eax ; /* did we get it? */ \ jz 1f ; /* no */ \ ; \ AVCPL_LOCK ; /* MP-safe */ \ testl $IRQ_BIT(irq_num), _cpl ; \ jne 2f ; /* this INT masked */ \ AVCPL_UNLOCK ; \ ; \ ;;; movl $0, lapic_eoi ; /* XXX too soon? */ \ incb _intr_nesting_level ; \ ; \ /* entry point used by doreti_unpend for HWIs. */ \ __CONCAT(Xresume,irq_num): ; \ FAKE_MCOUNT(12*4(%esp)) ; /* XXX avoid dbl cnt */ \ lock ; incl _cnt+V_INTR ; /* tally interrupts */ \ movl _intr_countp + (irq_num) * 4, %eax ; \ lock ; incl (%eax) ; \ ; \ AVCPL_LOCK ; /* MP-safe */ \ movl _cpl, %eax ; \ pushl %eax ; \ orl _intr_mask + (irq_num) * 4, %eax ; \ movl %eax, _cpl ; \ AVCPL_UNLOCK ; \ ; \ pushl _intr_unit + (irq_num) * 4 ; \ sti ; \ call *_intr_handler + (irq_num) * 4 ; \ cli ; \ ; \ lock ; andl $~IRQ_BIT(irq_num), iactive ; \ UNMASK_IRQ(irq_num) ; \ sti ; /* doreti repeats cli/sti */ \ MEXITCOUNT ; \ jmp _doreti ; \ ; \ ALIGN_TEXT ; \ 1: ; /* active or locked */ \ MASK_LEVEL_IRQ(irq_num) ; \ movl $0, lapic_eoi ; /* do the EOI */ \ ; \ AVCPL_LOCK ; /* MP-safe */ \ orl $IRQ_BIT(irq_num), _ipending ; \ AVCPL_UNLOCK ; \ ; \ POP_FRAME ; \ iret ; \ ; \ ALIGN_TEXT ; \ 2: ; /* masked by cpl */ \ AVCPL_UNLOCK ; \ ISR_RELLOCK ; /* XXX this is going away... */ \ jmp 1b #endif /* CPL_AND_CML */ /* * Handle "spurious INTerrupts". * Notes: * This is different than the "spurious INTerrupt" generated by an * 8259 PIC for missing INTs. See the APIC documentation for details. * This routine should NOT do an 'EOI' cycle. */ .text SUPERALIGN_TEXT .globl _Xspuriousint _Xspuriousint: /* No EOI cycle used here */ iret /* * Handle TLB shootdowns. */ .text SUPERALIGN_TEXT .globl _Xinvltlb _Xinvltlb: pushl %eax #ifdef COUNT_XINVLTLB_HITS ss movl _cpuid, %eax ss incl _xhits(,%eax,4) #endif /* COUNT_XINVLTLB_HITS */ movl %cr3, %eax /* invalidate the TLB */ movl %eax, %cr3 ss /* stack segment, avoid %ds load */ movl $0, lapic_eoi /* End Of Interrupt to APIC */ popl %eax iret #ifdef BETTER_CLOCK /* * Executed by a CPU when it receives an Xcpucheckstate IPI from another CPU, * * - Stores current cpu state in checkstate_cpustate[cpuid] * 0 == user, 1 == sys, 2 == intr * - Stores current process in checkstate_curproc[cpuid] * * - Signals its receipt by setting bit cpuid in checkstate_probed_cpus. * * stack: 0 -> ds, 4 -> ebx, 8 -> eax, 12 -> eip, 16 -> cs, 20 -> eflags */ .text SUPERALIGN_TEXT .globl _Xcpucheckstate .globl _checkstate_cpustate .globl _checkstate_curproc .globl _checkstate_pc _Xcpucheckstate: pushl %eax pushl %ebx pushl %ds /* save current data segment */ movl $KDSEL, %eax movl %ax, %ds /* use KERNEL data segment */ movl $0, lapic_eoi /* End Of Interrupt to APIC */ movl $0, %ebx movl 16(%esp), %eax andl $3, %eax cmpl $3, %eax je 1f #ifdef VM86 testl $PSL_VM, 20(%esp) jne 1f #endif incl %ebx /* system or interrupt */ #ifdef CPL_AND_CML cmpl $0, _inside_intr je 1f incl %ebx /* interrupt */ #endif 1: movl _cpuid, %eax movl %ebx, _checkstate_cpustate(,%eax,4) movl _curproc, %ebx movl %ebx, _checkstate_curproc(,%eax,4) movl 12(%esp), %ebx movl %ebx, _checkstate_pc(,%eax,4) lock /* checkstate_probed_cpus |= (1<