The branch main has been updated by olce:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=87ba088fa3108dd180008a04f25760ec71476c87

commit 87ba088fa3108dd180008a04f25760ec71476c87
Author:     Koine Yuusuke <[email protected]>
AuthorDate: 2026-07-01 15:28:21 +0000
Commit:     Olivier Certner <[email protected]>
CommitDate: 2026-07-07 16:33:31 +0000

    x86/local_apic.c: Add support for installing a thermal interrupt handler
    
    The thermal interrupt is initially masked.
    
    Thermal interrupt handling is enabled by calling lapic_enable_thermal(),
    which installs a (single) handler.
    
    [olce: Wrote the commit message.]
    
    Reviewed by:    kib, olce
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D44454
---
 sys/amd64/amd64/apic_vector.S |  9 ++++++
 sys/x86/include/apicvar.h     | 12 ++++---
 sys/x86/x86/local_apic.c      | 74 +++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 89 insertions(+), 6 deletions(-)

diff --git a/sys/amd64/amd64/apic_vector.S b/sys/amd64/amd64/apic_vector.S
index c753ddbad9be..85136b2f17df 100644
--- a/sys/amd64/amd64/apic_vector.S
+++ b/sys/amd64/amd64/apic_vector.S
@@ -144,6 +144,15 @@ IDTVEC(spuriousint)
        KMSAN_LEAVE
        jmp     doreti
 
+/*
+ * Local APIC thermal interrupt handler.
+ */
+       INTR_HANDLER thermalint
+       KMSAN_ENTER
+       call    lapic_handle_thermal
+       KMSAN_LEAVE
+       jmp     doreti
+
 #ifdef XENHVM
 /*
  * Xen event channel upcall interrupt handler.
diff --git a/sys/x86/include/apicvar.h b/sys/x86/include/apicvar.h
index 2dda3103f93c..66d53bbf7e5d 100644
--- a/sys/x86/include/apicvar.h
+++ b/sys/x86/include/apicvar.h
@@ -185,12 +185,12 @@ struct apic_enumerator {
 inthand_t
        IDTVEC(apic_isr1), IDTVEC(apic_isr2), IDTVEC(apic_isr3),
        IDTVEC(apic_isr4), IDTVEC(apic_isr5), IDTVEC(apic_isr6),
-       IDTVEC(apic_isr7), IDTVEC(cmcint), IDTVEC(errorint),
-       IDTVEC(spuriousint), IDTVEC(timerint),
+       IDTVEC(apic_isr7), IDTVEC(cmcint), IDTVEC(thermalint),
+       IDTVEC(errorint), IDTVEC(spuriousint), IDTVEC(timerint),
        IDTVEC(apic_isr1_pti), IDTVEC(apic_isr2_pti), IDTVEC(apic_isr3_pti),
        IDTVEC(apic_isr4_pti), IDTVEC(apic_isr5_pti), IDTVEC(apic_isr6_pti),
-       IDTVEC(apic_isr7_pti), IDTVEC(cmcint_pti), IDTVEC(errorint_pti),
-       IDTVEC(spuriousint_pti), IDTVEC(timerint_pti);
+       IDTVEC(apic_isr7_pti), IDTVEC(cmcint_pti), IDTVEC(thermalint_pti),
+       IDTVEC(errorint_pti), IDTVEC(spuriousint_pti), IDTVEC(timerint_pti);
 
 extern vm_paddr_t lapic_paddr;
 extern int *apic_cpuids;
@@ -199,6 +199,7 @@ extern int *apic_cpuids;
 extern void (*ipi_vectored)(u_int, int);
 
 typedef struct ioapic *ioapic_drv_t;
+typedef void (*lapic_thermal_handle_function)(int, void *);
 
 void   apic_register_enumerator(struct apic_enumerator *enumerator);
 ioapic_drv_t   ioapic_create(vm_paddr_t addr, int32_t apic_id, int intbase);
@@ -233,6 +234,8 @@ void        apic_enable_vector(u_int apic_id, u_int vector);
 void   apic_disable_vector(u_int apic_id, u_int vector);
 void   apic_free_vector(u_int apic_id, u_int vector, u_int irq);
 void   lapic_calibrate_timer(void);
+void   lapic_enable_thermal(lapic_thermal_handle_function thermfunc, void 
*value);
+void   lapic_disable_thermal(void);
 int    lapic_enable_pcint(void);
 void   lapic_disable_pcint(void);
 void   lapic_reenable_pcint(void);
@@ -257,6 +260,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_handle_cmc(void);
+void   lapic_handle_thermal(void);
 void   lapic_handle_error(void);
 void   lapic_handle_intr(int vector, struct trapframe *frame);
 void   lapic_handle_timer(struct trapframe *frame);
diff --git a/sys/x86/x86/local_apic.c b/sys/x86/x86/local_apic.c
index cd5e4d474080..08748a1fe7c2 100644
--- a/sys/x86/x86/local_apic.c
+++ b/sys/x86/x86/local_apic.c
@@ -60,6 +60,7 @@
 #include <vm/pmap.h>
 
 #include <x86/apicreg.h>
+#include <machine/atomic.h>
 #include <machine/clock.h>
 #include <machine/cpufunc.h>
 #include <machine/cputypes.h>
@@ -313,6 +314,8 @@ static uint64_t lapic_ipi_wait_mult;
 static int __read_mostly lapic_ds_idle_timeout = 1000000;
 #endif
 unsigned int max_apic_id;
+static void *lapic_thermal_function_value;
+static lapic_thermal_handle_function lapic_thermal_function_ptr;
 static int pcint_refcnt = 0;
 
 SYSCTL_NODE(_hw, OID_AUTO, apic, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
@@ -596,7 +599,9 @@ lapic_init(vm_paddr_t addr)
        setidt(APIC_ERROR_INT, pti ? IDTVEC(errorint_pti) : IDTVEC(errorint),
            SDT_APIC, SEL_KPL, GSEL_APIC);
 
-       /* XXX: Thermal interrupt */
+       /* Thermal interrupt */
+       setidt(APIC_THERMAL_INT, pti ? IDTVEC(thermalint_pti) : 
IDTVEC(thermalint),
+           SDT_APIC, SEL_KPL, GSEL_APIC);
 
        /* Local APIC CMCI. */
        setidt(APIC_CMC_INT, pti ? IDTVEC(cmcint_pti) : IDTVEC(cmcint),
@@ -923,7 +928,9 @@ lapic_setup(int boot)
            lapic_read32(LAPIC_LVT_ERROR)));
        lapic_write32(LAPIC_ESR, 0);
 
