summaryrefslogtreecommitdiffstats
path: root/sys/amd64/isa
diff options
context:
space:
mode:
authorjasone <jasone@FreeBSD.org>2000-09-07 01:33:02 +0000
committerjasone <jasone@FreeBSD.org>2000-09-07 01:33:02 +0000
commit769e0f974d8929599ba599ac496510fffc90ff34 (patch)
tree9387522900085835de81e7830e570ef3f6b3ea80 /sys/amd64/isa
parentacf1927de02afda4855ec278b1128fd9446405ea (diff)
downloadFreeBSD-src-769e0f974d8929599ba599ac496510fffc90ff34.zip
FreeBSD-src-769e0f974d8929599ba599ac496510fffc90ff34.tar.gz
Major update to the way synchronization is done in the kernel. Highlights
include: * Mutual exclusion is used instead of spl*(). See mutex(9). (Note: The alpha port is still in transition and currently uses both.) * Per-CPU idle processes. * Interrupts are run in their own separate kernel threads and can be preempted (i386 only). Partially contributed by: BSDi (BSD/OS) Submissions by (at least): cp, dfr, dillon, grog, jake, jhb, sheldonh
Diffstat (limited to 'sys/amd64/isa')
-rw-r--r--sys/amd64/isa/atpic_vector.S92
-rw-r--r--sys/amd64/isa/clock.c155
-rw-r--r--sys/amd64/isa/icu_ipl.S57
-rw-r--r--sys/amd64/isa/icu_ipl.s57
-rw-r--r--sys/amd64/isa/icu_vector.S92
-rw-r--r--sys/amd64/isa/icu_vector.s92
-rw-r--r--sys/amd64/isa/intr_machdep.c524
-rw-r--r--sys/amd64/isa/intr_machdep.h50
-rw-r--r--sys/amd64/isa/ithread.c353
-rw-r--r--sys/amd64/isa/nmi.c524
-rw-r--r--sys/amd64/isa/npx.c18
-rw-r--r--sys/amd64/isa/vector.S9
-rw-r--r--sys/amd64/isa/vector.s9
13 files changed, 950 insertions, 1082 deletions
diff --git a/sys/amd64/isa/atpic_vector.S b/sys/amd64/isa/atpic_vector.S
index e427351..d2b88bf 100644
--- a/sys/amd64/isa/atpic_vector.S
+++ b/sys/amd64/isa/atpic_vector.S
@@ -53,9 +53,11 @@ IDTVEC(vec_name) ; \
pushl %ecx ; \
pushl %edx ; \
pushl %ds ; \
+ pushl %fs ; \
MAYBE_PUSHL_ES ; \
mov $KDSEL,%ax ; \
mov %ax,%ds ; \
+ mov %ax,%fs ; \
MAYBE_MOVW_AX_ES ; \
FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \
pushl _intr_unit + (irq_num) * 4 ; \
@@ -65,18 +67,21 @@ IDTVEC(vec_name) ; \
incl _cnt+V_INTR ; /* book-keeping can wait */ \
movl _intr_countp + (irq_num) * 4,%eax ; \
incl (%eax) ; \
- movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \
+/* movl _cpl,%eax ; // are we unmasking pending SWIs? / \
notl %eax ; \
- andl _ipending,%eax ; \
- jne 2f ; /* yes, maybe handle them */ \
+ andl _spending,$SWI_MASK ; \
+ jne 2f ; // yes, maybe handle them */ \
1: ; \
MEXITCOUNT ; \
MAYBE_POPL_ES ; \
+ popl %fs ; \
popl %ds ; \
popl %edx ; \
popl %ecx ; \
popl %eax ; \
iret ; \
+
+#if 0
; \
ALIGN_TEXT ; \
2: ; \
@@ -88,6 +93,7 @@ IDTVEC(vec_name) ; \
incb _intr_nesting_level ; /* ... really limit it ... */ \
sti ; /* ... to do this as early as possible */ \
MAYBE_POPL_ES ; /* discard most of thin frame ... */ \
+ popl %fs ; \
popl %ecx ; /* ... original %ds ... */ \
popl %edx ; \
xchgl %eax,4(%esp) ; /* orig %eax; save cpl */ \
@@ -101,11 +107,20 @@ IDTVEC(vec_name) ; \
movl (3+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \
movl %ecx,(3+6)*4(%esp) ; /* ... to fat frame ... */ \
movl (3+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \
- pushl %eax ; \
subl $4,%esp ; /* junk for unit number */ \
MEXITCOUNT ; \
jmp _doreti
+#endif
+/*
+ * 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, icu, enable_icus, reg, maybe_extra_ipending) \
.text ; \
SUPERALIGN_TEXT ; \
@@ -116,8 +131,8 @@ IDTVEC(vec_name) ; \
pushl %ds ; /* save our data and extra segments ... */ \
pushl %es ; \
pushl %fs ; \
- mov $KDSEL,%ax ; /* ... and reload with kernel's own ... */ \
- mov %ax,%ds ; /* ... early for obsolete reasons */ \
+ mov $KDSEL,%ax ; /* load kernel ds, es and fs */ \
+ mov %ax,%ds ; \
mov %ax,%es ; \
mov %ax,%fs ; \
maybe_extra_ipending ; \
@@ -126,43 +141,37 @@ IDTVEC(vec_name) ; \
movb %al,_imen + IRQ_BYTE(irq_num) ; \
outb %al,$icu+ICU_IMR_OFFSET ; \
enable_icus ; \
- movl _cpl,%eax ; \
- testb $IRQ_BIT(irq_num),%reg ; \
- jne 2f ; \
- incb _intr_nesting_level ; \
+ incb _intr_nesting_level ; /* XXX do we need this? */ \
__CONCAT(Xresume,irq_num): ; \
FAKE_MCOUNT(13*4(%esp)) ; /* XXX late to avoid double count */ \
- incl _cnt+V_INTR ; /* tally interrupts */ \
- movl _intr_countp + (irq_num) * 4,%eax ; \
- incl (%eax) ; \
- movl _cpl,%eax ; \
- pushl %eax ; \
- pushl _intr_unit + (irq_num) * 4 ; \
- orl _intr_mask + (irq_num) * 4,%eax ; \
- movl %eax,_cpl ; \
+ pushl $irq_num; /* pass the IRQ */ \
sti ; \
- call *_intr_handler + (irq_num) * 4 ; \
- cli ; /* must unmask _imen and icu atomically */ \
- movb _imen + IRQ_BYTE(irq_num),%al ; \
- andb $~IRQ_BIT(irq_num),%al ; \
- movb %al,_imen + IRQ_BYTE(irq_num) ; \
- outb %al,$icu+ICU_IMR_OFFSET ; \
- sti ; /* XXX _doreti repeats the cli/sti */ \
+ call _sched_ithd ; \
+ addl $4, %esp ; /* discard the parameter */ \
MEXITCOUNT ; \
/* We could usually avoid the following jmp by inlining some of */ \
/* _doreti, but it's probably better to use less cache. */ \
- jmp _doreti ; \
-; \
- ALIGN_TEXT ; \
-2: ; \
- /* XXX skip mcounting here to avoid double count */ \
- orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \
- popl %fs ; \
- popl %es ; \
- popl %ds ; \
- popal ; \
- addl $4+4,%esp ; \
- iret
+ jmp doreti_next /* and catch up inside doreti */
+
+/*
+ * Reenable the interrupt mask after completing an interrupt. Called
+ * from ithd_loop. There are two separate functions, one for each
+ * ICU.
+ */
+ .globl setimask0, setimask1
+setimask0:
+ cli
+ movb _imen,%al
+ outb %al,$IO_ICU1 + ICU_IMR_OFFSET
+ sti
+ ret
+
+setimask1:
+ cli
+ movb _imen + 1,%al
+ outb %al,$IO_ICU2 + ICU_IMR_OFFSET
+ sti
+ ret
MCOUNT_LABEL(bintr)
FAST_INTR(0,fastintr0, ENABLE_ICU1)
@@ -181,7 +190,9 @@ MCOUNT_LABEL(bintr)
FAST_INTR(13,fastintr13, ENABLE_ICU1_AND_2)
FAST_INTR(14,fastintr14, ENABLE_ICU1_AND_2)
FAST_INTR(15,fastintr15, ENABLE_ICU1_AND_2)
+
#define CLKINTR_PENDING movl $1,CNAME(clkintr_pending)
+/* Threaded interrupts */
INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al, CLKINTR_PENDING)
INTR(1,intr1, IO_ICU1, ENABLE_ICU1, al,)
INTR(2,intr2, IO_ICU1, ENABLE_ICU1, al,)
@@ -198,6 +209,7 @@ MCOUNT_LABEL(bintr)
INTR(13,intr13, IO_ICU2, ENABLE_ICU1_AND_2, ah,)
INTR(14,intr14, IO_ICU2, ENABLE_ICU1_AND_2, ah,)
INTR(15,intr15, IO_ICU2, ENABLE_ICU1_AND_2, ah,)
+
MCOUNT_LABEL(eintr)
.data
@@ -211,10 +223,4 @@ _ihandlers: /* addresses of interrupt handlers */
.long _swi_null, swi_net, _swi_null, _swi_null
.long _swi_vm, _swi_null, _softclock
-imasks: /* masks for interrupt handlers */
- .space NHWI*4 /* padding; HWI masks are elsewhere */
-
- .long SWI_TTY_MASK, SWI_NET_MASK, SWI_CAMNET_MASK, SWI_CAMBIO_MASK
- .long SWI_VM_MASK, SWI_TQ_MASK, SWI_CLOCK_MASK
-
.text
diff --git a/sys/amd64/isa/clock.c b/sys/amd64/isa/clock.c
index 15044ab..724f3c2 100644
--- a/sys/amd64/isa/clock.c
+++ b/sys/amd64/isa/clock.c
@@ -54,6 +54,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
+#include <sys/proc.h>
#include <sys/time.h>
#include <sys/timetc.h>
#include <sys/kernel.h>
@@ -93,10 +94,6 @@
#include <i386/isa/mca_machdep.h>
#endif
-#ifdef SMP
-#define disable_intr() CLOCK_DISABLE_INTR()
-#define enable_intr() CLOCK_ENABLE_INTR()
-
#ifdef APIC_IO
#include <i386/isa/intr_machdep.h>
/* The interrupt triggered by the 8254 (timer) chip */
@@ -104,7 +101,6 @@ int apic_8254_intr;
static u_long read_intr_count __P((int vec));
static void setup_8254_mixed_mode __P((void));
#endif
-#endif /* SMP */
/*
* 32-bit time_t's can't reach leap years before 1904 or after 2036, so we
@@ -147,7 +143,9 @@ int tsc_is_broken;
int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
static int beeping = 0;
+#if 0
static u_int clk_imask = HWI_MASK | SWI_MASK;
+#endif
static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
static u_int hardclock_max_count;
static u_int32_t i8254_lastcount;
@@ -205,8 +203,12 @@ SYSCTL_OPAQUE(_debug, OID_AUTO, i8254_timecounter, CTLFLAG_RD,
static void
clkintr(struct clockframe frame)
{
+ int intrsave;
+
if (timecounter->tc_get_timecount == i8254_get_timecount) {
+ intrsave = save_intr();
disable_intr();
+ CLOCK_LOCK();
if (i8254_ticked)
i8254_ticked = 0;
else {
@@ -214,7 +216,8 @@ clkintr(struct clockframe frame)
i8254_lastcount = 0;
}
clkintr_pending = 0;
- enable_intr();
+ CLOCK_UNLOCK();
+ restore_intr(intrsave);
}
timer_func(&frame);
switch (timer0_state) {
@@ -233,14 +236,17 @@ clkintr(struct clockframe frame)
break;
case ACQUIRE_PENDING:
+ intrsave = save_intr();
disable_intr();
+ CLOCK_LOCK();
i8254_offset = i8254_get_timecount(NULL);
i8254_lastcount = 0;
timer0_max_count = TIMER_DIV(new_rate);
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
outb(TIMER_CNTR0, timer0_max_count & 0xff);
outb(TIMER_CNTR0, timer0_max_count >> 8);
- enable_intr();
+ CLOCK_UNLOCK();
+ restore_intr(intrsave);
timer_func = new_function;
timer0_state = ACQUIRED;
setdelayed();
@@ -249,7 +255,9 @@ clkintr(struct clockframe frame)
case RELEASE_PENDING:
if ((timer0_prescaler_count += timer0_max_count)
>= hardclock_max_count) {
+ intrsave = save_intr();
disable_intr();
+ CLOCK_LOCK();
i8254_offset = i8254_get_timecount(NULL);
i8254_lastcount = 0;
timer0_max_count = hardclock_max_count;
@@ -257,7 +265,8 @@ clkintr(struct clockframe frame)
TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
outb(TIMER_CNTR0, timer0_max_count & 0xff);
outb(TIMER_CNTR0, timer0_max_count >> 8);
- enable_intr();
+ CLOCK_UNLOCK();
+ restore_intr(intrsave);
timer0_prescaler_count = 0;
timer_func = hardclock;
timer0_state = RELEASED;
@@ -404,11 +413,11 @@ DB_SHOW_COMMAND(rtc, rtc)
static int
getit(void)
{
- u_long ef;
- int high, low;
+ int high, low, intrsave;
- ef = read_eflags();
+ intrsave = save_intr();
disable_intr();
+ CLOCK_LOCK();
/* Select timer0 and latch counter value. */
outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
@@ -417,7 +426,7 @@ getit(void)
high = inb(TIMER_CNTR0);
CLOCK_UNLOCK();
- write_eflags(ef);
+ restore_intr(intrsave);
return ((high << 8) | low);
}
@@ -523,6 +532,7 @@ sysbeepstop(void *chan)
int
sysbeep(int pitch, int period)
{
+ int intrsave;
int x = splclock();
if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
@@ -531,10 +541,13 @@ sysbeep(int pitch, int period)
splx(x);
return (-1); /* XXX Should be EBUSY, but nobody cares anyway. */
}
+ intrsave = save_intr();
disable_intr();
+ CLOCK_LOCK();
outb(TIMER_CNTR2, pitch);
outb(TIMER_CNTR2, (pitch>>8));
- enable_intr();
+ CLOCK_UNLOCK();
+ restore_intr(intrsave);
if (!beeping) {
/* enable counter2 output to speaker */
outb(IO_PPI, inb(IO_PPI) | 3);
@@ -683,11 +696,12 @@ fail:
static void
set_timer_freq(u_int freq, int intr_freq)
{
- u_long ef;
+ int intrsave;
int new_timer0_max_count;
- ef = read_eflags();
+ intrsave = save_intr();
disable_intr();
+ CLOCK_LOCK();
timer_freq = freq;
new_timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
if (new_timer0_max_count != timer0_max_count) {
@@ -697,7 +711,7 @@ set_timer_freq(u_int freq, int intr_freq)
outb(TIMER_CNTR0, timer0_max_count >> 8);
}
CLOCK_UNLOCK();
- write_eflags(ef);
+ restore_intr(intrsave);
}
/*
@@ -711,15 +725,16 @@ set_timer_freq(u_int freq, int intr_freq)
void
i8254_restore(void)
{
- u_long ef;
+ int intrsave;
- ef = read_eflags();
+ intrsave = save_intr();
disable_intr();
+ CLOCK_LOCK();
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
outb(TIMER_CNTR0, timer0_max_count & 0xff);
outb(TIMER_CNTR0, timer0_max_count >> 8);
CLOCK_UNLOCK();
- write_eflags(ef);
+ restore_intr(intrsave);
}
/*
@@ -979,8 +994,8 @@ cpu_initclocks()
{
int diag;
#ifdef APIC_IO
- int apic_8254_trial;
- struct intrec *clkdesc;
+ int apic_8254_trial, num_8254_ticks;
+ struct intrec *clkdesc, *rtcdesc;
#endif /* APIC_IO */
if (statclock_disable) {
@@ -1014,14 +1029,15 @@ cpu_initclocks()
} else
panic("APIC_IO: Cannot route 8254 interrupt to CPU");
}
-
- clkdesc = inthand_add("clk", apic_8254_intr, (inthand2_t *)clkintr,
- NULL, &clk_imask, INTR_EXCL);
- INTREN(1 << apic_8254_intr);
-
#else /* APIC_IO */
- inthand_add("clk", 0, (inthand2_t *)clkintr, NULL, &clk_imask,
+ /*
+ * XXX Check the priority of this interrupt handler. I
+ * couldn't find anything suitable in the BSD/OS code (grog,
+ * 19 July 2000).
+ */
+ /* Setup the PIC clk handler. The APIC handler is setup later */
+ inthand_add("clk", 0, (inthand2_t *)clkintr, NULL, PI_REALTIME,
INTR_EXCL);
INTREN(IRQ0);
@@ -1032,8 +1048,18 @@ cpu_initclocks()
writertc(RTC_STATUSB, RTCSB_24HR);
/* Don't bother enabling the statistics clock. */
- if (statclock_disable)
+ if (statclock_disable) {
+#ifdef APIC_IO
+ /*
+ * XXX - if statclock is disabled, don't attempt the APIC
+ * trial. Not sure this is sane for APIC_IO.
+ */
+ inthand_add("clk", apic_8254_intr, (inthand2_t *)clkintr, NULL,
+ PI_REALTIME, INTR_EXCL);
+ INTREN(1 << apic_8254_intr);
+#endif /* APIC_IO */
return;
+ }
diag = rtcin(RTC_DIAG);
if (diag != 0)
printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
@@ -1041,34 +1067,44 @@ cpu_initclocks()
#ifdef APIC_IO
if (isa_apic_irq(8) != 8)
panic("APIC RTC != 8");
-#endif /* APIC_IO */
- inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL, &stat_imask,
- INTR_EXCL);
-
-#ifdef APIC_IO
- INTREN(APIC_IRQ8);
-#else
- INTREN(IRQ8);
-#endif /* APIC_IO */
+ if (apic_8254_trial) {
+ /*
+ * XXX - We use fast interrupts for clk and rtc long enough to
+ * perform the APIC probe and then revert to exclusive
+ * interrupts.
+ */
+ clkdesc = inthand_add("clk", apic_8254_intr,
+ (inthand2_t *)clkintr, NULL, PI_REALTIME, INTR_FAST);
+ INTREN(1 << apic_8254_intr);
- writertc(RTC_STATUSB, rtc_statusb);
+ rtcdesc = inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL,
+ PI_REALTIME, INTR_FAST); /* XXX */
+ INTREN(APIC_IRQ8);
+ 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) {
+ num_8254_ticks = read_intr_count(apic_8254_intr);
+
+ /* disable and remove our fake handlers */
+ INTRDIS(1 << apic_8254_intr);
+ inthand_remove(clkdesc);
+
+ writertc(RTC_STATUSA, rtc_statusa);
+ writertc(RTC_STATUSB, RTCSB_24HR);
+
+ INTRDIS(APIC_IRQ8);
+ inthand_remove(rtcdesc);
+
+ if (num_8254_ticks < 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.
*/
- INTRDIS(1 << apic_8254_intr);
- inthand_remove(clkdesc);
printf("APIC_IO: Broken MP table detected: "
"8254 is not connected to "
"IOAPIC #%d intpin %d\n",
@@ -1087,13 +1123,27 @@ cpu_initclocks()
}
apic_8254_intr = apic_irq(0, 0);
setup_8254_mixed_mode();
- inthand_add("clk", apic_8254_intr,
- (inthand2_t *)clkintr,
- NULL, &clk_imask, INTR_EXCL);
- INTREN(1 << apic_8254_intr);
}
}
+
+ /* Finally, setup the real clock handlers */
+ inthand_add("clk", apic_8254_intr, (inthand2_t *)clkintr, NULL,
+ PI_REALTIME, INTR_EXCL);
+ INTREN(1 << apic_8254_intr);
+#endif
+
+ inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL, PI_REALTIME,
+ INTR_EXCL);
+#ifdef APIC_IO
+ INTREN(APIC_IRQ8);
+#else
+ INTREN(IRQ8);
+#endif
+
+ writertc(RTC_STATUSB, rtc_statusb);
+
+#ifdef APIC_IO
if (apic_int_type(0, 0) != 3 ||
int_to_apicintpin[apic_8254_intr].ioapic != 0 ||
int_to_apicintpin[apic_8254_intr].int_pin != 0)
@@ -1198,11 +1248,12 @@ static unsigned
i8254_get_timecount(struct timecounter *tc)
{
u_int count;
- u_long ef;
+ int intrsave;
u_int high, low;
- ef = read_eflags();
+ intrsave = save_intr();
disable_intr();
+ CLOCK_LOCK();
/* Select timer0 and latch counter value. */
outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
@@ -1212,7 +1263,7 @@ i8254_get_timecount(struct timecounter *tc)
count = timer0_max_count - ((high << 8) | low);
if (count < i8254_lastcount ||
(!i8254_ticked && (clkintr_pending ||
- ((count < 20 || (!(ef & PSL_I) && count < timer0_max_count / 2u)) &&
+ ((count < 20 || (!(intrsave & 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. */
@@ -1227,7 +1278,7 @@ i8254_get_timecount(struct timecounter *tc)
i8254_lastcount = count;
count += i8254_offset;
CLOCK_UNLOCK();
- write_eflags(ef);
+ restore_intr(intrsave);
return (count);
}
diff --git a/sys/amd64/isa/icu_ipl.S b/sys/amd64/isa/icu_ipl.S
index 3475358..d178d5c 100644
--- a/sys/amd64/isa/icu_ipl.S
+++ b/sys/amd64/isa/icu_ipl.S
@@ -55,63 +55,6 @@ _imen: .long HWI_MASK
SUPERALIGN_TEXT
/*
- * Interrupt priority mechanism
- * -- soft splXX masks with group mechanism (cpl)
- * -- h/w masks for currently active or unused interrupts (imen)
- * -- ipending = active interrupts currently masked by cpl
- */
-
-ENTRY(splz)
- /*
- * The caller has restored cpl and checked that (ipending & ~cpl)
- * is nonzero. We have to repeat the check since if there is an
- * interrupt while we're looking, _doreti processing for the
- * interrupt will handle all the unmasked pending interrupts
- * because we restored early. We're repeating the calculation
- * of (ipending & ~cpl) anyway so that the caller doesn't have
- * to pass it, so this only costs one "jne". "bsfl %ecx,%ecx"
- * is undefined when %ecx is 0 so we can't rely on the secondary
- * btrl tests.
- */
- movl _cpl,%eax
-splz_next:
- /*
- * We don't need any locking here. (ipending & ~cpl) cannot grow
- * while we're looking at it - any interrupt will shrink it to 0.
- */
- movl %eax,%ecx
- notl %ecx
- andl _ipending,%ecx
- jne splz_unpend
- ret
-
- ALIGN_TEXT
-splz_unpend:
- bsfl %ecx,%ecx
- btrl %ecx,_ipending
- jnc splz_next
- cmpl $NHWI,%ecx
- jae splz_swi
- /*
- * We would prefer to call the intr handler directly here but that
- * doesn't work for badly behaved handlers that want the interrupt
- * frame. Also, there's a problem determining the unit number.
- * We should change the interface so that the unit number is not
- * determined at config time.
- */
- jmp *vec(,%ecx,4)
-
- ALIGN_TEXT
-splz_swi:
- pushl %eax
- orl imasks(,%ecx,4),%eax
- movl %eax,_cpl
- call *_ihandlers(,%ecx,4)
- popl %eax
- movl %eax,_cpl
- jmp splz_next
-
-/*
* Fake clock interrupt(s) so that they appear to come from our caller instead
* of from here, so that system profiling works.
* XXX do this more generally (for all vectors; look up the C entry point).
diff --git a/sys/amd64/isa/icu_ipl.s b/sys/amd64/isa/icu_ipl.s
index 3475358..d178d5c 100644
--- a/sys/amd64/isa/icu_ipl.s
+++ b/sys/amd64/isa/icu_ipl.s
@@ -55,63 +55,6 @@ _imen: .long HWI_MASK
SUPERALIGN_TEXT
/*
- * Interrupt priority mechanism
- * -- soft splXX masks with group mechanism (cpl)
- * -- h/w masks for currently active or unused interrupts (imen)
- * -- ipending = active interrupts currently masked by cpl
- */
-
-ENTRY(splz)
- /*
- * The caller has restored cpl and checked that (ipending & ~cpl)
- * is nonzero. We have to repeat the check since if there is an
- * interrupt while we're looking, _doreti processing for the
- * interrupt will handle all the unmasked pending interrupts
- * because we restored early. We're repeating the calculation
- * of (ipending & ~cpl) anyway so that the caller doesn't have
- * to pass it, so this only costs one "jne". "bsfl %ecx,%ecx"
- * is undefined when %ecx is 0 so we can't rely on the secondary
- * btrl tests.
- */
- movl _cpl,%eax
-splz_next:
- /*
- * We don't need any locking here. (ipending & ~cpl) cannot grow
- * while we're looking at it - any interrupt will shrink it to 0.
- */
- movl %eax,%ecx
- notl %ecx
- andl _ipending,%ecx
- jne splz_unpend
- ret
-
- ALIGN_TEXT
-splz_unpend:
- bsfl %ecx,%ecx
- btrl %ecx,_ipending
- jnc splz_next
- cmpl $NHWI,%ecx
- jae splz_swi
- /*
- * We would prefer to call the intr handler directly here but that
- * doesn't work for badly behaved handlers that want the interrupt
- * frame. Also, there's a problem determining the unit number.
- * We should change the interface so that the unit number is not
- * determined at config time.
- */
- jmp *vec(,%ecx,4)
-
- ALIGN_TEXT
-splz_swi:
- pushl %eax
- orl imasks(,%ecx,4),%eax
- movl %eax,_cpl
- call *_ihandlers(,%ecx,4)
- popl %eax
- movl %eax,_cpl
- jmp splz_next
-
-/*
* Fake clock interrupt(s) so that they appear to come from our caller instead
* of from here, so that system profiling works.
* XXX do this more generally (for all vectors; look up the C entry point).
diff --git a/sys/amd64/isa/icu_vector.S b/sys/amd64/isa/icu_vector.S
index e427351..d2b88bf 100644
--- a/sys/amd64/isa/icu_vector.S
+++ b/sys/amd64/isa/icu_vector.S
@@ -53,9 +53,11 @@ IDTVEC(vec_name) ; \
pushl %ecx ; \
pushl %edx ; \
pushl %ds ; \
+ pushl %fs ; \
MAYBE_PUSHL_ES ; \
mov $KDSEL,%ax ; \
mov %ax,%ds ; \
+ mov %ax,%fs ; \
MAYBE_MOVW_AX_ES ; \
FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \
pushl _intr_unit + (irq_num) * 4 ; \
@@ -65,18 +67,21 @@ IDTVEC(vec_name) ; \
incl _cnt+V_INTR ; /* book-keeping can wait */ \
movl _intr_countp + (irq_num) * 4,%eax ; \
incl (%eax) ; \
- movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \
+/* movl _cpl,%eax ; // are we unmasking pending SWIs? / \
notl %eax ; \
- andl _ipending,%eax ; \
- jne 2f ; /* yes, maybe handle them */ \
+ andl _spending,$SWI_MASK ; \
+ jne 2f ; // yes, maybe handle them */ \
1: ; \
MEXITCOUNT ; \
MAYBE_POPL_ES ; \
+ popl %fs ; \
popl %ds ; \
popl %edx ; \
popl %ecx ; \
popl %eax ; \
iret ; \
+
+#if 0
; \
ALIGN_TEXT ; \
2: ; \
@@ -88,6 +93,7 @@ IDTVEC(vec_name) ; \
incb _intr_nesting_level ; /* ... really limit it ... */ \
sti ; /* ... to do this as early as possible */ \
MAYBE_POPL_ES ; /* discard most of thin frame ... */ \
+ popl %fs ; \
popl %ecx ; /* ... original %ds ... */ \
popl %edx ; \
xchgl %eax,4(%esp) ; /* orig %eax; save cpl */ \
@@ -101,11 +107,20 @@ IDTVEC(vec_name) ; \
movl (3+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \
movl %ecx,(3+6)*4(%esp) ; /* ... to fat frame ... */ \
movl (3+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \
- pushl %eax ; \
subl $4,%esp ; /* junk for unit number */ \
MEXITCOUNT ; \
jmp _doreti
+#endif
+/*
+ * 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, icu, enable_icus, reg, maybe_extra_ipending) \
.text ; \
SUPERALIGN_TEXT ; \
@@ -116,8 +131,8 @@ IDTVEC(vec_name) ; \
pushl %ds ; /* save our data and extra segments ... */ \
pushl %es ; \
pushl %fs ; \
- mov $KDSEL,%ax ; /* ... and reload with kernel's own ... */ \
- mov %ax,%ds ; /* ... early for obsolete reasons */ \
+ mov $KDSEL,%ax ; /* load kernel ds, es and fs */ \
+ mov %ax,%ds ; \
mov %ax,%es ; \
mov %ax,%fs ; \
maybe_extra_ipending ; \
@@ -126,43 +141,37 @@ IDTVEC(vec_name) ; \
movb %al,_imen + IRQ_BYTE(irq_num) ; \
outb %al,$icu+ICU_IMR_OFFSET ; \
enable_icus ; \
- movl _cpl,%eax ; \
- testb $IRQ_BIT(irq_num),%reg ; \
- jne 2f ; \
- incb _intr_nesting_level ; \
+ incb _intr_nesting_level ; /* XXX do we need this? */ \
__CONCAT(Xresume,irq_num): ; \
FAKE_MCOUNT(13*4(%esp)) ; /* XXX late to avoid double count */ \
- incl _cnt+V_INTR ; /* tally interrupts */ \
- movl _intr_countp + (irq_num) * 4,%eax ; \
- incl (%eax) ; \
- movl _cpl,%eax ; \
- pushl %eax ; \
- pushl _intr_unit + (irq_num) * 4 ; \
- orl _intr_mask + (irq_num) * 4,%eax ; \
- movl %eax,_cpl ; \
+ pushl $irq_num; /* pass the IRQ */ \
sti ; \
- call *_intr_handler + (irq_num) * 4 ; \
- cli ; /* must unmask _imen and icu atomically */ \
- movb _imen + IRQ_BYTE(irq_num),%al ; \
- andb $~IRQ_BIT(irq_num),%al ; \
- movb %al,_imen + IRQ_BYTE(irq_num) ; \
- outb %al,$icu+ICU_IMR_OFFSET ; \
- sti ; /* XXX _doreti repeats the cli/sti */ \
+ call _sched_ithd ; \
+ addl $4, %esp ; /* discard the parameter */ \
MEXITCOUNT ; \
/* We could usually avoid the following jmp by inlining some of */ \
/* _doreti, but it's probably better to use less cache. */ \
- jmp _doreti ; \
-; \
- ALIGN_TEXT ; \
-2: ; \
- /* XXX skip mcounting here to avoid double count */ \
- orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \
- popl %fs ; \
- popl %es ; \
- popl %ds ; \
- popal ; \
- addl $4+4,%esp ; \
- iret
+ jmp doreti_next /* and catch up inside doreti */
+
+/*
+ * Reenable the interrupt mask after completing an interrupt. Called
+ * from ithd_loop. There are two separate functions, one for each
+ * ICU.
+ */
+ .globl setimask0, setimask1
+setimask0:
+ cli
+ movb _imen,%al
+ outb %al,$IO_ICU1 + ICU_IMR_OFFSET
+ sti
+ ret
+
+setimask1:
+ cli
+ movb _imen + 1,%al
+ outb %al,$IO_ICU2 + ICU_IMR_OFFSET
+ sti
+ ret
MCOUNT_LABEL(bintr)
FAST_INTR(0,fastintr0, ENABLE_ICU1)
@@ -181,7 +190,9 @@ MCOUNT_LABEL(bintr)
FAST_INTR(13,fastintr13, ENABLE_ICU1_AND_2)
FAST_INTR(14,fastintr14, ENABLE_ICU1_AND_2)
FAST_INTR(15,fastintr15, ENABLE_ICU1_AND_2)
+
#define CLKINTR_PENDING movl $1,CNAME(clkintr_pending)
+/* Threaded interrupts */
INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al, CLKINTR_PENDING)
INTR(1,intr1, IO_ICU1, ENABLE_ICU1, al,)
INTR(2,intr2, IO_ICU1, ENABLE_ICU1, al,)
@@ -198,6 +209,7 @@ MCOUNT_LABEL(bintr)
INTR(13,intr13, IO_ICU2, ENABLE_ICU1_AND_2, ah,)
INTR(14,intr14, IO_ICU2, ENABLE_ICU1_AND_2, ah,)
INTR(15,intr15, IO_ICU2, ENABLE_ICU1_AND_2, ah,)
+
MCOUNT_LABEL(eintr)
.data
@@ -211,10 +223,4 @@ _ihandlers: /* addresses of interrupt handlers */
.long _swi_null, swi_net, _swi_null, _swi_null
.long _swi_vm, _swi_null, _softclock
-imasks: /* masks for interrupt handlers */
- .space NHWI*4 /* padding; HWI masks are elsewhere */
-
- .long SWI_TTY_MASK, SWI_NET_MASK, SWI_CAMNET_MASK, SWI_CAMBIO_MASK
- .long SWI_VM_MASK, SWI_TQ_MASK, SWI_CLOCK_MASK
-
.text
diff --git a/sys/amd64/isa/icu_vector.s b/sys/amd64/isa/icu_vector.s
index e427351..d2b88bf 100644
--- a/sys/amd64/isa/icu_vector.s
+++ b/sys/amd64/isa/icu_vector.s
@@ -53,9 +53,11 @@ IDTVEC(vec_name) ; \
pushl %ecx ; \
pushl %edx ; \
pushl %ds ; \
+ pushl %fs ; \
MAYBE_PUSHL_ES ; \
mov $KDSEL,%ax ; \
mov %ax,%ds ; \
+ mov %ax,%fs ; \
MAYBE_MOVW_AX_ES ; \
FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \
pushl _intr_unit + (irq_num) * 4 ; \
@@ -65,18 +67,21 @@ IDTVEC(vec_name) ; \
incl _cnt+V_INTR ; /* book-keeping can wait */ \
movl _intr_countp + (irq_num) * 4,%eax ; \
incl (%eax) ; \
- movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \
+/* movl _cpl,%eax ; // are we unmasking pending SWIs? / \
notl %eax ; \
- andl _ipending,%eax ; \
- jne 2f ; /* yes, maybe handle them */ \
+ andl _spending,$SWI_MASK ; \
+ jne 2f ; // yes, maybe handle them */ \
1: ; \
MEXITCOUNT ; \
MAYBE_POPL_ES ; \
+ popl %fs ; \
popl %ds ; \
popl %edx ; \
popl %ecx ; \
popl %eax ; \
iret ; \
+
+#if 0
; \
ALIGN_TEXT ; \
2: ; \
@@ -88,6 +93,7 @@ IDTVEC(vec_name) ; \
incb _intr_nesting_level ; /* ... really limit it ... */ \
sti ; /* ... to do this as early as possible */ \
MAYBE_POPL_ES ; /* discard most of thin frame ... */ \
+ popl %fs ; \
popl %ecx ; /* ... original %ds ... */ \
popl %edx ; \
xchgl %eax,4(%esp) ; /* orig %eax; save cpl */ \
@@ -101,11 +107,20 @@ IDTVEC(vec_name) ; \
movl (3+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \
movl %ecx,(3+6)*4(%esp) ; /* ... to fat frame ... */ \
movl (3+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \
- pushl %eax ; \
subl $4,%esp ; /* junk for unit number */ \
MEXITCOUNT ; \
jmp _doreti
+#endif
+/*
+ * 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, icu, enable_icus, reg, maybe_extra_ipending) \
.text ; \
SUPERALIGN_TEXT ; \
@@ -116,8 +131,8 @@ IDTVEC(vec_name) ; \
pushl %ds ; /* save our data and extra segments ... */ \
pushl %es ; \
pushl %fs ; \
- mov $KDSEL,%ax ; /* ... and reload with kernel's own ... */ \
- mov %ax,%ds ; /* ... early for obsolete reasons */ \
+ mov $KDSEL,%ax ; /* load kernel ds, es and fs */ \
+ mov %ax,%ds ; \
mov %ax,%es ; \
mov %ax,%fs ; \
maybe_extra_ipending ; \
@@ -126,43 +141,37 @@ IDTVEC(vec_name) ; \
movb %al,_imen + IRQ_BYTE(irq_num) ; \
outb %al,$icu+ICU_IMR_OFFSET ; \
enable_icus ; \
- movl _cpl,%eax ; \
- testb $IRQ_BIT(irq_num),%reg ; \
- jne 2f ; \
- incb _intr_nesting_level ; \
+ incb _intr_nesting_level ; /* XXX do we need this? */ \
__CONCAT(Xresume,irq_num): ; \
FAKE_MCOUNT(13*4(%esp)) ; /* XXX late to avoid double count */ \
- incl _cnt+V_INTR ; /* tally interrupts */ \
- movl _intr_countp + (irq_num) * 4,%eax ; \
- incl (%eax) ; \
- movl _cpl,%eax ; \
- pushl %eax ; \
- pushl _intr_unit + (irq_num) * 4 ; \
- orl _intr_mask + (irq_num) * 4,%eax ; \
- movl %eax,_cpl ; \
+ pushl $irq_num; /* pass the IRQ */ \
sti ; \
- call *_intr_handler + (irq_num) * 4 ; \
- cli ; /* must unmask _imen and icu atomically */ \
- movb _imen + IRQ_BYTE(irq_num),%al ; \
- andb $~IRQ_BIT(irq_num),%al ; \
- movb %al,_imen + IRQ_BYTE(irq_num) ; \
- outb %al,$icu+ICU_IMR_OFFSET ; \
- sti ; /* XXX _doreti repeats the cli/sti */ \
+ call _sched_ithd ; \
+ addl $4, %esp ; /* discard the parameter */ \
MEXITCOUNT ; \
/* We could usually avoid the following jmp by inlining some of */ \
/* _doreti, but it's probably better to use less cache. */ \
- jmp _doreti ; \
-; \
- ALIGN_TEXT ; \
-2: ; \
- /* XXX skip mcounting here to avoid double count */ \
- orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \
- popl %fs ; \
- popl %es ; \
- popl %ds ; \
- popal ; \
- addl $4+4,%esp ; \
- iret
+ jmp doreti_next /* and catch up inside doreti */
+
+/*
+ * Reenable the interrupt mask after completing an interrupt. Called
+ * from ithd_loop. There are two separate functions, one for each
+ * ICU.
+ */
+ .globl setimask0, setimask1
+setimask0:
+ cli
+ movb _imen,%al
+ outb %al,$IO_ICU1 + ICU_IMR_OFFSET
+ sti
+ ret
+
+setimask1:
+ cli
+ movb _imen + 1,%al
+ outb %al,$IO_ICU2 + ICU_IMR_OFFSET
+ sti
+ ret
MCOUNT_LABEL(bintr)
FAST_INTR(0,fastintr0, ENABLE_ICU1)
@@ -181,7 +190,9 @@ MCOUNT_LABEL(bintr)
FAST_INTR(13,fastintr13, ENABLE_ICU1_AND_2)
FAST_INTR(14,fastintr14, ENABLE_ICU1_AND_2)
FAST_INTR(15,fastintr15, ENABLE_ICU1_AND_2)
+
#define CLKINTR_PENDING movl $1,CNAME(clkintr_pending)
+/* Threaded interrupts */
INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al, CLKINTR_PENDING)
INTR(1,intr1, IO_ICU1, ENABLE_ICU1, al,)
INTR(2,intr2, IO_ICU1, ENABLE_ICU1, al,)
@@ -198,6 +209,7 @@ MCOUNT_LABEL(bintr)
INTR(13,intr13, IO_ICU2, ENABLE_ICU1_AND_2, ah,)
INTR(14,intr14, IO_ICU2, ENABLE_ICU1_AND_2, ah,)
INTR(15,intr15, IO_ICU2, ENABLE_ICU1_AND_2, ah,)
+
MCOUNT_LABEL(eintr)
.data
@@ -211,10 +223,4 @@ _ihandlers: /* addresses of interrupt handlers */
.long _swi_null, swi_net, _swi_null, _swi_null
.long _swi_vm, _swi_null, _softclock
-imasks: /* masks for interrupt handlers */
- .space NHWI*4 /* padding; HWI masks are elsewhere */
-
- .long SWI_TTY_MASK, SWI_NET_MASK, SWI_CAMNET_MASK, SWI_CAMBIO_MASK
- .long SWI_VM_MASK, SWI_TQ_MASK, SWI_CLOCK_MASK
-
.text
diff --git a/sys/amd64/isa/intr_machdep.c b/sys/amd64/isa/intr_machdep.c
index 34a8c22..870760e 100644
--- a/sys/amd64/isa/intr_machdep.c
+++ b/sys/amd64/isa/intr_machdep.c
@@ -36,12 +36,6 @@
* from: @(#)isa.c 7.2 (Berkeley) 5/13/91
* $FreeBSD$
*/
-/*
- * This file contains an aggregated module marked:
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
- * All rights reserved.
- * See the notice for details.
- */
#include "opt_auto_eoi.h"
@@ -51,11 +45,14 @@
#ifndef SMP
#include <machine/lock.h>
#endif
+#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <sys/kernel.h>
+#include <sys/kthread.h>
#include <sys/malloc.h>
#include <sys/module.h>
+#include <sys/unistd.h>
#include <sys/errno.h>
#include <sys/interrupt.h>
#include <machine/ipl.h>
@@ -91,30 +88,14 @@
#include <i386/isa/mca_machdep.h>
#endif
-/* XXX should be in suitable include files */
-#ifdef PC98
-#define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */
-#define ICU_SLAVEID 7
-#else
-#define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */
-#define ICU_SLAVEID 2
-#endif
-
-#ifdef APIC_IO
/*
- * This is to accommodate "mixed-mode" programming for
- * motherboards that don't connect the 8254 to the IO APIC.
+ * Per-interrupt data. We consider the soft interrupt to be a special
+ * case, so these arrays have NHWI + NSWI entries, not ICU_LEN.
*/
-#define AUTO_EOI_1 1
-#endif
-
-#define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN)
-
-u_long *intr_countp[ICU_LEN];
-inthand2_t *intr_handler[ICU_LEN];
-u_int intr_mask[ICU_LEN];
-static u_int* intr_mptr[ICU_LEN];
-void *intr_unit[ICU_LEN];
+u_long *intr_countp[NHWI + NSWI]; /* pointers to interrupt counters */
+inthand2_t *intr_handler[NHWI + NSWI]; /* first level interrupt handler */
+ithd *ithds[NHWI + NSWI]; /* real interrupt handler */
+void *intr_unit[NHWI + NSWI];
static inthand_t *fastintr[ICU_LEN] = {
&IDTVEC(fastintr0), &IDTVEC(fastintr1),
@@ -292,8 +273,9 @@ isa_nmi(cd)
}
/*
- * Fill in default interrupt table (in case of spuruious interrupt
- * during configuration of kernel, setup interrupt control unit
+ * Create a default interrupt table to avoid problems caused by
+ * spurious interrupts during configuration of kernel, then setup
+ * interrupt control unit.
*/
void
isa_defaultirq()
@@ -364,16 +346,6 @@ isa_strayintr(vcookiep)
{
int intr = (void **)vcookiep - &intr_unit[0];
- /* DON'T BOTHER FOR NOW! */
- /* for some reason, we get bursts of intr #7, even if not enabled! */
- /*
- * Well the reason you got bursts of intr #7 is because someone
- * raised an interrupt line and dropped it before the 8259 could
- * prioritize it. This is documented in the intel data book. This
- * means you have BAD hardware! I have changed this so that only
- * the first 5 get logged, then it quits logging them, and puts
- * out a special message. rgrimes 3/25/1993
- */
/*
* XXX TODO print a different message for #7 if it is for a
* glitch. Glitches can be distinguished from real #7's by
@@ -405,36 +377,10 @@ isa_irq_pending()
}
#endif
-int
-update_intr_masks(void)
-{
- int intr, n=0;
- u_int mask,*maskptr;
-
- for (intr=0; intr < ICU_LEN; intr ++) {
-#if defined(APIC_IO)
- /* no 8259 SLAVE to ignore */
-#else
- if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */
-#endif /* APIC_IO */
- maskptr = intr_mptr[intr];
- if (!maskptr)
- continue;
- *maskptr |= SWI_LOW_MASK | (1 << intr);
- mask = *maskptr;
- if (mask != intr_mask[intr]) {
-#if 0
- printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n",
- intr, intr_mask[intr], mask, maskptr);
-#endif
- intr_mask[intr]=mask;
- n++;
- }
-
- }
- return (n);
-}
-
+/*
+ * Update intrnames array with the specified name. This is used by
+ * vmstat(8) and the like.
+ */
static void
update_intrname(int intr, char *name)
{
@@ -485,7 +431,7 @@ found:
}
int
-icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
+icu_setup(int intr, inthand2_t *handler, void *arg, int flags)
{
#ifdef FAST_HI
int select; /* the select register is 8 bits */
@@ -493,7 +439,6 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
u_int32_t value; /* the window register is 32 bits */
#endif /* FAST_HI */
u_long ef;
- u_int mask = (maskptr ? *maskptr : 0);
#if defined(APIC_IO)
if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */
@@ -506,8 +451,6 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
ef = read_eflags();
disable_intr();
intr_handler[intr] = handler;
- intr_mptr[intr] = maskptr;
- intr_mask[intr] = mask | SWI_LOW_MASK | (1 << intr);
intr_unit[intr] = arg;
#ifdef FAST_HI
if (flags & INTR_FAST) {
@@ -547,11 +490,15 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
#endif /* FAST_HI */
INTREN(1 << intr);
- MPINTR_UNLOCK();
write_eflags(ef);
return (0);
}
+/*
+ * Dissociate an interrupt handler from an IRQ and set the handler to
+ * the stray interrupt handler. The 'handler' parameter is used only
+ * for consistency checking.
+ */
int
icu_unset(intr, handler)
int intr;
@@ -567,8 +514,6 @@ icu_unset(intr, handler)
disable_intr();
intr_countp[intr] = &intrcnt[1 + intr];
intr_handler[intr] = isa_strayintr;
- intr_mptr[intr] = NULL;
- intr_mask[intr] = HWI_MASK | SWI_MASK;
intr_unit[intr] = &intr_unit[intr];
#ifdef FAST_HI_XXX
/* XXX how do I re-create dvp here? */
@@ -581,353 +526,172 @@ icu_unset(intr, handler)
setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL,
GSEL(GCODE_SEL, SEL_KPL));
#endif /* FAST_HI */
- MPINTR_UNLOCK();
write_eflags(ef);
return (0);
}
-/* The following notice applies beyond this point in the file */
-
-/*
- * Copyright (c) 1997, Stefan Esser <se@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 unmodified, 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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$
- *
- */
-
-typedef struct intrec {
- intrmask_t mask;
- inthand2_t *handler;
- void *argument;
- struct intrec *next;
- char *name;
- int intr;
- intrmask_t *maskptr;
- int flags;
-} intrec;
-
-static intrec *intreclist_head[ICU_LEN];
-
-/*
- * The interrupt multiplexer calls each of the handlers in turn. The
- * ipl is initially quite low. It is raised as necessary for each call
- * and lowered after the call. Thus out of order handling is possible
- * even for interrupts of the same type. This is probably no more
- * harmful than out of order handling in general (not harmful except
- * for real time response which we don't support anyway).
- */
-static void
-intr_mux(void *arg)
-{
- intrec *p;
- intrmask_t oldspl;
-
- for (p = arg; p != NULL; p = p->next) {
- oldspl = splq(p->mask);
- p->handler(p->argument);
- splx(oldspl);
- }
-}
-
-static intrec*
-find_idesc(unsigned *maskptr, int irq)
-{
- intrec *p = intreclist_head[irq];
-
- while (p && p->maskptr != maskptr)
- p = p->next;
-
- return (p);
-}
-
-static intrec**
-find_pred(intrec *idesc, int irq)
+intrec *
+inthand_add(const char *name, int irq, inthand2_t handler, void *arg,
+ int pri, int flags)
{
- intrec **pp = &intreclist_head[irq];
- intrec *p = *pp;
-
- while (p != idesc) {
- if (p == NULL)
- return (NULL);
- pp = &p->next;
- p = *pp;
- }
- return (pp);
-}
-
-/*
- * Both the low level handler and the shared interrupt multiplexer
- * block out further interrupts as set in the handlers "mask", while
- * the handler is running. In fact *maskptr should be used for this
- * purpose, but since this requires one more pointer dereference on
- * each interrupt, we rather bother update "mask" whenever *maskptr
- * changes. The function "update_masks" should be called **after**
- * all manipulation of the linked list of interrupt handlers hung
- * off of intrdec_head[irq] is complete, since the chain of handlers
- * will both determine the *maskptr values and the instances of mask
- * that are fixed. This function should be called with the irq for
- * which a new handler has been add blocked, since the masks may not
- * yet know about the use of this irq for a device of a certain class.
- */
+ ithd *ithd = ithds[irq]; /* descriptor for the IRQ */
+ intrec *head; /* chain of handlers for IRQ */
+ intrec *idesc; /* descriptor for this handler */
+ struct proc *p; /* interrupt thread */
+ int errcode = 0;
-static void
-update_mux_masks(void)
-{
- int irq;
- for (irq = 0; irq < ICU_LEN; irq++) {
- intrec *idesc = intreclist_head[irq];
- while (idesc != NULL) {
- if (idesc->maskptr != NULL) {
- /* our copy of *maskptr may be stale, refresh */
- idesc->mask = *idesc->maskptr;
- }
- idesc = idesc->next;
+ if (name == NULL) /* no name? */
+ panic ("anonymous interrupt");
+ if (ithd == NULL || ithd->it_ih == NULL) {
+ /* first handler for this irq. */
+ if (ithd == NULL) {
+ ithd = malloc(sizeof (struct ithd), M_DEVBUF, M_WAITOK);
+ if (ithd == NULL)
+ return (NULL);
+ bzero(ithd, sizeof(struct ithd));
+ ithd->irq = irq;
+ ithds[irq] = ithd;
}
- }
-}
-
-static void
-update_masks(intrmask_t *maskptr, int irq)
-{
- intrmask_t mask = 1 << irq;
-
- if (maskptr == NULL)
- return;
-
- if (find_idesc(maskptr, irq) == NULL) {
- /* no reference to this maskptr was found in this irq's chain */
- if ((*maskptr & mask) == 0)
- return;
- /* the irq was included in the classes mask, remove it */
- *maskptr &= ~mask;
- } else {
- /* a reference to this maskptr was found in this irq's chain */
- if ((*maskptr & mask) != 0)
- return;
- /* put the irq into the classes mask */
- *maskptr |= mask;
- }
- /* we need to update all values in the intr_mask[irq] array */
- update_intr_masks();
- /* update mask in chains of the interrupt multiplex handler as well */
- update_mux_masks();
-}
-
-/*
- * Add interrupt handler to linked list hung off of intreclist_head[irq]
- * and install shared interrupt multiplex handler, if necessary
- */
-
-static int
-add_intrdesc(intrec *idesc)
-{
- int irq = idesc->intr;
-
- intrec *head = intreclist_head[irq];
-
- if (head == NULL) {
- /* first handler for this irq, just install it */
- if (icu_setup(irq, idesc->handler, idesc->argument,
- idesc->maskptr, idesc->flags) != 0)
- return (-1);
-
- update_intrname(irq, idesc->name);
- /* keep reference */
- intreclist_head[irq] = idesc;
- } else {
- if ((idesc->flags & INTR_EXCL) != 0
- || (head->flags & INTR_EXCL) != 0) {
+ /*
+ * If we have a fast interrupt, we need to set the
+ * handler address directly. Do that below. For a
+ * slow interrupt, we don't need to know more details,
+ * so do it here because it's tidier.
+ */
+ if ((flags & INTR_FAST) == 0) {
/*
- * can't append new handler, if either list head or
- * new handler do not allow interrupts to be shared
+ * Only create a kernel thread if we don't already
+ * have one.
*/
- if (bootverbose)
- printf("\tdevice combination doesn't support "
- "shared irq%d\n", irq);
- return (-1);
- }
- if (head->next == NULL) {
+ if (ithd->it_proc == NULL) {
+ errcode = kthread_create(ithd_loop, NULL, &p,
+ RFSTOPPED | RFHIGHPID, "irq%d: %s", irq,
+ name);
+ if (errcode)
+ panic("inthand_add: Can't create "
+ "interrupt thread");
+ p->p_rtprio.type = RTP_PRIO_ITHREAD;
+ p->p_stat = SWAIT; /* we're idle */
+
+ /* Put in linkages. */
+ ithd->it_proc = p;
+ p->p_ithd = ithd;
+ } else
+ snprintf(ithd->it_proc->p_comm, MAXCOMLEN,
+ "irq%d: %s", irq, name);
+ p->p_rtprio.prio = pri;
+
/*
- * second handler for this irq, replace device driver's
- * handler by shared interrupt multiplexer function
+ * The interrupt process must be in place, but
+ * not necessarily schedulable, before we
+ * initialize the ICU, since it may cause an
+ * immediate interrupt.
*/
- icu_unset(irq, head->handler);
- if (icu_setup(irq, intr_mux, head, 0, 0) != 0)
- return (-1);
- if (bootverbose)
- printf("\tusing shared irq%d.\n", irq);
- update_intrname(irq, "mux");
+ if (icu_setup(irq, &sched_ithd, arg, flags) != 0)
+ panic("inthand_add: Can't initialize ICU");
}
- /* just append to the end of the chain */
- while (head->next != NULL)
- head = head->next;
- head->next = idesc;
- }
- update_masks(idesc->maskptr, irq);
- return (0);
-}
-
-/*
- * Create and activate an interrupt handler descriptor data structure.
- *
- * The dev_instance pointer is required for resource management, and will
- * only be passed through to resource_claim().
- *
- * There will be functions that derive a driver and unit name from a
- * dev_instance variable, and those functions will be used to maintain the
- * interrupt counter label array referenced by systat and vmstat to report
- * device interrupt rates (->update_intrlabels).
- *
- * Add the interrupt handler descriptor data structure created by an
- * earlier call of create_intr() to the linked list for its irq and
- * adjust the interrupt masks if necessary.
- *
- * WARNING: This is an internal function and not to be used by device
- * drivers. It is subject to change without notice.
- */
-
-intrec *
-inthand_add(const char *name, int irq, inthand2_t handler, void *arg,
- intrmask_t *maskptr, int flags)
-{
- intrec *idesc;
- int errcode = -1;
- intrmask_t oldspl;
-
- if (ICU_LEN > 8 * sizeof *maskptr) {
- printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n",
- ICU_LEN, 8 * sizeof *maskptr);
+ } else if ((flags & INTR_EXCL) != 0
+ || (ithd->it_ih->flags & INTR_EXCL) != 0) {
+ /*
+ * We can't append the new handler if either
+ * list ithd or new handler do not allow
+ * interrupts to be shared.
+ */
+ if (bootverbose)
+ printf("\tdevice combination %s and %s "
+ "doesn't support shared irq%d\n",
+ ithd->it_ih->name, name, irq);
+ return(NULL);
+ } else if (flags & INTR_FAST) {
+ /* We can only have one fast interrupt by itself. */
+ if (bootverbose)
+ printf("\tCan't add fast interrupt %s"
+ " to normal interrupt %s on irq%d",
+ name, ithd->it_ih->name, irq);
return (NULL);
+ } else { /* update p_comm */
+ p = ithd->it_proc;
+ if (strlen(p->p_comm) + strlen(name) < MAXCOMLEN) {
+ strcat(p->p_comm, " ");
+ strcat(p->p_comm, name);
+ } else if (strlen(p->p_comm) == MAXCOMLEN)
+ p->p_comm[MAXCOMLEN - 1] = '+';
+ else
+ strcat(p->p_comm, "+");
}
- if ((unsigned)irq >= ICU_LEN) {
- printf("create_intr: requested irq%d too high, limit is %d\n",
- irq, ICU_LEN -1);
+ idesc = malloc(sizeof (struct intrec), M_DEVBUF, M_WAITOK);
+ if (idesc == NULL)
return (NULL);
- }
+ bzero(idesc, sizeof (struct intrec));
- idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK);
- if (idesc == NULL)
- return NULL;
- bzero(idesc, sizeof *idesc);
+ idesc->handler = handler;
+ idesc->argument = arg;
+ idesc->flags = flags;
+ idesc->ithd = ithd;
- if (name == NULL)
- name = "???";
idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK);
if (idesc->name == NULL) {
free(idesc, M_DEVBUF);
- return NULL;
+ return (NULL);
}
strcpy(idesc->name, name);
- idesc->handler = handler;
- idesc->argument = arg;
- idesc->maskptr = maskptr;
- idesc->intr = irq;
- idesc->flags = flags;
-
- /* block this irq */
- oldspl = splq(1 << irq);
-
- /* add irq to class selected by maskptr */
- errcode = add_intrdesc(idesc);
- splx(oldspl);
-
- if (errcode != 0) {
+ /* Slow interrupts got set up above. */
+ if ((flags & INTR_FAST)
+ && (icu_setup(irq, idesc->handler, idesc->argument,
+ idesc->flags) != 0) ) {
if (bootverbose)
- printf("\tintr_connect(irq%d) failed, result=%d\n",
+ printf("\tinthand_add(irq%d) failed, result=%d\n",
irq, errcode);
free(idesc->name, M_DEVBUF);
free(idesc, M_DEVBUF);
- idesc = NULL;
+ return NULL;
}
-
+ head = ithd->it_ih; /* look at chain of handlers */
+ if (head) {
+ while (head->next != NULL)
+ head = head->next; /* find the end */
+ head->next = idesc; /* hook it in there */
+ } else
+ ithd->it_ih = idesc; /* put it up front */
+ update_intrname(irq, idesc->name);
return (idesc);
}
/*
- * Deactivate and remove the interrupt handler descriptor data connected
- * created by an earlier call of intr_connect() from the linked list and
- * adjust theinterrupt masks if necessary.
+ * Deactivate and remove linked list the interrupt handler descriptor
+ * data connected created by an earlier call of inthand_add(), then
+ * adjust the interrupt masks if necessary.
*
- * Return the memory held by the interrupt handler descriptor data structure
- * to the system. Make sure, the handler is not actively used anymore, before.
+ * Return the memory held by the interrupt handler descriptor data
+ * structure to the system. First ensure the handler is not actively
+ * in use.
*/
int
inthand_remove(intrec *idesc)
{
- intrec **hook, *head;
- int irq;
- int errcode = 0;
- intrmask_t oldspl;
+ ithd *ithd; /* descriptor for the IRQ */
+ intrec *ih; /* chain of handlers */
if (idesc == NULL)
return (-1);
+ ithd = idesc->ithd;
+ ih = ithd->it_ih;
- irq = idesc->intr;
-
- /* find pointer that keeps the reference to this interrupt descriptor */
- hook = find_pred(idesc, irq);
- if (hook == NULL)
+ if (ih == idesc) /* first in the chain */
+ ithd->it_ih = idesc->next; /* unhook it */
+ else {
+ while ((ih != NULL)
+ && (ih->next != idesc) )
+ ih = ih->next;
+ if (ih->next != idesc)
return (-1);
-
- /* make copy of original list head, the line after may overwrite it */
- head = intreclist_head[irq];
-
- /* unlink: make predecessor point to idesc->next instead of to idesc */
- *hook = idesc->next;
-
- /* now check whether the element we removed was the list head */
- if (idesc == head) {
-
- oldspl = splq(1 << irq);
-
- /* check whether the new list head is the only element on list */
- head = intreclist_head[irq];
- if (head != NULL) {
- icu_unset(irq, intr_mux);
- if (head->next != NULL) {
- /* install the multiplex handler with new list head as argument */
- errcode = icu_setup(irq, intr_mux, head, 0, 0);
- if (errcode == 0)
- update_intrname(irq, NULL);
- } else {
- /* install the one remaining handler for this irq */
- errcode = icu_setup(irq, head->handler,
- head->argument,
- head->maskptr, head->flags);
- if (errcode == 0)
- update_intrname(irq, head->name);
+ ih->next = ih->next->next;
}
- } else {
- /* revert to old handler, eg: strayintr */
- icu_unset(irq, idesc->handler);
- }
- splx(oldspl);
- }
- update_masks(idesc->maskptr, irq);
+
+ if (ithd->it_ih == NULL) /* no handlers left, */
+ icu_unset(ithd->irq, idesc->handler);
free(idesc, M_DEVBUF);
return (0);
}
diff --git a/sys/amd64/isa/intr_machdep.h b/sys/amd64/isa/intr_machdep.h
index 5982295..87c97a3 100644
--- a/sys/amd64/isa/intr_machdep.h
+++ b/sys/amd64/isa/intr_machdep.h
@@ -98,7 +98,6 @@
#define TPR_BLOCK_XCPUSTOP 0xaf /* */
#define TPR_BLOCK_ALL 0xff /* all INTs */
-
#ifdef TEST_TEST1
/* put a 'fake' HWI in top of APIC prio 0x3x, 32 + 31 = 63 = 0x3f */
#define XTEST1_OFFSET (ICU_OFFSET + 31)
@@ -145,8 +144,9 @@ extern u_long intrcnt[]; /* counts for for each device and stray */
extern char intrnames[]; /* string table containing device names */
extern u_long *intr_countp[]; /* pointers into intrcnt[] */
extern inthand2_t *intr_handler[]; /* C entry points of intr handlers */
-extern u_int intr_mask[]; /* sets of intrs masked during handling of 1 */
+extern ithd *ithds[];
extern void *intr_unit[]; /* cookies to pass to intr handlers */
+extern ithd softinterrupt; /* soft interrupt thread */
inthand_t
IDTVEC(fastintr0), IDTVEC(fastintr1),
@@ -190,26 +190,60 @@ inthand_t
#endif /** TEST_TEST1 */
#endif /* SMP || APIC_IO */
+#ifdef PC98
+#define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */
+#define ICU_SLAVEID 7
+#else
+#define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */
+#define ICU_SLAVEID 2
+#endif
+
+#ifdef APIC_IO
+/*
+ * This is to accommodate "mixed-mode" programming for
+ * motherboards that don't connect the 8254 to the IO APIC.
+ */
+#define AUTO_EOI_1 1
+#endif
+
+#define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN)
+
void isa_defaultirq __P((void));
int isa_nmi __P((int cd));
int icu_setup __P((int intr, inthand2_t *func, void *arg,
- u_int *maskptr, int flags));
+ int flags));
int icu_unset __P((int intr, inthand2_t *handler));
-int update_intr_masks __P((void));
intrmask_t splq __P((intrmask_t mask));
-#define INTR_FAST 0x00000001 /* fast interrupt handler */
-#define INTR_EXCL 0x00010000 /* excl. intr, default is shared */
+/*
+ * Describe a hardware interrupt handler. These structures are
+ * accessed via the array intreclist, which contains one pointer per
+ * hardware interrupt.
+ *
+ * Multiple interrupt handlers for a specific IRQ can be chained
+ * together via the 'next' pointer.
+ */
+typedef struct intrec {
+ inthand2_t *handler; /* code address of handler */
+ void *argument; /* argument to pass to handler */
+ enum intr_type flags; /* flag bits (sys/bus.h) */
+ char *name; /* name of handler */
+ ithd *ithd; /* handler we're connected to */
+ struct intrec *next; /* next handler for this irq */
+} intrec;
/*
* WARNING: These are internal functions and not to be used by device drivers!
* They are subject to change without notice.
*/
struct intrec *inthand_add(const char *name, int irq, inthand2_t handler,
- void *arg, intrmask_t *maskptr, int flags);
-
+ void *arg, int pri, int flags);
int inthand_remove(struct intrec *idesc);
+void sched_ithd(void *);
+void ithd_loop(void *);
+void start_softintr(void *);
+void intr_soft(void *);
#endif /* LOCORE */
diff --git a/sys/amd64/isa/ithread.c b/sys/amd64/isa/ithread.c
new file mode 100644
index 0000000..4ceac42
--- /dev/null
+++ b/sys/amd64/isa/ithread.c
@@ -0,0 +1,353 @@
+/*-
+ * Copyright (c) 1997 Berkeley Software Design, Inc. 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. Berkeley Software Design Inc's name may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``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 BERKELEY SOFTWARE DESIGN INC 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 BSDI: intr.c,v 1.6.2.5 1999/07/06 19:16:52 cp Exp
+ * $FreeBSD$
+ */
+
+/* Interrupt thread code. */
+
+#include "opt_auto_eoi.h"
+
+#include "isa.h"
+
+#include <sys/param.h>
+#include <sys/rtprio.h> /* change this name XXX */
+#ifndef SMP
+#include <machine/lock.h>
+#endif
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/syslog.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/unistd.h>
+#include <sys/errno.h>
+#include <sys/interrupt.h>
+#include <machine/ipl.h>
+#include <machine/md_var.h>
+#include <machine/segments.h>
+#include <sys/bus.h>
+
+#if defined(APIC_IO)
+#include <machine/smp.h>
+#include <machine/smptests.h> /** FAST_HI */
+#include <machine/resource.h>
+#endif /* APIC_IO */
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_machdep.h>
+#include <pc98/pc98/epsonio.h>
+#else
+#include <i386/isa/isa.h>
+#endif
+#include <i386/isa/icu.h>
+
+#if NISA > 0
+#include <isa/isavar.h>
+#endif
+#include <i386/isa/intr_machdep.h>
+#include <sys/interrupt.h>
+#ifdef APIC_IO
+#include <machine/clock.h>
+#endif
+
+#include "mca.h"
+#if NMCA > 0
+#include <i386/isa/mca_machdep.h>
+#endif
+
+#include <sys/vmmeter.h>
+#include <machine/mutex.h>
+#include <sys/ktr.h>
+#include <machine/cpu.h>
+#if 0
+#include <ddb/ddb.h>
+#endif
+
+u_long softintrcnt [NSWI];
+
+SYSINIT(start_softintr, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softintr, NULL)
+
+/*
+ * Schedule a heavyweight interrupt process. This function is called
+ * from the interrupt handlers Xintr<num>.
+ */
+void
+sched_ithd(void *cookie)
+{
+ int irq = (int) cookie; /* IRQ we're handling */
+ ithd *ir = ithds[irq]; /* and the process that does it */
+
+ /* This used to be in icu_vector.s */
+ /*
+ * We count software interrupts when we process them. The
+ * code here follows previous practice, but there's an
+ * argument for counting hardware interrupts when they're
+ * processed too.
+ */
+ if (irq < NHWI) /* real interrupt, */
+ atomic_add_long(intr_countp[irq], 1); /* one more for this IRQ */
+ atomic_add_int(&cnt.v_intr, 1); /* one more global interrupt */
+
+ CTR3(KTR_INTR, "sched_ithd pid %d(%s) need=%d",
+ ir->it_proc->p_pid, ir->it_proc->p_comm, ir->it_need);
+
+#if 0
+ /*
+ * If we are in the debugger, we can't use interrupt threads to
+ * process interrupts since the threads are scheduled. Instead,
+ * call the interrupt handlers directly. This should be able to
+ * go away once we have light-weight interrupt handlers.
+ */
+ if (db_active) {
+ intrec *ih; /* and our interrupt handler chain */
+#if 0
+ membar_unlock(); /* push out "it_need=0" */
+#endif
+ for (ih = ir->it_ih; ih != NULL; ih = ih->next) {
+ if ((ih->flags & INTR_MPSAFE) == 0)
+ mtx_enter(&Giant, MTX_DEF);
+ ih->handler(ih->argument);
+ if ((ih->flags & INTR_MPSAFE) == 0)
+ mtx_exit(&Giant, MTX_DEF);
+ }
+
+ INTREN (1 << ir->irq); /* reset the mask bit */
+ return;
+ }
+#endif
+
+ /*
+ * Set it_need so that if the thread is already running but close
+ * to done, it will do another go-round. Then get the sched lock
+ * and see if the thread is on whichkqs yet. If not, put it on
+ * there. In any case, kick everyone so that if the new thread
+ * is higher priority than their current thread, it gets run now.
+ */
+ ir->it_need = 1;
+ mtx_enter(&sched_lock, MTX_SPIN);
+ if (ir->it_proc->p_stat == SWAIT) { /* not on run queue */
+ CTR1(KTR_INTR, "sched_ithd: setrunqueue %d",
+ ir->it_proc->p_pid);
+/* membar_lock(); */
+ ir->it_proc->p_stat = SRUN;
+ setrunqueue(ir->it_proc);
+ aston();
+ }
+ else {
+if (irq < NHWI && (irq & 7) != 0)
+ CTR3(KTR_INTR, "sched_ithd %d: it_need %d, state %d",
+ ir->it_proc->p_pid,
+ ir->it_need,
+ ir->it_proc->p_stat );
+ }
+ mtx_exit(&sched_lock, MTX_SPIN);
+#if 0
+ aston(); /* ??? check priorities first? */
+#else
+ need_resched();
+#endif
+}
+
+/*
+ * This is the main code for all interrupt threads. It gets put on
+ * whichkqs by setrunqueue above.
+ */
+void
+ithd_loop(void *dummy)
+{
+ ithd *me; /* our thread context */
+ intrec *ih; /* and our interrupt handler chain */
+
+ me = curproc->p_ithd; /* point to myself */
+
+ /*
+ * As long as we have interrupts outstanding, go through the
+ * list of handlers, giving each one a go at it.
+ */
+ for (;;) {
+ CTR3(KTR_INTR, "ithd_loop pid %d(%s) need=%d",
+ me->it_proc->p_pid, me->it_proc->p_comm, me->it_need);
+ while (me->it_need) {
+ /*
+ * Service interrupts. If another interrupt
+ * arrives while we are running, they will set
+ * it_need to denote that we should make
+ * another pass.
+ */
+ me->it_need = 0;
+#if 0
+ membar_unlock(); /* push out "it_need=0" */
+#endif
+ for (ih = me->it_ih; ih != NULL; ih = ih->next) {
+ CTR5(KTR_INTR,
+ "ithd_loop pid %d ih=%p: %p(%p) flg=%x",
+ me->it_proc->p_pid, (void *)ih,
+ (void *)ih->handler, ih->argument,
+ ih->flags);
+
+ if ((ih->flags & INTR_MPSAFE) == 0)
+ mtx_enter(&Giant, MTX_DEF);
+ ih->handler(ih->argument);
+ if ((ih->flags & INTR_MPSAFE) == 0)
+ mtx_exit(&Giant, MTX_DEF);
+ }
+ }
+
+ /*
+ * Processed all our interrupts. Now get the sched
+ * lock. This may take a while and it_need may get
+ * set again, so we have to check it again.
+ */
+ mtx_enter(&sched_lock, MTX_SPIN);
+ if (!me->it_need) {
+
+ INTREN (1 << me->irq); /* reset the mask bit */
+ me->it_proc->p_stat = SWAIT; /* we're idle */
+#ifdef APIC_IO
+ CTR1(KTR_INTR, "ithd_loop pid %d: done",
+ me->it_proc->p_pid);
+#else
+ CTR2(KTR_INTR, "ithd_loop pid %d: done, imen=%x",
+ me->it_proc->p_pid, imen);
+#endif
+ mi_switch();
+ CTR1(KTR_INTR, "ithd_loop pid %d: resumed",
+ me->it_proc->p_pid);
+ }
+ mtx_exit(&sched_lock, MTX_SPIN);
+ }
+}
+
+/*
+ * Start soft interrupt thread.
+ */
+void
+start_softintr(void *dummy)
+{
+ int error;
+ struct proc *p;
+ ithd *softintr; /* descriptor for the "IRQ" */
+ intrec *idesc; /* descriptor for this handler */
+ char *name = "sintr"; /* name for idesc */
+ int i;
+
+ if (ithds[SOFTINTR]) { /* we already have a thread */
+ printf("start_softintr: already running");
+ return;
+ }
+ /* first handler for this irq. */
+ softintr = malloc(sizeof (struct ithd), M_DEVBUF, M_WAITOK);
+ if (softintr == NULL)
+ panic ("Can't create soft interrupt thread");
+ bzero(softintr, sizeof(struct ithd));
+ softintr->irq = SOFTINTR;
+ ithds[SOFTINTR] = softintr;
+ error = kthread_create(intr_soft, NULL, &p,
+ RFSTOPPED | RFHIGHPID, "softinterrupt");
+ if (error)
+ panic("start_softintr: kthread_create error %d\n", error);
+
+ p->p_rtprio.type = RTP_PRIO_ITHREAD;
+ p->p_rtprio.prio = PI_SOFT; /* soft interrupt */
+ p->p_stat = SWAIT; /* we're idle */
+
+ /* Put in linkages. */
+ softintr->it_proc = p;
+ p->p_ithd = softintr; /* reverse link */
+
+ idesc = malloc(sizeof (struct intrec), M_DEVBUF, M_WAITOK);
+ if (idesc == NULL)
+ panic ("Can't create soft interrupt thread");
+ bzero(idesc, sizeof (struct intrec));
+
+ idesc->ithd = softintr;
+ idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK);
+ if (idesc->name == NULL)
+ panic ("Can't create soft interrupt thread");
+ strcpy(idesc->name, name);
+ for (i = NHWI; i < NHWI + NSWI; i++)
+ intr_countp[i] = &softintrcnt [i - NHWI];
+}
+
+/*
+ * Software interrupt process code.
+ */
+void
+intr_soft(void *dummy)
+{
+ int i;
+ ithd *me; /* our thread context */
+
+ me = curproc->p_ithd; /* point to myself */
+
+ /* Main loop */
+ for (;;) {
+#if 0
+ CTR3(KTR_INTR, "intr_soft pid %d(%s) need=%d",
+ me->it_proc->p_pid, me->it_proc->p_comm,
+ me->it_need);
+#endif
+
+ /*
+ * Service interrupts. If another interrupt arrives
+ * while we are running, they will set it_need to
+ * denote that we should make another pass.
+ */
+ me->it_need = 0;
+ while ((i = ffs(spending))) {
+ i--;
+ atomic_add_long(intr_countp[i], 1);
+ spending &= ~ (1 << i);
+ mtx_enter(&Giant, MTX_DEF);
+ (ihandlers[i])();
+ mtx_exit(&Giant, MTX_DEF);
+ }
+ /*
+ * Processed all our interrupts. Now get the sched
+ * lock. This may take a while and it_need may get
+ * set again, so we have to check it again.
+ */
+ mtx_enter(&sched_lock, MTX_SPIN);
+ if (!me->it_need) {
+#if 0
+ CTR1(KTR_INTR, "intr_soft pid %d: done",
+ me->it_proc->p_pid);
+#endif
+ me->it_proc->p_stat = SWAIT; /* we're idle */
+ mi_switch();
+#if 0
+ CTR1(KTR_INTR, "intr_soft pid %d: resumed",
+ me->it_proc->p_pid);
+#endif
+ }
+ mtx_exit(&sched_lock, MTX_SPIN);
+ }
+}
diff --git a/sys/amd64/isa/nmi.c b/sys/amd64/isa/nmi.c
index 34a8c22..870760e 100644
--- a/sys/amd64/isa/nmi.c
+++ b/sys/amd64/isa/nmi.c
@@ -36,12 +36,6 @@
* from: @(#)isa.c 7.2 (Berkeley) 5/13/91
* $FreeBSD$
*/
-/*
- * This file contains an aggregated module marked:
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
- * All rights reserved.
- * See the notice for details.
- */
#include "opt_auto_eoi.h"
@@ -51,11 +45,14 @@
#ifndef SMP
#include <machine/lock.h>
#endif
+#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <sys/kernel.h>
+#include <sys/kthread.h>
#include <sys/malloc.h>
#include <sys/module.h>
+#include <sys/unistd.h>
#include <sys/errno.h>
#include <sys/interrupt.h>
#include <machine/ipl.h>
@@ -91,30 +88,14 @@
#include <i386/isa/mca_machdep.h>
#endif
-/* XXX should be in suitable include files */
-#ifdef PC98
-#define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */
-#define ICU_SLAVEID 7
-#else
-#define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */
-#define ICU_SLAVEID 2
-#endif
-
-#ifdef APIC_IO
/*
- * This is to accommodate "mixed-mode" programming for
- * motherboards that don't connect the 8254 to the IO APIC.
+ * Per-interrupt data. We consider the soft interrupt to be a special
+ * case, so these arrays have NHWI + NSWI entries, not ICU_LEN.
*/
-#define AUTO_EOI_1 1
-#endif
-
-#define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN)
-
-u_long *intr_countp[ICU_LEN];
-inthand2_t *intr_handler[ICU_LEN];
-u_int intr_mask[ICU_LEN];
-static u_int* intr_mptr[ICU_LEN];
-void *intr_unit[ICU_LEN];
+u_long *intr_countp[NHWI + NSWI]; /* pointers to interrupt counters */
+inthand2_t *intr_handler[NHWI + NSWI]; /* first level interrupt handler */
+ithd *ithds[NHWI + NSWI]; /* real interrupt handler */
+void *intr_unit[NHWI + NSWI];
static inthand_t *fastintr[ICU_LEN] = {
&IDTVEC(fastintr0), &IDTVEC(fastintr1),
@@ -292,8 +273,9 @@ isa_nmi(cd)
}
/*
- * Fill in default interrupt table (in case of spuruious interrupt
- * during configuration of kernel, setup interrupt control unit
+ * Create a default interrupt table to avoid problems caused by
+ * spurious interrupts during configuration of kernel, then setup
+ * interrupt control unit.
*/
void
isa_defaultirq()
@@ -364,16 +346,6 @@ isa_strayintr(vcookiep)
{
int intr = (void **)vcookiep - &intr_unit[0];
- /* DON'T BOTHER FOR NOW! */
- /* for some reason, we get bursts of intr #7, even if not enabled! */
- /*
- * Well the reason you got bursts of intr #7 is because someone
- * raised an interrupt line and dropped it before the 8259 could
- * prioritize it. This is documented in the intel data book. This
- * means you have BAD hardware! I have changed this so that only
- * the first 5 get logged, then it quits logging them, and puts
- * out a special message. rgrimes 3/25/1993
- */
/*
* XXX TODO print a different message for #7 if it is for a
* glitch. Glitches can be distinguished from real #7's by
@@ -405,36 +377,10 @@ isa_irq_pending()
}
#endif
-int
-update_intr_masks(void)
-{
- int intr, n=0;
- u_int mask,*maskptr;
-
- for (intr=0; intr < ICU_LEN; intr ++) {
-#if defined(APIC_IO)
- /* no 8259 SLAVE to ignore */
-#else
- if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */
-#endif /* APIC_IO */
- maskptr = intr_mptr[intr];
- if (!maskptr)
- continue;
- *maskptr |= SWI_LOW_MASK | (1 << intr);
- mask = *maskptr;
- if (mask != intr_mask[intr]) {
-#if 0
- printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n",
- intr, intr_mask[intr], mask, maskptr);
-#endif
- intr_mask[intr]=mask;
- n++;
- }
-
- }
- return (n);
-}
-
+/*
+ * Update intrnames array with the specified name. This is used by
+ * vmstat(8) and the like.
+ */
static void
update_intrname(int intr, char *name)
{
@@ -485,7 +431,7 @@ found:
}
int
-icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
+icu_setup(int intr, inthand2_t *handler, void *arg, int flags)
{
#ifdef FAST_HI
int select; /* the select register is 8 bits */
@@ -493,7 +439,6 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
u_int32_t value; /* the window register is 32 bits */
#endif /* FAST_HI */
u_long ef;
- u_int mask = (maskptr ? *maskptr : 0);
#if defined(APIC_IO)
if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */
@@ -506,8 +451,6 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
ef = read_eflags();
disable_intr();
intr_handler[intr] = handler;
- intr_mptr[intr] = maskptr;
- intr_mask[intr] = mask | SWI_LOW_MASK | (1 << intr);
intr_unit[intr] = arg;
#ifdef FAST_HI
if (flags & INTR_FAST) {
@@ -547,11 +490,15 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
#endif /* FAST_HI */
INTREN(1 << intr);
- MPINTR_UNLOCK();
write_eflags(ef);
return (0);
}
+/*
+ * Dissociate an interrupt handler from an IRQ and set the handler to
+ * the stray interrupt handler. The 'handler' parameter is used only
+ * for consistency checking.
+ */
int
icu_unset(intr, handler)
int intr;
@@ -567,8 +514,6 @@ icu_unset(intr, handler)
disable_intr();
intr_countp[intr] = &intrcnt[1 + intr];
intr_handler[intr] = isa_strayintr;
- intr_mptr[intr] = NULL;
- intr_mask[intr] = HWI_MASK | SWI_MASK;
intr_unit[intr] = &intr_unit[intr];
#ifdef FAST_HI_XXX
/* XXX how do I re-create dvp here? */
@@ -581,353 +526,172 @@ icu_unset(intr, handler)
setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL,
GSEL(GCODE_SEL, SEL_KPL));
#endif /* FAST_HI */
- MPINTR_UNLOCK();
write_eflags(ef);
return (0);
}
-/* The following notice applies beyond this point in the file */
-
-/*
- * Copyright (c) 1997, Stefan Esser <se@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 unmodified, 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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$
- *
- */
-
-typedef struct intrec {
- intrmask_t mask;
- inthand2_t *handler;
- void *argument;
- struct intrec *next;
- char *name;
- int intr;
- intrmask_t *maskptr;
- int flags;
-} intrec;
-
-static intrec *intreclist_head[ICU_LEN];
-
-/*
- * The interrupt multiplexer calls each of the handlers in turn. The
- * ipl is initially quite low. It is raised as necessary for each call
- * and lowered after the call. Thus out of order handling is possible
- * even for interrupts of the same type. This is probably no more
- * harmful than out of order handling in general (not harmful except
- * for real time response which we don't support anyway).
- */
-static void
-intr_mux(void *arg)
-{
- intrec *p;
- intrmask_t oldspl;
-
- for (p = arg; p != NULL; p = p->next) {
- oldspl = splq(p->mask);
- p->handler(p->argument);
- splx(oldspl);
- }
-}
-
-static intrec*
-find_idesc(unsigned *maskptr, int irq)
-{
- intrec *p = intreclist_head[irq];
-
- while (p && p->maskptr != maskptr)
- p = p->next;
-
- return (p);
-}
-
-static intrec**
-find_pred(intrec *idesc, int irq)
+intrec *
+inthand_add(const char *name, int irq, inthand2_t handler, void *arg,
+ int pri, int flags)
{
- intrec **pp = &intreclist_head[irq];
- intrec *p = *pp;
-
- while (p != idesc) {
- if (p == NULL)
- return (NULL);
- pp = &p->next;
- p = *pp;
- }
- return (pp);
-}
-
-/*
- * Both the low level handler and the shared interrupt multiplexer
- * block out further interrupts as set in the handlers "mask", while
- * the handler is running. In fact *maskptr should be used for this
- * purpose, but since this requires one more pointer dereference on
- * each interrupt, we rather bother update "mask" whenever *maskptr
- * changes. The function "update_masks" should be called **after**
- * all manipulation of the linked list of interrupt handlers hung
- * off of intrdec_head[irq] is complete, since the chain of handlers
- * will both determine the *maskptr values and the instances of mask
- * that are fixed. This function should be called with the irq for
- * which a new handler has been add blocked, since the masks may not
- * yet know about the use of this irq for a device of a certain class.
- */
+ ithd *ithd = ithds[irq]; /* descriptor for the IRQ */
+ intrec *head; /* chain of handlers for IRQ */
+ intrec *idesc; /* descriptor for this handler */
+ struct proc *p; /* interrupt thread */
+ int errcode = 0;
-static void
-update_mux_masks(void)
-{
- int irq;
- for (irq = 0; irq < ICU_LEN; irq++) {
- intrec *idesc = intreclist_head[irq];
- while (idesc != NULL) {
- if (idesc->maskptr != NULL) {
- /* our copy of *maskptr may be stale, refresh */
- idesc->mask = *idesc->maskptr;
- }
- idesc = idesc->next;
+ if (name == NULL) /* no name? */
+ panic ("anonymous interrupt");
+ if (ithd == NULL || ithd->it_ih == NULL) {
+ /* first handler for this irq. */
+ if (ithd == NULL) {
+ ithd = malloc(sizeof (struct ithd), M_DEVBUF, M_WAITOK);
+ if (ithd == NULL)
+ return (NULL);
+ bzero(ithd, sizeof(struct ithd));
+ ithd->irq = irq;
+ ithds[irq] = ithd;
}
- }
-}
-
-static void
-update_masks(intrmask_t *maskptr, int irq)
-{
- intrmask_t mask = 1 << irq;
-
- if (maskptr == NULL)
- return;
-
- if (find_idesc(maskptr, irq) == NULL) {
- /* no reference to this maskptr was found in this irq's chain */
- if ((*maskptr & mask) == 0)
- return;
- /* the irq was included in the classes mask, remove it */
- *maskptr &= ~mask;
- } else {
- /* a reference to this maskptr was found in this irq's chain */
- if ((*maskptr & mask) != 0)
- return;
- /* put the irq into the classes mask */
- *maskptr |= mask;
- }
- /* we need to update all values in the intr_mask[irq] array */
- update_intr_masks();
- /* update mask in chains of the interrupt multiplex handler as well */
- update_mux_masks();
-}
-
-/*
- * Add interrupt handler to linked list hung off of intreclist_head[irq]
- * and install shared interrupt multiplex handler, if necessary
- */
-
-static int
-add_intrdesc(intrec *idesc)
-{
- int irq = idesc->intr;
-
- intrec *head = intreclist_head[irq];
-
- if (head == NULL) {
- /* first handler for this irq, just install it */
- if (icu_setup(irq, idesc->handler, idesc->argument,
- idesc->maskptr, idesc->flags) != 0)
- return (-1);
-
- update_intrname(irq, idesc->name);
- /* keep reference */
- intreclist_head[irq] = idesc;
- } else {
- if ((idesc->flags & INTR_EXCL) != 0
- || (head->flags & INTR_EXCL) != 0) {
+ /*
+ * If we have a fast interrupt, we need to set the
+ * handler address directly. Do that below. For a
+ * slow interrupt, we don't need to know more details,
+ * so do it here because it's tidier.
+ */
+ if ((flags & INTR_FAST) == 0) {
/*
- * can't append new handler, if either list head or
- * new handler do not allow interrupts to be shared
+ * Only create a kernel thread if we don't already
+ * have one.
*/
- if (bootverbose)
- printf("\tdevice combination doesn't support "
- "shared irq%d\n", irq);
- return (-1);
- }
- if (head->next == NULL) {
+ if (ithd->it_proc == NULL) {
+ errcode = kthread_create(ithd_loop, NULL, &p,
+ RFSTOPPED | RFHIGHPID, "irq%d: %s", irq,
+ name);
+ if (errcode)
+ panic("inthand_add: Can't create "
+ "interrupt thread");
+ p->p_rtprio.type = RTP_PRIO_ITHREAD;
+ p->p_stat = SWAIT; /* we're idle */
+
+ /* Put in linkages. */
+ ithd->it_proc = p;
+ p->p_ithd = ithd;
+ } else
+ snprintf(ithd->it_proc->p_comm, MAXCOMLEN,
+ "irq%d: %s", irq, name);
+ p->p_rtprio.prio = pri;
+
/*
- * second handler for this irq, replace device driver's
- * handler by shared interrupt multiplexer function
+ * The interrupt process must be in place, but
+ * not necessarily schedulable, before we
+ * initialize the ICU, since it may cause an
+ * immediate interrupt.
*/
- icu_unset(irq, head->handler);
- if (icu_setup(irq, intr_mux, head, 0, 0) != 0)
- return (-1);
- if (bootverbose)
- printf("\tusing shared irq%d.\n", irq);
- update_intrname(irq, "mux");
+ if (icu_setup(irq, &sched_ithd, arg, flags) != 0)
+ panic("inthand_add: Can't initialize ICU");
}
- /* just append to the end of the chain */
- while (head->next != NULL)
- head = head->next;
- head->next = idesc;
- }
- update_masks(idesc->maskptr, irq);
- return (0);
-}
-
-/*
- * Create and activate an interrupt handler descriptor data structure.
- *
- * The dev_instance pointer is required for resource management, and will
- * only be passed through to resource_claim().
- *
- * There will be functions that derive a driver and unit name from a
- * dev_instance variable, and those functions will be used to maintain the
- * interrupt counter label array referenced by systat and vmstat to report
- * device interrupt rates (->update_intrlabels).
- *
- * Add the interrupt handler descriptor data structure created by an
- * earlier call of create_intr() to the linked list for its irq and
- * adjust the interrupt masks if necessary.
- *
- * WARNING: This is an internal function and not to be used by device
- * drivers. It is subject to change without notice.
- */
-
-intrec *
-inthand_add(const char *name, int irq, inthand2_t handler, void *arg,
- intrmask_t *maskptr, int flags)
-{
- intrec *idesc;
- int errcode = -1;
- intrmask_t oldspl;
-
- if (ICU_LEN > 8 * sizeof *maskptr) {
- printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n",
- ICU_LEN, 8 * sizeof *maskptr);
+ } else if ((flags & INTR_EXCL) != 0
+ || (ithd->it_ih->flags & INTR_EXCL) != 0) {
+ /*
+ * We can't append the new handler if either
+ * list ithd or new handler do not allow
+ * interrupts to be shared.
+ */
+ if (bootverbose)
+ printf("\tdevice combination %s and %s "
+ "doesn't support shared irq%d\n",
+ ithd->it_ih->name, name, irq);
+ return(NULL);
+ } else if (flags & INTR_FAST) {
+ /* We can only have one fast interrupt by itself. */
+ if (bootverbose)
+ printf("\tCan't add fast interrupt %s"
+ " to normal interrupt %s on irq%d",
+ name, ithd->it_ih->name, irq);
return (NULL);
+ } else { /* update p_comm */
+ p = ithd->it_proc;
+ if (strlen(p->p_comm) + strlen(name) < MAXCOMLEN) {
+ strcat(p->p_comm, " ");
+ strcat(p->p_comm, name);
+ } else if (strlen(p->p_comm) == MAXCOMLEN)
+ p->p_comm[MAXCOMLEN - 1] = '+';
+ else
+ strcat(p->p_comm, "+");
}
- if ((unsigned)irq >= ICU_LEN) {
- printf("create_intr: requested irq%d too high, limit is %d\n",
- irq, ICU_LEN -1);
+ idesc = malloc(sizeof (struct intrec), M_DEVBUF, M_WAITOK);
+ if (idesc == NULL)
return (NULL);
- }
+ bzero(idesc, sizeof (struct intrec));
- idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK);
- if (idesc == NULL)
- return NULL;
- bzero(idesc, sizeof *idesc);
+ idesc->handler = handler;
+ idesc->argument = arg;
+ idesc->flags = flags;
+ idesc->ithd = ithd;
- if (name == NULL)
- name = "???";
idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK);
if (idesc->name == NULL) {
free(idesc, M_DEVBUF);
- return NULL;
+ return (NULL);
}
strcpy(idesc->name, name);
- idesc->handler = handler;
- idesc->argument = arg;
- idesc->maskptr = maskptr;
- idesc->intr = irq;
- idesc->flags = flags;
-
- /* block this irq */
- oldspl = splq(1 << irq);
-
- /* add irq to class selected by maskptr */
- errcode = add_intrdesc(idesc);
- splx(oldspl);
-
- if (errcode != 0) {
+ /* Slow interrupts got set up above. */
+ if ((flags & INTR_FAST)
+ && (icu_setup(irq, idesc->handler, idesc->argument,
+ idesc->flags) != 0) ) {
if (bootverbose)
- printf("\tintr_connect(irq%d) failed, result=%d\n",
+ printf("\tinthand_add(irq%d) failed, result=%d\n",
irq, errcode);
free(idesc->name, M_DEVBUF);
free(idesc, M_DEVBUF);
- idesc = NULL;
+ return NULL;
}
-
+ head = ithd->it_ih; /* look at chain of handlers */
+ if (head) {
+ while (head->next != NULL)
+ head = head->next; /* find the end */
+ head->next = idesc; /* hook it in there */
+ } else
+ ithd->it_ih = idesc; /* put it up front */
+ update_intrname(irq, idesc->name);
return (idesc);
}
/*
- * Deactivate and remove the interrupt handler descriptor data connected
- * created by an earlier call of intr_connect() from the linked list and
- * adjust theinterrupt masks if necessary.
+ * Deactivate and remove linked list the interrupt handler descriptor
+ * data connected created by an earlier call of inthand_add(), then
+ * adjust the interrupt masks if necessary.
*
- * Return the memory held by the interrupt handler descriptor data structure
- * to the system. Make sure, the handler is not actively used anymore, before.
+ * Return the memory held by the interrupt handler descriptor data
+ * structure to the system. First ensure the handler is not actively
+ * in use.
*/
int
inthand_remove(intrec *idesc)
{
- intrec **hook, *head;
- int irq;
- int errcode = 0;
- intrmask_t oldspl;
+ ithd *ithd; /* descriptor for the IRQ */
+ intrec *ih; /* chain of handlers */
if (idesc == NULL)
return (-1);
+ ithd = idesc->ithd;
+ ih = ithd->it_ih;
- irq = idesc->intr;
-
- /* find pointer that keeps the reference to this interrupt descriptor */
- hook = find_pred(idesc, irq);
- if (hook == NULL)
+ if (ih == idesc) /* first in the chain */
+ ithd->it_ih = idesc->next; /* unhook it */
+ else {
+ while ((ih != NULL)
+ && (ih->next != idesc) )
+ ih = ih->next;
+ if (ih->next != idesc)
return (-1);
-
- /* make copy of original list head, the line after may overwrite it */
- head = intreclist_head[irq];
-
- /* unlink: make predecessor point to idesc->next instead of to idesc */
- *hook = idesc->next;
-
- /* now check whether the element we removed was the list head */
- if (idesc == head) {
-
- oldspl = splq(1 << irq);
-
- /* check whether the new list head is the only element on list */
- head = intreclist_head[irq];
- if (head != NULL) {
- icu_unset(irq, intr_mux);
- if (head->next != NULL) {
- /* install the multiplex handler with new list head as argument */
- errcode = icu_setup(irq, intr_mux, head, 0, 0);
- if (errcode == 0)
- update_intrname(irq, NULL);
- } else {
- /* install the one remaining handler for this irq */
- errcode = icu_setup(irq, head->handler,
- head->argument,
- head->maskptr, head->flags);
- if (errcode == 0)
- update_intrname(irq, head->name);
+ ih->next = ih->next->next;
}
- } else {
- /* revert to old handler, eg: strayintr */
- icu_unset(irq, idesc->handler);
- }
- splx(oldspl);
- }
- update_masks(idesc->maskptr, irq);
+
+ if (ithd->it_ih == NULL) /* no handlers left, */
+ icu_unset(ithd->irq, idesc->handler);
free(idesc, M_DEVBUF);
return (0);
}
diff --git a/sys/amd64/isa/npx.c b/sys/amd64/isa/npx.c
index 637853e..8610e35 100644
--- a/sys/amd64/isa/npx.c
+++ b/sys/amd64/isa/npx.c
@@ -245,6 +245,12 @@ npx_probe(dev)
setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
npx_idt_probeintr = idt[npx_intrno];
+
+ /*
+ * XXX This looks highly bogus, but it appears that npc_probe1
+ * needs interrupts enabled. Does this make any difference
+ * here?
+ */
enable_intr();
result = npx_probe1(dev);
disable_intr();
@@ -797,7 +803,7 @@ npxdna()
/*
* Record new context early in case frstor causes an IRQ13.
*/
- npxproc = curproc;
+ PCPU_SET(npxproc, CURPROC);
curpcb->pcb_savefpu.sv_ex_sw = 0;
/*
* The following frstor may cause an IRQ13 when the state being
@@ -834,16 +840,18 @@ npxsave(addr)
fnsave(addr);
/* fnop(); */
start_emulating();
- npxproc = NULL;
+ PCPU_SET(npxproc, NULL);
#else /* SMP */
+ int intrstate;
u_char icu1_mask;
u_char icu2_mask;
u_char old_icu1_mask;
u_char old_icu2_mask;
struct gate_descriptor save_idt_npxintr;
+ intrstate = save_intr();
disable_intr();
old_icu1_mask = inb(IO_ICU1 + 1);
old_icu2_mask = inb(IO_ICU2 + 1);
@@ -851,12 +859,12 @@ npxsave(addr)
outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask));
outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8));
idt[npx_intrno] = npx_idt_probeintr;
- enable_intr();
+ write_eflags(intrstate);
stop_emulating();
fnsave(addr);
fnop();
start_emulating();
- npxproc = NULL;
+ PCPU_SET(npxproc, NULL);
disable_intr();
icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
icu2_mask = inb(IO_ICU2 + 1);
@@ -866,7 +874,7 @@ npxsave(addr)
(icu2_mask & ~(npx0_imask >> 8))
| (old_icu2_mask & (npx0_imask >> 8)));
idt[npx_intrno] = save_idt_npxintr;
- enable_intr(); /* back to usual state */
+ restore_intr(intrstate); /* back to previous state */
#endif /* SMP */
}
diff --git a/sys/amd64/isa/vector.S b/sys/amd64/isa/vector.S
index 5447a90..79f2320 100644
--- a/sys/amd64/isa/vector.S
+++ b/sys/amd64/isa/vector.S
@@ -16,9 +16,10 @@
#include <i386/isa/isa.h>
#endif
+#define FAST_INTR_HANDLER_USES_ES 1
#ifdef FAST_INTR_HANDLER_USES_ES
#define ACTUALLY_PUSHED 1
-#define MAYBE_MOVW_AX_ES movl %ax,%es
+#define MAYBE_MOVW_AX_ES movw %ax,%es
#define MAYBE_POPL_ES popl %es
#define MAYBE_PUSHL_ES pushl %es
#else
@@ -36,11 +37,6 @@
.data
ALIGN_DATA
- .globl _intr_nesting_level
-_intr_nesting_level:
- .byte 0
- .space 3
-
/*
* Interrupt counters and names for export to vmstat(8) and friends.
*
@@ -58,7 +54,6 @@ _eintrcnt:
_intrnames:
.space NR_INTRNAMES * 16
_eintrnames:
-
.text
/*
diff --git a/sys/amd64/isa/vector.s b/sys/amd64/isa/vector.s
index 5447a90..79f2320 100644
--- a/sys/amd64/isa/vector.s
+++ b/sys/amd64/isa/vector.s
@@ -16,9 +16,10 @@
#include <i386/isa/isa.h>
#endif
+#define FAST_INTR_HANDLER_USES_ES 1
#ifdef FAST_INTR_HANDLER_USES_ES
#define ACTUALLY_PUSHED 1
-#define MAYBE_MOVW_AX_ES movl %ax,%es
+#define MAYBE_MOVW_AX_ES movw %ax,%es
#define MAYBE_POPL_ES popl %es
#define MAYBE_PUSHL_ES pushl %es
#else
@@ -36,11 +37,6 @@
.data
ALIGN_DATA
- .globl _intr_nesting_level
-_intr_nesting_level:
- .byte 0
- .space 3
-
/*
* Interrupt counters and names for export to vmstat(8) and friends.
*
@@ -58,7 +54,6 @@ _eintrcnt:
_intrnames:
.space NR_INTRNAMES * 16
_eintrnames:
-
.text
/*
OpenPOWER on IntegriCloud