summaryrefslogtreecommitdiff
path: root/src/sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/sys')
-rw-r--r--src/sys/arch/amd64/cpu/cpu_lapic.c64
-rw-r--r--src/sys/include/arch/amd64/lapic.h60
2 files changed, 124 insertions, 0 deletions
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 <sys/syslog.h>
#include <sys/cpuvar.h>
+#include <sys/errno.h>
#include <sys/panic.h>
#include <sys/cdefs.h>
#include <sys/param.h>
@@ -239,6 +240,69 @@ lapic_timer_oneshot(bool mask, uint32_t 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
*/
void
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
@@ -34,6 +34,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.
*/