From 2efd4670416be5bcc64782745b745d2183c8ca11 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Sun, 4 Feb 2024 22:57:58 -0500 Subject: kernel/amd64: lapic: Add support for sending IPIs Signed-off-by: Ian Moffett --- sys/arch/amd64/amd64/lapic.c | 46 +++++++++++++++++++++++++++++++++++++++ sys/include/arch/amd64/lapic.h | 14 ++++++++++++ sys/include/arch/amd64/lapicvar.h | 2 ++ 3 files changed, 62 insertions(+) (limited to 'sys') diff --git a/sys/arch/amd64/amd64/lapic.c b/sys/arch/amd64/amd64/lapic.c index 246a44c..bd3822e 100644 --- a/sys/arch/amd64/amd64/lapic.c +++ b/sys/arch/amd64/amd64/lapic.c @@ -262,6 +262,52 @@ lapic_timer_oneshot_us(uint32_t us) lapic_timer_oneshot(false, ticks); } +/* + * Send an Interprocessor interrupt. + * + * @id: Target LAPIC ID + * @shorthand: Dest shorthand + * @vector: Interrupt vector + */ +void lapic_send_ipi(uint8_t id, uint8_t shorthand, uint8_t vector) +{ + const uint32_t x2APIC_IPI_SELF = 0x3F0; + uint8_t icr_lo = vector | IPI_DEST_PHYSICAL; + bool x2apic_supported = has_x2apic(); + + if (x2apic_supported && shorthand == IPI_SHORTHAND_SELF) { + lapic_writel(x2APIC_IPI_SELF, vector); + return; + } + + switch (shorthand) { + case IPI_SHORTHAND_SELF: + icr_lo |= (1 << 18); + break; + case IPI_SHORTHAND_ALL: + icr_lo |= (2 << 18); + break; + case IPI_SHORTHAND_OTHERS: + icr_lo |= (3 << 18); + break; + } + + /* + * In contrast to xAPICs two 32-bit ICR registers, the x2APIC + * ICR register is one big 64-bit register, thus requiring only + * a single write. In xAPIC mode, the Delivery Status bit (bit 12) + * must be polled until clear. However, in x2APIC mode, this bit + * does not exist meaning we do not need to worry about it. + */ + if (x2apic_supported) { + lapic_writel(LAPIC_ICRLO, ((uint64_t)id << 32) | icr_lo); + } else { + lapic_writel(LAPIC_ICRHI, ((uint32_t)id << 24)); + lapic_writel(LAPIC_ICRLO, icr_lo); + while (__TEST(lapic_readl(LAPIC_ICRLO), __BIT(12))); + } +} + /* * Calibrates the Local APIC timer * diff --git a/sys/include/arch/amd64/lapic.h b/sys/include/arch/amd64/lapic.h index 6c72c02..b8fae1b 100644 --- a/sys/include/arch/amd64/lapic.h +++ b/sys/include/arch/amd64/lapic.h @@ -35,9 +35,23 @@ #define LAPIC_TMR_ONESHOT 0x00 #define LAPIC_TMR_PERIODIC 0x01 +/* IPI Destination Shorthands */ +enum { + IPI_SHORTHAND_SELF, + IPI_SHORTHAND_ALL, + IPI_SHORTHAND_OTHERS +}; + +/* IPI Destination Modes */ +enum { + IPI_DEST_PHYSICAL, + IPI_DEST_LOGICAL +}; + void lapic_timer_init(size_t *freq_out); void lapic_timer_oneshot(bool mask, uint32_t count); void lapic_timer_oneshot_us(uint32_t us); +void lapic_send_ipi(uint8_t id, uint8_t shorthand, uint8_t vector); void lapic_init(void); #endif /* !_AMD64_LAPIC_H_ */ diff --git a/sys/include/arch/amd64/lapicvar.h b/sys/include/arch/amd64/lapicvar.h index 2743be8..84f73d2 100644 --- a/sys/include/arch/amd64/lapicvar.h +++ b/sys/include/arch/amd64/lapicvar.h @@ -47,6 +47,8 @@ #define LAPIC_TMR 0x0180U /* Trigger Mode Register (max=0x0220) */ #define LAPIC_IRR 0x0200U /* Interrupt Request Register (max=0x0270) */ #define LAPIC_ERR 0x0280U /* Error Status Register */ +#define LAPIC_ICRLO 0x0300U /* Interrupt Command Low Register */ +#define LAPIC_ICRHI 0x0310U /* Interrupt Command High Register */ #define LAPIC_LVT_TMR 0x0320U /* LVT Timer Register */ #define LAPIC_DCR 0x03E0U /* Divide Configuration Register (for timer) */ #define LAPIC_INIT_CNT 0x0380U /* Initial Count Register (for timer) */ -- cgit v1.2.3