-       /* XXX: Thermal LVT */
+       /* Thermal LVT */
+       lapic_write32(LAPIC_LVT_THERMAL, lvt_mode(la, APIC_LVT_THERMAL,
+           lapic_read32(LAPIC_LVT_THERMAL)));
 
        /* Program the CMCI LVT entry if present. */
        if (maxlvt >= APIC_LVT_CMCI) {
@@ -1632,6 +1639,69 @@ lapic_enable_mca_elvt(void)
        return (APIC_ELVT_MCA);
 }
 
+void
+lapic_handle_thermal(void)
+{
+       lapic_thermal_handle_function func;
+
+       func = (lapic_thermal_handle_function)atomic_load_acq_ptr(
+           (void *)&lapic_thermal_function_ptr);
+
+       if (func != NULL)
+               func(PCPU_GET(cpuid), lapic_thermal_function_value);
+
+       lapic_eoi();
+}
+
+static void
+lapic_update_thermal(void *dummy __unused)
+{
+       struct lapic *la;
+
+       la = &lapics[lapic_id()];
+       lapic_write32(LAPIC_LVT_THERMAL, lvt_mode(la, APIC_LVT_THERMAL,
+           lapic_read32(LAPIC_LVT_THERMAL)));
+}
+
+void
+lapic_enable_thermal(lapic_thermal_handle_function thermfunc, void *value)
+{
+#ifdef DEV_ATPIC
+       /* Fail if the local APIC is not present. */
+       if (!x2apic_mode && lapic_map == NULL)
+               return;
+#endif
+
+       lapic_thermal_function_value = value;
+       atomic_store_rel_ptr((uintptr_t *)&lapic_thermal_function_ptr,
+           (uintptr_t)thermfunc);
+
+       lvts[APIC_LVT_THERMAL].lvt_masked = 0;
+
+       MPASS(mp_ncpus == 1 || smp_started);
+       smp_rendezvous(NULL, lapic_update_thermal, NULL, NULL);
+}
+
+void
+lapic_disable_thermal(void)
+{
+#ifdef DEV_ATPIC
+       /* Fail if the local APIC is not present. */
+       if (!x2apic_mode && lapic_map == NULL)
+               return;
+#endif
+
+       lvts[APIC_LVT_THERMAL].lvt_masked = 1;
+
+#ifdef SMP
+       KASSERT(mp_ncpus == 1 || smp_started, ("thermal driver unloaded too 
early"));
+#endif
+       smp_rendezvous(NULL, lapic_update_thermal, NULL, NULL);
+
+       atomic_store_rel_ptr((uintptr_t *)&lapic_thermal_function_ptr,
+           (uintptr_t)NULL);
+}
+
 void
 lapic_handle_error(void)
 {

Reply via email to