New APIC support code:
- The apic interrupt entry points have been rewritten so that each entry point can serve 32 different vectors. When the entry is executed, it uses one of the 32-bit ISR registers to determine which vector in its assigned range was triggered. Thus, the apic code can support 159 different interrupt vectors with only 5 entry points. - We now always to disable the local APIC to work around an errata in certain PPros and then re-enable it again if we decide to use the APICs to route interrupts. - We no longer map IO APICs or local APICs using special page table entries. Instead, we just use pmap_mapdev(). We also no longer export the virtual address of the local APIC as a global symbol to the rest of the system, but only in local_apic.c. To aid this, the APIC ID of each CPU is exported as a per-CPU variable. - Interrupt sources are provided for each intpin on each IO APIC. Currently, each source is given a unique interrupt vector meaning that PCI interrupts are not shared on most machines with an I/O APIC. That mapping for interrupt sources to interrupt vectors is up to the APIC enumerator driver however. - We no longer probe to see if we need to use mixed mode to route IRQ 0, instead we always use mixed mode to route IRQ 0 for now. This can be disabled via the 'NO_MIXED_MODE' kernel option. - The npx(4) driver now always probes to see if a built-in FPU is present since this test can now be performed with the new APIC code. However, an SMP kernel will panic if there is more than one CPU and a built-in FPU is not found. - PCI interrupts are now properly routed when using APICs to route interrupts, so remove the hack to psuedo-route interrupts when the intpin register was read. - The apic.h header was moved to apicreg.h and a new apicvar.h header that declares the APIs used by the new APIC code was added.
This commit is contained in:
parent
223e573bbd
commit
6f92bdd0c1
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=121986
@ -1,19 +1,53 @@
|
||||
/*
|
||||
/*-
|
||||
* Copyright (c) 1989, 1990 William F. Jolitz.
|
||||
* Copyright (c) 1990 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* from: vector.s, 386BSD 0.1 unknown origin
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <machine/apic.h>
|
||||
#include <machine/smp.h>
|
||||
/*
|
||||
* Interrupt entry points for external interrupts triggered by I/O APICs
|
||||
* as well as IPI handlers.
|
||||
*/
|
||||
|
||||
/* convert an absolute IRQ# into a bitmask */
|
||||
#define IRQ_BIT(irq_num) (1 << (irq_num))
|
||||
#include <machine/asmacros.h>
|
||||
#include <machine/apicreg.h>
|
||||
#include <machine/smptests.h>
|
||||
|
||||
/* make an index into the IO APIC from the IRQ# */
|
||||
#define REDTBL_IDX(irq_num) (0x10 + ((irq_num) * 2))
|
||||
#include "assym.s"
|
||||
|
||||
/*
|
||||
*
|
||||
* Macros to create and destroy a trap frame.
|
||||
*/
|
||||
#define PUSH_FRAME \
|
||||
pushl $0 ; /* dummy error code */ \
|
||||
@ -23,14 +57,6 @@
|
||||
pushl %es ; \
|
||||
pushl %fs
|
||||
|
||||
#define PUSH_DUMMY \
|
||||
pushfl ; /* eflags */ \
|
||||
pushl %cs ; /* cs */ \
|
||||
pushl 12(%esp) ; /* original caller eip */ \
|
||||
pushl $0 ; /* dummy error code */ \
|
||||
pushl $0 ; /* dummy trap type */ \
|
||||
subl $11*4,%esp ;
|
||||
|
||||
#define POP_FRAME \
|
||||
popl %fs ; \
|
||||
popl %es ; \
|
||||
@ -38,209 +64,40 @@
|
||||
popal ; \
|
||||
addl $4+4,%esp
|
||||
|
||||
#define POP_DUMMY \
|
||||
addl $16*4,%esp
|
||||
|
||||
#define IOAPICADDR(irq_num) CNAME(int_to_apicintpin) + 16 * (irq_num) + 8
|
||||
#define REDIRIDX(irq_num) CNAME(int_to_apicintpin) + 16 * (irq_num) + 12
|
||||
|
||||
#define MASK_IRQ(irq_num) \
|
||||
ICU_LOCK ; /* into critical reg */ \
|
||||
testl $IRQ_BIT(irq_num), apic_imen ; \
|
||||
jne 7f ; /* masked, don't mask */ \
|
||||
orl $IRQ_BIT(irq_num), apic_imen ; /* set the mask bit */ \
|
||||
movl IOAPICADDR(irq_num), %ecx ; /* ioapic addr */ \
|
||||
movl REDIRIDX(irq_num), %eax ; /* get the index */ \
|
||||
movl %eax, (%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 */ \
|
||||
7: ; /* already masked */ \
|
||||
ICU_UNLOCK
|
||||
/*
|
||||
* 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.
|
||||
* I/O Interrupt Entry Point. Rather than having one entry point for
|
||||
* each interrupt source, we use one entry point for each 32-bit word
|
||||
* in the ISR. The handler determines the highest bit set in the ISR,
|
||||
* translates that into a vector, and passes the vector to the
|
||||
* lapic_handle_intr() function.
|
||||
*/
|
||||
#define MASK_LEVEL_IRQ(irq_num) \
|
||||
testl $IRQ_BIT(irq_num), apic_pin_trigger ; \
|
||||
jz 9f ; /* edge, don't mask */ \
|
||||
MASK_IRQ(irq_num) ; \
|
||||
9:
|
||||
|
||||
|
||||
#ifdef APIC_INTR_REORDER
|
||||
#define EOI_IRQ(irq_num) \
|
||||
movl apic_isrbit_location + 8 * (irq_num), %eax ; \
|
||||
movl (%eax), %eax ; \
|
||||
testl apic_isrbit_location + 4 + 8 * (irq_num), %eax ; \
|
||||
jz 9f ; /* not active */ \
|
||||
movl $0, lapic+LA_EOI ; \
|
||||
9:
|
||||
|
||||
#else
|
||||
#define EOI_IRQ(irq_num) \
|
||||
testl $IRQ_BIT(irq_num), lapic+LA_ISR1; \
|
||||
jz 9f ; /* not active */ \
|
||||
movl $0, lapic+LA_EOI; \
|
||||
9:
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Test to see if the source is currently masked, clear if so.
|
||||
*/
|
||||
#define UNMASK_IRQ(irq_num) \
|
||||
ICU_LOCK ; /* into critical reg */ \
|
||||
testl $IRQ_BIT(irq_num), apic_imen ; \
|
||||
je 7f ; /* bit clear, not masked */ \
|
||||
andl $~IRQ_BIT(irq_num), apic_imen ;/* clear mask bit */ \
|
||||
movl IOAPICADDR(irq_num), %ecx ; /* ioapic addr */ \
|
||||
movl REDIRIDX(irq_num), %eax ; /* get the index */ \
|
||||
movl %eax, (%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 */ \
|
||||
7: ; /* already unmasked */ \
|
||||
ICU_UNLOCK
|
||||
|
||||
/*
|
||||
* Test to see whether we are handling an edge or level triggered INT.
|
||||
* Level-triggered INTs have to be unmasked.
|
||||
*/
|
||||
#define UNMASK_LEVEL_IRQ(irq_num) \
|
||||
testl $IRQ_BIT(irq_num), apic_pin_trigger ; \
|
||||
jz 9f ; /* edge, don't unmask */ \
|
||||
UNMASK_IRQ(irq_num) ; \
|
||||
9:
|
||||
|
||||
/*
|
||||
* Macros for interrupt entry, call to handler, and exit.
|
||||
*/
|
||||
|
||||
#define FAST_INTR(irq_num, vec_name) \
|
||||
#define ISR_VEC(index, vec_name) \
|
||||
.text ; \
|
||||
SUPERALIGN_TEXT ; \
|
||||
IDTVEC(vec_name) ; \
|
||||
PUSH_FRAME ; \
|
||||
movl $KDSEL,%eax ; \
|
||||
mov %ax,%ds ; \
|
||||
mov %ax,%es ; \
|
||||
movl $KPSEL,%eax ; \
|
||||
mov %ax,%fs ; \
|
||||
FAKE_MCOUNT(13*4(%esp)) ; \
|
||||
movl PCPU(CURTHREAD),%ebx ; \
|
||||
cmpl $0,TD_CRITNEST(%ebx) ; \
|
||||
je 1f ; \
|
||||
; \
|
||||
movl $1,PCPU(INT_PENDING) ; \
|
||||
orl $IRQ_BIT(irq_num),PCPU(FPENDING) ; \
|
||||
MASK_LEVEL_IRQ(irq_num) ; \
|
||||
movl $0, lapic+LA_EOI ; \
|
||||
jmp 10f ; \
|
||||
1: ; \
|
||||
incl TD_CRITNEST(%ebx) ; \
|
||||
incl TD_INTR_NESTING_LEVEL(%ebx) ; \
|
||||
pushl intr_unit + (irq_num) * 4 ; \
|
||||
call *intr_handler + (irq_num) * 4 ; /* do the work ASAP */ \
|
||||
addl $4, %esp ; \
|
||||
movl $0, lapic+LA_EOI ; \
|
||||
lock ; \
|
||||
incl cnt+V_INTR ; /* book-keeping can wait */ \
|
||||
movl intr_countp + (irq_num) * 4, %eax ; \
|
||||
lock ; \
|
||||
incl (%eax) ; \
|
||||
decl TD_CRITNEST(%ebx) ; \
|
||||
cmpl $0,PCPU(INT_PENDING) ; \
|
||||
je 2f ; \
|
||||
; \
|
||||
call i386_unpend ; \
|
||||
2: ; \
|
||||
decl TD_INTR_NESTING_LEVEL(%ebx) ; \
|
||||
10: ; \
|
||||
MEXITCOUNT ; \
|
||||
jmp doreti
|
||||
|
||||
/*
|
||||
* Restart a fast interrupt that was held up by a critical section.
|
||||
* This routine is called from unpend(). unpend() ensures we are
|
||||
* in a critical section and deals with the interrupt nesting level
|
||||
* for us. If we previously masked the irq, we have to unmask it.
|
||||
*
|
||||
* We have a choice. We can regenerate the irq using the 'int'
|
||||
* instruction or we can create a dummy frame and call the interrupt
|
||||
* handler directly. I've chosen to use the dummy-frame method.
|
||||
*/
|
||||
#define FAST_UNPEND(irq_num, vec_name) \
|
||||
.text ; \
|
||||
SUPERALIGN_TEXT ; \
|
||||
IDTVEC(vec_name) ; \
|
||||
; \
|
||||
pushl %ebp ; \
|
||||
movl %esp, %ebp ; \
|
||||
PUSH_DUMMY ; \
|
||||
pushl intr_unit + (irq_num) * 4 ; \
|
||||
call *intr_handler + (irq_num) * 4 ; /* do the work ASAP */ \
|
||||
addl $4, %esp ; \
|
||||
lock ; \
|
||||
incl cnt+V_INTR ; /* book-keeping can wait */ \
|
||||
movl intr_countp + (irq_num) * 4, %eax ; \
|
||||
lock ; \
|
||||
incl (%eax) ; \
|
||||
UNMASK_LEVEL_IRQ(irq_num) ; \
|
||||
POP_DUMMY ; \
|
||||
popl %ebp ; \
|
||||
ret ; \
|
||||
|
||||
|
||||
/*
|
||||
* Slow, threaded interrupts.
|
||||
*
|
||||
* XXX Most of the parameters here are obsolete. Fix this when we're
|
||||
* done.
|
||||
* XXX we really shouldn't return via doreti if we just schedule the
|
||||
* interrupt handler and don't run anything. We could just do an
|
||||
* iret. FIXME.
|
||||
*/
|
||||
#define INTR(irq_num, vec_name, maybe_extra_ipending) \
|
||||
.text ; \
|
||||
SUPERALIGN_TEXT ; \
|
||||
/* _XintrNN: entry point used by IDT/HWIs via _vec[]. */ \
|
||||
IDTVEC(vec_name) ; \
|
||||
PUSH_FRAME ; \
|
||||
movl $KDSEL, %eax ; /* reload with kernel's data segment */ \
|
||||
mov %ax, %ds ; \
|
||||
mov %ax, %es ; \
|
||||
movl $KPSEL, %eax ; \
|
||||
movl $KPSEL, %eax ; /* reload with per-CPU data segment */ \
|
||||
mov %ax, %fs ; \
|
||||
; \
|
||||
maybe_extra_ipending ; \
|
||||
; \
|
||||
MASK_LEVEL_IRQ(irq_num) ; \
|
||||
EOI_IRQ(irq_num) ; \
|
||||
; \
|
||||
movl PCPU(CURTHREAD),%ebx ; \
|
||||
cmpl $0,TD_CRITNEST(%ebx) ; \
|
||||
je 1f ; \
|
||||
movl $1,PCPU(INT_PENDING) ; \
|
||||
orl $IRQ_BIT(irq_num),PCPU(IPENDING) ; \
|
||||
jmp 10f ; \
|
||||
1: ; \
|
||||
movl lapic, %edx ; /* pointer to local APIC */ \
|
||||
movl PCPU(CURTHREAD), %ebx ; \
|
||||
movl LA_ISR + 16 * (index)(%edx), %eax ; /* load ISR */ \
|
||||
incl TD_INTR_NESTING_LEVEL(%ebx) ; \
|
||||
; \
|
||||
FAKE_MCOUNT(13*4(%esp)) ; /* XXX avoid dbl cnt */ \
|
||||
cmpl $0,PCPU(INT_PENDING) ; \
|
||||
je 9f ; \
|
||||
call i386_unpend ; \
|
||||
9: ; \
|
||||
pushl $irq_num; /* pass the IRQ */ \
|
||||
call sched_ithd ; \
|
||||
addl $4, %esp ; /* discard the parameter */ \
|
||||
; \
|
||||
bsrl %eax, %eax ; /* index of highset set bit in ISR */ \
|
||||
jz 2f ; \
|
||||
addl $(32 * index),%eax ; \
|
||||
1: ; \
|
||||
FAKE_MCOUNT(13*4(%esp)) ; /* XXX avoid double count */ \
|
||||
pushl %eax ; /* pass the IRQ */ \
|
||||
call lapic_handle_intr ; \
|
||||
addl $4, %esp ; /* discard parameter */ \
|
||||
decl TD_INTR_NESTING_LEVEL(%ebx) ; \
|
||||
10: ; \
|
||||
MEXITCOUNT ; \
|
||||
jmp doreti
|
||||
jmp doreti ; \
|
||||
2: movl $-1, %eax ; /* send a vector of -1 */ \
|
||||
jmp 1b
|
||||
|
||||
/*
|
||||
* Handle "spurious INTerrupts".
|
||||
@ -257,6 +114,14 @@ IDTVEC(spuriousint)
|
||||
|
||||
iret
|
||||
|
||||
MCOUNT_LABEL(bintr2)
|
||||
ISR_VEC(1,apic_isr1)
|
||||
ISR_VEC(2,apic_isr2)
|
||||
ISR_VEC(3,apic_isr3)
|
||||
ISR_VEC(4,apic_isr4)
|
||||
ISR_VEC(5,apic_isr5)
|
||||
MCOUNT_LABEL(eintr2)
|
||||
|
||||
#ifdef SMP
|
||||
/*
|
||||
* Global address space TLB shootdown.
|
||||
@ -281,7 +146,8 @@ IDTVEC(invltlb)
|
||||
movl %cr3, %eax /* invalidate the TLB */
|
||||
movl %eax, %cr3
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
|
||||
lock
|
||||
incl smp_tlb_wait
|
||||
@ -313,7 +179,8 @@ IDTVEC(invlpg)
|
||||
movl smp_tlb_addr1, %eax
|
||||
invlpg (%eax) /* invalidate single page */
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
|
||||
lock
|
||||
incl smp_tlb_wait
|
||||
@ -350,7 +217,8 @@ IDTVEC(invlrng)
|
||||
cmpl %eax, %edx
|
||||
jb 1b
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
|
||||
lock
|
||||
incl smp_tlb_wait
|
||||
@ -374,21 +242,15 @@ IDTVEC(hardclock)
|
||||
movl $KPSEL, %eax
|
||||
mov %ax, %fs
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %edx
|
||||
movl $0, LA_EOI(%edx) /* End Of Interrupt to APIC */
|
||||
|
||||
movl PCPU(CURTHREAD),%ebx
|
||||
cmpl $0,TD_CRITNEST(%ebx)
|
||||
je 1f
|
||||
movl $1,PCPU(INT_PENDING)
|
||||
orl $1,PCPU(SPENDING);
|
||||
jmp 10f
|
||||
1:
|
||||
incl TD_INTR_NESTING_LEVEL(%ebx)
|
||||
pushl $0 /* XXX convert trapframe to clockframe */
|
||||
call forwarded_hardclock
|
||||
addl $4, %esp /* XXX convert clockframe to trapframe */
|
||||
decl TD_INTR_NESTING_LEVEL(%ebx)
|
||||
10:
|
||||
MEXITCOUNT
|
||||
jmp doreti
|
||||
|
||||
@ -406,23 +268,17 @@ IDTVEC(statclock)
|
||||
movl $KPSEL, %eax
|
||||
mov %ax, %fs
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %edx
|
||||
movl $0, LA_EOI(%edx) /* End Of Interrupt to APIC */
|
||||
|
||||
FAKE_MCOUNT(13*4(%esp))
|
||||
|
||||
movl PCPU(CURTHREAD),%ebx
|
||||
cmpl $0,TD_CRITNEST(%ebx)
|
||||
je 1f
|
||||
movl $1,PCPU(INT_PENDING)
|
||||
orl $2,PCPU(SPENDING);
|
||||
jmp 10f
|
||||
1:
|
||||
incl TD_INTR_NESTING_LEVEL(%ebx)
|
||||
pushl $0 /* XXX convert trapframe to clockframe */
|
||||
call forwarded_statclock
|
||||
addl $4, %esp /* XXX convert clockframe to trapframe */
|
||||
decl TD_INTR_NESTING_LEVEL(%ebx)
|
||||
10:
|
||||
MEXITCOUNT
|
||||
jmp doreti
|
||||
|
||||
@ -444,7 +300,8 @@ IDTVEC(cpuast)
|
||||
movl $KPSEL, %eax
|
||||
mov %ax, %fs
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %edx
|
||||
movl $0, LA_EOI(%edx) /* End Of Interrupt to APIC */
|
||||
|
||||
FAKE_MCOUNT(13*4(%esp))
|
||||
|
||||
@ -476,7 +333,8 @@ IDTVEC(cpustop)
|
||||
movl $KPSEL, %eax
|
||||
mov %ax, %fs
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
|
||||
movl PCPU(CPUID), %eax
|
||||
imull $PCB_SIZE, %eax
|
||||
@ -518,111 +376,6 @@ IDTVEC(cpustop)
|
||||
popl %ebp
|
||||
iret
|
||||
|
||||
#endif /* SMP */
|
||||
|
||||
MCOUNT_LABEL(bintr)
|
||||
FAST_INTR(0,fastintr0)
|
||||
FAST_INTR(1,fastintr1)
|
||||
FAST_INTR(2,fastintr2)
|
||||
FAST_INTR(3,fastintr3)
|
||||
FAST_INTR(4,fastintr4)
|
||||
FAST_INTR(5,fastintr5)
|
||||
FAST_INTR(6,fastintr6)
|
||||
FAST_INTR(7,fastintr7)
|
||||
FAST_INTR(8,fastintr8)
|
||||
FAST_INTR(9,fastintr9)
|
||||
FAST_INTR(10,fastintr10)
|
||||
FAST_INTR(11,fastintr11)
|
||||
FAST_INTR(12,fastintr12)
|
||||
FAST_INTR(13,fastintr13)
|
||||
FAST_INTR(14,fastintr14)
|
||||
FAST_INTR(15,fastintr15)
|
||||
FAST_INTR(16,fastintr16)
|
||||
FAST_INTR(17,fastintr17)
|
||||
FAST_INTR(18,fastintr18)
|
||||
FAST_INTR(19,fastintr19)
|
||||
FAST_INTR(20,fastintr20)
|
||||
FAST_INTR(21,fastintr21)
|
||||
FAST_INTR(22,fastintr22)
|
||||
FAST_INTR(23,fastintr23)
|
||||
FAST_INTR(24,fastintr24)
|
||||
FAST_INTR(25,fastintr25)
|
||||
FAST_INTR(26,fastintr26)
|
||||
FAST_INTR(27,fastintr27)
|
||||
FAST_INTR(28,fastintr28)
|
||||
FAST_INTR(29,fastintr29)
|
||||
FAST_INTR(30,fastintr30)
|
||||
FAST_INTR(31,fastintr31)
|
||||
#define CLKINTR_PENDING movl $1,CNAME(clkintr_pending)
|
||||
/* Threaded interrupts */
|
||||
INTR(0,intr0, CLKINTR_PENDING)
|
||||
INTR(1,intr1,)
|
||||
INTR(2,intr2,)
|
||||
INTR(3,intr3,)
|
||||
INTR(4,intr4,)
|
||||
INTR(5,intr5,)
|
||||
INTR(6,intr6,)
|
||||
INTR(7,intr7,)
|
||||
INTR(8,intr8,)
|
||||
INTR(9,intr9,)
|
||||
INTR(10,intr10,)
|
||||
INTR(11,intr11,)
|
||||
INTR(12,intr12,)
|
||||
INTR(13,intr13,)
|
||||
INTR(14,intr14,)
|
||||
INTR(15,intr15,)
|
||||
INTR(16,intr16,)
|
||||
INTR(17,intr17,)
|
||||
INTR(18,intr18,)
|
||||
INTR(19,intr19,)
|
||||
INTR(20,intr20,)
|
||||
INTR(21,intr21,)
|
||||
INTR(22,intr22,)
|
||||
INTR(23,intr23,)
|
||||
INTR(24,intr24,)
|
||||
INTR(25,intr25,)
|
||||
INTR(26,intr26,)
|
||||
INTR(27,intr27,)
|
||||
INTR(28,intr28,)
|
||||
INTR(29,intr29,)
|
||||
INTR(30,intr30,)
|
||||
INTR(31,intr31,)
|
||||
|
||||
FAST_UNPEND(0,fastunpend0)
|
||||
FAST_UNPEND(1,fastunpend1)
|
||||
FAST_UNPEND(2,fastunpend2)
|
||||
FAST_UNPEND(3,fastunpend3)
|
||||
FAST_UNPEND(4,fastunpend4)
|
||||
FAST_UNPEND(5,fastunpend5)
|
||||
FAST_UNPEND(6,fastunpend6)
|
||||
FAST_UNPEND(7,fastunpend7)
|
||||
FAST_UNPEND(8,fastunpend8)
|
||||
FAST_UNPEND(9,fastunpend9)
|
||||
FAST_UNPEND(10,fastunpend10)
|
||||
FAST_UNPEND(11,fastunpend11)
|
||||
FAST_UNPEND(12,fastunpend12)
|
||||
FAST_UNPEND(13,fastunpend13)
|
||||
FAST_UNPEND(14,fastunpend14)
|
||||
FAST_UNPEND(15,fastunpend15)
|
||||
FAST_UNPEND(16,fastunpend16)
|
||||
FAST_UNPEND(17,fastunpend17)
|
||||
FAST_UNPEND(18,fastunpend18)
|
||||
FAST_UNPEND(19,fastunpend19)
|
||||
FAST_UNPEND(20,fastunpend20)
|
||||
FAST_UNPEND(21,fastunpend21)
|
||||
FAST_UNPEND(22,fastunpend22)
|
||||
FAST_UNPEND(23,fastunpend23)
|
||||
FAST_UNPEND(24,fastunpend24)
|
||||
FAST_UNPEND(25,fastunpend25)
|
||||
FAST_UNPEND(26,fastunpend26)
|
||||
FAST_UNPEND(27,fastunpend27)
|
||||
FAST_UNPEND(28,fastunpend28)
|
||||
FAST_UNPEND(29,fastunpend29)
|
||||
FAST_UNPEND(30,fastunpend30)
|
||||
FAST_UNPEND(31,fastunpend31)
|
||||
MCOUNT_LABEL(eintr)
|
||||
|
||||
#ifdef SMP
|
||||
/*
|
||||
* Executed by a CPU when it receives a RENDEZVOUS IPI from another CPU.
|
||||
*
|
||||
@ -640,7 +393,8 @@ IDTVEC(rendezvous)
|
||||
|
||||
call smp_rendezvous_action
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
POP_FRAME
|
||||
iret
|
||||
|
||||
@ -658,16 +412,9 @@ IDTVEC(lazypmap)
|
||||
mov %ax, %fs
|
||||
|
||||
call pmap_lazyfix_action
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
POP_FRAME
|
||||
iret
|
||||
#endif /* SMP */
|
||||
|
||||
.data
|
||||
|
||||
.globl apic_pin_trigger
|
||||
apic_pin_trigger:
|
||||
.long 0
|
||||
|
||||
.text
|
||||
|
691
sys/amd64/amd64/io_apic.c
Normal file
691
sys/amd64/amd64/io_apic.c
Normal file
@ -0,0 +1,691 @@
|
||||
/*-
|
||||
* Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_isa.h"
|
||||
#include "opt_no_mixed_mode.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <machine/apicreg.h>
|
||||
#include <machine/frame.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/apicvar.h>
|
||||
#include <machine/segments.h>
|
||||
|
||||
#if defined(DEV_ISA) && !defined(NO_MIXED_MODE)
|
||||
#define MIXED_MODE
|
||||
#endif
|
||||
|
||||
#define IOAPIC_ISA_INTS 16
|
||||
#define IOAPIC_MEM_REGION 32
|
||||
#define IOAPIC_REDTBL_LO(i) (IOAPIC_REDTBL + (i) * 2)
|
||||
#define IOAPIC_REDTBL_HI(i) (IOAPIC_REDTBL_LO(i) + 1)
|
||||
|
||||
#define VECTOR_EXTINT -1
|
||||
#define VECTOR_NMI -2
|
||||
#define VECTOR_SMI -3
|
||||
#define VECTOR_DISABLED -4
|
||||
|
||||
#define DEST_NONE -1
|
||||
#define DEST_EXTINT -2
|
||||
|
||||
#define TODO printf("%s: not implemented!\n", __func__)
|
||||
|
||||
MALLOC_DEFINE(M_IOAPIC, "I/O APIC", "I/O APIC structures");
|
||||
|
||||
/*
|
||||
* New interrupt support code..
|
||||
*
|
||||
* XXX: we really should have the interrupt cookie passed up from new-bus
|
||||
* just be a int pin, and not map 1:1 to interrupt vector number but should
|
||||
* use INTR_TYPE_FOO to set priority bands for device classes and do all the
|
||||
* magic remapping of intpin to vector in here. For now we just cheat as on
|
||||
* ia64 and map intpin X to vector NRSVIDT + X. Note that we assume that the
|
||||
* first IO APIC has ISA interrupts on pins 1-15. Not sure how you are
|
||||
* really supposed to figure out which IO APIC in a system with multiple IO
|
||||
* APIC's actually has the ISA interrupts routed to it. As far as interrupt
|
||||
* pin numbers, we use the ACPI System Interrupt number model where each
|
||||
* IO APIC has a contiguous chunk of the System Interrupt address space.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Direct the ExtINT pin on the first I/O APIC to a logical cluster of
|
||||
* CPUs rather than a physical destination of just the BSP.
|
||||
*
|
||||
* Note: This is disabled by default as test systems seem to croak with it
|
||||
* enabled.
|
||||
#define ENABLE_EXTINT_LOGICAL_DESTINATION
|
||||
*/
|
||||
|
||||
struct ioapic_intsrc {
|
||||
struct intsrc io_intsrc;
|
||||
int io_intpin:8;
|
||||
int io_vector:8;
|
||||
int io_activehi:1;
|
||||
int io_edgetrigger:1;
|
||||
int io_masked:1;
|
||||
int io_dest:5;
|
||||
};
|
||||
|
||||
struct ioapic {
|
||||
struct pic io_pic;
|
||||
u_int io_id:8; /* logical ID */
|
||||
u_int io_apic_id:4;
|
||||
u_int io_intbase:8; /* System Interrupt base */
|
||||
u_int io_numintr:8;
|
||||
volatile ioapic_t *io_addr; /* XXX: should use bus_space */
|
||||
STAILQ_ENTRY(ioapic) io_next;
|
||||
struct ioapic_intsrc io_pins[0];
|
||||
};
|
||||
|
||||
static STAILQ_HEAD(,ioapic) ioapic_list = STAILQ_HEAD_INITIALIZER(ioapic_list);
|
||||
static u_int next_id, program_logical_dest;
|
||||
|
||||
static u_int ioapic_read(volatile ioapic_t *apic, int reg);
|
||||
static void ioapic_write(volatile ioapic_t *apic, int reg, u_int val);
|
||||
static void ioapic_enable_source(struct intsrc *isrc);
|
||||
static void ioapic_disable_source(struct intsrc *isrc);
|
||||
static void ioapic_eoi_source(struct intsrc *isrc);
|
||||
static void ioapic_enable_intr(struct intsrc *isrc);
|
||||
static int ioapic_vector(struct intsrc *isrc);
|
||||
static int ioapic_source_pending(struct intsrc *isrc);
|
||||
static void ioapic_suspend(struct intsrc *isrc);
|
||||
static void ioapic_resume(struct intsrc *isrc);
|
||||
static void ioapic_program_destination(struct ioapic_intsrc *intpin);
|
||||
#ifdef MIXED_MODE
|
||||
static void ioapic_setup_mixed_mode(struct ioapic_intsrc *intpin);
|
||||
#endif
|
||||
|
||||
struct pic ioapic_template = { ioapic_enable_source, ioapic_disable_source,
|
||||
ioapic_eoi_source, ioapic_enable_intr,
|
||||
ioapic_vector, ioapic_source_pending,
|
||||
ioapic_suspend, ioapic_resume };
|
||||
|
||||
static int next_ioapic_base, logical_clusters, current_cluster;
|
||||
|
||||
static u_int
|
||||
ioapic_read(volatile ioapic_t *apic, int reg)
|
||||
{
|
||||
|
||||
mtx_assert(&icu_lock, MA_OWNED);
|
||||
apic->ioregsel = reg;
|
||||
return (apic->iowin);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_write(volatile ioapic_t *apic, int reg, u_int val)
|
||||
{
|
||||
|
||||
mtx_assert(&icu_lock, MA_OWNED);
|
||||
apic->ioregsel = reg;
|
||||
apic->iowin = val;
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_enable_source(struct intsrc *isrc)
|
||||
{
|
||||
struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
|
||||
struct ioapic *io = (struct ioapic *)isrc->is_pic;
|
||||
uint32_t flags;
|
||||
|
||||
mtx_lock_spin(&icu_lock);
|
||||
if (intpin->io_masked) {
|
||||
flags = ioapic_read(io->io_addr,
|
||||
IOAPIC_REDTBL_LO(intpin->io_intpin));
|
||||
flags &= ~(IOART_INTMASK);
|
||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin),
|
||||
flags);
|
||||
intpin->io_masked = 0;
|
||||
}
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_disable_source(struct intsrc *isrc)
|
||||
{
|
||||
struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
|
||||
struct ioapic *io = (struct ioapic *)isrc->is_pic;
|
||||
uint32_t flags;
|
||||
|
||||
mtx_lock_spin(&icu_lock);
|
||||
if (!intpin->io_masked && !intpin->io_edgetrigger) {
|
||||
flags = ioapic_read(io->io_addr,
|
||||
IOAPIC_REDTBL_LO(intpin->io_intpin));
|
||||
flags |= IOART_INTMSET;
|
||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin),
|
||||
flags);
|
||||
intpin->io_masked = 1;
|
||||
}
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_eoi_source(struct intsrc *isrc)
|
||||
{
|
||||
TODO;
|
||||
/* lapic_eoi(); */
|
||||
}
|
||||
|
||||
/*
|
||||
* Program an individual intpin's logical destination.
|
||||
*/
|
||||
static void
|
||||
ioapic_program_destination(struct ioapic_intsrc *intpin)
|
||||
{
|
||||
struct ioapic *io = (struct ioapic *)intpin->io_intsrc.is_pic;
|
||||
uint32_t value;
|
||||
|
||||
KASSERT(intpin->io_dest != DEST_NONE,
|
||||
("intpin not assigned to a cluster"));
|
||||
KASSERT(intpin->io_dest != DEST_EXTINT,
|
||||
("intpin routed via ExtINT"));
|
||||
if (bootverbose) {
|
||||
printf("ioapic%u: routing intpin %u (", io->io_id,
|
||||
intpin->io_intpin);
|
||||
if (intpin->io_vector == VECTOR_EXTINT)
|
||||
printf("ExtINT");
|
||||
else
|
||||
printf("IRQ %u", intpin->io_vector);
|
||||
printf(") to cluster %u\n", intpin->io_dest);
|
||||
}
|
||||
mtx_lock_spin(&icu_lock);
|
||||
value = ioapic_read(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin));
|
||||
value &= ~IOART_DESTMOD;
|
||||
value |= IOART_DESTLOG;
|
||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin), value);
|
||||
value = ioapic_read(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin));
|
||||
value &= ~IOART_DEST;
|
||||
value |= (intpin->io_dest << APIC_ID_CLUSTER_SHIFT |
|
||||
APIC_ID_CLUSTER_ID) << APIC_ID_SHIFT;
|
||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin), value);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_assign_cluster(struct ioapic_intsrc *intpin)
|
||||
{
|
||||
/*
|
||||
* Assign this intpin to a logical APIC cluster in a
|
||||
* round-robin fashion. We don't actually use the logical
|
||||
* destination for this intpin until after all the CPU's
|
||||
* have been started so that we don't end up with interrupts
|
||||
* that don't go anywhere. Another alternative might be to
|
||||
* start up the CPU's earlier so that they can handle interrupts
|
||||
* sooner.
|
||||
*/
|
||||
intpin->io_dest = current_cluster;
|
||||
current_cluster++;
|
||||
if (current_cluster >= logical_clusters)
|
||||
current_cluster = 0;
|
||||
if (program_logical_dest)
|
||||
ioapic_program_destination(intpin);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_enable_intr(struct intsrc *isrc)
|
||||
{
|
||||
struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
|
||||
|
||||
KASSERT(intpin->io_dest != DEST_EXTINT,
|
||||
("ExtINT pin trying to use ioapic enable_intr method"));
|
||||
if (intpin->io_dest == DEST_NONE) {
|
||||
ioapic_assign_cluster(intpin);
|
||||
lapic_enable_intr(intpin->io_vector);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ioapic_vector(struct intsrc *isrc)
|
||||
{
|
||||
struct ioapic_intsrc *pin;
|
||||
|
||||
pin = (struct ioapic_intsrc *)isrc;
|
||||
return (pin->io_vector);
|
||||
}
|
||||
|
||||
static int
|
||||
ioapic_source_pending(struct intsrc *isrc)
|
||||
{
|
||||
struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
|
||||
|
||||
return (lapic_intr_pending(intpin->io_vector));
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_suspend(struct intsrc *isrc)
|
||||
{
|
||||
|
||||
TODO;
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_resume(struct intsrc *isrc)
|
||||
{
|
||||
|
||||
TODO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and return a logical cluster ID. Note that the first time
|
||||
* this is called, it returns cluster 0. ioapic_enable_intr() treats
|
||||
* the two cases of logical_clusters == 0 and logical_clusters == 1 the
|
||||
* same: one cluster of ID 0 exists. The logical_clusters == 0 case is
|
||||
* for UP kernels, which should never call this function.
|
||||
*/
|
||||
int
|
||||
ioapic_next_logical_cluster(void)
|
||||
{
|
||||
|
||||
if (logical_clusters >= APIC_MAX_CLUSTER)
|
||||
panic("WARNING: Local APIC cluster IDs exhausted!");
|
||||
return (logical_clusters++);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a plain I/O APIC object.
|
||||
*/
|
||||
void *
|
||||
ioapic_create(uintptr_t addr, int32_t apic_id, int intbase)
|
||||
{
|
||||
struct ioapic *io;
|
||||
struct ioapic_intsrc *intpin;
|
||||
volatile ioapic_t *apic;
|
||||
u_int numintr, i;
|
||||
uint32_t value;
|
||||
|
||||
apic = (ioapic_t *)pmap_mapdev(addr, IOAPIC_MEM_REGION);
|
||||
mtx_lock_spin(&icu_lock);
|
||||
numintr = ((ioapic_read(apic, IOAPIC_VER) & IOART_VER_MAXREDIR) >>
|
||||
MAXREDIRSHIFT) + 1;
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
io = malloc(sizeof(struct ioapic) +
|
||||
numintr * sizeof(struct ioapic_intsrc), M_IOAPIC, M_WAITOK);
|
||||
io->io_pic = ioapic_template;
|
||||
mtx_lock_spin(&icu_lock);
|
||||
io->io_id = next_id++;
|
||||
io->io_apic_id = ioapic_read(apic, IOAPIC_ID) >> APIC_ID_SHIFT;
|
||||
if (apic_id != -1 && io->io_apic_id != apic_id) {
|
||||
ioapic_write(apic, IOAPIC_ID, apic_id << APIC_ID_SHIFT);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
io->io_apic_id = apic_id;
|
||||
printf("ioapic%u: Changing APIC ID to %d\n", io->io_id,
|
||||
apic_id);
|
||||
} else
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
if (intbase == -1) {
|
||||
intbase = next_ioapic_base;
|
||||
printf("ioapic%u: Assuming intbase of %d\n", io->io_id,
|
||||
intbase);
|
||||
} else if (intbase != next_ioapic_base)
|
||||
printf("ioapic%u: WARNING: intbase %d != expected base %d\n",
|
||||
io->io_id, intbase, next_ioapic_base);
|
||||
io->io_intbase = intbase;
|
||||
next_ioapic_base += numintr;
|
||||
io->io_numintr = numintr;
|
||||
io->io_addr = apic;
|
||||
|
||||
/*
|
||||
* Initialize pins. Start off with interrupts disabled. Default
|
||||
* to active-hi and edge-triggered for ISA interrupts and active-lo
|
||||
* and level-triggered for all others.
|
||||
*/
|
||||
bzero(io->io_pins, sizeof(struct ioapic_intsrc) * numintr);
|
||||
mtx_lock_spin(&icu_lock);
|
||||
for (i = 0, intpin = io->io_pins; i < numintr; i++, intpin++) {
|
||||
intpin->io_intsrc.is_pic = (struct pic *)io;
|
||||
intpin->io_intpin = i;
|
||||
intpin->io_vector = intbase + i;
|
||||
|
||||
/*
|
||||
* Assume that pin 0 on the first IO APIC is an ExtINT pin by
|
||||
* default. Assume that intpins 1-15 are ISA interrupts and
|
||||
* use suitable defaults for those. Assume that all other
|
||||
* intpins are PCI interrupts. Enable the ExtINT pin by
|
||||
* default but mask all other pins.
|
||||
*/
|
||||
if (intpin->io_vector == 0) {
|
||||
intpin->io_activehi = 1;
|
||||
intpin->io_edgetrigger = 1;
|
||||
intpin->io_vector = VECTOR_EXTINT;
|
||||
intpin->io_masked = 0;
|
||||
} else if (intpin->io_vector < IOAPIC_ISA_INTS) {
|
||||
intpin->io_activehi = 1;
|
||||
intpin->io_edgetrigger = 1;
|
||||
intpin->io_masked = 1;
|
||||
} else {
|
||||
intpin->io_activehi = 0;
|
||||
intpin->io_edgetrigger = 0;
|
||||
intpin->io_masked = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start off without a logical cluster destination until
|
||||
* the pin is enabled.
|
||||
*/
|
||||
intpin->io_dest = DEST_NONE;
|
||||
if (bootverbose) {
|
||||
printf("ioapic%u: intpin %d -> ", io->io_id, i);
|
||||
if (intpin->io_vector == VECTOR_EXTINT)
|
||||
printf("ExtINT\n");
|
||||
else
|
||||
printf("irq %d\n", intpin->io_vector);
|
||||
}
|
||||
value = ioapic_read(apic, IOAPIC_REDTBL_LO(i));
|
||||
ioapic_write(apic, IOAPIC_REDTBL_LO(i), value | IOART_INTMSET);
|
||||
}
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
|
||||
return (io);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_get_vector(void *cookie, u_int pin)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (-1);
|
||||
return (io->io_pins[pin].io_vector);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_disable_pin(void *cookie, u_int pin)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector == VECTOR_DISABLED)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_vector = VECTOR_DISABLED;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: intpin %d disabled\n", io->io_id, pin);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_remap_vector(void *cookie, u_int pin, int vector)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr || vector < 0)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_vector = vector;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: Routing IRQ %d -> intpin %d\n", io->io_id,
|
||||
vector, pin);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_set_nmi(void *cookie, u_int pin)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_vector = VECTOR_NMI;
|
||||
io->io_pins[pin].io_masked = 0;
|
||||
io->io_pins[pin].io_edgetrigger = 1;
|
||||
io->io_pins[pin].io_activehi = 1;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: Routing NMI -> intpin %d\n",
|
||||
io->io_id, pin);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_set_smi(void *cookie, u_int pin)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_vector = VECTOR_SMI;
|
||||
io->io_pins[pin].io_masked = 0;
|
||||
io->io_pins[pin].io_edgetrigger = 1;
|
||||
io->io_pins[pin].io_activehi = 1;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: Routing SMI -> intpin %d\n",
|
||||
io->io_id, pin);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_set_extint(void *cookie, u_int pin)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_vector = VECTOR_EXTINT;
|
||||
io->io_pins[pin].io_masked = 0;
|
||||
io->io_pins[pin].io_edgetrigger = 1;
|
||||
io->io_pins[pin].io_activehi = 1;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: Routing external 8259A's -> intpin %d\n",
|
||||
io->io_id, pin);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_set_polarity(void *cookie, u_int pin, char activehi)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_activehi = activehi;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: intpin %d polarity: %s\n", io->io_id, pin,
|
||||
activehi ? "active-hi" : "active-lo");
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_set_triggermode(void *cookie, u_int pin, char edgetrigger)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_edgetrigger = edgetrigger;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: intpin %d trigger: %s\n", io->io_id, pin,
|
||||
edgetrigger ? "edge" : "level");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a complete I/O APIC object with the interrupt subsystem.
|
||||
*/
|
||||
void
|
||||
ioapic_register(void *cookie)
|
||||
{
|
||||
struct ioapic_intsrc *pin;
|
||||
struct ioapic *io;
|
||||
volatile ioapic_t *apic;
|
||||
uint32_t flags;
|
||||
int i;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
apic = io->io_addr;
|
||||
mtx_lock_spin(&icu_lock);
|
||||
flags = ioapic_read(apic, IOAPIC_VER) & IOART_VER_VERSION;
|
||||
STAILQ_INSERT_TAIL(&ioapic_list, io, io_next);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
printf("ioapic%u <Version %u> irqs %u-%u on motherboard\n", io->io_id,
|
||||
flags, io->io_intbase, io->io_intbase + io->io_numintr - 1);
|
||||
for (i = 0, pin = io->io_pins; i < io->io_numintr; i++, pin++) {
|
||||
/*
|
||||
* Finish initializing the pins by programming the vectors
|
||||
* and delivery mode.
|
||||
*/
|
||||
if (pin->io_vector == VECTOR_DISABLED)
|
||||
continue;
|
||||
flags = IOART_DESTPHY;
|
||||
if (pin->io_edgetrigger)
|
||||
flags |= IOART_TRGREDG;
|
||||
else
|
||||
flags |= IOART_TRGRLVL;
|
||||
if (pin->io_activehi)
|
||||
flags |= IOART_INTAHI;
|
||||
else
|
||||
flags |= IOART_INTALO;
|
||||
if (pin->io_masked)
|
||||
flags |= IOART_INTMSET;
|
||||
switch (pin->io_vector) {
|
||||
case VECTOR_EXTINT:
|
||||
KASSERT(pin->io_edgetrigger,
|
||||
("EXTINT not edge triggered"));
|
||||
flags |= IOART_DELEXINT;
|
||||
break;
|
||||
case VECTOR_NMI:
|
||||
KASSERT(pin->io_edgetrigger,
|
||||
("NMI not edge triggered"));
|
||||
flags |= IOART_DELNMI;
|
||||
break;
|
||||
case VECTOR_SMI:
|
||||
KASSERT(pin->io_edgetrigger,
|
||||
("SMI not edge triggered"));
|
||||
flags |= IOART_DELSMI;
|
||||
break;
|
||||
default:
|
||||
flags |= IOART_DELLOPRI |
|
||||
apic_irq_to_idt(pin->io_vector);
|
||||
}
|
||||
mtx_lock_spin(&icu_lock);
|
||||
ioapic_write(apic, IOAPIC_REDTBL_LO(i), flags);
|
||||
|
||||
/*
|
||||
* Route interrupts to the BSP by default using physical
|
||||
* addressing. Vectored interrupts get readdressed using
|
||||
* logical IDs to CPU clusters when they are enabled.
|
||||
*/
|
||||
flags = ioapic_read(apic, IOAPIC_REDTBL_HI(i));
|
||||
flags &= ~IOART_DEST;
|
||||
flags |= PCPU_GET(apic_id) << APIC_ID_SHIFT;
|
||||
ioapic_write(apic, IOAPIC_REDTBL_HI(i), flags);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
if (pin->io_vector >= 0) {
|
||||
#ifdef MIXED_MODE
|
||||
/* Route IRQ0 via the 8259A using mixed mode. */
|
||||
if (pin->io_vector == 0)
|
||||
ioapic_setup_mixed_mode(pin);
|
||||
else
|
||||
#endif
|
||||
intr_register_source(&pin->io_intsrc);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Program all the intpins to use logical destinations once the AP's
|
||||
* have been launched.
|
||||
*/
|
||||
static void
|
||||
ioapic_set_logical_destinations(void *arg __unused)
|
||||
{
|
||||
struct ioapic *io;
|
||||
int i;
|
||||
|
||||
program_logical_dest = 1;
|
||||
STAILQ_FOREACH(io, &ioapic_list, io_next)
|
||||
for (i = 0; i < io->io_numintr; i++)
|
||||
if (io->io_pins[i].io_dest != DEST_NONE &&
|
||||
io->io_pins[i].io_dest != DEST_EXTINT)
|
||||
ioapic_program_destination(&io->io_pins[i]);
|
||||
}
|
||||
SYSINIT(ioapic_destinations, SI_SUB_SMP, SI_ORDER_SECOND,
|
||||
ioapic_set_logical_destinations, NULL)
|
||||
|
||||
#ifdef MIXED_MODE
|
||||
/*
|
||||
* Support for mixed-mode interrupt sources. These sources route an ISA
|
||||
* IRQ through the 8259A's via the ExtINT on pin 0 of the I/O APIC that
|
||||
* routes the ISA interrupts. We just ignore the intpins that use this
|
||||
* mode and allow the atpic driver to register its interrupt source for
|
||||
* that IRQ instead.
|
||||
*/
|
||||
|
||||
void
|
||||
ioapic_setup_mixed_mode(struct ioapic_intsrc *intpin)
|
||||
{
|
||||
struct ioapic_intsrc *extint;
|
||||
struct ioapic *io;
|
||||
|
||||
/*
|
||||
* Mark the associated I/O APIC intpin as being delivered via
|
||||
* ExtINT and enable the ExtINT pin on the I/O APIC if needed.
|
||||
*/
|
||||
intpin->io_dest = DEST_EXTINT;
|
||||
io = (struct ioapic *)intpin->io_intsrc.is_pic;
|
||||
extint = &io->io_pins[0];
|
||||
if (extint->io_vector != VECTOR_EXTINT)
|
||||
panic("Can't find ExtINT pin to route through!");
|
||||
#ifdef ENABLE_EXTINT_LOGICAL_DESTINATION
|
||||
if (extint->io_dest == DEST_NONE)
|
||||
ioapic_assign_cluster(extint);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* MIXED_MODE */
|
758
sys/amd64/amd64/local_apic.c
Normal file
758
sys/amd64/amd64/local_apic.c
Normal file
@ -0,0 +1,758 @@
|
||||
/*-
|
||||
* Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
|
||||
* Copyright (c) 1996, by Steve Passe
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. The name of the developer may NOT be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
* 3. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Local APIC support on Pentium and later processors.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/pcpu.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <machine/apicreg.h>
|
||||
#include <machine/cputypes.h>
|
||||
#include <machine/frame.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/apicvar.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/smp.h>
|
||||
#include <machine/specialreg.h>
|
||||
|
||||
/*
|
||||
* We can handle up to 60 APICs via our logical cluster IDs, but currently
|
||||
* the physical IDs on Intel processors up to the Pentium 4 are limited to
|
||||
* 16.
|
||||
*/
|
||||
#define MAX_APICID 16
|
||||
|
||||
/*
|
||||
* Support for local APICs. Local APICs manage interrupts on each
|
||||
* individual processor as opposed to I/O APICs which receive interrupts
|
||||
* from I/O devices and then forward them on to the local APICs.
|
||||
*
|
||||
* Local APICs can also send interrupts to each other thus providing the
|
||||
* mechanism for IPIs.
|
||||
*/
|
||||
|
||||
struct lvt {
|
||||
u_int lvt_edgetrigger:1;
|
||||
u_int lvt_activehi:1;
|
||||
u_int lvt_masked:1;
|
||||
u_int lvt_active:1;
|
||||
u_int lvt_mode:16;
|
||||
u_int lvt_vector:8;
|
||||
};
|
||||
|
||||
struct lapic {
|
||||
struct lvt la_lvts[LVT_MAX + 1];
|
||||
u_int la_id:8;
|
||||
u_int la_cluster:4;
|
||||
u_int la_cluster_id:2;
|
||||
u_int la_present:1;
|
||||
} static lapics[MAX_APICID];
|
||||
|
||||
/* XXX: should thermal be an NMI? */
|
||||
|
||||
/* Global defaults for local APIC LVT entries. */
|
||||
static struct lvt lvts[LVT_MAX + 1] = {
|
||||
{ 1, 1, 1, 1, APIC_LVT_DM_EXTINT, 0 }, /* LINT0: masked ExtINT */
|
||||
{ 1, 1, 0, 1, APIC_LVT_DM_NMI, 0 }, /* LINT1: NMI */
|
||||
{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* Timer: needs a vector */
|
||||
{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* Error: needs a vector */
|
||||
{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* PMC */
|
||||
{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* Thermal: needs a vector */
|
||||
};
|
||||
|
||||
static inthand_t *ioint_handlers[] = {
|
||||
NULL, /* 0 - 31 */
|
||||
IDTVEC(apic_isr1), /* 32 - 63 */
|
||||
IDTVEC(apic_isr2), /* 64 - 95 */
|
||||
IDTVEC(apic_isr3), /* 96 - 127 */
|
||||
IDTVEC(apic_isr4), /* 128 - 159 */
|
||||
IDTVEC(apic_isr5), /* 160 - 191 */
|
||||
NULL, /* 192 - 223 */
|
||||
NULL /* 224 - 255 */
|
||||
};
|
||||
|
||||
volatile lapic_t *lapic;
|
||||
|
||||
static uint32_t
|
||||
lvt_mode(struct lapic *la, u_int pin, uint32_t value)
|
||||
{
|
||||
struct lvt *lvt;
|
||||
|
||||
KASSERT(pin <= LVT_MAX, ("%s: pin %u out of range", __func__, pin));
|
||||
if (la->la_lvts[pin].lvt_active)
|
||||
lvt = &la->la_lvts[pin];
|
||||
else
|
||||
lvt = &lvts[pin];
|
||||
|
||||
value &= ~(APIC_LVT_M | APIC_LVT_TM | APIC_LVT_IIPP | APIC_LVT_DM |
|
||||
APIC_LVT_VECTOR);
|
||||
if (lvt->lvt_edgetrigger == 0)
|
||||
value |= APIC_LVT_TM;
|
||||
if (lvt->lvt_activehi == 0)
|
||||
value |= APIC_LVT_IIPP_INTALO;
|
||||
if (lvt->lvt_masked)
|
||||
value |= APIC_LVT_M;
|
||||
value |= lvt->lvt_mode;
|
||||
switch (lvt->lvt_mode) {
|
||||
case APIC_LVT_DM_NMI:
|
||||
case APIC_LVT_DM_SMI:
|
||||
case APIC_LVT_DM_INIT:
|
||||
case APIC_LVT_DM_EXTINT:
|
||||
if (!lvt->lvt_edgetrigger) {
|
||||
printf("lapic%u: Forcing LINT%u to edge trigger\n",
|
||||
la->la_id, pin);
|
||||
value |= APIC_LVT_TM;
|
||||
}
|
||||
/* Use a vector of 0. */
|
||||
break;
|
||||
case APIC_LVT_DM_FIXED:
|
||||
#if 0
|
||||
value |= lvt->lvt_vector;
|
||||
#else
|
||||
panic("Fixed LINT pins not supported");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
panic("bad APIC LVT delivery mode: %#x\n", value);
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map the local APIC and setup necessary interrupt vectors.
|
||||
*/
|
||||
void
|
||||
lapic_init(uintptr_t addr)
|
||||
{
|
||||
u_int32_t value;
|
||||
|
||||
/* Map the local APIC and setup the spurious interrupt handler. */
|
||||
KASSERT(trunc_page(addr) == addr,
|
||||
("local APIC not aligned on a page boundary"));
|
||||
lapic = (lapic_t *)pmap_mapdev(addr, sizeof(lapic_t));
|
||||
setidt(APIC_SPURIOUS_INT, IDTVEC(spuriousint), SDT_SYS386IGT, SEL_KPL,
|
||||
GSEL(GCODE_SEL, SEL_KPL));
|
||||
|
||||
/* Perform basic initialization of the BSP's local APIC. */
|
||||
value = lapic->svr;
|
||||
value &= ~(APIC_SVR_VECTOR | APIC_SVR_FOCUS);
|
||||
value |= (APIC_SVR_FEN | APIC_SVR_SWEN | APIC_SPURIOUS_INT);
|
||||
lapic->svr = value;
|
||||
|
||||
/* Set BSP's per-CPU local APIC ID. */
|
||||
PCPU_SET(apic_id, lapic_id());
|
||||
|
||||
/* XXX: timer/error/thermal interrupts */
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a local APIC instance.
|
||||
*/
|
||||
void
|
||||
lapic_create(u_int apic_id, int boot_cpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (apic_id > MAX_APICID) {
|
||||
printf("APIC: Ignoring local APIC with ID %d\n", apic_id);
|
||||
if (boot_cpu)
|
||||
panic("Can't ignore BSP");
|
||||
return;
|
||||
}
|
||||
KASSERT(!lapics[apic_id].la_present, ("duplicate local APIC %u",
|
||||
apic_id));
|
||||
|
||||
/*
|
||||
* Assume no local LVT overrides and a cluster of 0 and
|
||||
* intra-cluster ID of 0.
|
||||
*/
|
||||
lapics[apic_id].la_present = 1;
|
||||
lapics[apic_id].la_id = apic_id;
|
||||
for (i = 0; i < LVT_MAX; i++) {
|
||||
lapics[apic_id].la_lvts[i] = lvts[i];
|
||||
lapics[apic_id].la_lvts[i].lvt_active = 0;
|
||||
}
|
||||
|
||||
#ifdef SMP
|
||||
cpu_add(apic_id, boot_cpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump contents of local APIC registers
|
||||
*/
|
||||
void
|
||||
lapic_dump(const char* str)
|
||||
{
|
||||
|
||||
printf("cpu%d %s:\n", PCPU_GET(cpuid), str);
|
||||
printf(" ID: 0x%08x VER: 0x%08x LDR: 0x%08x DFR: 0x%08x\n",
|
||||
lapic->id, lapic->version, lapic->ldr, lapic->dfr);
|
||||
printf(" lint0: 0x%08x lint1: 0x%08x TPR: 0x%08x SVR: 0x%08x\n",
|
||||
lapic->lvt_lint0, lapic->lvt_lint1, lapic->tpr, lapic->svr);
|
||||
}
|
||||
|
||||
void
|
||||
lapic_enable_intr(u_int irq)
|
||||
{
|
||||
u_int vector;
|
||||
|
||||
vector = apic_irq_to_idt(irq);
|
||||
KASSERT(vector != IDT_SYSCALL, ("Attempt to overwrite syscall entry"));
|
||||
KASSERT(ioint_handlers[vector / 32] != NULL,
|
||||
("No ISR handler for IRQ %u", irq));
|
||||
setidt(vector, ioint_handlers[vector / 32], SDT_SYS386IGT, SEL_KPL,
|
||||
GSEL(GCODE_SEL, SEL_KPL));
|
||||
}
|
||||
|
||||
void
|
||||
lapic_setup(void)
|
||||
{
|
||||
struct lapic *la;
|
||||
u_int32_t value, maxlvt;
|
||||
register_t eflags;
|
||||
|
||||
la = &lapics[lapic_id()];
|
||||
KASSERT(la->la_present, ("missing APIC structure"));
|
||||
eflags = intr_disable();
|
||||
maxlvt = (lapic->version & APIC_VER_MAXLVT) >> MAXLVTSHIFT;
|
||||
|
||||
/* Program LINT[01] LVT entries. */
|
||||
lapic->lvt_lint0 = lvt_mode(la, LVT_LINT0, lapic->lvt_lint0);
|
||||
lapic->lvt_lint1 = lvt_mode(la, LVT_LINT1, lapic->lvt_lint1);
|
||||
|
||||
/* XXX: more LVT entries */
|
||||
|
||||
/* Clear the TPR. */
|
||||
value = lapic->tpr;
|
||||
value &= ~APIC_TPR_PRIO;
|
||||
lapic->tpr = value;
|
||||
|
||||
/* Use the cluster model for logical IDs. */
|
||||
value = lapic->dfr;
|
||||
value &= ~APIC_DFR_MODEL_MASK;
|
||||
value |= APIC_DFR_MODEL_CLUSTER;
|
||||
lapic->dfr = value;
|
||||
|
||||
/* Set this APIC's logical ID. */
|
||||
value = lapic->ldr;
|
||||
value &= ~APIC_ID_MASK;
|
||||
value |= (la->la_cluster << APIC_ID_CLUSTER_SHIFT |
|
||||
1 << la->la_cluster_id) << APIC_ID_SHIFT;
|
||||
lapic->ldr = value;
|
||||
|
||||
/* Setup spurious vector and enable the local APIC. */
|
||||
value = lapic->svr;
|
||||
value &= ~(APIC_SVR_VECTOR | APIC_SVR_FOCUS);
|
||||
value |= (APIC_SVR_FEN | APIC_SVR_SWEN | APIC_SPURIOUS_INT);
|
||||
lapic->svr = value;
|
||||
intr_restore(eflags);
|
||||
}
|
||||
|
||||
void
|
||||
lapic_disable(void)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
/* Software disable the local APIC. */
|
||||
value = lapic->svr;
|
||||
value &= ~APIC_SVR_SWEN;
|
||||
lapic->svr = value;
|
||||
}
|
||||
|
||||
int
|
||||
lapic_id(void)
|
||||
{
|
||||
|
||||
KASSERT(lapic != NULL, ("local APIC is not mapped"));
|
||||
return (lapic->id >> APIC_ID_SHIFT);
|
||||
}
|
||||
|
||||
int
|
||||
lapic_intr_pending(u_int vector)
|
||||
{
|
||||
volatile u_int32_t *irr;
|
||||
|
||||
/*
|
||||
* The IRR registers are an array of 128-bit registers each of
|
||||
* which only describes 32 interrupts in the low 32 bits.. Thus,
|
||||
* we divide the vector by 32 to get the 128-bit index. We then
|
||||
* multiply that index by 4 to get the equivalent index from
|
||||
* treating the IRR as an array of 32-bit registers. Finally, we
|
||||
* modulus the vector by 32 to determine the individual bit to
|
||||
* test.
|
||||
*/
|
||||
irr = &lapic->irr0;
|
||||
return (irr[(vector / 32) * 4] & 1 << (vector % 32));
|
||||
}
|
||||
|
||||
void
|
||||
lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id)
|
||||
{
|
||||
struct lapic *la;
|
||||
|
||||
KASSERT(lapics[apic_id].la_present, ("%s: APIC %u doesn't exist",
|
||||
__func__, apic_id));
|
||||
KASSERT(cluster <= APIC_MAX_CLUSTER, ("%s: cluster %u too big",
|
||||
__func__, cluster));
|
||||
KASSERT(cluster_id <= APIC_MAX_INTRACLUSTER_ID,
|
||||
("%s: intra cluster id %u too big", __func__, cluster_id));
|
||||
la = &lapics[apic_id];
|
||||
la->la_cluster = cluster;
|
||||
la->la_cluster_id = cluster_id;
|
||||
}
|
||||
|
||||
int
|
||||
lapic_set_lvt_mask(u_int apic_id, u_int pin, u_char masked)
|
||||
{
|
||||
|
||||
if (pin > LVT_MAX)
|
||||
return (EINVAL);
|
||||
if (apic_id == APIC_ID_ALL) {
|
||||
lvts[pin].lvt_masked = masked;
|
||||
if (bootverbose)
|
||||
printf("lapic:");
|
||||
} else {
|
||||
KASSERT(lapics[apic_id].la_present,
|
||||
("%s: missing APIC %u", __func__, apic_id));
|
||||
lapics[apic_id].la_lvts[pin].lvt_masked = masked;
|
||||
lapics[apic_id].la_lvts[pin].lvt_active = 1;
|
||||
if (bootverbose)
|
||||
printf("lapic%u:", apic_id);
|
||||
}
|
||||
if (bootverbose)
|
||||
printf(" LINT%u %s\n", pin, masked ? "masked" : "unmasked");
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lapic_set_lvt_mode(u_int apic_id, u_int pin, u_int32_t mode)
|
||||
{
|
||||
struct lvt *lvt;
|
||||
|
||||
if (pin > LVT_MAX)
|
||||
return (EINVAL);
|
||||
if (apic_id == APIC_ID_ALL) {
|
||||
lvt = &lvts[pin];
|
||||
if (bootverbose)
|
||||
printf("lapic:");
|
||||
} else {
|
||||
KASSERT(lapics[apic_id].la_present,
|
||||
("%s: missing APIC %u", __func__, apic_id));
|
||||
lvt = &lapics[apic_id].la_lvts[pin];
|
||||
lvt->lvt_active = 1;
|
||||
if (bootverbose)
|
||||
printf("lapic%u:", apic_id);
|
||||
}
|
||||
lvt->lvt_mode = mode;
|
||||
switch (mode) {
|
||||
case APIC_LVT_DM_NMI:
|
||||
case APIC_LVT_DM_SMI:
|
||||
case APIC_LVT_DM_INIT:
|
||||
case APIC_LVT_DM_EXTINT:
|
||||
lvt->lvt_edgetrigger = 1;
|
||||
lvt->lvt_activehi = 1;
|
||||
if (mode == APIC_LVT_DM_EXTINT)
|
||||
lvt->lvt_masked = 1;
|
||||
else
|
||||
lvt->lvt_masked = 0;
|
||||
break;
|
||||
default:
|
||||
panic("Unsupported delivery mode: 0x%x\n", mode);
|
||||
}
|
||||
if (bootverbose) {
|
||||
printf(" Routing ");
|
||||
switch (mode) {
|
||||
case APIC_LVT_DM_NMI:
|
||||
printf("NMI");
|
||||
break;
|
||||
case APIC_LVT_DM_SMI:
|
||||
printf("SMI");
|
||||
break;
|
||||
case APIC_LVT_DM_INIT:
|
||||
printf("INIT");
|
||||
break;
|
||||
case APIC_LVT_DM_EXTINT:
|
||||
printf("ExtINT");
|
||||
break;
|
||||
}
|
||||
printf(" -> LINT%u\n", pin);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lapic_set_lvt_polarity(u_int apic_id, u_int pin, u_char activehi)
|
||||
{
|
||||
|
||||
if (pin > LVT_MAX)
|
||||
return (EINVAL);
|
||||
if (apic_id == APIC_ID_ALL) {
|
||||
lvts[pin].lvt_activehi = activehi;
|
||||
if (bootverbose)
|
||||
printf("lapic:");
|
||||
} else {
|
||||
KASSERT(lapics[apic_id].la_present,
|
||||
("%s: missing APIC %u", __func__, apic_id));
|
||||
lapics[apic_id].la_lvts[pin].lvt_active = 1;
|
||||
lapics[apic_id].la_lvts[pin].lvt_activehi = activehi;
|
||||
if (bootverbose)
|
||||
printf("lapic%u:", apic_id);
|
||||
}
|
||||
if (bootverbose)
|
||||
printf(" LINT%u polarity: active-%s\n", pin,
|
||||
activehi ? "hi" : "lo");
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lapic_set_lvt_triggermode(u_int apic_id, u_int pin, u_char edgetrigger)
|
||||
{
|
||||
|
||||
if (pin > LVT_MAX)
|
||||
return (EINVAL);
|
||||
if (apic_id == APIC_ID_ALL) {
|
||||
lvts[pin].lvt_edgetrigger = edgetrigger;
|
||||
if (bootverbose)
|
||||
printf("lapic:");
|
||||
} else {
|
||||
KASSERT(lapics[apic_id].la_present,
|
||||
("%s: missing APIC %u", __func__, apic_id));
|
||||
lapics[apic_id].la_lvts[pin].lvt_edgetrigger = edgetrigger;
|
||||
lapics[apic_id].la_lvts[pin].lvt_active = 1;
|
||||
if (bootverbose)
|
||||
printf("lapic%u:", apic_id);
|
||||
}
|
||||
if (bootverbose)
|
||||
printf(" LINT%u trigger: %s\n", pin,
|
||||
edgetrigger ? "edge" : "level");
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
lapic_handle_intr(struct intrframe frame)
|
||||
{
|
||||
struct intsrc *isrc;
|
||||
|
||||
if (frame.if_vec == -1)
|
||||
panic("Couldn't get vector from ISR!");
|
||||
isrc = intr_lookup_source(apic_idt_to_irq(frame.if_vec));
|
||||
isrc->is_pic->pic_disable_source(isrc);
|
||||
lapic->eoi = 0;
|
||||
intr_execute_handlers(isrc, &frame);
|
||||
}
|
||||
|
||||
/* Translate between IDT vectors and IRQ vectors. */
|
||||
u_int
|
||||
apic_irq_to_idt(u_int irq)
|
||||
{
|
||||
u_int vector;
|
||||
|
||||
KASSERT(irq < NUM_IO_INTS, ("Invalid IRQ %u", irq));
|
||||
vector = irq + IDT_IO_INTS;
|
||||
if (vector >= IDT_SYSCALL)
|
||||
vector++;
|
||||
return (vector);
|
||||
}
|
||||
|
||||
u_int
|
||||
apic_idt_to_irq(u_int vector)
|
||||
{
|
||||
|
||||
KASSERT(vector >= IDT_IO_INTS && vector != IDT_SYSCALL &&
|
||||
vector <= IDT_IO_INTS + NUM_IO_INTS,
|
||||
("Vector %u does not map to an IRQ line", vector));
|
||||
if (vector > IDT_SYSCALL)
|
||||
vector--;
|
||||
return (vector - IDT_IO_INTS);
|
||||
}
|
||||
|
||||
/*
|
||||
* APIC probing support code. This includes code to manage enumerators.
|
||||
*/
|
||||
|
||||
static SLIST_HEAD(, apic_enumerator) enumerators =
|
||||
SLIST_HEAD_INITIALIZER(enumerators);
|
||||
static struct apic_enumerator *best_enum;
|
||||
|
||||
void
|
||||
apic_register_enumerator(struct apic_enumerator *enumerator)
|
||||
{
|
||||
#ifdef INVARIANTS
|
||||
struct apic_enumerator *apic_enum;
|
||||
|
||||
SLIST_FOREACH(apic_enum, &enumerators, apic_next) {
|
||||
if (apic_enum == enumerator)
|
||||
panic("%s: Duplicate register of %s", __func__,
|
||||
enumerator->apic_name);
|
||||
}
|
||||
#endif
|
||||
SLIST_INSERT_HEAD(&enumerators, enumerator, apic_next);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to look for CPU's very, very early because certain subsystems
|
||||
* want to know how many CPU's we have extremely early on in the boot
|
||||
* process.
|
||||
*/
|
||||
static void
|
||||
apic_init(void *dummy __unused)
|
||||
{
|
||||
struct apic_enumerator *enumerator;
|
||||
int retval, best;
|
||||
|
||||
/* We only support built in local APICs. */
|
||||
if (!(cpu_feature & CPUID_APIC))
|
||||
return;
|
||||
|
||||
/* First, probe all the enumerators to find the best match. */
|
||||
best_enum = NULL;
|
||||
best = 0;
|
||||
SLIST_FOREACH(enumerator, &enumerators, apic_next) {
|
||||
retval = enumerator->apic_probe();
|
||||
if (retval > 0)
|
||||
continue;
|
||||
if (best_enum == NULL || best < retval) {
|
||||
best_enum = enumerator;
|
||||
best = retval;
|
||||
}
|
||||
}
|
||||
if (best_enum == NULL) {
|
||||
if (bootverbose)
|
||||
printf("APIC: Could not find any APICs.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bootverbose)
|
||||
printf("APIC: Using the %s enumerator.\n",
|
||||
best_enum->apic_name);
|
||||
|
||||
/* Second, probe the CPU's in the system. */
|
||||
retval = best_enum->apic_probe_cpus();
|
||||
if (retval != 0)
|
||||
printf("%s: Failed to probe CPUs: returned %d\n",
|
||||
best_enum->apic_name, retval);
|
||||
}
|
||||
SYSINIT(apic_init, SI_SUB_TUNABLES - 1, SI_ORDER_SECOND, apic_init, NULL)
|
||||
|
||||
/*
|
||||
* Setup the local APIC. We have to do this prior to starting up the APs
|
||||
* in the SMP case.
|
||||
*/
|
||||
static void
|
||||
apic_setup_local(void *dummy __unused)
|
||||
{
|
||||
int retval;
|
||||
uint64_t apic_base;
|
||||
|
||||
if (best_enum == NULL)
|
||||
return;
|
||||
/*
|
||||
* To work around an errata, we disable the local APIC on some
|
||||
* CPUs during early startup. We need to turn the local APIC back
|
||||
* on on such CPUs now.
|
||||
*/
|
||||
if (cpu == CPU_686 && strcmp(cpu_vendor, "GenuineIntel") == 0 &&
|
||||
(cpu_id & 0xff0) == 0x610) {
|
||||
apic_base = rdmsr(MSR_APICBASE);
|
||||
apic_base |= APICBASE_ENABLED;
|
||||
wrmsr(MSR_APICBASE, apic_base);
|
||||
}
|
||||
retval = best_enum->apic_setup_local();
|
||||
if (retval != 0)
|
||||
printf("%s: Failed to setup the local APIC: returned %d\n",
|
||||
best_enum->apic_name, retval);
|
||||
}
|
||||
SYSINIT(apic_setup_local, SI_SUB_CPU, SI_ORDER_FIRST, apic_setup_local, NULL)
|
||||
|
||||
/*
|
||||
* Setup the I/O APICs.
|
||||
*/
|
||||
static void
|
||||
apic_setup_io(void *dummy __unused)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (best_enum == NULL)
|
||||
return;
|
||||
retval = best_enum->apic_setup_io();
|
||||
if (retval != 0)
|
||||
printf("%s: Failed to setup I/O APICs: returned %d\n",
|
||||
best_enum->apic_name, retval);
|
||||
|
||||
/*
|
||||
* Finish setting up the local APIC on the BSP once we know how to
|
||||
* properly program the LINT pins.
|
||||
*/
|
||||
lapic_setup();
|
||||
if (bootverbose)
|
||||
lapic_dump("BSP");
|
||||
}
|
||||
SYSINIT(apic_setup_io, SI_SUB_INTR, SI_ORDER_SECOND, apic_setup_io, NULL)
|
||||
|
||||
#ifdef SMP
|
||||
/*
|
||||
* Inter Processor Interrupt functions. The lapic_ipi_*() functions are
|
||||
* private the sys/i386 code. The public interface for the rest of the
|
||||
* kernel is defined in mp_machdep.c.
|
||||
*/
|
||||
#define DETECT_DEADLOCK
|
||||
|
||||
int
|
||||
lapic_ipi_wait(int delay)
|
||||
{
|
||||
int x, incr;
|
||||
|
||||
/*
|
||||
* Wait delay loops for IPI to be sent. This is highly bogus
|
||||
* since this is sensitive to CPU clock speed. If delay is
|
||||
* -1, we wait forever.
|
||||
*/
|
||||
if (delay == -1) {
|
||||
incr = 0;
|
||||
delay = 1;
|
||||
} else
|
||||
incr = 1;
|
||||
for (x = 0; x < delay; x += incr) {
|
||||
if ((lapic->icr_lo & APIC_DELSTAT_MASK) == APIC_DELSTAT_IDLE)
|
||||
return (1);
|
||||
ia32_pause();
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
lapic_ipi_raw(register_t icrlo, u_int dest)
|
||||
{
|
||||
register_t value, eflags;
|
||||
|
||||
/* XXX: Need more sanity checking of icrlo? */
|
||||
KASSERT(lapic != NULL, ("%s called too early", __func__));
|
||||
KASSERT((dest & ~(APIC_ID_MASK >> APIC_ID_SHIFT)) == 0,
|
||||
("%s: invalid dest field", __func__));
|
||||
KASSERT((icrlo & APIC_ICRLO_RESV_MASK) == 0,
|
||||
("%s: reserved bits set in ICR LO register", __func__));
|
||||
|
||||
/* Set destination in ICR HI register if it is being used. */
|
||||
eflags = intr_disable();
|
||||
if ((icrlo & APIC_DEST_MASK) == APIC_DEST_DESTFLD) {
|
||||
value = lapic->icr_hi;
|
||||
value &= ~APIC_ID_MASK;
|
||||
value |= dest << APIC_ID_SHIFT;
|
||||
lapic->icr_hi = value;
|
||||
}
|
||||
|
||||
/* Program the contents of the IPI and dispatch it. */
|
||||
value = lapic->icr_lo;
|
||||
value &= APIC_ICRLO_RESV_MASK;
|
||||
value |= icrlo;
|
||||
lapic->icr_lo = value;
|
||||
intr_restore(eflags);
|
||||
}
|
||||
|
||||
#ifdef DETECT_DEADLOCK
|
||||
#define BEFORE_SPIN 1000000
|
||||
#define AFTER_SPIN 1000
|
||||
#endif
|
||||
|
||||
void
|
||||
lapic_ipi_vectored(u_int vector, int dest)
|
||||
{
|
||||
register_t icrlo, destfield;
|
||||
|
||||
KASSERT((vector & ~APIC_VECTOR_MASK) == 0,
|
||||
("%s: invalid vector %d", __func__, vector));
|
||||
|
||||
icrlo = vector | APIC_DELMODE_FIXED | APIC_DESTMODE_PHY |
|
||||
APIC_LEVEL_DEASSERT | APIC_TRIGMOD_EDGE;
|
||||
destfield = 0;
|
||||
switch (dest) {
|
||||
case APIC_IPI_DEST_SELF:
|
||||
icrlo |= APIC_DEST_SELF;
|
||||
break;
|
||||
case APIC_IPI_DEST_ALL:
|
||||
icrlo |= APIC_DEST_ALLISELF;
|
||||
break;
|
||||
case APIC_IPI_DEST_OTHERS:
|
||||
icrlo |= APIC_DEST_ALLESELF;
|
||||
break;
|
||||
default:
|
||||
KASSERT((dest & ~(APIC_ID_MASK >> APIC_ID_SHIFT)) == 0,
|
||||
("%s: invalid destination 0x%x", __func__, dest));
|
||||
destfield = dest;
|
||||
}
|
||||
|
||||
#ifdef DETECT_DEADLOCK
|
||||
/* Check for an earlier stuck IPI. */
|
||||
if (!lapic_ipi_wait(BEFORE_SPIN))
|
||||
panic("APIC: Previous IPI is stuck");
|
||||
#endif
|
||||
|
||||
lapic_ipi_raw(icrlo, destfield);
|
||||
|
||||
#ifdef DETECT_DEADLOCK
|
||||
/* Wait for IPI to be delivered. */
|
||||
if (!lapic_ipi_wait(AFTER_SPIN)) {
|
||||
#ifdef needsattention
|
||||
/*
|
||||
* XXX FIXME:
|
||||
*
|
||||
* The above function waits for the message to actually be
|
||||
* delivered. It breaks out after an arbitrary timeout
|
||||
* since the message should eventually be delivered (at
|
||||
* least in theory) and that if it wasn't we would catch
|
||||
* the failure with the check above when the next IPI is
|
||||
* sent.
|
||||
*
|
||||
* We could skiip this wait entirely, EXCEPT it probably
|
||||
* protects us from other routines that assume that the
|
||||
* message was delivered and acted upon when this function
|
||||
* returns.
|
||||
*/
|
||||
printf("APIC: IPI might be stuck\n");
|
||||
#else /* !needsattention */
|
||||
/* Wait until mesage is sent without a timeout. */
|
||||
while (lapic->icr_lo & APIC_DELSTAT_PEND)
|
||||
ia32_pause();
|
||||
#endif /* needsattention */
|
||||
}
|
||||
#endif /* DETECT_DEADLOCK */
|
||||
}
|
||||
#endif /* SMP */
|
@ -25,8 +25,8 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _MACHINE_APIC_H_
|
||||
#define _MACHINE_APIC_H_
|
||||
#ifndef _MACHINE_APICREG_H_
|
||||
#define _MACHINE_APICREG_H_
|
||||
|
||||
/*
|
||||
* Local && I/O APIC definitions.
|
||||
@ -221,11 +221,29 @@ typedef struct IOAPIC ioapic_t;
|
||||
/* default physical locations of LOCAL (CPU) APICs */
|
||||
#define DEFAULT_APIC_BASE 0xfee00000
|
||||
|
||||
/* constants relating to APIC ID registers */
|
||||
#define APIC_ID_MASK 0xff000000
|
||||
#define APIC_ID_SHIFT 24
|
||||
#define APIC_ID_CLUSTER 0xf0
|
||||
#define APIC_ID_CLUSTER_ID 0x0f
|
||||
#define APIC_MAX_CLUSTER 0xe
|
||||
#define APIC_MAX_INTRACLUSTER_ID 3
|
||||
#define APIC_ID_CLUSTER_SHIFT 4
|
||||
|
||||
/* fields in VER */
|
||||
#define APIC_VER_VERSION 0x000000ff
|
||||
#define APIC_VER_MAXLVT 0x00ff0000
|
||||
#define MAXLVTSHIFT 16
|
||||
|
||||
/* fields in LDR */
|
||||
#define APIC_LDR_RESERVED 0x00ffffff
|
||||
|
||||
/* fields in DFR */
|
||||
#define APIC_DFR_RESERVED 0x0fffffff
|
||||
#define APIC_DFR_MODEL_MASK 0xf0000000
|
||||
#define APIC_DFR_MODEL_FLAT 0xf0000000
|
||||
#define APIC_DFR_MODEL_CLUSTER 0x00000000
|
||||
|
||||
/* fields in SVR */
|
||||
#define APIC_SVR_VECTOR 0x000000ff
|
||||
#define APIC_SVR_VEC_PROG 0x000000f0
|
||||
@ -290,10 +308,6 @@ typedef struct IOAPIC ioapic_t;
|
||||
|
||||
#define APIC_ICRLO_RESV_MASK (APIC_RESV1_MASK | APIC_RESV2_MASK)
|
||||
|
||||
/* fields in ICR_HIGH */
|
||||
#define APIC_ID_MASK 0xff000000
|
||||
#define APIC_ID_SHIFT 24
|
||||
|
||||
/* fields in LVT1/2 */
|
||||
#define APIC_LVT_VECTOR 0x000000ff
|
||||
#define APIC_LVT_DM 0x00000700
|
||||
@ -444,4 +458,4 @@ typedef struct IOAPIC ioapic_t;
|
||||
|
||||
#define IOART_INTVEC 0x000000ff /* R/W: INTerrupt vector field */
|
||||
|
||||
#endif /* _MACHINE_APIC_H_ */
|
||||
#endif /* _MACHINE_APICREG_H_ */
|
||||
|
165
sys/amd64/include/apicvar.h
Normal file
165
sys/amd64/include/apicvar.h
Normal file
@ -0,0 +1,165 @@
|
||||
/*-
|
||||
* Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _MACHINE_APICVAR_H_
|
||||
#define _MACHINE_APICVAR_H_
|
||||
|
||||
/*
|
||||
* Local && I/O APIC variable definitions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Layout of local APIC interrupt vectors:
|
||||
*
|
||||
* 0xff (255) +-------------+
|
||||
* | | 15 (Spurios Vector)
|
||||
* 0xf0 (240) +-------------+
|
||||
* | | 14 (Interprocessor Interrupts)
|
||||
* 0xe0 (224) +-------------+
|
||||
* | | 13 (Local Interrupt (LINT[01]))
|
||||
* 0xd0 (208) +-------------+
|
||||
* | | 12 (Local Timer and Error Interrupts)
|
||||
* 0xc0 (192) +-------------+
|
||||
* | | 11 (I/O Interrupts)
|
||||
* 0xb0 (176) +-------------+
|
||||
* | | 10 (I/O Interrupts)
|
||||
* 0xa0 (160) +-------------+
|
||||
* | | 9 (I/O Interrupts)
|
||||
* 0x90 (144) +-------------+
|
||||
* | | 8 (I/O Interrupts / System Calls)
|
||||
* 0x80 (128) +-------------+
|
||||
* | | 7 (I/O Interrupts)
|
||||
* 0x70 (112) +-------------+
|
||||
* | | 6 (I/O Interrupts)
|
||||
* 0x60 (96) +-------------+
|
||||
* | | 5 (I/O Interrupts)
|
||||
* 0x50 (80) +-------------+
|
||||
* | | 4 (I/O Interrupts)
|
||||
* 0x40 (64) +-------------+
|
||||
* | | 3 (I/O Interrupts)
|
||||
* 0x30 (48) +-------------+
|
||||
* | | 2 (I/O Interrupts)
|
||||
* 0x20 (32) +-------------+
|
||||
* | | 1 (Exceptions, traps, faults, etc.)
|
||||
* 0x10 (16) +-------------+
|
||||
* | | 0 (Exceptions, traps, faults, etc.)
|
||||
* 0x00 (0) +-------------+
|
||||
*
|
||||
* Note: 0x80 needs to be handled specially and not allocated to an
|
||||
* I/O device!
|
||||
*/
|
||||
|
||||
#define APIC_ID_ALL 0xff
|
||||
#define APIC_NUM_IOINTS 160
|
||||
|
||||
#define APIC_LOCAL_INTS (IDT_IO_INTS + APIC_NUM_IOINTS)
|
||||
#define APIC_TIMER_INT APIC_LOCAL_INTS
|
||||
#define APIC_ERROR_INT (APIC_LOCAL_INTS + 1)
|
||||
#define APIC_THERMAL_INT (APIC_LOCAL_INTS + 2)
|
||||
|
||||
#define APIC_IPI_INTS (APIC_LOCAL_INTS + 32)
|
||||
#define IPI_AST APIC_IPI_INTS /* Generate software trap. */
|
||||
#define IPI_INVLTLB (APIC_IPI_INTS + 1) /* TLB Shootdown IPIs */
|
||||
#define IPI_INVLPG (APIC_IPI_INTS + 2)
|
||||
#define IPI_INVLRNG (APIC_IPI_INTS + 3)
|
||||
#define IPI_HARDCLOCK (APIC_IPI_INTS + 8) /* Inter-CPU clock handling. */
|
||||
#define IPI_STATCLOCK (APIC_IPI_INTS + 9)
|
||||
#define IPI_RENDEZVOUS (APIC_IPI_INTS + 10) /* Inter-CPU rendezvous. */
|
||||
#define IPI_LAZYPMAP (APIC_IPI_INTS + 11) /* Lazy pmap release. */
|
||||
#define IPI_STOP (APIC_IPI_INTS + 12) /* Stop CPU until restarted. */
|
||||
|
||||
#define APIC_SPURIOUS_INT 255
|
||||
|
||||
#define LVT_LINT0 0
|
||||
#define LVT_LINT1 1
|
||||
#define LVT_TIMER 2
|
||||
#define LVT_ERROR 3
|
||||
#define LVT_PMC 4
|
||||
#define LVT_THERMAL 5
|
||||
#define LVT_MAX LVT_THERMAL
|
||||
|
||||
#ifndef LOCORE
|
||||
|
||||
#define APIC_IPI_DEST_SELF -1
|
||||
#define APIC_IPI_DEST_ALL -2
|
||||
#define APIC_IPI_DEST_OTHERS -3
|
||||
|
||||
/*
|
||||
* An APIC enumerator is a psuedo bus driver that enumerates APIC's including
|
||||
* CPU's and I/O APIC's.
|
||||
*/
|
||||
struct apic_enumerator {
|
||||
const char *apic_name;
|
||||
int (*apic_probe)(void);
|
||||
int (*apic_probe_cpus)(void);
|
||||
int (*apic_setup_local)(void);
|
||||
int (*apic_setup_io)(void);
|
||||
SLIST_ENTRY(apic_enumerator) apic_next;
|
||||
};
|
||||
|
||||
inthand_t
|
||||
IDTVEC(apic_isr1), IDTVEC(apic_isr2), IDTVEC(apic_isr3),
|
||||
IDTVEC(apic_isr4), IDTVEC(apic_isr5), IDTVEC(spuriousint);
|
||||
|
||||
u_int apic_irq_to_idt(u_int irq);
|
||||
u_int apic_idt_to_irq(u_int vector);
|
||||
void apic_register_enumerator(struct apic_enumerator *enumerator);
|
||||
void *ioapic_create(uintptr_t addr, int32_t id, int intbase);
|
||||
int ioapic_disable_pin(void *cookie, u_int pin);
|
||||
int ioapic_get_vector(void *cookie, u_int pin);
|
||||
int ioapic_next_logical_cluster(void);
|
||||
void ioapic_register(void *cookie);
|
||||
int ioapic_remap_vector(void *cookie, u_int pin, int vector);
|
||||
int ioapic_set_extint(void *cookie, u_int pin);
|
||||
int ioapic_set_nmi(void *cookie, u_int pin);
|
||||
int ioapic_set_polarity(void *cookie, u_int pin, char activehi);
|
||||
int ioapic_set_triggermode(void *cookie, u_int pin, char edgetrigger);
|
||||
int ioapic_set_smi(void *cookie, u_int pin);
|
||||
void lapic_create(u_int apic_id, int boot_cpu);
|
||||
void lapic_disable(void);
|
||||
void lapic_dump(const char *str);
|
||||
void lapic_enable_intr(u_int vector);
|
||||
int lapic_id(void);
|
||||
void lapic_init(uintptr_t addr);
|
||||
int lapic_intr_pending(u_int vector);
|
||||
void lapic_ipi_raw(register_t icrlo, u_int dest);
|
||||
void lapic_ipi_vectored(u_int vector, int dest);
|
||||
int lapic_ipi_wait(int delay);
|
||||
void lapic_handle_intr(struct intrframe frame);
|
||||
void lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id);
|
||||
int lapic_set_lvt_mask(u_int apic_id, u_int lvt, u_char masked);
|
||||
int lapic_set_lvt_mode(u_int apic_id, u_int lvt, u_int32_t mode);
|
||||
int lapic_set_lvt_polarity(u_int apic_id, u_int lvt, u_char activehi);
|
||||
int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt, u_char edgetrigger);
|
||||
void lapic_setup(void);
|
||||
|
||||
#endif /* !LOCORE */
|
||||
#endif /* _MACHINE_APICVAR_H_ */
|
@ -1,19 +1,53 @@
|
||||
/*
|
||||
/*-
|
||||
* Copyright (c) 1989, 1990 William F. Jolitz.
|
||||
* Copyright (c) 1990 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* from: vector.s, 386BSD 0.1 unknown origin
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <machine/apic.h>
|
||||
#include <machine/smp.h>
|
||||
/*
|
||||
* Interrupt entry points for external interrupts triggered by I/O APICs
|
||||
* as well as IPI handlers.
|
||||
*/
|
||||
|
||||
/* convert an absolute IRQ# into a bitmask */
|
||||
#define IRQ_BIT(irq_num) (1 << (irq_num))
|
||||
#include <machine/asmacros.h>
|
||||
#include <machine/apicreg.h>
|
||||
#include <machine/smptests.h>
|
||||
|
||||
/* make an index into the IO APIC from the IRQ# */
|
||||
#define REDTBL_IDX(irq_num) (0x10 + ((irq_num) * 2))
|
||||
#include "assym.s"
|
||||
|
||||
/*
|
||||
*
|
||||
* Macros to create and destroy a trap frame.
|
||||
*/
|
||||
#define PUSH_FRAME \
|
||||
pushl $0 ; /* dummy error code */ \
|
||||
@ -23,14 +57,6 @@
|
||||
pushl %es ; \
|
||||
pushl %fs
|
||||
|
||||
#define PUSH_DUMMY \
|
||||
pushfl ; /* eflags */ \
|
||||
pushl %cs ; /* cs */ \
|
||||
pushl 12(%esp) ; /* original caller eip */ \
|
||||
pushl $0 ; /* dummy error code */ \
|
||||
pushl $0 ; /* dummy trap type */ \
|
||||
subl $11*4,%esp ;
|
||||
|
||||
#define POP_FRAME \
|
||||
popl %fs ; \
|
||||
popl %es ; \
|
||||
@ -38,209 +64,40 @@
|
||||
popal ; \
|
||||
addl $4+4,%esp
|
||||
|
||||
#define POP_DUMMY \
|
||||
addl $16*4,%esp
|
||||
|
||||
#define IOAPICADDR(irq_num) CNAME(int_to_apicintpin) + 16 * (irq_num) + 8
|
||||
#define REDIRIDX(irq_num) CNAME(int_to_apicintpin) + 16 * (irq_num) + 12
|
||||
|
||||
#define MASK_IRQ(irq_num) \
|
||||
ICU_LOCK ; /* into critical reg */ \
|
||||
testl $IRQ_BIT(irq_num), apic_imen ; \
|
||||
jne 7f ; /* masked, don't mask */ \
|
||||
orl $IRQ_BIT(irq_num), apic_imen ; /* set the mask bit */ \
|
||||
movl IOAPICADDR(irq_num), %ecx ; /* ioapic addr */ \
|
||||
movl REDIRIDX(irq_num), %eax ; /* get the index */ \
|
||||
movl %eax, (%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 */ \
|
||||
7: ; /* already masked */ \
|
||||
ICU_UNLOCK
|
||||
/*
|
||||
* 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.
|
||||
* I/O Interrupt Entry Point. Rather than having one entry point for
|
||||
* each interrupt source, we use one entry point for each 32-bit word
|
||||
* in the ISR. The handler determines the highest bit set in the ISR,
|
||||
* translates that into a vector, and passes the vector to the
|
||||
* lapic_handle_intr() function.
|
||||
*/
|
||||
#define MASK_LEVEL_IRQ(irq_num) \
|
||||
testl $IRQ_BIT(irq_num), apic_pin_trigger ; \
|
||||
jz 9f ; /* edge, don't mask */ \
|
||||
MASK_IRQ(irq_num) ; \
|
||||
9:
|
||||
|
||||
|
||||
#ifdef APIC_INTR_REORDER
|
||||
#define EOI_IRQ(irq_num) \
|
||||
movl apic_isrbit_location + 8 * (irq_num), %eax ; \
|
||||
movl (%eax), %eax ; \
|
||||
testl apic_isrbit_location + 4 + 8 * (irq_num), %eax ; \
|
||||
jz 9f ; /* not active */ \
|
||||
movl $0, lapic+LA_EOI ; \
|
||||
9:
|
||||
|
||||
#else
|
||||
#define EOI_IRQ(irq_num) \
|
||||
testl $IRQ_BIT(irq_num), lapic+LA_ISR1; \
|
||||
jz 9f ; /* not active */ \
|
||||
movl $0, lapic+LA_EOI; \
|
||||
9:
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Test to see if the source is currently masked, clear if so.
|
||||
*/
|
||||
#define UNMASK_IRQ(irq_num) \
|
||||
ICU_LOCK ; /* into critical reg */ \
|
||||
testl $IRQ_BIT(irq_num), apic_imen ; \
|
||||
je 7f ; /* bit clear, not masked */ \
|
||||
andl $~IRQ_BIT(irq_num), apic_imen ;/* clear mask bit */ \
|
||||
movl IOAPICADDR(irq_num), %ecx ; /* ioapic addr */ \
|
||||
movl REDIRIDX(irq_num), %eax ; /* get the index */ \
|
||||
movl %eax, (%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 */ \
|
||||
7: ; /* already unmasked */ \
|
||||
ICU_UNLOCK
|
||||
|
||||
/*
|
||||
* Test to see whether we are handling an edge or level triggered INT.
|
||||
* Level-triggered INTs have to be unmasked.
|
||||
*/
|
||||
#define UNMASK_LEVEL_IRQ(irq_num) \
|
||||
testl $IRQ_BIT(irq_num), apic_pin_trigger ; \
|
||||
jz 9f ; /* edge, don't unmask */ \
|
||||
UNMASK_IRQ(irq_num) ; \
|
||||
9:
|
||||
|
||||
/*
|
||||
* Macros for interrupt entry, call to handler, and exit.
|
||||
*/
|
||||
|
||||
#define FAST_INTR(irq_num, vec_name) \
|
||||
#define ISR_VEC(index, vec_name) \
|
||||
.text ; \
|
||||
SUPERALIGN_TEXT ; \
|
||||
IDTVEC(vec_name) ; \
|
||||
PUSH_FRAME ; \
|
||||
movl $KDSEL,%eax ; \
|
||||
mov %ax,%ds ; \
|
||||
mov %ax,%es ; \
|
||||
movl $KPSEL,%eax ; \
|
||||
mov %ax,%fs ; \
|
||||
FAKE_MCOUNT(13*4(%esp)) ; \
|
||||
movl PCPU(CURTHREAD),%ebx ; \
|
||||
cmpl $0,TD_CRITNEST(%ebx) ; \
|
||||
je 1f ; \
|
||||
; \
|
||||
movl $1,PCPU(INT_PENDING) ; \
|
||||
orl $IRQ_BIT(irq_num),PCPU(FPENDING) ; \
|
||||
MASK_LEVEL_IRQ(irq_num) ; \
|
||||
movl $0, lapic+LA_EOI ; \
|
||||
jmp 10f ; \
|
||||
1: ; \
|
||||
incl TD_CRITNEST(%ebx) ; \
|
||||
incl TD_INTR_NESTING_LEVEL(%ebx) ; \
|
||||
pushl intr_unit + (irq_num) * 4 ; \
|
||||
call *intr_handler + (irq_num) * 4 ; /* do the work ASAP */ \
|
||||
addl $4, %esp ; \
|
||||
movl $0, lapic+LA_EOI ; \
|
||||
lock ; \
|
||||
incl cnt+V_INTR ; /* book-keeping can wait */ \
|
||||
movl intr_countp + (irq_num) * 4, %eax ; \
|
||||
lock ; \
|
||||
incl (%eax) ; \
|
||||
decl TD_CRITNEST(%ebx) ; \
|
||||
cmpl $0,PCPU(INT_PENDING) ; \
|
||||
je 2f ; \
|
||||
; \
|
||||
call i386_unpend ; \
|
||||
2: ; \
|
||||
decl TD_INTR_NESTING_LEVEL(%ebx) ; \
|
||||
10: ; \
|
||||
MEXITCOUNT ; \
|
||||
jmp doreti
|
||||
|
||||
/*
|
||||
* Restart a fast interrupt that was held up by a critical section.
|
||||
* This routine is called from unpend(). unpend() ensures we are
|
||||
* in a critical section and deals with the interrupt nesting level
|
||||
* for us. If we previously masked the irq, we have to unmask it.
|
||||
*
|
||||
* We have a choice. We can regenerate the irq using the 'int'
|
||||
* instruction or we can create a dummy frame and call the interrupt
|
||||
* handler directly. I've chosen to use the dummy-frame method.
|
||||
*/
|
||||
#define FAST_UNPEND(irq_num, vec_name) \
|
||||
.text ; \
|
||||
SUPERALIGN_TEXT ; \
|
||||
IDTVEC(vec_name) ; \
|
||||
; \
|
||||
pushl %ebp ; \
|
||||
movl %esp, %ebp ; \
|
||||
PUSH_DUMMY ; \
|
||||
pushl intr_unit + (irq_num) * 4 ; \
|
||||
call *intr_handler + (irq_num) * 4 ; /* do the work ASAP */ \
|
||||
addl $4, %esp ; \
|
||||
lock ; \
|
||||
incl cnt+V_INTR ; /* book-keeping can wait */ \
|
||||
movl intr_countp + (irq_num) * 4, %eax ; \
|
||||
lock ; \
|
||||
incl (%eax) ; \
|
||||
UNMASK_LEVEL_IRQ(irq_num) ; \
|
||||
POP_DUMMY ; \
|
||||
popl %ebp ; \
|
||||
ret ; \
|
||||
|
||||
|
||||
/*
|
||||
* Slow, threaded interrupts.
|
||||
*
|
||||
* XXX Most of the parameters here are obsolete. Fix this when we're
|
||||
* done.
|
||||
* XXX we really shouldn't return via doreti if we just schedule the
|
||||
* interrupt handler and don't run anything. We could just do an
|
||||
* iret. FIXME.
|
||||
*/
|
||||
#define INTR(irq_num, vec_name, maybe_extra_ipending) \
|
||||
.text ; \
|
||||
SUPERALIGN_TEXT ; \
|
||||
/* _XintrNN: entry point used by IDT/HWIs via _vec[]. */ \
|
||||
IDTVEC(vec_name) ; \
|
||||
PUSH_FRAME ; \
|
||||
movl $KDSEL, %eax ; /* reload with kernel's data segment */ \
|
||||
mov %ax, %ds ; \
|
||||
mov %ax, %es ; \
|
||||
movl $KPSEL, %eax ; \
|
||||
movl $KPSEL, %eax ; /* reload with per-CPU data segment */ \
|
||||
mov %ax, %fs ; \
|
||||
; \
|
||||
maybe_extra_ipending ; \
|
||||
; \
|
||||
MASK_LEVEL_IRQ(irq_num) ; \
|
||||
EOI_IRQ(irq_num) ; \
|
||||
; \
|
||||
movl PCPU(CURTHREAD),%ebx ; \
|
||||
cmpl $0,TD_CRITNEST(%ebx) ; \
|
||||
je 1f ; \
|
||||
movl $1,PCPU(INT_PENDING) ; \
|
||||
orl $IRQ_BIT(irq_num),PCPU(IPENDING) ; \
|
||||
jmp 10f ; \
|
||||
1: ; \
|
||||
movl lapic, %edx ; /* pointer to local APIC */ \
|
||||
movl PCPU(CURTHREAD), %ebx ; \
|
||||
movl LA_ISR + 16 * (index)(%edx), %eax ; /* load ISR */ \
|
||||
incl TD_INTR_NESTING_LEVEL(%ebx) ; \
|
||||
; \
|
||||
FAKE_MCOUNT(13*4(%esp)) ; /* XXX avoid dbl cnt */ \
|
||||
cmpl $0,PCPU(INT_PENDING) ; \
|
||||
je 9f ; \
|
||||
call i386_unpend ; \
|
||||
9: ; \
|
||||
pushl $irq_num; /* pass the IRQ */ \
|
||||
call sched_ithd ; \
|
||||
addl $4, %esp ; /* discard the parameter */ \
|
||||
; \
|
||||
bsrl %eax, %eax ; /* index of highset set bit in ISR */ \
|
||||
jz 2f ; \
|
||||
addl $(32 * index),%eax ; \
|
||||
1: ; \
|
||||
FAKE_MCOUNT(13*4(%esp)) ; /* XXX avoid double count */ \
|
||||
pushl %eax ; /* pass the IRQ */ \
|
||||
call lapic_handle_intr ; \
|
||||
addl $4, %esp ; /* discard parameter */ \
|
||||
decl TD_INTR_NESTING_LEVEL(%ebx) ; \
|
||||
10: ; \
|
||||
MEXITCOUNT ; \
|
||||
jmp doreti
|
||||
jmp doreti ; \
|
||||
2: movl $-1, %eax ; /* send a vector of -1 */ \
|
||||
jmp 1b
|
||||
|
||||
/*
|
||||
* Handle "spurious INTerrupts".
|
||||
@ -257,6 +114,14 @@ IDTVEC(spuriousint)
|
||||
|
||||
iret
|
||||
|
||||
MCOUNT_LABEL(bintr2)
|
||||
ISR_VEC(1,apic_isr1)
|
||||
ISR_VEC(2,apic_isr2)
|
||||
ISR_VEC(3,apic_isr3)
|
||||
ISR_VEC(4,apic_isr4)
|
||||
ISR_VEC(5,apic_isr5)
|
||||
MCOUNT_LABEL(eintr2)
|
||||
|
||||
#ifdef SMP
|
||||
/*
|
||||
* Global address space TLB shootdown.
|
||||
@ -281,7 +146,8 @@ IDTVEC(invltlb)
|
||||
movl %cr3, %eax /* invalidate the TLB */
|
||||
movl %eax, %cr3
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
|
||||
lock
|
||||
incl smp_tlb_wait
|
||||
@ -313,7 +179,8 @@ IDTVEC(invlpg)
|
||||
movl smp_tlb_addr1, %eax
|
||||
invlpg (%eax) /* invalidate single page */
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
|
||||
lock
|
||||
incl smp_tlb_wait
|
||||
@ -350,7 +217,8 @@ IDTVEC(invlrng)
|
||||
cmpl %eax, %edx
|
||||
jb 1b
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
|
||||
lock
|
||||
incl smp_tlb_wait
|
||||
@ -374,21 +242,15 @@ IDTVEC(hardclock)
|
||||
movl $KPSEL, %eax
|
||||
mov %ax, %fs
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %edx
|
||||
movl $0, LA_EOI(%edx) /* End Of Interrupt to APIC */
|
||||
|
||||
movl PCPU(CURTHREAD),%ebx
|
||||
cmpl $0,TD_CRITNEST(%ebx)
|
||||
je 1f
|
||||
movl $1,PCPU(INT_PENDING)
|
||||
orl $1,PCPU(SPENDING);
|
||||
jmp 10f
|
||||
1:
|
||||
incl TD_INTR_NESTING_LEVEL(%ebx)
|
||||
pushl $0 /* XXX convert trapframe to clockframe */
|
||||
call forwarded_hardclock
|
||||
addl $4, %esp /* XXX convert clockframe to trapframe */
|
||||
decl TD_INTR_NESTING_LEVEL(%ebx)
|
||||
10:
|
||||
MEXITCOUNT
|
||||
jmp doreti
|
||||
|
||||
@ -406,23 +268,17 @@ IDTVEC(statclock)
|
||||
movl $KPSEL, %eax
|
||||
mov %ax, %fs
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %edx
|
||||
movl $0, LA_EOI(%edx) /* End Of Interrupt to APIC */
|
||||
|
||||
FAKE_MCOUNT(13*4(%esp))
|
||||
|
||||
movl PCPU(CURTHREAD),%ebx
|
||||
cmpl $0,TD_CRITNEST(%ebx)
|
||||
je 1f
|
||||
movl $1,PCPU(INT_PENDING)
|
||||
orl $2,PCPU(SPENDING);
|
||||
jmp 10f
|
||||
1:
|
||||
incl TD_INTR_NESTING_LEVEL(%ebx)
|
||||
pushl $0 /* XXX convert trapframe to clockframe */
|
||||
call forwarded_statclock
|
||||
addl $4, %esp /* XXX convert clockframe to trapframe */
|
||||
decl TD_INTR_NESTING_LEVEL(%ebx)
|
||||
10:
|
||||
MEXITCOUNT
|
||||
jmp doreti
|
||||
|
||||
@ -444,7 +300,8 @@ IDTVEC(cpuast)
|
||||
movl $KPSEL, %eax
|
||||
mov %ax, %fs
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %edx
|
||||
movl $0, LA_EOI(%edx) /* End Of Interrupt to APIC */
|
||||
|
||||
FAKE_MCOUNT(13*4(%esp))
|
||||
|
||||
@ -476,7 +333,8 @@ IDTVEC(cpustop)
|
||||
movl $KPSEL, %eax
|
||||
mov %ax, %fs
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
|
||||
movl PCPU(CPUID), %eax
|
||||
imull $PCB_SIZE, %eax
|
||||
@ -518,111 +376,6 @@ IDTVEC(cpustop)
|
||||
popl %ebp
|
||||
iret
|
||||
|
||||
#endif /* SMP */
|
||||
|
||||
MCOUNT_LABEL(bintr)
|
||||
FAST_INTR(0,fastintr0)
|
||||
FAST_INTR(1,fastintr1)
|
||||
FAST_INTR(2,fastintr2)
|
||||
FAST_INTR(3,fastintr3)
|
||||
FAST_INTR(4,fastintr4)
|
||||
FAST_INTR(5,fastintr5)
|
||||
FAST_INTR(6,fastintr6)
|
||||
FAST_INTR(7,fastintr7)
|
||||
FAST_INTR(8,fastintr8)
|
||||
FAST_INTR(9,fastintr9)
|
||||
FAST_INTR(10,fastintr10)
|
||||
FAST_INTR(11,fastintr11)
|
||||
FAST_INTR(12,fastintr12)
|
||||
FAST_INTR(13,fastintr13)
|
||||
FAST_INTR(14,fastintr14)
|
||||
FAST_INTR(15,fastintr15)
|
||||
FAST_INTR(16,fastintr16)
|
||||
FAST_INTR(17,fastintr17)
|
||||
FAST_INTR(18,fastintr18)
|
||||
FAST_INTR(19,fastintr19)
|
||||
FAST_INTR(20,fastintr20)
|
||||
FAST_INTR(21,fastintr21)
|
||||
FAST_INTR(22,fastintr22)
|
||||
FAST_INTR(23,fastintr23)
|
||||
FAST_INTR(24,fastintr24)
|
||||
FAST_INTR(25,fastintr25)
|
||||
FAST_INTR(26,fastintr26)
|
||||
FAST_INTR(27,fastintr27)
|
||||
FAST_INTR(28,fastintr28)
|
||||
FAST_INTR(29,fastintr29)
|
||||
FAST_INTR(30,fastintr30)
|
||||
FAST_INTR(31,fastintr31)
|
||||
#define CLKINTR_PENDING movl $1,CNAME(clkintr_pending)
|
||||
/* Threaded interrupts */
|
||||
INTR(0,intr0, CLKINTR_PENDING)
|
||||
INTR(1,intr1,)
|
||||
INTR(2,intr2,)
|
||||
INTR(3,intr3,)
|
||||
INTR(4,intr4,)
|
||||
INTR(5,intr5,)
|
||||
INTR(6,intr6,)
|
||||
INTR(7,intr7,)
|
||||
INTR(8,intr8,)
|
||||
INTR(9,intr9,)
|
||||
INTR(10,intr10,)
|
||||
INTR(11,intr11,)
|
||||
INTR(12,intr12,)
|
||||
INTR(13,intr13,)
|
||||
INTR(14,intr14,)
|
||||
INTR(15,intr15,)
|
||||
INTR(16,intr16,)
|
||||
INTR(17,intr17,)
|
||||
INTR(18,intr18,)
|
||||
INTR(19,intr19,)
|
||||
INTR(20,intr20,)
|
||||
INTR(21,intr21,)
|
||||
INTR(22,intr22,)
|
||||
INTR(23,intr23,)
|
||||
INTR(24,intr24,)
|
||||
INTR(25,intr25,)
|
||||
INTR(26,intr26,)
|
||||
INTR(27,intr27,)
|
||||
INTR(28,intr28,)
|
||||
INTR(29,intr29,)
|
||||
INTR(30,intr30,)
|
||||
INTR(31,intr31,)
|
||||
|
||||
FAST_UNPEND(0,fastunpend0)
|
||||
FAST_UNPEND(1,fastunpend1)
|
||||
FAST_UNPEND(2,fastunpend2)
|
||||
FAST_UNPEND(3,fastunpend3)
|
||||
FAST_UNPEND(4,fastunpend4)
|
||||
FAST_UNPEND(5,fastunpend5)
|
||||
FAST_UNPEND(6,fastunpend6)
|
||||
FAST_UNPEND(7,fastunpend7)
|
||||
FAST_UNPEND(8,fastunpend8)
|
||||
FAST_UNPEND(9,fastunpend9)
|
||||
FAST_UNPEND(10,fastunpend10)
|
||||
FAST_UNPEND(11,fastunpend11)
|
||||
FAST_UNPEND(12,fastunpend12)
|
||||
FAST_UNPEND(13,fastunpend13)
|
||||
FAST_UNPEND(14,fastunpend14)
|
||||
FAST_UNPEND(15,fastunpend15)
|
||||
FAST_UNPEND(16,fastunpend16)
|
||||
FAST_UNPEND(17,fastunpend17)
|
||||
FAST_UNPEND(18,fastunpend18)
|
||||
FAST_UNPEND(19,fastunpend19)
|
||||
FAST_UNPEND(20,fastunpend20)
|
||||
FAST_UNPEND(21,fastunpend21)
|
||||
FAST_UNPEND(22,fastunpend22)
|
||||
FAST_UNPEND(23,fastunpend23)
|
||||
FAST_UNPEND(24,fastunpend24)
|
||||
FAST_UNPEND(25,fastunpend25)
|
||||
FAST_UNPEND(26,fastunpend26)
|
||||
FAST_UNPEND(27,fastunpend27)
|
||||
FAST_UNPEND(28,fastunpend28)
|
||||
FAST_UNPEND(29,fastunpend29)
|
||||
FAST_UNPEND(30,fastunpend30)
|
||||
FAST_UNPEND(31,fastunpend31)
|
||||
MCOUNT_LABEL(eintr)
|
||||
|
||||
#ifdef SMP
|
||||
/*
|
||||
* Executed by a CPU when it receives a RENDEZVOUS IPI from another CPU.
|
||||
*
|
||||
@ -640,7 +393,8 @@ IDTVEC(rendezvous)
|
||||
|
||||
call smp_rendezvous_action
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
POP_FRAME
|
||||
iret
|
||||
|
||||
@ -658,16 +412,9 @@ IDTVEC(lazypmap)
|
||||
mov %ax, %fs
|
||||
|
||||
call pmap_lazyfix_action
|
||||
|
||||
movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
|
||||
|
||||
movl lapic, %eax
|
||||
movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */
|
||||
POP_FRAME
|
||||
iret
|
||||
#endif /* SMP */
|
||||
|
||||
.data
|
||||
|
||||
.globl apic_pin_trigger
|
||||
apic_pin_trigger:
|
||||
.long 0
|
||||
|
||||
.text
|
||||
|
@ -474,7 +474,6 @@ init_6x86MX(void)
|
||||
static void
|
||||
init_ppro(void)
|
||||
{
|
||||
#ifndef SMP
|
||||
u_int64_t apicbase;
|
||||
|
||||
/*
|
||||
@ -483,7 +482,6 @@ init_ppro(void)
|
||||
apicbase = rdmsr(MSR_APICBASE);
|
||||
apicbase &= ~APICBASE_ENABLED;
|
||||
wrmsr(MSR_APICBASE, apicbase);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
691
sys/i386/i386/io_apic.c
Normal file
691
sys/i386/i386/io_apic.c
Normal file
@ -0,0 +1,691 @@
|
||||
/*-
|
||||
* Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_isa.h"
|
||||
#include "opt_no_mixed_mode.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <machine/apicreg.h>
|
||||
#include <machine/frame.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/apicvar.h>
|
||||
#include <machine/segments.h>
|
||||
|
||||
#if defined(DEV_ISA) && !defined(NO_MIXED_MODE)
|
||||
#define MIXED_MODE
|
||||
#endif
|
||||
|
||||
#define IOAPIC_ISA_INTS 16
|
||||
#define IOAPIC_MEM_REGION 32
|
||||
#define IOAPIC_REDTBL_LO(i) (IOAPIC_REDTBL + (i) * 2)
|
||||
#define IOAPIC_REDTBL_HI(i) (IOAPIC_REDTBL_LO(i) + 1)
|
||||
|
||||
#define VECTOR_EXTINT -1
|
||||
#define VECTOR_NMI -2
|
||||
#define VECTOR_SMI -3
|
||||
#define VECTOR_DISABLED -4
|
||||
|
||||
#define DEST_NONE -1
|
||||
#define DEST_EXTINT -2
|
||||
|
||||
#define TODO printf("%s: not implemented!\n", __func__)
|
||||
|
||||
MALLOC_DEFINE(M_IOAPIC, "I/O APIC", "I/O APIC structures");
|
||||
|
||||
/*
|
||||
* New interrupt support code..
|
||||
*
|
||||
* XXX: we really should have the interrupt cookie passed up from new-bus
|
||||
* just be a int pin, and not map 1:1 to interrupt vector number but should
|
||||
* use INTR_TYPE_FOO to set priority bands for device classes and do all the
|
||||
* magic remapping of intpin to vector in here. For now we just cheat as on
|
||||
* ia64 and map intpin X to vector NRSVIDT + X. Note that we assume that the
|
||||
* first IO APIC has ISA interrupts on pins 1-15. Not sure how you are
|
||||
* really supposed to figure out which IO APIC in a system with multiple IO
|
||||
* APIC's actually has the ISA interrupts routed to it. As far as interrupt
|
||||
* pin numbers, we use the ACPI System Interrupt number model where each
|
||||
* IO APIC has a contiguous chunk of the System Interrupt address space.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Direct the ExtINT pin on the first I/O APIC to a logical cluster of
|
||||
* CPUs rather than a physical destination of just the BSP.
|
||||
*
|
||||
* Note: This is disabled by default as test systems seem to croak with it
|
||||
* enabled.
|
||||
#define ENABLE_EXTINT_LOGICAL_DESTINATION
|
||||
*/
|
||||
|
||||
struct ioapic_intsrc {
|
||||
struct intsrc io_intsrc;
|
||||
int io_intpin:8;
|
||||
int io_vector:8;
|
||||
int io_activehi:1;
|
||||
int io_edgetrigger:1;
|
||||
int io_masked:1;
|
||||
int io_dest:5;
|
||||
};
|
||||
|
||||
struct ioapic {
|
||||
struct pic io_pic;
|
||||
u_int io_id:8; /* logical ID */
|
||||
u_int io_apic_id:4;
|
||||
u_int io_intbase:8; /* System Interrupt base */
|
||||
u_int io_numintr:8;
|
||||
volatile ioapic_t *io_addr; /* XXX: should use bus_space */
|
||||
STAILQ_ENTRY(ioapic) io_next;
|
||||
struct ioapic_intsrc io_pins[0];
|
||||
};
|
||||
|
||||
static STAILQ_HEAD(,ioapic) ioapic_list = STAILQ_HEAD_INITIALIZER(ioapic_list);
|
||||
static u_int next_id, program_logical_dest;
|
||||
|
||||
static u_int ioapic_read(volatile ioapic_t *apic, int reg);
|
||||
static void ioapic_write(volatile ioapic_t *apic, int reg, u_int val);
|
||||
static void ioapic_enable_source(struct intsrc *isrc);
|
||||
static void ioapic_disable_source(struct intsrc *isrc);
|
||||
static void ioapic_eoi_source(struct intsrc *isrc);
|
||||
static void ioapic_enable_intr(struct intsrc *isrc);
|
||||
static int ioapic_vector(struct intsrc *isrc);
|
||||
static int ioapic_source_pending(struct intsrc *isrc);
|
||||
static void ioapic_suspend(struct intsrc *isrc);
|
||||
static void ioapic_resume(struct intsrc *isrc);
|
||||
static void ioapic_program_destination(struct ioapic_intsrc *intpin);
|
||||
#ifdef MIXED_MODE
|
||||
static void ioapic_setup_mixed_mode(struct ioapic_intsrc *intpin);
|
||||
#endif
|
||||
|
||||
struct pic ioapic_template = { ioapic_enable_source, ioapic_disable_source,
|
||||
ioapic_eoi_source, ioapic_enable_intr,
|
||||
ioapic_vector, ioapic_source_pending,
|
||||
ioapic_suspend, ioapic_resume };
|
||||
|
||||
static int next_ioapic_base, logical_clusters, current_cluster;
|
||||
|
||||
static u_int
|
||||
ioapic_read(volatile ioapic_t *apic, int reg)
|
||||
{
|
||||
|
||||
mtx_assert(&icu_lock, MA_OWNED);
|
||||
apic->ioregsel = reg;
|
||||
return (apic->iowin);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_write(volatile ioapic_t *apic, int reg, u_int val)
|
||||
{
|
||||
|
||||
mtx_assert(&icu_lock, MA_OWNED);
|
||||
apic->ioregsel = reg;
|
||||
apic->iowin = val;
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_enable_source(struct intsrc *isrc)
|
||||
{
|
||||
struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
|
||||
struct ioapic *io = (struct ioapic *)isrc->is_pic;
|
||||
uint32_t flags;
|
||||
|
||||
mtx_lock_spin(&icu_lock);
|
||||
if (intpin->io_masked) {
|
||||
flags = ioapic_read(io->io_addr,
|
||||
IOAPIC_REDTBL_LO(intpin->io_intpin));
|
||||
flags &= ~(IOART_INTMASK);
|
||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin),
|
||||
flags);
|
||||
intpin->io_masked = 0;
|
||||
}
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_disable_source(struct intsrc *isrc)
|
||||
{
|
||||
struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
|
||||
struct ioapic *io = (struct ioapic *)isrc->is_pic;
|
||||
uint32_t flags;
|
||||
|
||||
mtx_lock_spin(&icu_lock);
|
||||
if (!intpin->io_masked && !intpin->io_edgetrigger) {
|
||||
flags = ioapic_read(io->io_addr,
|
||||
IOAPIC_REDTBL_LO(intpin->io_intpin));
|
||||
flags |= IOART_INTMSET;
|
||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin),
|
||||
flags);
|
||||
intpin->io_masked = 1;
|
||||
}
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_eoi_source(struct intsrc *isrc)
|
||||
{
|
||||
TODO;
|
||||
/* lapic_eoi(); */
|
||||
}
|
||||
|
||||
/*
|
||||
* Program an individual intpin's logical destination.
|
||||
*/
|
||||
static void
|
||||
ioapic_program_destination(struct ioapic_intsrc *intpin)
|
||||
{
|
||||
struct ioapic *io = (struct ioapic *)intpin->io_intsrc.is_pic;
|
||||
uint32_t value;
|
||||
|
||||
KASSERT(intpin->io_dest != DEST_NONE,
|
||||
("intpin not assigned to a cluster"));
|
||||
KASSERT(intpin->io_dest != DEST_EXTINT,
|
||||
("intpin routed via ExtINT"));
|
||||
if (bootverbose) {
|
||||
printf("ioapic%u: routing intpin %u (", io->io_id,
|
||||
intpin->io_intpin);
|
||||
if (intpin->io_vector == VECTOR_EXTINT)
|
||||
printf("ExtINT");
|
||||
else
|
||||
printf("IRQ %u", intpin->io_vector);
|
||||
printf(") to cluster %u\n", intpin->io_dest);
|
||||
}
|
||||
mtx_lock_spin(&icu_lock);
|
||||
value = ioapic_read(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin));
|
||||
value &= ~IOART_DESTMOD;
|
||||
value |= IOART_DESTLOG;
|
||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin), value);
|
||||
value = ioapic_read(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin));
|
||||
value &= ~IOART_DEST;
|
||||
value |= (intpin->io_dest << APIC_ID_CLUSTER_SHIFT |
|
||||
APIC_ID_CLUSTER_ID) << APIC_ID_SHIFT;
|
||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin), value);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_assign_cluster(struct ioapic_intsrc *intpin)
|
||||
{
|
||||
/*
|
||||
* Assign this intpin to a logical APIC cluster in a
|
||||
* round-robin fashion. We don't actually use the logical
|
||||
* destination for this intpin until after all the CPU's
|
||||
* have been started so that we don't end up with interrupts
|
||||
* that don't go anywhere. Another alternative might be to
|
||||
* start up the CPU's earlier so that they can handle interrupts
|
||||
* sooner.
|
||||
*/
|
||||
intpin->io_dest = current_cluster;
|
||||
current_cluster++;
|
||||
if (current_cluster >= logical_clusters)
|
||||
current_cluster = 0;
|
||||
if (program_logical_dest)
|
||||
ioapic_program_destination(intpin);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_enable_intr(struct intsrc *isrc)
|
||||
{
|
||||
struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
|
||||
|
||||
KASSERT(intpin->io_dest != DEST_EXTINT,
|
||||
("ExtINT pin trying to use ioapic enable_intr method"));
|
||||
if (intpin->io_dest == DEST_NONE) {
|
||||
ioapic_assign_cluster(intpin);
|
||||
lapic_enable_intr(intpin->io_vector);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ioapic_vector(struct intsrc *isrc)
|
||||
{
|
||||
struct ioapic_intsrc *pin;
|
||||
|
||||
pin = (struct ioapic_intsrc *)isrc;
|
||||
return (pin->io_vector);
|
||||
}
|
||||
|
||||
static int
|
||||
ioapic_source_pending(struct intsrc *isrc)
|
||||
{
|
||||
struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
|
||||
|
||||
return (lapic_intr_pending(intpin->io_vector));
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_suspend(struct intsrc *isrc)
|
||||
{
|
||||
|
||||
TODO;
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_resume(struct intsrc *isrc)
|
||||
{
|
||||
|
||||
TODO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and return a logical cluster ID. Note that the first time
|
||||
* this is called, it returns cluster 0. ioapic_enable_intr() treats
|
||||
* the two cases of logical_clusters == 0 and logical_clusters == 1 the
|
||||
* same: one cluster of ID 0 exists. The logical_clusters == 0 case is
|
||||
* for UP kernels, which should never call this function.
|
||||
*/
|
||||
int
|
||||
ioapic_next_logical_cluster(void)
|
||||
{
|
||||
|
||||
if (logical_clusters >= APIC_MAX_CLUSTER)
|
||||
panic("WARNING: Local APIC cluster IDs exhausted!");
|
||||
return (logical_clusters++);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a plain I/O APIC object.
|
||||
*/
|
||||
void *
|
||||
ioapic_create(uintptr_t addr, int32_t apic_id, int intbase)
|
||||
{
|
||||
struct ioapic *io;
|
||||
struct ioapic_intsrc *intpin;
|
||||
volatile ioapic_t *apic;
|
||||
u_int numintr, i;
|
||||
uint32_t value;
|
||||
|
||||
apic = (ioapic_t *)pmap_mapdev(addr, IOAPIC_MEM_REGION);
|
||||
mtx_lock_spin(&icu_lock);
|
||||
numintr = ((ioapic_read(apic, IOAPIC_VER) & IOART_VER_MAXREDIR) >>
|
||||
MAXREDIRSHIFT) + 1;
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
io = malloc(sizeof(struct ioapic) +
|
||||
numintr * sizeof(struct ioapic_intsrc), M_IOAPIC, M_WAITOK);
|
||||
io->io_pic = ioapic_template;
|
||||
mtx_lock_spin(&icu_lock);
|
||||
io->io_id = next_id++;
|
||||
io->io_apic_id = ioapic_read(apic, IOAPIC_ID) >> APIC_ID_SHIFT;
|
||||
if (apic_id != -1 && io->io_apic_id != apic_id) {
|
||||
ioapic_write(apic, IOAPIC_ID, apic_id << APIC_ID_SHIFT);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
io->io_apic_id = apic_id;
|
||||
printf("ioapic%u: Changing APIC ID to %d\n", io->io_id,
|
||||
apic_id);
|
||||
} else
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
if (intbase == -1) {
|
||||
intbase = next_ioapic_base;
|
||||
printf("ioapic%u: Assuming intbase of %d\n", io->io_id,
|
||||
intbase);
|
||||
} else if (intbase != next_ioapic_base)
|
||||
printf("ioapic%u: WARNING: intbase %d != expected base %d\n",
|
||||
io->io_id, intbase, next_ioapic_base);
|
||||
io->io_intbase = intbase;
|
||||
next_ioapic_base += numintr;
|
||||
io->io_numintr = numintr;
|
||||
io->io_addr = apic;
|
||||
|
||||
/*
|
||||
* Initialize pins. Start off with interrupts disabled. Default
|
||||
* to active-hi and edge-triggered for ISA interrupts and active-lo
|
||||
* and level-triggered for all others.
|
||||
*/
|
||||
bzero(io->io_pins, sizeof(struct ioapic_intsrc) * numintr);
|
||||
mtx_lock_spin(&icu_lock);
|
||||
for (i = 0, intpin = io->io_pins; i < numintr; i++, intpin++) {
|
||||
intpin->io_intsrc.is_pic = (struct pic *)io;
|
||||
intpin->io_intpin = i;
|
||||
intpin->io_vector = intbase + i;
|
||||
|
||||
/*
|
||||
* Assume that pin 0 on the first IO APIC is an ExtINT pin by
|
||||
* default. Assume that intpins 1-15 are ISA interrupts and
|
||||
* use suitable defaults for those. Assume that all other
|
||||
* intpins are PCI interrupts. Enable the ExtINT pin by
|
||||
* default but mask all other pins.
|
||||
*/
|
||||
if (intpin->io_vector == 0) {
|
||||
intpin->io_activehi = 1;
|
||||
intpin->io_edgetrigger = 1;
|
||||
intpin->io_vector = VECTOR_EXTINT;
|
||||
intpin->io_masked = 0;
|
||||
} else if (intpin->io_vector < IOAPIC_ISA_INTS) {
|
||||
intpin->io_activehi = 1;
|
||||
intpin->io_edgetrigger = 1;
|
||||
intpin->io_masked = 1;
|
||||
} else {
|
||||
intpin->io_activehi = 0;
|
||||
intpin->io_edgetrigger = 0;
|
||||
intpin->io_masked = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start off without a logical cluster destination until
|
||||
* the pin is enabled.
|
||||
*/
|
||||
intpin->io_dest = DEST_NONE;
|
||||
if (bootverbose) {
|
||||
printf("ioapic%u: intpin %d -> ", io->io_id, i);
|
||||
if (intpin->io_vector == VECTOR_EXTINT)
|
||||
printf("ExtINT\n");
|
||||
else
|
||||
printf("irq %d\n", intpin->io_vector);
|
||||
}
|
||||
value = ioapic_read(apic, IOAPIC_REDTBL_LO(i));
|
||||
ioapic_write(apic, IOAPIC_REDTBL_LO(i), value | IOART_INTMSET);
|
||||
}
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
|
||||
return (io);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_get_vector(void *cookie, u_int pin)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (-1);
|
||||
return (io->io_pins[pin].io_vector);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_disable_pin(void *cookie, u_int pin)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector == VECTOR_DISABLED)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_vector = VECTOR_DISABLED;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: intpin %d disabled\n", io->io_id, pin);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_remap_vector(void *cookie, u_int pin, int vector)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr || vector < 0)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_vector = vector;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: Routing IRQ %d -> intpin %d\n", io->io_id,
|
||||
vector, pin);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_set_nmi(void *cookie, u_int pin)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_vector = VECTOR_NMI;
|
||||
io->io_pins[pin].io_masked = 0;
|
||||
io->io_pins[pin].io_edgetrigger = 1;
|
||||
io->io_pins[pin].io_activehi = 1;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: Routing NMI -> intpin %d\n",
|
||||
io->io_id, pin);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_set_smi(void *cookie, u_int pin)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_vector = VECTOR_SMI;
|
||||
io->io_pins[pin].io_masked = 0;
|
||||
io->io_pins[pin].io_edgetrigger = 1;
|
||||
io->io_pins[pin].io_activehi = 1;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: Routing SMI -> intpin %d\n",
|
||||
io->io_id, pin);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_set_extint(void *cookie, u_int pin)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_vector = VECTOR_EXTINT;
|
||||
io->io_pins[pin].io_masked = 0;
|
||||
io->io_pins[pin].io_edgetrigger = 1;
|
||||
io->io_pins[pin].io_activehi = 1;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: Routing external 8259A's -> intpin %d\n",
|
||||
io->io_id, pin);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_set_polarity(void *cookie, u_int pin, char activehi)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_activehi = activehi;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: intpin %d polarity: %s\n", io->io_id, pin,
|
||||
activehi ? "active-hi" : "active-lo");
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ioapic_set_triggermode(void *cookie, u_int pin, char edgetrigger)
|
||||
{
|
||||
struct ioapic *io;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
if (pin >= io->io_numintr)
|
||||
return (EINVAL);
|
||||
if (io->io_pins[pin].io_vector < 0)
|
||||
return (EINVAL);
|
||||
io->io_pins[pin].io_edgetrigger = edgetrigger;
|
||||
if (bootverbose)
|
||||
printf("ioapic%u: intpin %d trigger: %s\n", io->io_id, pin,
|
||||
edgetrigger ? "edge" : "level");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a complete I/O APIC object with the interrupt subsystem.
|
||||
*/
|
||||
void
|
||||
ioapic_register(void *cookie)
|
||||
{
|
||||
struct ioapic_intsrc *pin;
|
||||
struct ioapic *io;
|
||||
volatile ioapic_t *apic;
|
||||
uint32_t flags;
|
||||
int i;
|
||||
|
||||
io = (struct ioapic *)cookie;
|
||||
apic = io->io_addr;
|
||||
mtx_lock_spin(&icu_lock);
|
||||
flags = ioapic_read(apic, IOAPIC_VER) & IOART_VER_VERSION;
|
||||
STAILQ_INSERT_TAIL(&ioapic_list, io, io_next);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
printf("ioapic%u <Version %u> irqs %u-%u on motherboard\n", io->io_id,
|
||||
flags, io->io_intbase, io->io_intbase + io->io_numintr - 1);
|
||||
for (i = 0, pin = io->io_pins; i < io->io_numintr; i++, pin++) {
|
||||
/*
|
||||
* Finish initializing the pins by programming the vectors
|
||||
* and delivery mode.
|
||||
*/
|
||||
if (pin->io_vector == VECTOR_DISABLED)
|
||||
continue;
|
||||
flags = IOART_DESTPHY;
|
||||
if (pin->io_edgetrigger)
|
||||
flags |= IOART_TRGREDG;
|
||||
else
|
||||
flags |= IOART_TRGRLVL;
|
||||
if (pin->io_activehi)
|
||||
flags |= IOART_INTAHI;
|
||||
else
|
||||
flags |= IOART_INTALO;
|
||||
if (pin->io_masked)
|
||||
flags |= IOART_INTMSET;
|
||||
switch (pin->io_vector) {
|
||||
case VECTOR_EXTINT:
|
||||
KASSERT(pin->io_edgetrigger,
|
||||
("EXTINT not edge triggered"));
|
||||
flags |= IOART_DELEXINT;
|
||||
break;
|
||||
case VECTOR_NMI:
|
||||
KASSERT(pin->io_edgetrigger,
|
||||
("NMI not edge triggered"));
|
||||
flags |= IOART_DELNMI;
|
||||
break;
|
||||
case VECTOR_SMI:
|
||||
KASSERT(pin->io_edgetrigger,
|
||||
("SMI not edge triggered"));
|
||||
flags |= IOART_DELSMI;
|
||||
break;
|
||||
default:
|
||||
flags |= IOART_DELLOPRI |
|
||||
apic_irq_to_idt(pin->io_vector);
|
||||
}
|
||||
mtx_lock_spin(&icu_lock);
|
||||
ioapic_write(apic, IOAPIC_REDTBL_LO(i), flags);
|
||||
|
||||
/*
|
||||
* Route interrupts to the BSP by default using physical
|
||||
* addressing. Vectored interrupts get readdressed using
|
||||
* logical IDs to CPU clusters when they are enabled.
|
||||
*/
|
||||
flags = ioapic_read(apic, IOAPIC_REDTBL_HI(i));
|
||||
flags &= ~IOART_DEST;
|
||||
flags |= PCPU_GET(apic_id) << APIC_ID_SHIFT;
|
||||
ioapic_write(apic, IOAPIC_REDTBL_HI(i), flags);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
if (pin->io_vector >= 0) {
|
||||
#ifdef MIXED_MODE
|
||||
/* Route IRQ0 via the 8259A using mixed mode. */
|
||||
if (pin->io_vector == 0)
|
||||
ioapic_setup_mixed_mode(pin);
|
||||
else
|
||||
#endif
|
||||
intr_register_source(&pin->io_intsrc);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Program all the intpins to use logical destinations once the AP's
|
||||
* have been launched.
|
||||
*/
|
||||
static void
|
||||
ioapic_set_logical_destinations(void *arg __unused)
|
||||
{
|
||||
struct ioapic *io;
|
||||
int i;
|
||||
|
||||
program_logical_dest = 1;
|
||||
STAILQ_FOREACH(io, &ioapic_list, io_next)
|
||||
for (i = 0; i < io->io_numintr; i++)
|
||||
if (io->io_pins[i].io_dest != DEST_NONE &&
|
||||
io->io_pins[i].io_dest != DEST_EXTINT)
|
||||
ioapic_program_destination(&io->io_pins[i]);
|
||||
}
|
||||
SYSINIT(ioapic_destinations, SI_SUB_SMP, SI_ORDER_SECOND,
|
||||
ioapic_set_logical_destinations, NULL)
|
||||
|
||||
#ifdef MIXED_MODE
|
||||
/*
|
||||
* Support for mixed-mode interrupt sources. These sources route an ISA
|
||||
* IRQ through the 8259A's via the ExtINT on pin 0 of the I/O APIC that
|
||||
* routes the ISA interrupts. We just ignore the intpins that use this
|
||||
* mode and allow the atpic driver to register its interrupt source for
|
||||
* that IRQ instead.
|
||||
*/
|
||||
|
||||
void
|
||||
ioapic_setup_mixed_mode(struct ioapic_intsrc *intpin)
|
||||
{
|
||||
struct ioapic_intsrc *extint;
|
||||
struct ioapic *io;
|
||||
|
||||
/*
|
||||
* Mark the associated I/O APIC intpin as being delivered via
|
||||
* ExtINT and enable the ExtINT pin on the I/O APIC if needed.
|
||||
*/
|
||||
intpin->io_dest = DEST_EXTINT;
|
||||
io = (struct ioapic *)intpin->io_intsrc.is_pic;
|
||||
extint = &io->io_pins[0];
|
||||
if (extint->io_vector != VECTOR_EXTINT)
|
||||
panic("Can't find ExtINT pin to route through!");
|
||||
#ifdef ENABLE_EXTINT_LOGICAL_DESTINATION
|
||||
if (extint->io_dest == DEST_NONE)
|
||||
ioapic_assign_cluster(extint);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* MIXED_MODE */
|
758
sys/i386/i386/local_apic.c
Normal file
758
sys/i386/i386/local_apic.c
Normal file
@ -0,0 +1,758 @@
|
||||
/*-
|
||||
* Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
|
||||
* Copyright (c) 1996, by Steve Passe
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. The name of the developer may NOT be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
* 3. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Local APIC support on Pentium and later processors.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/pcpu.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <machine/apicreg.h>
|
||||
#include <machine/cputypes.h>
|
||||
#include <machine/frame.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/apicvar.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/smp.h>
|
||||
#include <machine/specialreg.h>
|
||||
|
||||
/*
|
||||
* We can handle up to 60 APICs via our logical cluster IDs, but currently
|
||||
* the physical IDs on Intel processors up to the Pentium 4 are limited to
|
||||
* 16.
|
||||
*/
|
||||
#define MAX_APICID 16
|
||||
|
||||
/*
|
||||
* Support for local APICs. Local APICs manage interrupts on each
|
||||
* individual processor as opposed to I/O APICs which receive interrupts
|
||||
* from I/O devices and then forward them on to the local APICs.
|
||||
*
|
||||
* Local APICs can also send interrupts to each other thus providing the
|
||||
* mechanism for IPIs.
|
||||
*/
|
||||
|
||||
struct lvt {
|
||||
u_int lvt_edgetrigger:1;
|
||||
u_int lvt_activehi:1;
|
||||
u_int lvt_masked:1;
|
||||
u_int lvt_active:1;
|
||||
u_int lvt_mode:16;
|
||||
u_int lvt_vector:8;
|
||||
};
|
||||
|
||||
struct lapic {
|
||||
struct lvt la_lvts[LVT_MAX + 1];
|
||||
u_int la_id:8;
|
||||
u_int la_cluster:4;
|
||||
u_int la_cluster_id:2;
|
||||
u_int la_present:1;
|
||||
} static lapics[MAX_APICID];
|
||||
|
||||
/* XXX: should thermal be an NMI? */
|
||||
|
||||
/* Global defaults for local APIC LVT entries. */
|
||||
static struct lvt lvts[LVT_MAX + 1] = {
|
||||
{ 1, 1, 1, 1, APIC_LVT_DM_EXTINT, 0 }, /* LINT0: masked ExtINT */
|
||||
{ 1, 1, 0, 1, APIC_LVT_DM_NMI, 0 }, /* LINT1: NMI */
|
||||
{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* Timer: needs a vector */
|
||||
{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* Error: needs a vector */
|
||||
{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* PMC */
|
||||
{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* Thermal: needs a vector */
|
||||
};
|
||||
|
||||
static inthand_t *ioint_handlers[] = {
|
||||
NULL, /* 0 - 31 */
|
||||
IDTVEC(apic_isr1), /* 32 - 63 */
|
||||
IDTVEC(apic_isr2), /* 64 - 95 */
|
||||
IDTVEC(apic_isr3), /* 96 - 127 */
|
||||
IDTVEC(apic_isr4), /* 128 - 159 */
|
||||
IDTVEC(apic_isr5), /* 160 - 191 */
|
||||
NULL, /* 192 - 223 */
|
||||
NULL /* 224 - 255 */
|
||||
};
|
||||
|
||||
volatile lapic_t *lapic;
|
||||
|
||||
static uint32_t
|
||||
lvt_mode(struct lapic *la, u_int pin, uint32_t value)
|
||||
{
|
||||
struct lvt *lvt;
|
||||
|
||||
KASSERT(pin <= LVT_MAX, ("%s: pin %u out of range", __func__, pin));
|
||||
if (la->la_lvts[pin].lvt_active)
|
||||
lvt = &la->la_lvts[pin];
|
||||
else
|
||||
lvt = &lvts[pin];
|
||||
|
||||
value &= ~(APIC_LVT_M | APIC_LVT_TM | APIC_LVT_IIPP | APIC_LVT_DM |
|
||||
APIC_LVT_VECTOR);
|
||||
if (lvt->lvt_edgetrigger == 0)
|
||||
value |= APIC_LVT_TM;
|
||||
if (lvt->lvt_activehi == 0)
|
||||
value |= APIC_LVT_IIPP_INTALO;
|
||||
if (lvt->lvt_masked)
|
||||
value |= APIC_LVT_M;
|
||||
value |= lvt->lvt_mode;
|
||||
switch (lvt->lvt_mode) {
|
||||
case APIC_LVT_DM_NMI:
|
||||
case APIC_LVT_DM_SMI:
|
||||
case APIC_LVT_DM_INIT:
|
||||
case APIC_LVT_DM_EXTINT:
|
||||
if (!lvt->lvt_edgetrigger) {
|
||||
printf("lapic%u: Forcing LINT%u to edge trigger\n",
|
||||
la->la_id, pin);
|
||||
value |= APIC_LVT_TM;
|
||||
}
|
||||
/* Use a vector of 0. */
|
||||
break;
|
||||
case APIC_LVT_DM_FIXED:
|
||||
#if 0
|
||||
value |= lvt->lvt_vector;
|
||||
#else
|
||||
panic("Fixed LINT pins not supported");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
panic("bad APIC LVT delivery mode: %#x\n", value);
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map the local APIC and setup necessary interrupt vectors.
|
||||
*/
|
||||
void
|
||||
lapic_init(uintptr_t addr)
|
||||
{
|
||||
u_int32_t value;
|
||||
|
||||
/* Map the local APIC and setup the spurious interrupt handler. */
|
||||
KASSERT(trunc_page(addr) == addr,
|
||||
("local APIC not aligned on a page boundary"));
|
||||
lapic = (lapic_t *)pmap_mapdev(addr, sizeof(lapic_t));
|
||||
setidt(APIC_SPURIOUS_INT, IDTVEC(spuriousint), SDT_SYS386IGT, SEL_KPL,
|
||||
GSEL(GCODE_SEL, SEL_KPL));
|
||||
|
||||
/* Perform basic initialization of the BSP's local APIC. */
|
||||
value = lapic->svr;
|
||||
value &= ~(APIC_SVR_VECTOR | APIC_SVR_FOCUS);
|
||||
value |= (APIC_SVR_FEN | APIC_SVR_SWEN | APIC_SPURIOUS_INT);
|
||||
lapic->svr = value;
|
||||
|
||||
/* Set BSP's per-CPU local APIC ID. */
|
||||
PCPU_SET(apic_id, lapic_id());
|
||||
|
||||
/* XXX: timer/error/thermal interrupts */
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a local APIC instance.
|
||||
*/
|
||||
void
|
||||
lapic_create(u_int apic_id, int boot_cpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (apic_id > MAX_APICID) {
|
||||
printf("APIC: Ignoring local APIC with ID %d\n", apic_id);
|
||||
if (boot_cpu)
|
||||
panic("Can't ignore BSP");
|
||||
return;
|
||||
}
|
||||
KASSERT(!lapics[apic_id].la_present, ("duplicate local APIC %u",
|
||||
apic_id));
|
||||
|
||||
/*
|
||||
* Assume no local LVT overrides and a cluster of 0 and
|
||||
* intra-cluster ID of 0.
|
||||
*/
|
||||
lapics[apic_id].la_present = 1;
|
||||
lapics[apic_id].la_id = apic_id;
|
||||
for (i = 0; i < LVT_MAX; i++) {
|
||||
lapics[apic_id].la_lvts[i] = lvts[i];
|
||||
lapics[apic_id].la_lvts[i].lvt_active = 0;
|
||||
}
|
||||
|
||||
#ifdef SMP
|
||||
cpu_add(apic_id, boot_cpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump contents of local APIC registers
|
||||
*/
|
||||
void
|
||||
lapic_dump(const char* str)
|
||||
{
|
||||
|
||||
printf("cpu%d %s:\n", PCPU_GET(cpuid), str);
|
||||
printf(" ID: 0x%08x VER: 0x%08x LDR: 0x%08x DFR: 0x%08x\n",
|
||||
lapic->id, lapic->version, lapic->ldr, lapic->dfr);
|
||||
printf(" lint0: 0x%08x lint1: 0x%08x TPR: 0x%08x SVR: 0x%08x\n",
|
||||
lapic->lvt_lint0, lapic->lvt_lint1, lapic->tpr, lapic->svr);
|
||||
}
|
||||
|
||||
void
|
||||
lapic_enable_intr(u_int irq)
|
||||
{
|
||||
u_int vector;
|
||||
|
||||
vector = apic_irq_to_idt(irq);
|
||||
KASSERT(vector != IDT_SYSCALL, ("Attempt to overwrite syscall entry"));
|
||||
KASSERT(ioint_handlers[vector / 32] != NULL,
|
||||
("No ISR handler for IRQ %u", irq));
|
||||
setidt(vector, ioint_handlers[vector / 32], SDT_SYS386IGT, SEL_KPL,
|
||||
GSEL(GCODE_SEL, SEL_KPL));
|
||||
}
|
||||
|
||||
void
|
||||
lapic_setup(void)
|
||||
{
|
||||
struct lapic *la;
|
||||
u_int32_t value, maxlvt;
|
||||
register_t eflags;
|
||||
|
||||
la = &lapics[lapic_id()];
|
||||
KASSERT(la->la_present, ("missing APIC structure"));
|
||||
eflags = intr_disable();
|
||||
maxlvt = (lapic->version & APIC_VER_MAXLVT) >> MAXLVTSHIFT;
|
||||
|
||||
/* Program LINT[01] LVT entries. */
|
||||
lapic->lvt_lint0 = lvt_mode(la, LVT_LINT0, lapic->lvt_lint0);
|
||||
lapic->lvt_lint1 = lvt_mode(la, LVT_LINT1, lapic->lvt_lint1);
|
||||
|
||||
/* XXX: more LVT entries */
|
||||
|
||||
/* Clear the TPR. */
|
||||
value = lapic->tpr;
|
||||
value &= ~APIC_TPR_PRIO;
|
||||
lapic->tpr = value;
|
||||
|
||||
/* Use the cluster model for logical IDs. */
|
||||
value = lapic->dfr;
|
||||
value &= ~APIC_DFR_MODEL_MASK;
|
||||
value |= APIC_DFR_MODEL_CLUSTER;
|
||||
lapic->dfr = value;
|
||||
|
||||
/* Set this APIC's logical ID. */
|
||||
value = lapic->ldr;
|
||||
value &= ~APIC_ID_MASK;
|
||||
value |= (la->la_cluster << APIC_ID_CLUSTER_SHIFT |
|
||||
1 << la->la_cluster_id) << APIC_ID_SHIFT;
|
||||
lapic->ldr = value;
|
||||
|
||||
/* Setup spurious vector and enable the local APIC. */
|
||||
value = lapic->svr;
|
||||
value &= ~(APIC_SVR_VECTOR | APIC_SVR_FOCUS);
|
||||
value |= (APIC_SVR_FEN | APIC_SVR_SWEN | APIC_SPURIOUS_INT);
|
||||
lapic->svr = value;
|
||||
intr_restore(eflags);
|
||||
}
|
||||
|
||||
void
|
||||
lapic_disable(void)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
/* Software disable the local APIC. */
|
||||
value = lapic->svr;
|
||||
value &= ~APIC_SVR_SWEN;
|
||||
lapic->svr = value;
|
||||
}
|
||||
|
||||
int
|
||||
lapic_id(void)
|
||||
{
|
||||
|
||||
KASSERT(lapic != NULL, ("local APIC is not mapped"));
|
||||
return (lapic->id >> APIC_ID_SHIFT);
|
||||
}
|
||||
|
||||
int
|
||||
lapic_intr_pending(u_int vector)
|
||||
{
|
||||
volatile u_int32_t *irr;
|
||||
|
||||
/*
|
||||
* The IRR registers are an array of 128-bit registers each of
|
||||
* which only describes 32 interrupts in the low 32 bits.. Thus,
|
||||
* we divide the vector by 32 to get the 128-bit index. We then
|
||||
* multiply that index by 4 to get the equivalent index from
|
||||
* treating the IRR as an array of 32-bit registers. Finally, we
|
||||
* modulus the vector by 32 to determine the individual bit to
|
||||
* test.
|
||||
*/
|
||||
irr = &lapic->irr0;
|
||||
return (irr[(vector / 32) * 4] & 1 << (vector % 32));
|
||||
}
|
||||
|
||||
void
|
||||
lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id)
|
||||
{
|
||||
struct lapic *la;
|
||||
|
||||
KASSERT(lapics[apic_id].la_present, ("%s: APIC %u doesn't exist",
|
||||
__func__, apic_id));
|
||||
KASSERT(cluster <= APIC_MAX_CLUSTER, ("%s: cluster %u too big",
|
||||
__func__, cluster));
|
||||
KASSERT(cluster_id <= APIC_MAX_INTRACLUSTER_ID,
|
||||
("%s: intra cluster id %u too big", __func__, cluster_id));
|
||||
la = &lapics[apic_id];
|
||||
la->la_cluster = cluster;
|
||||
la->la_cluster_id = cluster_id;
|
||||
}
|
||||
|
||||
int
|
||||
lapic_set_lvt_mask(u_int apic_id, u_int pin, u_char masked)
|
||||
{
|
||||
|
||||
if (pin > LVT_MAX)
|
||||
return (EINVAL);
|
||||
if (apic_id == APIC_ID_ALL) {
|
||||
lvts[pin].lvt_masked = masked;
|
||||
if (bootverbose)
|
||||
printf("lapic:");
|
||||
} else {
|
||||
KASSERT(lapics[apic_id].la_present,
|
||||
("%s: missing APIC %u", __func__, apic_id));
|
||||
lapics[apic_id].la_lvts[pin].lvt_masked = masked;
|
||||
lapics[apic_id].la_lvts[pin].lvt_active = 1;
|
||||
if (bootverbose)
|
||||
printf("lapic%u:", apic_id);
|
||||
}
|
||||
if (bootverbose)
|
||||
printf(" LINT%u %s\n", pin, masked ? "masked" : "unmasked");
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lapic_set_lvt_mode(u_int apic_id, u_int pin, u_int32_t mode)
|
||||
{
|
||||
struct lvt *lvt;
|
||||
|
||||
if (pin > LVT_MAX)
|
||||
return (EINVAL);
|
||||
if (apic_id == APIC_ID_ALL) {
|
||||
lvt = &lvts[pin];
|
||||
if (bootverbose)
|
||||
printf("lapic:");
|
||||
} else {
|
||||
KASSERT(lapics[apic_id].la_present,
|
||||
("%s: missing APIC %u", __func__, apic_id));
|
||||
lvt = &lapics[apic_id].la_lvts[pin];
|
||||
lvt->lvt_active = 1;
|
||||
if (bootverbose)
|
||||
printf("lapic%u:", apic_id);
|
||||
}
|
||||
lvt->lvt_mode = mode;
|
||||
switch (mode) {
|
||||
case APIC_LVT_DM_NMI:
|
||||
case APIC_LVT_DM_SMI:
|
||||
case APIC_LVT_DM_INIT:
|
||||
case APIC_LVT_DM_EXTINT:
|
||||
lvt->lvt_edgetrigger = 1;
|
||||
lvt->lvt_activehi = 1;
|
||||
if (mode == APIC_LVT_DM_EXTINT)
|
||||
lvt->lvt_masked = 1;
|
||||
else
|
||||
lvt->lvt_masked = 0;
|
||||
break;
|
||||
default:
|
||||
panic("Unsupported delivery mode: 0x%x\n", mode);
|
||||
}
|
||||
if (bootverbose) {
|
||||
printf(" Routing ");
|
||||
switch (mode) {
|
||||
case APIC_LVT_DM_NMI:
|
||||
printf("NMI");
|
||||
break;
|
||||
case APIC_LVT_DM_SMI:
|
||||
printf("SMI");
|
||||
break;
|
||||
case APIC_LVT_DM_INIT:
|
||||
printf("INIT");
|
||||
break;
|
||||
case APIC_LVT_DM_EXTINT:
|
||||
printf("ExtINT");
|
||||
break;
|
||||
}
|
||||
printf(" -> LINT%u\n", pin);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lapic_set_lvt_polarity(u_int apic_id, u_int pin, u_char activehi)
|
||||
{
|
||||
|
||||
if (pin > LVT_MAX)
|
||||
return (EINVAL);
|
||||
if (apic_id == APIC_ID_ALL) {
|
||||
lvts[pin].lvt_activehi = activehi;
|
||||
if (bootverbose)
|
||||
printf("lapic:");
|
||||
} else {
|
||||
KASSERT(lapics[apic_id].la_present,
|
||||
("%s: missing APIC %u", __func__, apic_id));
|
||||
lapics[apic_id].la_lvts[pin].lvt_active = 1;
|
||||
lapics[apic_id].la_lvts[pin].lvt_activehi = activehi;
|
||||
if (bootverbose)
|
||||
printf("lapic%u:", apic_id);
|
||||
}
|
||||
if (bootverbose)
|
||||
printf(" LINT%u polarity: active-%s\n", pin,
|
||||
activehi ? "hi" : "lo");
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lapic_set_lvt_triggermode(u_int apic_id, u_int pin, u_char edgetrigger)
|
||||
{
|
||||
|
||||
if (pin > LVT_MAX)
|
||||
return (EINVAL);
|
||||
if (apic_id == APIC_ID_ALL) {
|
||||
lvts[pin].lvt_edgetrigger = edgetrigger;
|
||||
if (bootverbose)
|
||||
printf("lapic:");
|
||||
} else {
|
||||
KASSERT(lapics[apic_id].la_present,
|
||||
("%s: missing APIC %u", __func__, apic_id));
|
||||
lapics[apic_id].la_lvts[pin].lvt_edgetrigger = edgetrigger;
|
||||
lapics[apic_id].la_lvts[pin].lvt_active = 1;
|
||||
if (bootverbose)
|
||||
printf("lapic%u:", apic_id);
|
||||
}
|
||||
if (bootverbose)
|
||||
printf(" LINT%u trigger: %s\n", pin,
|
||||
edgetrigger ? "edge" : "level");
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
lapic_handle_intr(struct intrframe frame)
|
||||
{
|
||||
struct intsrc *isrc;
|
||||
|
||||
if (frame.if_vec == -1)
|
||||
panic("Couldn't get vector from ISR!");
|
||||
isrc = intr_lookup_source(apic_idt_to_irq(frame.if_vec));
|
||||
isrc->is_pic->pic_disable_source(isrc);
|
||||
lapic->eoi = 0;
|
||||
intr_execute_handlers(isrc, &frame);
|
||||
}
|
||||
|
||||
/* Translate between IDT vectors and IRQ vectors. */
|
||||
u_int
|
||||
apic_irq_to_idt(u_int irq)
|
||||
{
|
||||
u_int vector;
|
||||
|
||||
KASSERT(irq < NUM_IO_INTS, ("Invalid IRQ %u", irq));
|
||||
vector = irq + IDT_IO_INTS;
|
||||
if (vector >= IDT_SYSCALL)
|
||||
vector++;
|
||||
return (vector);
|
||||
}
|
||||
|
||||
u_int
|
||||
apic_idt_to_irq(u_int vector)
|
||||
{
|
||||
|
||||
KASSERT(vector >= IDT_IO_INTS && vector != IDT_SYSCALL &&
|
||||
vector <= IDT_IO_INTS + NUM_IO_INTS,
|
||||
("Vector %u does not map to an IRQ line", vector));
|
||||
if (vector > IDT_SYSCALL)
|
||||
vector--;
|
||||
return (vector - IDT_IO_INTS);
|
||||
}
|
||||
|
||||
/*
|
||||
* APIC probing support code. This includes code to manage enumerators.
|
||||
*/
|
||||
|
||||
static SLIST_HEAD(, apic_enumerator) enumerators =
|
||||
SLIST_HEAD_INITIALIZER(enumerators);
|
||||
static struct apic_enumerator *best_enum;
|
||||
|
||||
void
|
||||
apic_register_enumerator(struct apic_enumerator *enumerator)
|
||||
{
|
||||
#ifdef INVARIANTS
|
||||
struct apic_enumerator *apic_enum;
|
||||
|
||||
SLIST_FOREACH(apic_enum, &enumerators, apic_next) {
|
||||
if (apic_enum == enumerator)
|
||||
panic("%s: Duplicate register of %s", __func__,
|
||||
enumerator->apic_name);
|
||||
}
|
||||
#endif
|
||||
SLIST_INSERT_HEAD(&enumerators, enumerator, apic_next);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to look for CPU's very, very early because certain subsystems
|
||||
* want to know how many CPU's we have extremely early on in the boot
|
||||
* process.
|
||||
*/
|
||||
static void
|
||||
apic_init(void *dummy __unused)
|
||||
{
|
||||
struct apic_enumerator *enumerator;
|
||||
int retval, best;
|
||||
|
||||
/* We only support built in local APICs. */
|
||||
if (!(cpu_feature & CPUID_APIC))
|
||||
return;
|
||||
|
||||
/* First, probe all the enumerators to find the best match. */
|
||||
best_enum = NULL;
|
||||
best = 0;
|
||||
SLIST_FOREACH(enumerator, &enumerators, apic_next) {
|
||||
retval = enumerator->apic_probe();
|
||||
if (retval > 0)
|
||||
continue;
|
||||
if (best_enum == NULL || best < retval) {
|
||||
best_enum = enumerator;
|
||||
best = retval;
|
||||
}
|
||||
}
|
||||
if (best_enum == NULL) {
|
||||
if (bootverbose)
|
||||
printf("APIC: Could not find any APICs.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bootverbose)
|
||||
printf("APIC: Using the %s enumerator.\n",
|
||||
best_enum->apic_name);
|
||||
|
||||
/* Second, probe the CPU's in the system. */
|
||||
retval = best_enum->apic_probe_cpus();
|
||||
if (retval != 0)
|
||||
printf("%s: Failed to probe CPUs: returned %d\n",
|
||||
best_enum->apic_name, retval);
|
||||
}
|
||||
SYSINIT(apic_init, SI_SUB_TUNABLES - 1, SI_ORDER_SECOND, apic_init, NULL)
|
||||
|
||||
/*
|
||||
* Setup the local APIC. We have to do this prior to starting up the APs
|
||||
* in the SMP case.
|
||||
*/
|
||||
static void
|
||||
apic_setup_local(void *dummy __unused)
|
||||
{
|
||||
int retval;
|
||||
uint64_t apic_base;
|
||||
|
||||
if (best_enum == NULL)
|
||||
return;
|
||||
/*
|
||||
* To work around an errata, we disable the local APIC on some
|
||||
* CPUs during early startup. We need to turn the local APIC back
|
||||
* on on such CPUs now.
|
||||
*/
|
||||
if (cpu == CPU_686 && strcmp(cpu_vendor, "GenuineIntel") == 0 &&
|
||||
(cpu_id & 0xff0) == 0x610) {
|
||||
apic_base = rdmsr(MSR_APICBASE);
|
||||
apic_base |= APICBASE_ENABLED;
|
||||
wrmsr(MSR_APICBASE, apic_base);
|
||||
}
|
||||
retval = best_enum->apic_setup_local();
|
||||
if (retval != 0)
|
||||
printf("%s: Failed to setup the local APIC: returned %d\n",
|
||||
best_enum->apic_name, retval);
|
||||
}
|
||||
SYSINIT(apic_setup_local, SI_SUB_CPU, SI_ORDER_FIRST, apic_setup_local, NULL)
|
||||
|
||||
/*
|
||||
* Setup the I/O APICs.
|
||||
*/
|
||||
static void
|
||||
apic_setup_io(void *dummy __unused)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (best_enum == NULL)
|
||||
return;
|
||||
retval = best_enum->apic_setup_io();
|
||||
if (retval != 0)
|
||||
printf("%s: Failed to setup I/O APICs: returned %d\n",
|
||||
best_enum->apic_name, retval);
|
||||
|
||||
/*
|
||||
* Finish setting up the local APIC on the BSP once we know how to
|
||||
* properly program the LINT pins.
|
||||
*/
|
||||
lapic_setup();
|
||||
if (bootverbose)
|
||||
lapic_dump("BSP");
|
||||
}
|
||||
SYSINIT(apic_setup_io, SI_SUB_INTR, SI_ORDER_SECOND, apic_setup_io, NULL)
|
||||
|
||||
#ifdef SMP
|
||||
/*
|
||||
* Inter Processor Interrupt functions. The lapic_ipi_*() functions are
|
||||
* private the sys/i386 code. The public interface for the rest of the
|
||||
* kernel is defined in mp_machdep.c.
|
||||
*/
|
||||
#define DETECT_DEADLOCK
|
||||
|
||||
int
|
||||
lapic_ipi_wait(int delay)
|
||||
{
|
||||
int x, incr;
|
||||
|
||||
/*
|
||||
* Wait delay loops for IPI to be sent. This is highly bogus
|
||||
* since this is sensitive to CPU clock speed. If delay is
|
||||
* -1, we wait forever.
|
||||
*/
|
||||
if (delay == -1) {
|
||||
incr = 0;
|
||||
delay = 1;
|
||||
} else
|
||||
incr = 1;
|
||||
for (x = 0; x < delay; x += incr) {
|
||||
if ((lapic->icr_lo & APIC_DELSTAT_MASK) == APIC_DELSTAT_IDLE)
|
||||
return (1);
|
||||
ia32_pause();
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
lapic_ipi_raw(register_t icrlo, u_int dest)
|
||||
{
|
||||
register_t value, eflags;
|
||||
|
||||
/* XXX: Need more sanity checking of icrlo? */
|
||||
KASSERT(lapic != NULL, ("%s called too early", __func__));
|
||||
KASSERT((dest & ~(APIC_ID_MASK >> APIC_ID_SHIFT)) == 0,
|
||||
("%s: invalid dest field", __func__));
|
||||
KASSERT((icrlo & APIC_ICRLO_RESV_MASK) == 0,
|
||||
("%s: reserved bits set in ICR LO register", __func__));
|
||||
|
||||
/* Set destination in ICR HI register if it is being used. */
|
||||
eflags = intr_disable();
|
||||
if ((icrlo & APIC_DEST_MASK) == APIC_DEST_DESTFLD) {
|
||||
value = lapic->icr_hi;
|
||||
value &= ~APIC_ID_MASK;
|
||||
value |= dest << APIC_ID_SHIFT;
|
||||
lapic->icr_hi = value;
|
||||
}
|
||||
|
||||
/* Program the contents of the IPI and dispatch it. */
|
||||
value = lapic->icr_lo;
|
||||
value &= APIC_ICRLO_RESV_MASK;
|
||||
value |= icrlo;
|
||||
lapic->icr_lo = value;
|
||||
intr_restore(eflags);
|
||||
}
|
||||
|
||||
#ifdef DETECT_DEADLOCK
|
||||
#define BEFORE_SPIN 1000000
|
||||
#define AFTER_SPIN 1000
|
||||
#endif
|
||||
|
||||
void
|
||||
lapic_ipi_vectored(u_int vector, int dest)
|
||||
{
|
||||
register_t icrlo, destfield;
|
||||
|
||||
KASSERT((vector & ~APIC_VECTOR_MASK) == 0,
|
||||
("%s: invalid vector %d", __func__, vector));
|
||||
|
||||
icrlo = vector | APIC_DELMODE_FIXED | APIC_DESTMODE_PHY |
|
||||
APIC_LEVEL_DEASSERT | APIC_TRIGMOD_EDGE;
|
||||
destfield = 0;
|
||||
switch (dest) {
|
||||
case APIC_IPI_DEST_SELF:
|
||||
icrlo |= APIC_DEST_SELF;
|
||||
break;
|
||||
case APIC_IPI_DEST_ALL:
|
||||
icrlo |= APIC_DEST_ALLISELF;
|
||||
break;
|
||||
case APIC_IPI_DEST_OTHERS:
|
||||
icrlo |= APIC_DEST_ALLESELF;
|
||||
break;
|
||||
default:
|
||||
KASSERT((dest & ~(APIC_ID_MASK >> APIC_ID_SHIFT)) == 0,
|
||||
("%s: invalid destination 0x%x", __func__, dest));
|
||||
destfield = dest;
|
||||
}
|
||||
|
||||
#ifdef DETECT_DEADLOCK
|
||||
/* Check for an earlier stuck IPI. */
|
||||
if (!lapic_ipi_wait(BEFORE_SPIN))
|
||||
panic("APIC: Previous IPI is stuck");
|
||||
#endif
|
||||
|
||||
lapic_ipi_raw(icrlo, destfield);
|
||||
|
||||
#ifdef DETECT_DEADLOCK
|
||||
/* Wait for IPI to be delivered. */
|
||||
if (!lapic_ipi_wait(AFTER_SPIN)) {
|
||||
#ifdef needsattention
|
||||
/*
|
||||
* XXX FIXME:
|
||||
*
|
||||
* The above function waits for the message to actually be
|
||||
* delivered. It breaks out after an arbitrary timeout
|
||||
* since the message should eventually be delivered (at
|
||||
* least in theory) and that if it wasn't we would catch
|
||||
* the failure with the check above when the next IPI is
|
||||
* sent.
|
||||
*
|
||||
* We could skiip this wait entirely, EXCEPT it probably
|
||||
* protects us from other routines that assume that the
|
||||
* message was delivered and acted upon when this function
|
||||
* returns.
|
||||
*/
|
||||
printf("APIC: IPI might be stuck\n");
|
||||
#else /* !needsattention */
|
||||
/* Wait until mesage is sent without a timeout. */
|
||||
while (lapic->icr_lo & APIC_DELSTAT_PEND)
|
||||
ia32_pause();
|
||||
#endif /* needsattention */
|
||||
}
|
||||
#endif /* DETECT_DEADLOCK */
|
||||
}
|
||||
#endif /* SMP */
|
@ -82,9 +82,8 @@
|
||||
* This is "constructed" in locore.s on the BSP and in mp_machdep.c
|
||||
* for each AP. DO NOT REORDER THESE WITHOUT UPDATING THE REST!
|
||||
*/
|
||||
.globl SMP_prvspace, lapic
|
||||
.globl SMP_prvspace
|
||||
.set SMP_prvspace,(MPPTDI << PDRSHIFT)
|
||||
.set lapic,SMP_prvspace + (NPTEPG-1) * PAGE_SIZE
|
||||
#endif /* SMP */
|
||||
|
||||
/*
|
||||
|
@ -82,6 +82,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <vm/vm_extern.h>
|
||||
|
||||
#include <machine/cpu.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/pcb.h>
|
||||
#ifdef SMP
|
||||
@ -90,9 +91,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/tss.h>
|
||||
#include <machine/vm86.h>
|
||||
|
||||
#include <i386/isa/icu.h>
|
||||
#include <i386/isa/intr_machdep.h>
|
||||
|
||||
#ifdef POWERFAIL_NMI
|
||||
#include <sys/syslog.h>
|
||||
#include <machine/clock.h>
|
||||
@ -764,7 +762,7 @@ trap_fatal(frame, eva)
|
||||
#ifdef SMP
|
||||
/* two separate prints in case of a trap on an unmapped page */
|
||||
printf("cpuid = %d; ", PCPU_GET(cpuid));
|
||||
printf("lapic.id = %08x\n", lapic.id);
|
||||
printf("apic id = %02x\n", PCPU_GET(apic_id));
|
||||
#endif
|
||||
if (type == T_PAGEFLT) {
|
||||
printf("fault virtual address = 0x%x\n", eva);
|
||||
@ -847,7 +845,7 @@ dblfault_handler()
|
||||
#ifdef SMP
|
||||
/* two separate prints in case of a trap on an unmapped page */
|
||||
printf("cpuid = %d; ", PCPU_GET(cpuid));
|
||||
printf("lapic.id = %08x\n", lapic.id);
|
||||
printf("apic id = %02x\n", PCPU_GET(apic_id));
|
||||
#endif
|
||||
panic("double fault");
|
||||
}
|
||||
|
@ -25,8 +25,8 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _MACHINE_APIC_H_
|
||||
#define _MACHINE_APIC_H_
|
||||
#ifndef _MACHINE_APICREG_H_
|
||||
#define _MACHINE_APICREG_H_
|
||||
|
||||
/*
|
||||
* Local && I/O APIC definitions.
|
||||
@ -221,11 +221,29 @@ typedef struct IOAPIC ioapic_t;
|
||||
/* default physical locations of LOCAL (CPU) APICs */
|
||||
#define DEFAULT_APIC_BASE 0xfee00000
|
||||
|
||||
/* constants relating to APIC ID registers */
|
||||
#define APIC_ID_MASK 0xff000000
|
||||
#define APIC_ID_SHIFT 24
|
||||
#define APIC_ID_CLUSTER 0xf0
|
||||
#define APIC_ID_CLUSTER_ID 0x0f
|
||||
#define APIC_MAX_CLUSTER 0xe
|
||||
#define APIC_MAX_INTRACLUSTER_ID 3
|
||||
#define APIC_ID_CLUSTER_SHIFT 4
|
||||
|
||||
/* fields in VER */
|
||||
#define APIC_VER_VERSION 0x000000ff
|
||||
#define APIC_VER_MAXLVT 0x00ff0000
|
||||
#define MAXLVTSHIFT 16
|
||||
|
||||
/* fields in LDR */
|
||||
#define APIC_LDR_RESERVED 0x00ffffff
|
||||
|
||||
/* fields in DFR */
|
||||
#define APIC_DFR_RESERVED 0x0fffffff
|
||||
#define APIC_DFR_MODEL_MASK 0xf0000000
|
||||
#define APIC_DFR_MODEL_FLAT 0xf0000000
|
||||
#define APIC_DFR_MODEL_CLUSTER 0x00000000
|
||||
|
||||
/* fields in SVR */
|
||||
#define APIC_SVR_VECTOR 0x000000ff
|
||||
#define APIC_SVR_VEC_PROG 0x000000f0
|
||||
@ -290,10 +308,6 @@ typedef struct IOAPIC ioapic_t;
|
||||
|
||||
#define APIC_ICRLO_RESV_MASK (APIC_RESV1_MASK | APIC_RESV2_MASK)
|
||||
|
||||
/* fields in ICR_HIGH */
|
||||
#define APIC_ID_MASK 0xff000000
|
||||
#define APIC_ID_SHIFT 24
|
||||
|
||||
/* fields in LVT1/2 */
|
||||
#define APIC_LVT_VECTOR 0x000000ff
|
||||
#define APIC_LVT_DM 0x00000700
|
||||
@ -444,4 +458,4 @@ typedef struct IOAPIC ioapic_t;
|
||||
|
||||
#define IOART_INTVEC 0x000000ff /* R/W: INTerrupt vector field */
|
||||
|
||||
#endif /* _MACHINE_APIC_H_ */
|
||||
#endif /* _MACHINE_APICREG_H_ */
|
||||
|
165
sys/i386/include/apicvar.h
Normal file
165
sys/i386/include/apicvar.h
Normal file
@ -0,0 +1,165 @@
|
||||
/*-
|
||||
* Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _MACHINE_APICVAR_H_
|
||||
#define _MACHINE_APICVAR_H_
|
||||
|
||||
/*
|
||||
* Local && I/O APIC variable definitions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Layout of local APIC interrupt vectors:
|
||||
*
|
||||
* 0xff (255) +-------------+
|
||||
* | | 15 (Spurios Vector)
|
||||
* 0xf0 (240) +-------------+
|
||||
* | | 14 (Interprocessor Interrupts)
|
||||
* 0xe0 (224) +-------------+
|
||||
* | | 13 (Local Interrupt (LINT[01]))
|
||||
* 0xd0 (208) +-------------+
|
||||
* | | 12 (Local Timer and Error Interrupts)
|
||||
* 0xc0 (192) +-------------+
|
||||
* | | 11 (I/O Interrupts)
|
||||
* 0xb0 (176) +-------------+
|
||||
* | | 10 (I/O Interrupts)
|
||||
* 0xa0 (160) +-------------+
|
||||
* | | 9 (I/O Interrupts)
|
||||
* 0x90 (144) +-------------+
|
||||
* | | 8 (I/O Interrupts / System Calls)
|
||||
* 0x80 (128) +-------------+
|
||||
* | | 7 (I/O Interrupts)
|
||||
* 0x70 (112) +-------------+
|
||||
* | | 6 (I/O Interrupts)
|
||||
* 0x60 (96) +-------------+
|
||||
* | | 5 (I/O Interrupts)
|
||||
* 0x50 (80) +-------------+
|
||||
* | | 4 (I/O Interrupts)
|
||||
* 0x40 (64) +-------------+
|
||||
* | | 3 (I/O Interrupts)
|
||||
* 0x30 (48) +-------------+
|
||||
* | | 2 (I/O Interrupts)
|
||||
* 0x20 (32) +-------------+
|
||||
* | | 1 (Exceptions, traps, faults, etc.)
|
||||
* 0x10 (16) +-------------+
|
||||
* | | 0 (Exceptions, traps, faults, etc.)
|
||||
* 0x00 (0) +-------------+
|
||||
*
|
||||
* Note: 0x80 needs to be handled specially and not allocated to an
|
||||
* I/O device!
|
||||
*/
|
||||
|
||||
#define APIC_ID_ALL 0xff
|
||||
#define APIC_NUM_IOINTS 160
|
||||
|
||||
#define APIC_LOCAL_INTS (IDT_IO_INTS + APIC_NUM_IOINTS)
|
||||
#define APIC_TIMER_INT APIC_LOCAL_INTS
|
||||
#define APIC_ERROR_INT (APIC_LOCAL_INTS + 1)
|
||||
#define APIC_THERMAL_INT (APIC_LOCAL_INTS + 2)
|
||||
|
||||
#define APIC_IPI_INTS (APIC_LOCAL_INTS + 32)
|
||||
#define IPI_AST APIC_IPI_INTS /* Generate software trap. */
|
||||
#define IPI_INVLTLB (APIC_IPI_INTS + 1) /* TLB Shootdown IPIs */
|
||||
#define IPI_INVLPG (APIC_IPI_INTS + 2)
|
||||
#define IPI_INVLRNG (APIC_IPI_INTS + 3)
|
||||
#define IPI_HARDCLOCK (APIC_IPI_INTS + 8) /* Inter-CPU clock handling. */
|
||||
#define IPI_STATCLOCK (APIC_IPI_INTS + 9)
|
||||
#define IPI_RENDEZVOUS (APIC_IPI_INTS + 10) /* Inter-CPU rendezvous. */
|
||||
#define IPI_LAZYPMAP (APIC_IPI_INTS + 11) /* Lazy pmap release. */
|
||||
#define IPI_STOP (APIC_IPI_INTS + 12) /* Stop CPU until restarted. */
|
||||
|
||||
#define APIC_SPURIOUS_INT 255
|
||||
|
||||
#define LVT_LINT0 0
|
||||
#define LVT_LINT1 1
|
||||
#define LVT_TIMER 2
|
||||
#define LVT_ERROR 3
|
||||
#define LVT_PMC 4
|
||||
#define LVT_THERMAL 5
|
||||
#define LVT_MAX LVT_THERMAL
|
||||
|
||||
#ifndef LOCORE
|
||||
|
||||
#define APIC_IPI_DEST_SELF -1
|
||||
#define APIC_IPI_DEST_ALL -2
|
||||
#define APIC_IPI_DEST_OTHERS -3
|
||||
|
||||
/*
|
||||
* An APIC enumerator is a psuedo bus driver that enumerates APIC's including
|
||||
* CPU's and I/O APIC's.
|
||||
*/
|
||||
struct apic_enumerator {
|
||||
const char *apic_name;
|
||||
int (*apic_probe)(void);
|
||||
int (*apic_probe_cpus)(void);
|
||||
int (*apic_setup_local)(void);
|
||||
int (*apic_setup_io)(void);
|
||||
SLIST_ENTRY(apic_enumerator) apic_next;
|
||||
};
|
||||
|
||||
inthand_t
|
||||
IDTVEC(apic_isr1), IDTVEC(apic_isr2), IDTVEC(apic_isr3),
|
||||
IDTVEC(apic_isr4), IDTVEC(apic_isr5), IDTVEC(spuriousint);
|
||||
|
||||
u_int apic_irq_to_idt(u_int irq);
|
||||
u_int apic_idt_to_irq(u_int vector);
|
||||
void apic_register_enumerator(struct apic_enumerator *enumerator);
|
||||
void *ioapic_create(uintptr_t addr, int32_t id, int intbase);
|
||||
int ioapic_disable_pin(void *cookie, u_int pin);
|
||||
int ioapic_get_vector(void *cookie, u_int pin);
|
||||
int ioapic_next_logical_cluster(void);
|
||||
void ioapic_register(void *cookie);
|
||||
int ioapic_remap_vector(void *cookie, u_int pin, int vector);
|
||||
int ioapic_set_extint(void *cookie, u_int pin);
|
||||
int ioapic_set_nmi(void *cookie, u_int pin);
|
||||
int ioapic_set_polarity(void *cookie, u_int pin, char activehi);
|
||||
int ioapic_set_triggermode(void *cookie, u_int pin, char edgetrigger);
|
||||
int ioapic_set_smi(void *cookie, u_int pin);
|
||||
void lapic_create(u_int apic_id, int boot_cpu);
|
||||
void lapic_disable(void);
|
||||
void lapic_dump(const char *str);
|
||||
void lapic_enable_intr(u_int vector);
|
||||
int lapic_id(void);
|
||||
void lapic_init(uintptr_t addr);
|
||||
int lapic_intr_pending(u_int vector);
|
||||
void lapic_ipi_raw(register_t icrlo, u_int dest);
|
||||
void lapic_ipi_vectored(u_int vector, int dest);
|
||||
int lapic_ipi_wait(int delay);
|
||||
void lapic_handle_intr(struct intrframe frame);
|
||||
void lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id);
|
||||
int lapic_set_lvt_mask(u_int apic_id, u_int lvt, u_char masked);
|
||||
int lapic_set_lvt_mode(u_int apic_id, u_int lvt, u_int32_t mode);
|
||||
int lapic_set_lvt_polarity(u_int apic_id, u_int lvt, u_char activehi);
|
||||
int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt, u_char edgetrigger);
|
||||
void lapic_setup(void);
|
||||
|
||||
#endif /* !LOCORE */
|
||||
#endif /* _MACHINE_APICVAR_H_ */
|
@ -24,9 +24,6 @@ extern int timer0_max_count;
|
||||
extern uint64_t tsc_freq;
|
||||
extern int tsc_is_broken;
|
||||
extern int wall_cmos_clock;
|
||||
#ifdef APIC_IO
|
||||
extern int apic_8254_intr;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Driver to clock driver interface.
|
||||
|
@ -48,10 +48,8 @@
|
||||
struct segment_descriptor pc_common_tssd; \
|
||||
struct segment_descriptor *pc_tss_gdt; \
|
||||
int pc_currentldt; \
|
||||
u_int32_t pc_int_pending; /* master int pending flag */ \
|
||||
u_int32_t pc_ipending; /* pending slow interrupts */ \
|
||||
u_int32_t pc_fpending; /* pending fast interrupts */ \
|
||||
u_int32_t pc_spending /* pending soft interrupts */
|
||||
u_int pc_acpi_id; \
|
||||
u_int pc_apic_id;
|
||||
|
||||
#if defined(lint)
|
||||
|
||||
|
@ -71,14 +71,12 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/clock.h>
|
||||
#include <machine/cputypes.h>
|
||||
#include <machine/frame.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/psl.h>
|
||||
#ifdef APIC_IO
|
||||
#include <machine/segments.h>
|
||||
#endif
|
||||
#if defined(SMP) || defined(APIC_IO)
|
||||
#if defined(SMP)
|
||||
#include <machine/smp.h>
|
||||
#endif /* SMP || APIC_IO */
|
||||
#endif
|
||||
#include <machine/specialreg.h>
|
||||
|
||||
#include <i386/isa/icu.h>
|
||||
@ -89,20 +87,10 @@ __FBSDID("$FreeBSD$");
|
||||
#endif
|
||||
#include <i386/isa/timerreg.h>
|
||||
|
||||
#include <i386/isa/intr_machdep.h>
|
||||
|
||||
#ifdef DEV_MCA
|
||||
#include <i386/bios/mca_machdep.h>
|
||||
#endif
|
||||
|
||||
#ifdef APIC_IO
|
||||
#include <i386/isa/intr_machdep.h>
|
||||
/* The interrupt triggered by the 8254 (timer) chip */
|
||||
int apic_8254_intr;
|
||||
static u_long read_intr_count(int vec);
|
||||
static void setup_8254_mixed_mode(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 32-bit time_t's can't reach leap years before 1904 or after 2036, so we
|
||||
* can use a simple formula for leap years.
|
||||
@ -150,6 +138,7 @@ static u_int hardclock_max_count;
|
||||
static u_int32_t i8254_lastcount;
|
||||
static u_int32_t i8254_offset;
|
||||
static int i8254_ticked;
|
||||
static struct intsrc *i8254_intsrc;
|
||||
#ifndef BURN_BRIDGES
|
||||
/*
|
||||
* XXX new_function and timer_func should not handle clockframes, but
|
||||
@ -187,7 +176,7 @@ static struct timecounter i8254_timecounter = {
|
||||
};
|
||||
|
||||
static void
|
||||
clkintr(struct clockframe frame)
|
||||
clkintr(struct clockframe *frame)
|
||||
{
|
||||
|
||||
if (timecounter->tc_get_timecount == i8254_get_timecount) {
|
||||
@ -201,7 +190,7 @@ clkintr(struct clockframe frame)
|
||||
clkintr_pending = 0;
|
||||
mtx_unlock_spin(&clock_lock);
|
||||
}
|
||||
timer_func(&frame);
|
||||
timer_func(frame);
|
||||
#ifdef SMP
|
||||
if (timer_func == hardclock)
|
||||
forward_hardclock();
|
||||
@ -216,7 +205,7 @@ clkintr(struct clockframe frame)
|
||||
if ((timer0_prescaler_count += timer0_max_count)
|
||||
>= hardclock_max_count) {
|
||||
timer0_prescaler_count -= hardclock_max_count;
|
||||
hardclock(&frame);
|
||||
hardclock(frame);
|
||||
#ifdef SMP
|
||||
forward_hardclock();
|
||||
#endif
|
||||
@ -251,7 +240,7 @@ clkintr(struct clockframe frame)
|
||||
timer0_prescaler_count = 0;
|
||||
timer_func = hardclock;
|
||||
timer0_state = RELEASED;
|
||||
hardclock(&frame);
|
||||
hardclock(frame);
|
||||
#ifdef SMP
|
||||
forward_hardclock();
|
||||
#endif
|
||||
@ -379,16 +368,16 @@ release_timer2()
|
||||
* in the statistics, but the stat clock will no longer stop.
|
||||
*/
|
||||
static void
|
||||
rtcintr(struct clockframe frame)
|
||||
rtcintr(struct clockframe *frame)
|
||||
{
|
||||
while (rtcin(RTC_INTR) & RTCIR_PERIOD) {
|
||||
if (profprocs != 0) {
|
||||
if (--pscnt == 0)
|
||||
pscnt = psdiv;
|
||||
profclock(&frame);
|
||||
profclock(frame);
|
||||
}
|
||||
if (pscnt == psdiv)
|
||||
statclock(&frame);
|
||||
statclock(frame);
|
||||
#ifdef SMP
|
||||
forward_statclock();
|
||||
#endif
|
||||
@ -931,11 +920,6 @@ void
|
||||
cpu_initclocks()
|
||||
{
|
||||
int diag;
|
||||
#ifdef APIC_IO
|
||||
int apic_8254_trial;
|
||||
void *clkdesc;
|
||||
#endif /* APIC_IO */
|
||||
register_t crit;
|
||||
|
||||
if (statclock_disable) {
|
||||
/*
|
||||
@ -951,47 +935,9 @@ cpu_initclocks()
|
||||
profhz = RTC_PROFRATE;
|
||||
}
|
||||
|
||||
/* Finish initializing 8253 timer 0. */
|
||||
#ifdef APIC_IO
|
||||
|
||||
apic_8254_intr = isa_apic_irq(0);
|
||||
apic_8254_trial = 0;
|
||||
if (apic_8254_intr >= 0 ) {
|
||||
if (apic_int_type(0, 0) == 3)
|
||||
apic_8254_trial = 1;
|
||||
} else {
|
||||
/* look for ExtInt on pin 0 */
|
||||
if (apic_int_type(0, 0) == 3) {
|
||||
apic_8254_intr = apic_irq(0, 0);
|
||||
setup_8254_mixed_mode();
|
||||
} else
|
||||
panic("APIC_IO: Cannot route 8254 interrupt to CPU");
|
||||
}
|
||||
|
||||
inthand_add("clk", apic_8254_intr, (driver_intr_t *)clkintr, NULL,
|
||||
INTR_TYPE_CLK | INTR_FAST, &clkdesc);
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
INTREN(1 << apic_8254_intr);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
|
||||
#else /* APIC_IO */
|
||||
|
||||
/*
|
||||
* XXX Check the priority of this interrupt handler. I
|
||||
* couldn't find anything suitable in the BSD/OS code (grog,
|
||||
* 19 July 2000).
|
||||
*/
|
||||
inthand_add("clk", 0, (driver_intr_t *)clkintr, NULL,
|
||||
/* Finish initializing 8254 timer 0. */
|
||||
intr_add_handler("clk", 0, (driver_intr_t *)clkintr, NULL,
|
||||
INTR_TYPE_CLK | INTR_FAST, NULL);
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
INTREN(IRQ0);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
|
||||
#endif /* APIC_IO */
|
||||
|
||||
/* Initialize RTC. */
|
||||
writertc(RTC_STATUSA, rtc_statusa);
|
||||
@ -1004,120 +950,15 @@ cpu_initclocks()
|
||||
if (diag != 0)
|
||||
printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
|
||||
|
||||
#ifdef APIC_IO
|
||||
if (isa_apic_irq(8) != 8)
|
||||
panic("APIC RTC != 8");
|
||||
#endif /* APIC_IO */
|
||||
|
||||
inthand_add("rtc", 8, (driver_intr_t *)rtcintr, NULL,
|
||||
intr_add_handler("rtc", 8, (driver_intr_t *)rtcintr, NULL,
|
||||
INTR_TYPE_CLK | INTR_FAST, NULL);
|
||||
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
#ifdef APIC_IO
|
||||
INTREN(APIC_IRQ8);
|
||||
#else
|
||||
INTREN(IRQ8);
|
||||
#endif /* APIC_IO */
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
i8254_intsrc = intr_lookup_source(8);
|
||||
|
||||
writertc(RTC_STATUSB, rtc_statusb);
|
||||
|
||||
#ifdef APIC_IO
|
||||
if (apic_8254_trial) {
|
||||
|
||||
printf("APIC_IO: Testing 8254 interrupt delivery\n");
|
||||
while (read_intr_count(8) < 6)
|
||||
; /* nothing */
|
||||
if (read_intr_count(apic_8254_intr) < 3) {
|
||||
/*
|
||||
* The MP table is broken.
|
||||
* The 8254 was not connected to the specified pin
|
||||
* on the IO APIC.
|
||||
* Workaround: Limited variant of mixed mode.
|
||||
*/
|
||||
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
INTRDIS(1 << apic_8254_intr);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
inthand_remove(clkdesc);
|
||||
printf("APIC_IO: Broken MP table detected: "
|
||||
"8254 is not connected to "
|
||||
"IOAPIC #%d intpin %d\n",
|
||||
int_to_apicintpin[apic_8254_intr].ioapic,
|
||||
int_to_apicintpin[apic_8254_intr].int_pin);
|
||||
/*
|
||||
* Revoke current ISA IRQ 0 assignment and
|
||||
* configure a fallback interrupt routing from
|
||||
* the 8254 Timer via the 8259 PIC to the
|
||||
* an ExtInt interrupt line on IOAPIC #0 intpin 0.
|
||||
* We reuse the low level interrupt handler number.
|
||||
*/
|
||||
if (apic_irq(0, 0) < 0) {
|
||||
revoke_apic_irq(apic_8254_intr);
|
||||
assign_apic_irq(0, 0, apic_8254_intr);
|
||||
}
|
||||
apic_8254_intr = apic_irq(0, 0);
|
||||
setup_8254_mixed_mode();
|
||||
inthand_add("clk", apic_8254_intr,
|
||||
(driver_intr_t *)clkintr, NULL,
|
||||
INTR_TYPE_CLK | INTR_FAST, NULL);
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
INTREN(1 << apic_8254_intr);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
}
|
||||
|
||||
}
|
||||
if (apic_int_type(0, 0) != 3 ||
|
||||
int_to_apicintpin[apic_8254_intr].ioapic != 0 ||
|
||||
int_to_apicintpin[apic_8254_intr].int_pin != 0)
|
||||
printf("APIC_IO: routing 8254 via IOAPIC #%d intpin %d\n",
|
||||
int_to_apicintpin[apic_8254_intr].ioapic,
|
||||
int_to_apicintpin[apic_8254_intr].int_pin);
|
||||
else
|
||||
printf("APIC_IO: "
|
||||
"routing 8254 via 8259 and IOAPIC #0 intpin 0\n");
|
||||
#endif
|
||||
|
||||
init_TSC_tc();
|
||||
}
|
||||
|
||||
#ifdef APIC_IO
|
||||
static u_long
|
||||
read_intr_count(int vec)
|
||||
{
|
||||
u_long *up;
|
||||
up = intr_countp[vec];
|
||||
if (up)
|
||||
return *up;
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_8254_mixed_mode()
|
||||
{
|
||||
/*
|
||||
* Allow 8254 timer to INTerrupt 8259:
|
||||
* re-initialize master 8259:
|
||||
* reset; prog 4 bytes, single ICU, edge triggered
|
||||
*/
|
||||
outb(IO_ICU1, 0x13);
|
||||
outb(IO_ICU1 + 1, NRSVIDT); /* start vector (unused) */
|
||||
outb(IO_ICU1 + 1, 0x00); /* ignore slave */
|
||||
outb(IO_ICU1 + 1, 0x03); /* auto EOI, 8086 */
|
||||
outb(IO_ICU1 + 1, 0xfe); /* unmask INT0 */
|
||||
|
||||
/* program IO APIC for type 3 INT on INT0 */
|
||||
if (ext_int_setup(0, 0) < 0)
|
||||
panic("8254 redirect via APIC pin0 impossible!");
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
cpu_startprofclock(void)
|
||||
{
|
||||
@ -1181,14 +1022,8 @@ i8254_get_timecount(struct timecounter *tc)
|
||||
if (count < i8254_lastcount ||
|
||||
(!i8254_ticked && (clkintr_pending ||
|
||||
((count < 20 || (!(eflags & PSL_I) && count < timer0_max_count / 2u)) &&
|
||||
#ifdef APIC_IO
|
||||
#define lapic_irr1 ((volatile u_int *)&lapic)[0x210 / 4] /* XXX XXX */
|
||||
/* XXX this assumes that apic_8254_intr is < 24. */
|
||||
(lapic_irr1 & (1 << apic_8254_intr))))
|
||||
#else
|
||||
(inb(IO_ICU1) & 1)))
|
||||
#endif
|
||||
)) {
|
||||
i8254_intsrc != NULL &&
|
||||
i8254_intsrc->is_pic->pic_source_pending(i8254_intsrc))))) {
|
||||
i8254_ticked = 1;
|
||||
i8254_offset += timer0_max_count;
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/smp.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
@ -61,31 +62,25 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/signalvar.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#ifndef SMP
|
||||
#include <machine/asmacros.h>
|
||||
#endif
|
||||
#include <machine/cputypes.h>
|
||||
#include <machine/frame.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/pcb.h>
|
||||
#include <machine/psl.h>
|
||||
#ifndef SMP
|
||||
#include <machine/clock.h>
|
||||
#endif
|
||||
#include <machine/resource.h>
|
||||
#include <machine/specialreg.h>
|
||||
#include <machine/segments.h>
|
||||
#include <machine/ucontext.h>
|
||||
|
||||
#ifndef SMP
|
||||
#include <i386/isa/icu.h>
|
||||
#ifdef PC98
|
||||
#include <pc98/pc98/pc98.h>
|
||||
#else
|
||||
#include <i386/isa/isa.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <i386/isa/intr_machdep.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#ifdef DEV_ISA
|
||||
#include <isa/isavar.h>
|
||||
#endif
|
||||
@ -165,9 +160,7 @@ static void fpusave(union savefpu *);
|
||||
static void fpurstor(union savefpu *);
|
||||
static int npx_attach(device_t dev);
|
||||
static void npx_identify(driver_t *driver, device_t parent);
|
||||
#ifndef SMP
|
||||
static void npx_intr(void *);
|
||||
#endif
|
||||
static int npx_probe(device_t dev);
|
||||
#ifdef I586_CPU_XXX
|
||||
static long timezero(const char *funcname,
|
||||
@ -180,10 +173,8 @@ SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint,
|
||||
CTLFLAG_RD, &hw_float, 0,
|
||||
"Floatingpoint instructions executed in hardware");
|
||||
|
||||
#ifndef SMP
|
||||
static volatile u_int npx_intrs_while_probing;
|
||||
static volatile u_int npx_traps_while_probing;
|
||||
#endif
|
||||
|
||||
static union savefpu npx_cleanstate;
|
||||
static bool_t npx_cleanstate_ready;
|
||||
@ -191,7 +182,6 @@ static bool_t npx_ex16;
|
||||
static bool_t npx_exists;
|
||||
static bool_t npx_irq13;
|
||||
|
||||
#ifndef SMP
|
||||
alias_for_inthand_t probetrap;
|
||||
__asm(" \n\
|
||||
.text \n\
|
||||
@ -203,7 +193,6 @@ __asm(" \n\
|
||||
fnclex \n\
|
||||
iret \n\
|
||||
");
|
||||
#endif /* SMP */
|
||||
|
||||
/*
|
||||
* Identify routine. Create a connection point on our parent for probing.
|
||||
@ -220,7 +209,6 @@ npx_identify(driver, parent)
|
||||
panic("npx_identify");
|
||||
}
|
||||
|
||||
#ifndef SMP
|
||||
/*
|
||||
* Do minimal handling of npx interrupts to convert them to traps.
|
||||
*/
|
||||
@ -230,9 +218,7 @@ npx_intr(dummy)
|
||||
{
|
||||
struct thread *td;
|
||||
|
||||
#ifndef SMP
|
||||
npx_intrs_while_probing++;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The BUSY# latch must be cleared in all cases so that the next
|
||||
@ -264,7 +250,6 @@ npx_intr(dummy)
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
}
|
||||
}
|
||||
#endif /* !SMP */
|
||||
|
||||
/*
|
||||
* Probe routine. Initialize cr0 to give correct behaviour for [f]wait
|
||||
@ -276,7 +261,6 @@ static int
|
||||
npx_probe(dev)
|
||||
device_t dev;
|
||||
{
|
||||
#ifndef SMP
|
||||
struct gate_descriptor save_idt_npxtrap;
|
||||
struct resource *ioport_res, *irq_res;
|
||||
void *irq_cookie;
|
||||
@ -307,7 +291,6 @@ npx_probe(dev)
|
||||
if (bus_setup_intr(dev, irq_res, INTR_TYPE_MISC | INTR_FAST, npx_intr,
|
||||
NULL, &irq_cookie) != 0)
|
||||
panic("npx: can't create intr");
|
||||
#endif /* !SMP */
|
||||
|
||||
/*
|
||||
* Partially reset the coprocessor, if any. Some BIOS's don't reset
|
||||
@ -348,16 +331,6 @@ npx_probe(dev)
|
||||
|
||||
device_set_desc(dev, "math processor");
|
||||
|
||||
#ifdef SMP
|
||||
|
||||
/*
|
||||
* Exception 16 MUST work for SMP.
|
||||
*/
|
||||
npx_ex16 = hw_float = npx_exists = 1;
|
||||
return (0);
|
||||
|
||||
#else /* !SMP */
|
||||
|
||||
/*
|
||||
* Don't use fwait here because it might hang.
|
||||
* Don't use fnop here because it usually hangs if there is no FPU.
|
||||
@ -413,6 +386,10 @@ npx_probe(dev)
|
||||
*/
|
||||
npx_irq13 = 1;
|
||||
idt[IDT_MF] = save_idt_npxtrap;
|
||||
#ifdef SMP
|
||||
if (mp_ncpus > 1)
|
||||
panic("npx0 cannot use IRQ 13 on an SMP system");
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
/*
|
||||
@ -425,6 +402,10 @@ npx_probe(dev)
|
||||
* emulator and say that it has been installed. XXX handle devices
|
||||
* that aren't really devices better.
|
||||
*/
|
||||
#ifdef SMP
|
||||
if (mp_ncpus > 1)
|
||||
panic("npx0 cannot be emulated on an SMP system");
|
||||
#endif
|
||||
/* FALLTHROUGH */
|
||||
no_irq13:
|
||||
idt[IDT_MF] = save_idt_npxtrap;
|
||||
@ -435,20 +416,15 @@ npx_probe(dev)
|
||||
* irq active then we would get it instead of exception 16.
|
||||
*/
|
||||
{
|
||||
register_t crit;
|
||||
struct intsrc *isrc;
|
||||
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
INTRDIS(1 << irq_num);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
isrc = intr_lookup_source(irq_num);
|
||||
isrc->is_pic->pic_disable_source(isrc);
|
||||
}
|
||||
|
||||
bus_release_resource(dev, SYS_RES_IRQ, irq_rid, irq_res);
|
||||
bus_release_resource(dev, SYS_RES_IOPORT, ioport_rid, ioport_res);
|
||||
return (0);
|
||||
|
||||
#endif /* SMP */
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -48,10 +48,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/segments.h>
|
||||
#include <machine/pc/bios.h>
|
||||
|
||||
#ifdef APIC_IO
|
||||
#include <machine/smp.h>
|
||||
#endif /* APIC_IO */
|
||||
|
||||
#include "pcib_if.h"
|
||||
|
||||
#define PRVERB(a) do { \
|
||||
@ -201,49 +197,7 @@ u_int32_t
|
||||
pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
|
||||
{
|
||||
uint32_t line;
|
||||
#ifdef APIC_IO
|
||||
uint32_t pin;
|
||||
|
||||
/*
|
||||
* If we are using the APIC, the contents of the intline
|
||||
* register will probably be wrong (since they are set up for
|
||||
* use with the PIC. Rather than rewrite these registers
|
||||
* (maybe that would be smarter) we trap attempts to read them
|
||||
* and translate to our private vector numbers.
|
||||
*/
|
||||
if ((reg == PCIR_INTLINE) && (bytes == 1)) {
|
||||
|
||||
pin = pcireg_cfgread(bus, slot, func, PCIR_INTPIN, 1);
|
||||
line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
|
||||
|
||||
if (pin != 0) {
|
||||
int airq;
|
||||
|
||||
airq = pci_apic_irq(bus, slot, pin);
|
||||
if (airq >= 0) {
|
||||
/* PCI specific entry found in MP table */
|
||||
if (airq != line)
|
||||
undirect_pci_irq(line);
|
||||
return(airq);
|
||||
} else {
|
||||
/*
|
||||
* PCI interrupts might be redirected
|
||||
* to the ISA bus according to some MP
|
||||
* tables. Use the same methods as
|
||||
* used by the ISA devices devices to
|
||||
* find the proper IOAPIC int pin.
|
||||
*/
|
||||
airq = isa_apic_irq(line);
|
||||
if ((airq >= 0) && (airq != line)) {
|
||||
/* XXX: undirect_pci_irq() ? */
|
||||
undirect_isa_irq(line);
|
||||
return(airq);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(line);
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* Some BIOS writers seem to want to ignore the spec and put
|
||||
* 0 in the intline rather than 255 to indicate none. The rest of
|
||||
@ -251,10 +205,9 @@ pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
|
||||
*/
|
||||
if (reg == PCIR_INTLINE && bytes == 1) {
|
||||
line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
|
||||
return pci_i386_map_intline(line);
|
||||
return (pci_i386_map_intline(line));
|
||||
}
|
||||
#endif /* APIC_IO */
|
||||
return(pcireg_cfgread(bus, slot, func, reg, bytes));
|
||||
return (pcireg_cfgread(bus, slot, func, reg, bytes));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -48,10 +48,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/segments.h>
|
||||
#include <machine/pc/bios.h>
|
||||
|
||||
#ifdef APIC_IO
|
||||
#include <machine/smp.h>
|
||||
#endif /* APIC_IO */
|
||||
|
||||
#include "pcib_if.h"
|
||||
|
||||
#define PRVERB(a) do { \
|
||||
@ -201,49 +197,7 @@ u_int32_t
|
||||
pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
|
||||
{
|
||||
uint32_t line;
|
||||
#ifdef APIC_IO
|
||||
uint32_t pin;
|
||||
|
||||
/*
|
||||
* If we are using the APIC, the contents of the intline
|
||||
* register will probably be wrong (since they are set up for
|
||||
* use with the PIC. Rather than rewrite these registers
|
||||
* (maybe that would be smarter) we trap attempts to read them
|
||||
* and translate to our private vector numbers.
|
||||
*/
|
||||
if ((reg == PCIR_INTLINE) && (bytes == 1)) {
|
||||
|
||||
pin = pcireg_cfgread(bus, slot, func, PCIR_INTPIN, 1);
|
||||
line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
|
||||
|
||||
if (pin != 0) {
|
||||
int airq;
|
||||
|
||||
airq = pci_apic_irq(bus, slot, pin);
|
||||
if (airq >= 0) {
|
||||
/* PCI specific entry found in MP table */
|
||||
if (airq != line)
|
||||
undirect_pci_irq(line);
|
||||
return(airq);
|
||||
} else {
|
||||
/*
|
||||
* PCI interrupts might be redirected
|
||||
* to the ISA bus according to some MP
|
||||
* tables. Use the same methods as
|
||||
* used by the ISA devices devices to
|
||||
* find the proper IOAPIC int pin.
|
||||
*/
|
||||
airq = isa_apic_irq(line);
|
||||
if ((airq >= 0) && (airq != line)) {
|
||||
/* XXX: undirect_pci_irq() ? */
|
||||
undirect_isa_irq(line);
|
||||
return(airq);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(line);
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* Some BIOS writers seem to want to ignore the spec and put
|
||||
* 0 in the intline rather than 255 to indicate none. The rest of
|
||||
@ -251,10 +205,9 @@ pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
|
||||
*/
|
||||
if (reg == PCIR_INTLINE && bytes == 1) {
|
||||
line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
|
||||
return pci_i386_map_intline(line);
|
||||
return (pci_i386_map_intline(line));
|
||||
}
|
||||
#endif /* APIC_IO */
|
||||
return(pcireg_cfgread(bus, slot, func, reg, bytes));
|
||||
return (pcireg_cfgread(bus, slot, func, reg, bytes));
|
||||
}
|
||||
|
||||
/*
|
||||
|
199
sys/isa/atrtc.c
199
sys/isa/atrtc.c
@ -71,14 +71,12 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/clock.h>
|
||||
#include <machine/cputypes.h>
|
||||
#include <machine/frame.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/psl.h>
|
||||
#ifdef APIC_IO
|
||||
#include <machine/segments.h>
|
||||
#endif
|
||||
#if defined(SMP) || defined(APIC_IO)
|
||||
#if defined(SMP)
|
||||
#include <machine/smp.h>
|
||||
#endif /* SMP || APIC_IO */
|
||||
#endif
|
||||
#include <machine/specialreg.h>
|
||||
|
||||
#include <i386/isa/icu.h>
|
||||
@ -89,20 +87,10 @@ __FBSDID("$FreeBSD$");
|
||||
#endif
|
||||
#include <i386/isa/timerreg.h>
|
||||
|
||||
#include <i386/isa/intr_machdep.h>
|
||||
|
||||
#ifdef DEV_MCA
|
||||
#include <i386/bios/mca_machdep.h>
|
||||
#endif
|
||||
|
||||
#ifdef APIC_IO
|
||||
#include <i386/isa/intr_machdep.h>
|
||||
/* The interrupt triggered by the 8254 (timer) chip */
|
||||
int apic_8254_intr;
|
||||
static u_long read_intr_count(int vec);
|
||||
static void setup_8254_mixed_mode(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 32-bit time_t's can't reach leap years before 1904 or after 2036, so we
|
||||
* can use a simple formula for leap years.
|
||||
@ -150,6 +138,7 @@ static u_int hardclock_max_count;
|
||||
static u_int32_t i8254_lastcount;
|
||||
static u_int32_t i8254_offset;
|
||||
static int i8254_ticked;
|
||||
static struct intsrc *i8254_intsrc;
|
||||
#ifndef BURN_BRIDGES
|
||||
/*
|
||||
* XXX new_function and timer_func should not handle clockframes, but
|
||||
@ -187,7 +176,7 @@ static struct timecounter i8254_timecounter = {
|
||||
};
|
||||
|
||||
static void
|
||||
clkintr(struct clockframe frame)
|
||||
clkintr(struct clockframe *frame)
|
||||
{
|
||||
|
||||
if (timecounter->tc_get_timecount == i8254_get_timecount) {
|
||||
@ -201,7 +190,7 @@ clkintr(struct clockframe frame)
|
||||
clkintr_pending = 0;
|
||||
mtx_unlock_spin(&clock_lock);
|
||||
}
|
||||
timer_func(&frame);
|
||||
timer_func(frame);
|
||||
#ifdef SMP
|
||||
if (timer_func == hardclock)
|
||||
forward_hardclock();
|
||||
@ -216,7 +205,7 @@ clkintr(struct clockframe frame)
|
||||
if ((timer0_prescaler_count += timer0_max_count)
|
||||
>= hardclock_max_count) {
|
||||
timer0_prescaler_count -= hardclock_max_count;
|
||||
hardclock(&frame);
|
||||
hardclock(frame);
|
||||
#ifdef SMP
|
||||
forward_hardclock();
|
||||
#endif
|
||||
@ -251,7 +240,7 @@ clkintr(struct clockframe frame)
|
||||
timer0_prescaler_count = 0;
|
||||
timer_func = hardclock;
|
||||
timer0_state = RELEASED;
|
||||
hardclock(&frame);
|
||||
hardclock(frame);
|
||||
#ifdef SMP
|
||||
forward_hardclock();
|
||||
#endif
|
||||
@ -379,16 +368,16 @@ release_timer2()
|
||||
* in the statistics, but the stat clock will no longer stop.
|
||||
*/
|
||||
static void
|
||||
rtcintr(struct clockframe frame)
|
||||
rtcintr(struct clockframe *frame)
|
||||
{
|
||||
while (rtcin(RTC_INTR) & RTCIR_PERIOD) {
|
||||
if (profprocs != 0) {
|
||||
if (--pscnt == 0)
|
||||
pscnt = psdiv;
|
||||
profclock(&frame);
|
||||
profclock(frame);
|
||||
}
|
||||
if (pscnt == psdiv)
|
||||
statclock(&frame);
|
||||
statclock(frame);
|
||||
#ifdef SMP
|
||||
forward_statclock();
|
||||
#endif
|
||||
@ -931,11 +920,6 @@ void
|
||||
cpu_initclocks()
|
||||
{
|
||||
int diag;
|
||||
#ifdef APIC_IO
|
||||
int apic_8254_trial;
|
||||
void *clkdesc;
|
||||
#endif /* APIC_IO */
|
||||
register_t crit;
|
||||
|
||||
if (statclock_disable) {
|
||||
/*
|
||||
@ -951,47 +935,9 @@ cpu_initclocks()
|
||||
profhz = RTC_PROFRATE;
|
||||
}
|
||||
|
||||
/* Finish initializing 8253 timer 0. */
|
||||
#ifdef APIC_IO
|
||||
|
||||
apic_8254_intr = isa_apic_irq(0);
|
||||
apic_8254_trial = 0;
|
||||
if (apic_8254_intr >= 0 ) {
|
||||
if (apic_int_type(0, 0) == 3)
|
||||
apic_8254_trial = 1;
|
||||
} else {
|
||||
/* look for ExtInt on pin 0 */
|
||||
if (apic_int_type(0, 0) == 3) {
|
||||
apic_8254_intr = apic_irq(0, 0);
|
||||
setup_8254_mixed_mode();
|
||||
} else
|
||||
panic("APIC_IO: Cannot route 8254 interrupt to CPU");
|
||||
}
|
||||
|
||||
inthand_add("clk", apic_8254_intr, (driver_intr_t *)clkintr, NULL,
|
||||
INTR_TYPE_CLK | INTR_FAST, &clkdesc);
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
INTREN(1 << apic_8254_intr);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
|
||||
#else /* APIC_IO */
|
||||
|
||||
/*
|
||||
* XXX Check the priority of this interrupt handler. I
|
||||
* couldn't find anything suitable in the BSD/OS code (grog,
|
||||
* 19 July 2000).
|
||||
*/
|
||||
inthand_add("clk", 0, (driver_intr_t *)clkintr, NULL,
|
||||
/* Finish initializing 8254 timer 0. */
|
||||
intr_add_handler("clk", 0, (driver_intr_t *)clkintr, NULL,
|
||||
INTR_TYPE_CLK | INTR_FAST, NULL);
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
INTREN(IRQ0);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
|
||||
#endif /* APIC_IO */
|
||||
|
||||
/* Initialize RTC. */
|
||||
writertc(RTC_STATUSA, rtc_statusa);
|
||||
@ -1004,120 +950,15 @@ cpu_initclocks()
|
||||
if (diag != 0)
|
||||
printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
|
||||
|
||||
#ifdef APIC_IO
|
||||
if (isa_apic_irq(8) != 8)
|
||||
panic("APIC RTC != 8");
|
||||
#endif /* APIC_IO */
|
||||
|
||||
inthand_add("rtc", 8, (driver_intr_t *)rtcintr, NULL,
|
||||
intr_add_handler("rtc", 8, (driver_intr_t *)rtcintr, NULL,
|
||||
INTR_TYPE_CLK | INTR_FAST, NULL);
|
||||
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
#ifdef APIC_IO
|
||||
INTREN(APIC_IRQ8);
|
||||
#else
|
||||
INTREN(IRQ8);
|
||||
#endif /* APIC_IO */
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
i8254_intsrc = intr_lookup_source(8);
|
||||
|
||||
writertc(RTC_STATUSB, rtc_statusb);
|
||||
|
||||
#ifdef APIC_IO
|
||||
if (apic_8254_trial) {
|
||||
|
||||
printf("APIC_IO: Testing 8254 interrupt delivery\n");
|
||||
while (read_intr_count(8) < 6)
|
||||
; /* nothing */
|
||||
if (read_intr_count(apic_8254_intr) < 3) {
|
||||
/*
|
||||
* The MP table is broken.
|
||||
* The 8254 was not connected to the specified pin
|
||||
* on the IO APIC.
|
||||
* Workaround: Limited variant of mixed mode.
|
||||
*/
|
||||
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
INTRDIS(1 << apic_8254_intr);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
inthand_remove(clkdesc);
|
||||
printf("APIC_IO: Broken MP table detected: "
|
||||
"8254 is not connected to "
|
||||
"IOAPIC #%d intpin %d\n",
|
||||
int_to_apicintpin[apic_8254_intr].ioapic,
|
||||
int_to_apicintpin[apic_8254_intr].int_pin);
|
||||
/*
|
||||
* Revoke current ISA IRQ 0 assignment and
|
||||
* configure a fallback interrupt routing from
|
||||
* the 8254 Timer via the 8259 PIC to the
|
||||
* an ExtInt interrupt line on IOAPIC #0 intpin 0.
|
||||
* We reuse the low level interrupt handler number.
|
||||
*/
|
||||
if (apic_irq(0, 0) < 0) {
|
||||
revoke_apic_irq(apic_8254_intr);
|
||||
assign_apic_irq(0, 0, apic_8254_intr);
|
||||
}
|
||||
apic_8254_intr = apic_irq(0, 0);
|
||||
setup_8254_mixed_mode();
|
||||
inthand_add("clk", apic_8254_intr,
|
||||
(driver_intr_t *)clkintr, NULL,
|
||||
INTR_TYPE_CLK | INTR_FAST, NULL);
|
||||
crit = intr_disable();
|
||||
mtx_lock_spin(&icu_lock);
|
||||
INTREN(1 << apic_8254_intr);
|
||||
mtx_unlock_spin(&icu_lock);
|
||||
intr_restore(crit);
|
||||
}
|
||||
|
||||
}
|
||||
if (apic_int_type(0, 0) != 3 ||
|
||||
int_to_apicintpin[apic_8254_intr].ioapic != 0 ||
|
||||
int_to_apicintpin[apic_8254_intr].int_pin != 0)
|
||||
printf("APIC_IO: routing 8254 via IOAPIC #%d intpin %d\n",
|
||||
int_to_apicintpin[apic_8254_intr].ioapic,
|
||||
int_to_apicintpin[apic_8254_intr].int_pin);
|
||||
else
|
||||
printf("APIC_IO: "
|
||||
"routing 8254 via 8259 and IOAPIC #0 intpin 0\n");
|
||||
#endif
|
||||
|
||||
init_TSC_tc();
|
||||
}
|
||||
|
||||
#ifdef APIC_IO
|
||||
static u_long
|
||||
read_intr_count(int vec)
|
||||
{
|
||||
u_long *up;
|
||||
up = intr_countp[vec];
|
||||
if (up)
|
||||
return *up;
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_8254_mixed_mode()
|
||||
{
|
||||
/*
|
||||
* Allow 8254 timer to INTerrupt 8259:
|
||||
* re-initialize master 8259:
|
||||
* reset; prog 4 bytes, single ICU, edge triggered
|
||||
*/
|
||||
outb(IO_ICU1, 0x13);
|
||||
outb(IO_ICU1 + 1, NRSVIDT); /* start vector (unused) */
|
||||
outb(IO_ICU1 + 1, 0x00); /* ignore slave */
|
||||
outb(IO_ICU1 + 1, 0x03); /* auto EOI, 8086 */
|
||||
outb(IO_ICU1 + 1, 0xfe); /* unmask INT0 */
|
||||
|
||||
/* program IO APIC for type 3 INT on INT0 */
|
||||
if (ext_int_setup(0, 0) < 0)
|
||||
panic("8254 redirect via APIC pin0 impossible!");
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
cpu_startprofclock(void)
|
||||
{
|
||||
@ -1181,14 +1022,8 @@ i8254_get_timecount(struct timecounter *tc)
|
||||
if (count < i8254_lastcount ||
|
||||
(!i8254_ticked && (clkintr_pending ||
|
||||
((count < 20 || (!(eflags & PSL_I) && count < timer0_max_count / 2u)) &&
|
||||
#ifdef APIC_IO
|
||||
#define lapic_irr1 ((volatile u_int *)&lapic)[0x210 / 4] /* XXX XXX */
|
||||
/* XXX this assumes that apic_8254_intr is < 24. */
|
||||
(lapic_irr1 & (1 << apic_8254_intr))))
|
||||
#else
|
||||
(inb(IO_ICU1) & 1)))
|
||||
#endif
|
||||
)) {
|
||||
i8254_intsrc != NULL &&
|
||||
i8254_intsrc->is_pic->pic_source_pending(i8254_intsrc))))) {
|
||||
i8254_ticked = 1;
|
||||
i8254_offset += timer0_max_count;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user