aboutsummaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-06-29 18:38:13 -0400
committerIan Moffett <ian@osmora.org>2024-06-29 18:38:13 -0400
commit9113fa42e6794e39d7233e17e8106f677ad04d94 (patch)
tree2c8763b8b0c271d4f2d6b9b4bf2415da7612526d /sys/arch
parent1bcdbcf7b9b38de99c73ca44b6b9ea7785deaac7 (diff)
kernel/amd64: lapic: Add support for IPIs
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/amd64/amd64/lapic.c38
1 files changed, 38 insertions, 0 deletions
diff --git a/sys/arch/amd64/amd64/lapic.c b/sys/arch/amd64/amd64/lapic.c
index 896c2bd..6d17926 100644
--- a/sys/arch/amd64/amd64/lapic.c
+++ b/sys/arch/amd64/amd64/lapic.c
@@ -284,6 +284,44 @@ lapic_timer_init(void)
return freq;
}
+void
+lapic_send_ipi(uint8_t id, uint8_t shorthand, uint8_t vector)
+{
+ const uint32_t X2APIC_IPI_SELF = 0x3F0;
+ struct cpu_info *ci = this_cpu();
+ uint64_t icr_lo = 0;
+
+ /*
+ * If we are in x2APIC mode and the shorthand is "self", use
+ * the x2APIC SELF IPI register as it is more optimized.
+ */
+ if (shorthand == IPI_SHORTHAND_SELF && ci->has_x2apic) {
+ lapic_writel(X2APIC_IPI_SELF, vector);
+ return;
+ }
+
+ /* Encode dest into the low dword of the ICR */
+ icr_lo |= (vector | IPI_DEST_PHYSICAL);
+ icr_lo |= ((shorthand & 0x3) << 18);
+
+ /*
+ * In xAPIC mode, the Delivery Status bit (bit 12) must
+ * be polled until clear after sending an IPI. However,
+ * in x2APIC mode, this bit does not exist, so there's no
+ * need to worry about polling. Since the x2APIC interface
+ * uses MSRs, we can accomplish what we need with a single
+ * write, unlike with xAPICs where you'd need to write to the
+ * ICR high dword first.
+ */
+ if (ci->has_x2apic) {
+ 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 (ISSET(lapic_readl(LAPIC_ICRLO), BIT(12)));
+ }
+}
+
/*
* Indicates that the current interrupt is finished
* being serviced.