aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-02-04 22:57:58 -0500
committerIan Moffett <ian@osmora.org>2024-02-04 22:58:32 -0500
commit2efd4670416be5bcc64782745b745d2183c8ca11 (patch)
treeacc5d76ce5af864086c156ce40a529b292ab368a /sys
parent3d36c14c83e96370d423f65a8914dff196c3e10a (diff)
kernel/amd64: lapic: Add support for sending IPIs
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/amd64/amd64/lapic.c46
-rw-r--r--sys/include/arch/amd64/lapic.h14
-rw-r--r--sys/include/arch/amd64/lapicvar.h2
3 files changed, 62 insertions, 0 deletions
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
@@ -263,6 +263,52 @@ lapic_timer_oneshot_us(uint32_t us)
}
/*
+ * 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
*
* TODO: Disable interrupts and put them back in
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) */