aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsigsegv7 <ian@vegaa.systems>2023-10-18 18:00:49 -0400
committersigsegv7 <ian@vegaa.systems>2023-10-18 18:20:16 -0400
commit8775a43cf5267abba09195caf0031960d6cb2434 (patch)
treea3bc0aeddb97051b355bbc488bbb405a5edf117e
parent56a48e52c3abdba51af3178a16beff56600bc91a (diff)
kernel/amd64: lapic: Add basic x2APIC support
Signed-off-by: sigsegv7 <ian@vegaa.systems>
-rw-r--r--sys/arch/amd64/lapic.c63
-rw-r--r--sys/include/arch/amd64/lapicvar.h8
2 files changed, 61 insertions, 10 deletions
diff --git a/sys/arch/amd64/lapic.c b/sys/arch/amd64/lapic.c
index 35a2c16..af219d2 100644
--- a/sys/arch/amd64/lapic.c
+++ b/sys/arch/amd64/lapic.c
@@ -57,6 +57,7 @@ __KERNEL_META("$Vega$: lapic.c, Ian Marco Moffett, "
static void *lapic_base = NULL;
static struct timer lapic_timer = { 0 };
+static bool has_x2apic = false;
/*
* Returns true if LAPIC is supported.
@@ -83,8 +84,13 @@ lapic_readl(uint32_t reg)
{
void *addr;
- addr = (void *)((uintptr_t)lapic_base + reg);
- return mmio_read32(addr);
+ if (!has_x2apic) {
+ addr = (void *)((uintptr_t)lapic_base + reg);
+ return mmio_read32(addr);
+ } else {
+ reg >>= 4;
+ return rdmsr(x2APIC_MSR_BASE + reg);
+ }
}
/*
@@ -98,8 +104,13 @@ lapic_writel(uint32_t reg, uint32_t val)
{
void *addr;
- addr = (void *)((uintptr_t)lapic_base + reg);
- mmio_write32(addr, val);
+ if (!has_x2apic) {
+ addr = (void *)((uintptr_t)lapic_base + reg);
+ mmio_write32(addr, val);
+ } else {
+ reg >>= 4;
+ wrmsr(x2APIC_MSR_BASE + reg, val);
+ }
}
/*
@@ -157,15 +168,44 @@ lapic_reg_clear(uint32_t reg, uint32_t value)
}
/*
- * XXX: When adding x2APIC support it is IMPORTANT
- * to read the full 32 bits. Unlike standard
- * LAPIC mode, where bits 27:24 hold the ID,
- * x2APIC mode uses the full 32 bits.
+ * Reads the Local APIC ID of the
+ * current processor.
*/
static inline uint32_t
lapic_get_id(void)
{
- return (lapic_readl(LAPIC_ID) >> 24) & 0xF;
+ if (!has_x2apic) {
+ return (lapic_readl(LAPIC_ID) >> 24) & 0xF;
+ } else {
+ return lapic_readl(LAPIC_ID);
+ }
+}
+
+/*
+ * Checks if the processor supports x2APIC
+ * mode. Returns true if so.
+ */
+static inline bool
+lapic_has_x2apic(void)
+{
+ uint32_t ecx, tmp;
+
+ __CPUID(0x00000001, tmp, tmp, ecx, tmp);
+ return __TEST(ecx, 1 << 21);
+}
+
+/*
+ * Updates LDR to LAPIC_STARTUP_LID.
+ *
+ * XXX: This does *not* happen with x2APIC
+ * as the LDR register in x2APIC mode
+ * is readonly.
+ */
+static inline void
+lapic_set_ldr(void)
+{
+ if (!has_x2apic)
+ lapic_writel(LAPIC_LDR, LAPIC_STARTUP_LID);
}
void
@@ -231,15 +271,18 @@ lapic_init(void)
panic("This machine does not support LAPIC!\n");
}
+ has_x2apic = lapic_has_x2apic();
+
/* Hardware enable the Local APIC */
tmp = rdmsr(IA32_APIC_BASE_MSR);
+ tmp |= has_x2apic << x2APIC_ENABLE_SHIFT;
wrmsr(IA32_APIC_BASE_MSR, tmp | LAPIC_HW_ENABLE);
/* Software enable the Local APIC via SVR */
lapic_reg_set(LAPIC_SVR, LAPIC_SW_ENABLE);
BSP_KINFO("Enabled Local APIC for BSP\n");
- lapic_writel(LAPIC_LDR, LAPIC_STARTUP_LID);
+ lapic_set_ldr();
/* Setup the timer descriptor */
lapic_timer.name = "LAPIC_INTEGRATED_TIMER";
diff --git a/sys/include/arch/amd64/lapicvar.h b/sys/include/arch/amd64/lapicvar.h
index 98b66ef..67cb277 100644
--- a/sys/include/arch/amd64/lapicvar.h
+++ b/sys/include/arch/amd64/lapicvar.h
@@ -55,6 +55,13 @@
#define IA32_APIC_BASE_MSR 0x1B
/*
+ * The x2APIC register space is accessed via
+ * RDMSR/WRMSR instructions. The below defines
+ * the base MSR address for the register space.
+ */
+#define x2APIC_MSR_BASE 0x00000800
+
+/*
* To hardware enable, OR the value
* of the IA32_APIC_BASE MSR with
* LAPIC_HW_ENABLE and rewrite it.
@@ -70,6 +77,7 @@
*/
#define LAPIC_HW_ENABLE __BIT(11)
#define LAPIC_SW_ENABLE (__BIT(8) | 0xFF)
+#define x2APIC_ENABLE_SHIFT 10
/* The initial logical APIC ID to be set */
#define LAPIC_STARTUP_LID 0x1