From 47016cc15609864414d83f1a0745e95c1db04db5 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Fri, 26 Sep 2025 14:10:34 -0400 Subject: kern/amd64: Support APIC inter-processor interrupts This commit introduces support for sending inter-processor interrupts on the mainbus to other cores on the machine. Signed-off-by: Ian Moffett --- src/sys/arch/amd64/cpu/cpu_lapic.c | 64 ++++++++++++++++++++++++++++++++++++++ src/sys/include/arch/amd64/lapic.h | 60 +++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) (limited to 'src/sys') diff --git a/src/sys/arch/amd64/cpu/cpu_lapic.c b/src/sys/arch/amd64/cpu/cpu_lapic.c index be58428..bea0ddf 100644 --- a/src/sys/arch/amd64/cpu/cpu_lapic.c +++ b/src/sys/arch/amd64/cpu/cpu_lapic.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -238,6 +239,69 @@ lapic_timer_oneshot(bool mask, uint32_t count) lapic_timer_start(&core->md, mask, LVT_TMR_ONESHOT, count); } +/* + * Send off an inter-processor interrupt to the + * specified processor(s) + */ +int +lapic_tx_ipi(const struct lapic_ipi *ipip) +{ + const uint32_t X2APIC_IPI_SELF = 0x3F0; + uint32_t icr_lo, icr_hi; + struct pcore *core = this_core(); + struct mdcore *mdcore; + + if (ipip == NULL) { + return -EINVAL; + } + + mdcore = &core->md; + + /* + * If we are in x2APIC mode and the shorthand is "self", use + * the x2APIC SELF IPI register as it is more optimized. + */ + if (ipip->shorthand == IPI_SHAND_SELF && mdcore->x2apic) { + lapic_writel(mdcore, X2APIC_IPI_SELF, ipip->vector); + return 0; + } + + icr_lo = 0; + icr_hi = 0; + + /* Encode the low dword */ + icr_lo |= ipip->vector; + icr_lo |= (ipip->delmod & 0x7) << 8; + icr_lo |= (ipip->dest_mode & 0x1) << 11; + icr_lo |= (ipip->shorthand & 0x3) << 18; + + /* + * If we are in x2APIC mode, the LAPIC will queue it so there + * is no need to poll the Delivery Status bit as it does not + * exist. + */ + if (mdcore->x2apic) { + lapic_writel( + mdcore, LAPIC_ICRLO, + (((uint64_t)ipip->apic_id) << 32) | icr_lo + ); + return 0; + } + + /* + * In xAPIC mode, we'll need to poll the Delivery + * Status bit to manually serialize the operation + * until the next IPI is ready. + */ + lapic_writel(mdcore, LAPIC_ICRHI, ((uint32_t)ipip->apic_id << 24)); + lapic_writel(mdcore, LAPIC_ICRLO, icr_lo); + do { + __ASMV("pause"); + icr_lo = lapic_readl(mdcore, LAPIC_ICRLO); + } while (ISSET(icr_lo, BIT(12))); + return 0; +} + /* * Start Local APIC timer oneshot in usec */ diff --git a/src/sys/include/arch/amd64/lapic.h b/src/sys/include/arch/amd64/lapic.h index 3cb2f3a..c75e70c 100644 --- a/src/sys/include/arch/amd64/lapic.h +++ b/src/sys/include/arch/amd64/lapic.h @@ -33,6 +33,66 @@ #define LAPIC_TIMER_VEC 0x81 +/* + * Represents the possible values for the ICR + * destination shorthand register for IPIs + * + * @IPI_SHAND_NONE: No shorthand + * @IPI_SHAND_SELF: Send IPI to self + * @IPI_SHAND_AIS: Send IPI to all including self + * @IPI_SHAND_AXS: Send IPI to all excluding self + */ +typedef enum { + IPI_SHAND_NONE, + IPI_SHAND_SELF, + IPI_SHAND_AIS, + IPI_SHAND_AXS +} ipi_shand_t; + +/* + * Represents the possible values for the ICR + * delivery mode + */ +#define IPI_DELMOD_FIXED 0x0 /* Fixed */ +#define IPI_DELMOD_LOWPRI 0x1 /* Lowest priority */ +#define IPI_DELMOD_INIT 0x5 /* Init (for bootstrap) */ +#define IPI_DELMOD_STARTUP 0x6 /* Startup (for bootstrap) */ + +/* See above */ +typedef uint8_t ipi_delmod_t; + +/* Destination mode */ +#define IPI_DESTMODE_PHYSICAL 0x0 +#define IPI_DESTMODE_LOGICAL 0x01 + +/* + * Represents parameters used for generating + * IPIs on the mainbus + * + * @shorthand: Destination shorthand + * @delmod: Delivery mode + * @vector: Interrupt vector to trigger + * @dest_mode: Destination mode + */ +struct lapic_ipi { + ipi_shand_t shorthand; + ipi_delmod_t delmod; + uint8_t vector; + uint8_t apic_id; + uint8_t dest_mode : 1; +}; + +/* + * Transmit an IPI on the main bus to another processor, + * self, or both + * + * @ipip: IPI descriptor + * + * Returns zero on success, otherwise a less than zero value + * on failure. + */ +int lapic_tx_ipi(const struct lapic_ipi *ipip); + /* * Initialize the local APIC on the current * processor. -- cgit v1.2.3