git: 7b675fe7306d - stable/14 - x86/local_apic.c: Add support for installing a thermal interrupt handler
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 23 Jul 2026 10:01:12 UTC
The branch stable/14 has been updated by olce:
URL: https://cgit.FreeBSD.org/src/commit/?id=7b675fe7306db65581b3da800fc07c4cc7def134
commit 7b675fe7306db65581b3da800fc07c4cc7def134
Author: Koine Yuusuke <koinec@yahoo.co.jp>
AuthorDate: 2026-07-01 15:28:21 +0000
Commit: Olivier Certner <olce@FreeBSD.org>
CommitDate: 2026-07-23 09:59:14 +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
(cherry picked from commit 87ba088fa3108dd180008a04f25760ec71476c87)
---
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 6e51ebff298a..6b25f3b40c36 100644
--- a/sys/amd64/amd64/apic_vector.S
+++ b/sys/amd64/amd64/apic_vector.S
@@ -150,6 +150,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 dcfa79ffc0c0..0c205c0701b2 100644
--- a/sys/x86/include/apicvar.h
+++ b/sys/x86/include/apicvar.h
@@ -184,12 +184,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;
@@ -198,6 +198,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);
@@ -232,6 +233,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_pmc(void);
void lapic_disable_pmc(void);
void lapic_reenable_pmc(void);
@@ -256,6 +259,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 b9c699965b09..5712f7b3ff2b 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>
@@ -210,6 +211,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;
SYSCTL_NODE(_hw, OID_AUTO, apic, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"APIC options");
@@ -477,7 +480,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),
@@ -775,7 +780,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) {
@@ -1487,6 +1494,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)
{