From 520ffff83ecde11de34f4b6a6b00ac0082e3cd5e Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Tue, 10 Oct 2006 23:23:12 +0000 Subject: [PATCH] Change the x86 interrupt code to suspend/resume interrupt controllers (PICs) rather than interrupt sources. This allows interrupt controllers with no interrupt pics (such as the 8259As when APIC is in use) to participate in suspend/resume. - Always register the 8259A PICs even if we don't use any of their pins. - Explicitly reset the 8259As on resume on amd64 if 'device atpic' isn't included. - Add a "dummy" PIC for the local APIC on the BSP to reset the local APIC on resume. This gets suspend/resume working with APIC on UP systems. SMP still needs more work to bring the APs back to life. The MFC after is tentative. Tested by: anholt (i386) Submitted by: Andrea Bittau (3) MFC after: 1 week --- sys/amd64/amd64/intr_machdep.c | 84 ++++++++++++++++++++++++++++---- sys/amd64/amd64/io_apic.c | 23 +++------ sys/amd64/amd64/local_apic.c | 26 ++++++++-- sys/amd64/amd64/machdep.c | 14 +----- sys/amd64/amd64/mp_machdep.c | 2 +- sys/amd64/include/apicvar.h | 2 +- sys/amd64/include/intr_machdep.h | 9 +++- sys/amd64/isa/atpic.c | 23 +++++---- sys/i386/i386/intr_machdep.c | 58 ++++++++++++++++++---- sys/i386/i386/io_apic.c | 22 +++------ sys/i386/i386/local_apic.c | 26 ++++++++-- sys/i386/i386/mp_machdep.c | 2 +- sys/i386/include/apicvar.h | 2 +- sys/i386/include/intr_machdep.h | 6 ++- sys/i386/isa/atpic.c | 23 +++++---- 15 files changed, 224 insertions(+), 98 deletions(-) diff --git a/sys/amd64/amd64/intr_machdep.c b/sys/amd64/amd64/intr_machdep.c index 9a4eaca5a0ce..65dd8277263c 100644 --- a/sys/amd64/amd64/intr_machdep.c +++ b/sys/amd64/amd64/intr_machdep.c @@ -37,6 +37,7 @@ * that source. */ +#include "opt_atpic.h" #include "opt_ddb.h" #include @@ -62,6 +63,7 @@ typedef void (*mask_fn)(void *); static int intrcnt_index; static struct intsrc *interrupt_sources[NUM_IO_INTS]; static struct mtx intr_table_lock; +static STAILQ_HEAD(, pic) pics; #ifdef SMP static int assign_cpu; @@ -70,10 +72,45 @@ static void intr_assign_next_cpu(struct intsrc *isrc); #endif static void intr_init(void *__dummy); +static int intr_pic_registered(struct pic *pic); static void intrcnt_setname(const char *name, int index); static void intrcnt_updatename(struct intsrc *is); static void intrcnt_register(struct intsrc *is); +static int +intr_pic_registered(struct pic *pic) +{ + struct pic *p; + + STAILQ_FOREACH(p, &pics, pics) { + if (p == pic) + return (1); + } + return (0); +} + +/* + * Register a new interrupt controller (PIC). This is to support suspend + * and resume where we suspend/resume controllers rather than individual + * sources. This also allows controllers with no active sources (such as + * 8259As in a system using the APICs) to participate in suspend and resume. + */ +int +intr_register_pic(struct pic *pic) +{ + int error; + + mtx_lock_spin(&intr_table_lock); + if (intr_pic_registered(pic)) + error = EBUSY; + else { + STAILQ_INSERT_TAIL(&pics, pic, pics); + error = 0; + } + mtx_unlock_spin(&intr_table_lock); + return (error); +} + /* * Register a new interrupt source with the global interrupt system. * The global interrupts need to be disabled when this function is @@ -84,6 +121,7 @@ intr_register_source(struct intsrc *isrc) { int error, vector; + KASSERT(intr_pic_registered(isrc->is_pic), ("unregistered PIC")); vector = isrc->is_pic->pic_vector(isrc); if (interrupt_sources[vector] != NULL) return (EEXIST); @@ -255,26 +293,29 @@ intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame) void intr_resume(void) { - struct intsrc **isrc; - int i; + struct pic *pic; +#ifndef DEV_ATPIC + atpic_reset(); +#endif mtx_lock_spin(&intr_table_lock); - for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) - if (*isrc != NULL && (*isrc)->is_pic->pic_resume != NULL) - (*isrc)->is_pic->pic_resume(*isrc); + STAILQ_FOREACH(pic, &pics, pics) { + if (pic->pic_resume != NULL) + pic->pic_resume(pic); + } mtx_unlock_spin(&intr_table_lock); } void intr_suspend(void) { - struct intsrc **isrc; - int i; + struct pic *pic; mtx_lock_spin(&intr_table_lock); - for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) - if (*isrc != NULL && (*isrc)->is_pic->pic_suspend != NULL) - (*isrc)->is_pic->pic_suspend(*isrc); + STAILQ_FOREACH(pic, &pics, pics) { + if (pic->pic_suspend != NULL) + pic->pic_suspend(pic); + } mtx_unlock_spin(&intr_table_lock); } @@ -327,10 +368,33 @@ intr_init(void *dummy __unused) intrcnt_setname("???", 0); intrcnt_index = 1; + STAILQ_INIT(&pics); mtx_init(&intr_table_lock, "intr table", NULL, MTX_SPIN); } SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL) +#ifndef DEV_ATPIC +/* Initialize the two 8259A's to a known-good shutdown state. */ +void +atpic_reset(void) +{ + + outb(IO_ICU1, ICW1_RESET | ICW1_IC4); + outb(IO_ICU1 + ICU_IMR_OFFSET, IDT_IO_INTS); + outb(IO_ICU1 + ICU_IMR_OFFSET, 1 << 2); + outb(IO_ICU1 + ICU_IMR_OFFSET, ICW4_8086); + outb(IO_ICU1 + ICU_IMR_OFFSET, 0xff); + outb(IO_ICU1, OCW3_SEL | OCW3_RR); + + outb(IO_ICU2, ICW1_RESET | ICW1_IC4); + outb(IO_ICU2 + ICU_IMR_OFFSET, IDT_IO_INTS + 8); + outb(IO_ICU2 + ICU_IMR_OFFSET, 2); + outb(IO_ICU2 + ICU_IMR_OFFSET, ICW4_8086); + outb(IO_ICU2 + ICU_IMR_OFFSET, 0xff); + outb(IO_ICU2, OCW3_SEL | OCW3_RR); +} +#endif + #ifdef DDB /* * Dump data about interrupt handlers diff --git a/sys/amd64/amd64/io_apic.c b/sys/amd64/amd64/io_apic.c index be88bbd28c7e..3a893cdc53be 100644 --- a/sys/amd64/amd64/io_apic.c +++ b/sys/amd64/amd64/io_apic.c @@ -30,7 +30,6 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_atpic.h" #include "opt_isa.h" #include @@ -61,8 +60,6 @@ __FBSDID("$FreeBSD$"); #define IRQ_SMI (NUM_IO_INTS + 3) #define IRQ_DISABLED (NUM_IO_INTS + 4) -#define TODO printf("%s: not implemented!\n", __func__) - static MALLOC_DEFINE(M_IOAPIC, "io_apic", "I/O APIC structures"); /* @@ -115,8 +112,7 @@ static int ioapic_vector(struct intsrc *isrc); static int ioapic_source_pending(struct intsrc *isrc); static int ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig, enum intr_polarity pol); -static void ioapic_suspend(struct intsrc *isrc); -static void ioapic_resume(struct intsrc *isrc); +static void ioapic_resume(struct pic *pic); static void ioapic_assign_cpu(struct intsrc *isrc, u_int apic_id); static void ioapic_program_intpin(struct ioapic_intsrc *intpin); @@ -124,7 +120,7 @@ static STAILQ_HEAD(,ioapic) ioapic_list = STAILQ_HEAD_INITIALIZER(ioapic_list); 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, + NULL, ioapic_resume, ioapic_config_intr, ioapic_assign_cpu }; static int next_ioapic_base; @@ -419,17 +415,13 @@ ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig, } static void -ioapic_suspend(struct intsrc *isrc) +ioapic_resume(struct pic *pic) { + struct ioapic *io = (struct ioapic *)pic; + int i; - TODO; -} - -static void -ioapic_resume(struct intsrc *isrc) -{ - - ioapic_program_intpin((struct ioapic_intsrc *)isrc); + for (i = 0; i < io->io_numintr; i++) + ioapic_program_intpin(&io->io_pins[i]); } /* @@ -727,6 +719,7 @@ ioapic_register(void *cookie) io->io_intbase + io->io_numintr - 1); /* Register valid pins as interrupt sources. */ + intr_register_pic(&io->io_pic); for (i = 0, pin = io->io_pins; i < io->io_numintr; i++, pin++) if (pin->io_irq < NUM_IO_INTS) intr_register_source(&pin->io_intsrc); diff --git a/sys/amd64/amd64/local_apic.c b/sys/amd64/amd64/local_apic.c index 3fb31b509b73..a38d43c3dbd7 100644 --- a/sys/amd64/amd64/local_apic.c +++ b/sys/amd64/amd64/local_apic.c @@ -151,12 +151,15 @@ volatile lapic_t *lapic; static u_long lapic_timer_divisor, lapic_timer_period, lapic_timer_hz; static void lapic_enable(void); +static void lapic_resume(struct pic *pic); static void lapic_timer_enable_intr(void); static void lapic_timer_oneshot(u_int count); static void lapic_timer_periodic(u_int count); static void lapic_timer_set_divisor(u_int divisor); static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value); +struct pic lapic_pic = { .pic_resume = lapic_resume }; + static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value) { @@ -277,7 +280,7 @@ lapic_dump(const char* str) } void -lapic_setup(void) +lapic_setup(int boot) { struct lapic *la; u_int32_t maxlvt; @@ -306,9 +309,13 @@ lapic_setup(void) /* Program timer LVT and setup handler. */ lapic->lvt_timer = lvt_mode(la, LVT_TIMER, lapic->lvt_timer); - snprintf(buf, sizeof(buf), "cpu%d: timer", PCPU_GET(cpuid)); - intrcnt_add(buf, &la->la_timer_count); - if (PCPU_GET(cpuid) != 0) { + if (boot) { + snprintf(buf, sizeof(buf), "cpu%d: timer", PCPU_GET(cpuid)); + intrcnt_add(buf, &la->la_timer_count); + } + + /* We don't setup the timer during boot on the BSP until later. */ + if (!(boot && PCPU_GET(cpuid) == 0)) { KASSERT(lapic_timer_period != 0, ("lapic%u: zero divisor", lapic_id())); lapic_timer_set_divisor(lapic_timer_divisor); @@ -398,6 +405,14 @@ lapic_enable(void) lapic->svr = value; } +/* Reset the local APIC on the BSP during resume. */ +static void +lapic_resume(struct pic *pic) +{ + + lapic_setup(0); +} + int lapic_id(void) { @@ -983,7 +998,8 @@ apic_setup_io(void *dummy __unused) * Finish setting up the local APIC on the BSP once we know how to * properly program the LINT pins. */ - lapic_setup(); + lapic_setup(1); + intr_register_pic(&lapic_pic); if (bootverbose) lapic_dump("BSP"); } diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index bdbeeb61fe75..6be2c82cbdfa 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -1212,19 +1212,7 @@ hammer_time(u_int64_t modulep, u_int64_t physfree) atpic_startup(); #else /* Reset and mask the atpics and leave them shut down. */ - outb(IO_ICU1, ICW1_RESET | ICW1_IC4); - outb(IO_ICU1 + ICU_IMR_OFFSET, IDT_IO_INTS); - outb(IO_ICU1 + ICU_IMR_OFFSET, 1 << 2); - outb(IO_ICU1 + ICU_IMR_OFFSET, ICW4_8086); - outb(IO_ICU1 + ICU_IMR_OFFSET, 0xff); - outb(IO_ICU1, OCW3_SEL | OCW3_RR); - - outb(IO_ICU2, ICW1_RESET | ICW1_IC4); - outb(IO_ICU2 + ICU_IMR_OFFSET, IDT_IO_INTS + 8); - outb(IO_ICU2 + ICU_IMR_OFFSET, 2); - outb(IO_ICU2 + ICU_IMR_OFFSET, ICW4_8086); - outb(IO_ICU2 + ICU_IMR_OFFSET, 0xff); - outb(IO_ICU2, OCW3_SEL | OCW3_RR); + atpic_reset(); /* * Point the ICU spurious interrupt vectors at the APIC spurious diff --git a/sys/amd64/amd64/mp_machdep.c b/sys/amd64/amd64/mp_machdep.c index 2172c17329b2..dbd1e444544c 100644 --- a/sys/amd64/amd64/mp_machdep.c +++ b/sys/amd64/amd64/mp_machdep.c @@ -543,7 +543,7 @@ init_secondary(void) mtx_lock_spin(&ap_boot_mtx); /* Init local apic for irq's */ - lapic_setup(); + lapic_setup(1); /* Set memory range attributes for this CPU to match the BSP */ mem_range_AP_init(); diff --git a/sys/amd64/include/apicvar.h b/sys/amd64/include/apicvar.h index 98cf3a3db124..ebd8f04f4e5e 100644 --- a/sys/amd64/include/apicvar.h +++ b/sys/amd64/include/apicvar.h @@ -211,7 +211,7 @@ int lapic_set_lvt_polarity(u_int apic_id, u_int lvt, int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt, enum intr_trigger trigger); void lapic_set_tpr(u_int vector); -void lapic_setup(void); +void lapic_setup(int boot); int lapic_setup_clock(void); #endif /* !LOCORE */ diff --git a/sys/amd64/include/intr_machdep.h b/sys/amd64/include/intr_machdep.h index 886811c07d8c..5043774b2737 100644 --- a/sys/amd64/include/intr_machdep.h +++ b/sys/amd64/include/intr_machdep.h @@ -81,11 +81,12 @@ struct pic { void (*pic_enable_intr)(struct intsrc *); int (*pic_vector)(struct intsrc *); int (*pic_source_pending)(struct intsrc *); - void (*pic_suspend)(struct intsrc *); - void (*pic_resume)(struct intsrc *); + void (*pic_suspend)(struct pic *); + void (*pic_resume)(struct pic *); int (*pic_config_intr)(struct intsrc *, enum intr_trigger, enum intr_polarity); void (*pic_assign_cpu)(struct intsrc *, u_int apic_id); + STAILQ_ENTRY(pic) pics; }; /* Flags for pic_disable_source() */ @@ -114,6 +115,9 @@ struct trapframe; extern struct mtx icu_lock; extern int elcr_found; +#ifndef DEV_ATPIC +void atpic_reset(void); +#endif /* XXX: The elcr_* prototypes probably belong somewhere else. */ int elcr_probe(void); enum intr_trigger elcr_read_trigger(u_int irq); @@ -130,6 +134,7 @@ int intr_config_intr(int vector, enum intr_trigger trig, enum intr_polarity pol); void intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame); struct intsrc *intr_lookup_source(int vector); +int intr_register_pic(struct pic *pic); int intr_register_source(struct intsrc *isrc); int intr_remove_handler(void *cookie); void intr_resume(void); diff --git a/sys/amd64/isa/atpic.c b/sys/amd64/isa/atpic.c index 1398e47f7be8..1115c927324e 100644 --- a/sys/amd64/isa/atpic.c +++ b/sys/amd64/isa/atpic.c @@ -138,7 +138,7 @@ static void atpic_eoi_master(struct intsrc *isrc); static void atpic_eoi_slave(struct intsrc *isrc); static void atpic_enable_intr(struct intsrc *isrc); static int atpic_vector(struct intsrc *isrc); -static void atpic_resume(struct intsrc *isrc); +static void atpic_resume(struct pic *pic); static int atpic_source_pending(struct intsrc *isrc); static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, enum intr_polarity pol); @@ -285,16 +285,13 @@ atpic_source_pending(struct intsrc *isrc) } static void -atpic_resume(struct intsrc *isrc) +atpic_resume(struct pic *pic) { - struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; - struct atpic *ap = (struct atpic *)isrc->is_pic; + struct atpic *ap = (struct atpic *)pic; - if (ai->at_irq == 0) { - i8259_init(ap, ap == &atpics[SLAVE]); - if (ap == &atpics[SLAVE] && elcr_found) - elcr_resume(); - } + i8259_init(ap, ap == &atpics[SLAVE]); + if (ap == &atpics[SLAVE] && elcr_found) + elcr_resume(); } static int @@ -464,6 +461,14 @@ atpic_init(void *dummy __unused) struct atpic_intsrc *ai; int i; + /* + * Register our PICs, even if we aren't going to use any of their + * pins so that they are suspended and resumed. + */ + if (intr_register_pic(&atpics[0].at_pic) != 0 || + intr_register_pic(&atpics[1].at_pic) != 0) + panic("Unable to register ATPICs"); + /* * If any of the ISA IRQs have an interrupt source already, then * assume that the APICs are being used and don't register any diff --git a/sys/i386/i386/intr_machdep.c b/sys/i386/i386/intr_machdep.c index fe41cc60212a..da94c69f7e81 100644 --- a/sys/i386/i386/intr_machdep.c +++ b/sys/i386/i386/intr_machdep.c @@ -62,6 +62,7 @@ typedef void (*mask_fn)(void *); static int intrcnt_index; static struct intsrc *interrupt_sources[NUM_IO_INTS]; static struct mtx intr_table_lock; +static STAILQ_HEAD(, pic) pics; #ifdef SMP static int assign_cpu; @@ -70,10 +71,45 @@ static void intr_assign_next_cpu(struct intsrc *isrc); #endif static void intr_init(void *__dummy); +static int intr_pic_registered(struct pic *pic); static void intrcnt_setname(const char *name, int index); static void intrcnt_updatename(struct intsrc *is); static void intrcnt_register(struct intsrc *is); +static int +intr_pic_registered(struct pic *pic) +{ + struct pic *p; + + STAILQ_FOREACH(p, &pics, pics) { + if (p == pic) + return (1); + } + return (0); +} + +/* + * Register a new interrupt controller (PIC). This is to support suspend + * and resume where we suspend/resume controllers rather than individual + * sources. This also allows controllers with no active sources (such as + * 8259As in a system using the APICs) to participate in suspend and resume. + */ +int +intr_register_pic(struct pic *pic) +{ + int error; + + mtx_lock_spin(&intr_table_lock); + if (intr_pic_registered(pic)) + error = EBUSY; + else { + STAILQ_INSERT_TAIL(&pics, pic, pics); + error = 0; + } + mtx_unlock_spin(&intr_table_lock); + return (error); +} + /* * Register a new interrupt source with the global interrupt system. * The global interrupts need to be disabled when this function is @@ -84,6 +120,7 @@ intr_register_source(struct intsrc *isrc) { int error, vector; + KASSERT(intr_pic_registered(isrc->is_pic), ("unregistered PIC")); vector = isrc->is_pic->pic_vector(isrc); if (interrupt_sources[vector] != NULL) return (EEXIST); @@ -255,26 +292,26 @@ intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame) void intr_resume(void) { - struct intsrc **isrc; - int i; + struct pic *pic; mtx_lock_spin(&intr_table_lock); - for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) - if (*isrc != NULL && (*isrc)->is_pic->pic_resume != NULL) - (*isrc)->is_pic->pic_resume(*isrc); + STAILQ_FOREACH(pic, &pics, pics) { + if (pic->pic_resume != NULL) + pic->pic_resume(pic); + } mtx_unlock_spin(&intr_table_lock); } void intr_suspend(void) { - struct intsrc **isrc; - int i; + struct pic *pic; mtx_lock_spin(&intr_table_lock); - for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) - if (*isrc != NULL && (*isrc)->is_pic->pic_suspend != NULL) - (*isrc)->is_pic->pic_suspend(*isrc); + STAILQ_FOREACH(pic, &pics, pics) { + if (pic->pic_suspend != NULL) + pic->pic_suspend(pic); + } mtx_unlock_spin(&intr_table_lock); } @@ -327,6 +364,7 @@ intr_init(void *dummy __unused) intrcnt_setname("???", 0); intrcnt_index = 1; + STAILQ_INIT(&pics); mtx_init(&intr_table_lock, "intr table", NULL, MTX_SPIN); } SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL) diff --git a/sys/i386/i386/io_apic.c b/sys/i386/i386/io_apic.c index 30c2234a1762..3a893cdc53be 100644 --- a/sys/i386/i386/io_apic.c +++ b/sys/i386/i386/io_apic.c @@ -60,8 +60,6 @@ __FBSDID("$FreeBSD$"); #define IRQ_SMI (NUM_IO_INTS + 3) #define IRQ_DISABLED (NUM_IO_INTS + 4) -#define TODO printf("%s: not implemented!\n", __func__) - static MALLOC_DEFINE(M_IOAPIC, "io_apic", "I/O APIC structures"); /* @@ -114,8 +112,7 @@ static int ioapic_vector(struct intsrc *isrc); static int ioapic_source_pending(struct intsrc *isrc); static int ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig, enum intr_polarity pol); -static void ioapic_suspend(struct intsrc *isrc); -static void ioapic_resume(struct intsrc *isrc); +static void ioapic_resume(struct pic *pic); static void ioapic_assign_cpu(struct intsrc *isrc, u_int apic_id); static void ioapic_program_intpin(struct ioapic_intsrc *intpin); @@ -123,7 +120,7 @@ static STAILQ_HEAD(,ioapic) ioapic_list = STAILQ_HEAD_INITIALIZER(ioapic_list); 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, + NULL, ioapic_resume, ioapic_config_intr, ioapic_assign_cpu }; static int next_ioapic_base; @@ -418,17 +415,13 @@ ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig, } static void -ioapic_suspend(struct intsrc *isrc) +ioapic_resume(struct pic *pic) { + struct ioapic *io = (struct ioapic *)pic; + int i; - TODO; -} - -static void -ioapic_resume(struct intsrc *isrc) -{ - - ioapic_program_intpin((struct ioapic_intsrc *)isrc); + for (i = 0; i < io->io_numintr; i++) + ioapic_program_intpin(&io->io_pins[i]); } /* @@ -726,6 +719,7 @@ ioapic_register(void *cookie) io->io_intbase + io->io_numintr - 1); /* Register valid pins as interrupt sources. */ + intr_register_pic(&io->io_pic); for (i = 0, pin = io->io_pins; i < io->io_numintr; i++, pin++) if (pin->io_irq < NUM_IO_INTS) intr_register_source(&pin->io_intsrc); diff --git a/sys/i386/i386/local_apic.c b/sys/i386/i386/local_apic.c index 8c9b0858d4d3..596f236218ae 100644 --- a/sys/i386/i386/local_apic.c +++ b/sys/i386/i386/local_apic.c @@ -151,12 +151,15 @@ volatile lapic_t *lapic; static u_long lapic_timer_divisor, lapic_timer_period, lapic_timer_hz; static void lapic_enable(void); +static void lapic_resume(struct pic *pic); static void lapic_timer_enable_intr(void); static void lapic_timer_oneshot(u_int count); static void lapic_timer_periodic(u_int count); static void lapic_timer_set_divisor(u_int divisor); static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value); +struct pic lapic_pic = { .pic_resume = lapic_resume }; + static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value) { @@ -279,7 +282,7 @@ lapic_dump(const char* str) } void -lapic_setup(void) +lapic_setup(int boot) { struct lapic *la; u_int32_t maxlvt; @@ -308,9 +311,13 @@ lapic_setup(void) /* Program timer LVT and setup handler. */ lapic->lvt_timer = lvt_mode(la, LVT_TIMER, lapic->lvt_timer); - snprintf(buf, sizeof(buf), "cpu%d: timer", PCPU_GET(cpuid)); - intrcnt_add(buf, &la->la_timer_count); - if (PCPU_GET(cpuid) != 0) { + if (boot) { + snprintf(buf, sizeof(buf), "cpu%d: timer", PCPU_GET(cpuid)); + intrcnt_add(buf, &la->la_timer_count); + } + + /* We don't setup the timer during boot on the BSP until later. */ + if (!(boot && PCPU_GET(cpuid) == 0)) { KASSERT(lapic_timer_period != 0, ("lapic%u: zero divisor", lapic_id())); lapic_timer_set_divisor(lapic_timer_divisor); @@ -400,6 +407,14 @@ lapic_enable(void) lapic->svr = value; } +/* Reset the local APIC on the BSP during resume. */ +static void +lapic_resume(struct pic *pic) +{ + + lapic_setup(0); +} + int lapic_id(void) { @@ -986,7 +1001,8 @@ apic_setup_io(void *dummy __unused) * Finish setting up the local APIC on the BSP once we know how to * properly program the LINT pins. */ - lapic_setup(); + lapic_setup(1); + intr_register_pic(&lapic_pic); if (bootverbose) lapic_dump("BSP"); } diff --git a/sys/i386/i386/mp_machdep.c b/sys/i386/i386/mp_machdep.c index 8b881174d368..1933511b0f65 100644 --- a/sys/i386/i386/mp_machdep.c +++ b/sys/i386/i386/mp_machdep.c @@ -594,7 +594,7 @@ init_secondary(void) mtx_lock_spin(&ap_boot_mtx); /* Init local apic for irq's */ - lapic_setup(); + lapic_setup(1); /* Set memory range attributes for this CPU to match the BSP */ mem_range_AP_init(); diff --git a/sys/i386/include/apicvar.h b/sys/i386/include/apicvar.h index bca7bb1c42a5..99c3028c743f 100644 --- a/sys/i386/include/apicvar.h +++ b/sys/i386/include/apicvar.h @@ -210,7 +210,7 @@ int lapic_set_lvt_polarity(u_int apic_id, u_int lvt, int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt, enum intr_trigger trigger); void lapic_set_tpr(u_int vector); -void lapic_setup(void); +void lapic_setup(int boot); int lapic_setup_clock(void); #endif /* !LOCORE */ diff --git a/sys/i386/include/intr_machdep.h b/sys/i386/include/intr_machdep.h index 886811c07d8c..4884fd50dd71 100644 --- a/sys/i386/include/intr_machdep.h +++ b/sys/i386/include/intr_machdep.h @@ -81,11 +81,12 @@ struct pic { void (*pic_enable_intr)(struct intsrc *); int (*pic_vector)(struct intsrc *); int (*pic_source_pending)(struct intsrc *); - void (*pic_suspend)(struct intsrc *); - void (*pic_resume)(struct intsrc *); + void (*pic_suspend)(struct pic *); + void (*pic_resume)(struct pic *); int (*pic_config_intr)(struct intsrc *, enum intr_trigger, enum intr_polarity); void (*pic_assign_cpu)(struct intsrc *, u_int apic_id); + STAILQ_ENTRY(pic) pics; }; /* Flags for pic_disable_source() */ @@ -130,6 +131,7 @@ int intr_config_intr(int vector, enum intr_trigger trig, enum intr_polarity pol); void intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame); struct intsrc *intr_lookup_source(int vector); +int intr_register_pic(struct pic *pic); int intr_register_source(struct intsrc *isrc); int intr_remove_handler(void *cookie); void intr_resume(void); diff --git a/sys/i386/isa/atpic.c b/sys/i386/isa/atpic.c index c73e253e5f7a..9dfaf0ce7d46 100644 --- a/sys/i386/isa/atpic.c +++ b/sys/i386/isa/atpic.c @@ -156,7 +156,7 @@ static void atpic_eoi_master(struct intsrc *isrc); static void atpic_eoi_slave(struct intsrc *isrc); static void atpic_enable_intr(struct intsrc *isrc); static int atpic_vector(struct intsrc *isrc); -static void atpic_resume(struct intsrc *isrc); +static void atpic_resume(struct pic *pic); static int atpic_source_pending(struct intsrc *isrc); static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, enum intr_polarity pol); @@ -303,18 +303,15 @@ atpic_source_pending(struct intsrc *isrc) } static void -atpic_resume(struct intsrc *isrc) +atpic_resume(struct pic *pic) { - struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; - struct atpic *ap = (struct atpic *)isrc->is_pic; + struct atpic *ap = (struct atpic *)pic; - if (ai->at_irq == 0) { - i8259_init(ap, ap == &atpics[SLAVE]); + i8259_init(ap, ap == &atpics[SLAVE]); #ifndef PC98 - if (ap == &atpics[SLAVE] && elcr_found) - elcr_resume(); + if (ap == &atpics[SLAVE] && elcr_found) + elcr_resume(); #endif - } } static int @@ -528,6 +525,14 @@ atpic_init(void *dummy __unused) struct atpic_intsrc *ai; int i; + /* + * Register our PICs, even if we aren't going to use any of their + * pins so that they are suspended and resumed. + */ + if (intr_register_pic(&atpics[0].at_pic) != 0 || + intr_register_pic(&atpics[1].at_pic) != 0) + panic("Unable to register ATPICs"); + /* * If any of the ISA IRQs have an interrupt source already, then * assume that the APICs are being used and don't register any