Refactor APIC to unify most of the x2/xapic differences
authorJack Miller <jack@codezen.org>
Mon, 21 Mar 2016 18:20:28 +0000 (13:20 -0500)
committerJack Miller <jack@codezen.org>
Mon, 21 Mar 2016 18:20:28 +0000 (13:20 -0500)
include/apic.h
kernel/apic.c

index 8192be6..75286bd 100644 (file)
@@ -4,6 +4,10 @@
 struct apic {
     void (*init) (void);
     void (*eoi) (void);
+    void (*wake_secondary) (void);
+    void (*start_timer) (void);
+       u32 (*read) (u32 offset);
+       void (*write) (u32 offset, u32 val);
 };
 
 int apic_init(void);
index 6039713..4221cc8 100644 (file)
@@ -2,7 +2,7 @@
 
 /* Support for the built-in xAPIC, or x2APIC. We don't bother with the old 386
  * PIC except to disable it because xAPIC is a foundation technology of the
- * AMD64 technology.
+ * AMD64 arch.
  *
  * While I'm reticent to implement too much "old" hardware, QEMU implements
  * xapic, whereas if you turn on KVM you get x2apic. This is too fundamental to
 #define                XAPIC_ENABLE                    (1 << 11)
 #define                XAPIC_EXTD                              (1 << 10)
 
-#define MSR_X2APIC_BASE                                0x800
+#define APIC_EOI                                       0xb
+
+#define APIC_ICRL                                      0x30
+#define                ICRL_DSH(x)                             ((x & 0x3) << 18)
+#define                        DSH_ALL_EX_SELF         0x3
+#define                        DSH_ALL_IN_SELF         0x2
+#define                ICRL_MT(x)                              ((x & 0x7) << 8)
+#define                        MT_STARTUP                      0x6
+#define                ICRL_VEC(x)                             (x & 0xFF)
+#define                ICRL_DM                                 (1 << 11)
+#define                ICRL_DS                                 (1 << 12)
+#define                ICRL_L                                  (1 << 14)
 
-#define MSR_X2APIC_LVT_TIMER           (MSR_X2APIC_BASE | 0x32)
-#define MMIO_XAPIC_LVT_TIMER           0x320
+#define APIC_LVT_TIMER                         0x32
 #define                LVT_TIMER_PERIODIC              (1 << 17)
-#define                LVT_TIMER_VECTOR(x)             (x & 0xff)
+#define                LVT_VECTOR(x)                   (x & 0xff)
 #define                LVT_MASKED                              (1 << 16)
 
-#define MSR_X2APIC_TIMER_INIT_CT       (MSR_X2APIC_BASE | 0x38)
-#define MMIO_XAPIC_TIMER_INIT_CT       0x380
+#define        APIC_TIMER_INIT_CT                      0x38
+#define APIC_TIMER_CUR_CT                      0x39
+#define APIC_TIMER_DIV                         0x3e
 
-#define MSR_X2APIC_TIMER_CUR_CT                (MSR_X2APIC_BASE | 0x39)
-#define MMIO_XAPIC_TIMER_CUR_CT                0x390
-
-#define MSR_X2APIC_TIMER_DIVIDE                (MSR_X2APIC_BASE | 0x3e)
-#define MMIO_XAPIC_TIMER_DIVIDE                0x3e0
+/* 
+ * X2APIC uses MSRs, offset from the base register
+ *
+ * XAPIC uses MMIO, offset is based on 16 byte indexes into the region
+ */
 
-#define MSR_X2APIC_EOI                         (MSR_X2APIC_BASE | 0xb)
-#define MMIO_XAPIC_EOI                         0xb0
+#define MSR_X2APIC_BASE                                0x800
+#define MSR_OFFSET(x)                          (MSR_X2APIC_BASE | (x))
+#define MMIO_OFFSET(x)                         ((x) << 4)
 
 static volatile u64 *mmio_mem = NULL;
 
-static void xapic_init(void)
+static void apic_start_timer(void)
 {
-       u64 msr;
        u32 lvt;
 
-       /* Make sure the APIC is actually enabled */
-       msr = rdmsr(MSR_IA32_APIC_BASE);
-       wrmsr(MSR_IA32_APIC_BASE, msr | XAPIC_ENABLE);
-
-       /* Get a nocache virtual memory of the base memory */
-       mmio_mem = vmalloc("APIC", VMALLOC_HEAP_VIRTUAL, PAGE_SIZE);
-
-       map_page_nocache((u64) mmio_mem, msr & ~(0xFFFUL));
-
        /* Divide by 1 */
-       mmio_write(mmio_mem, MMIO_XAPIC_TIMER_DIVIDE, 0x11);
+       apic->write(APIC_TIMER_DIV, 0x11);
 
        /* Setup a periodic timer interrupt */
-       lvt = mmio_read(mmio_mem, MMIO_XAPIC_LVT_TIMER);
+       lvt = apic->read(APIC_LVT_TIMER);
 
        lvt &= ~LVT_MASKED;
-       lvt |= LVT_TIMER_VECTOR(32) | LVT_TIMER_PERIODIC;
+       lvt |= LVT_VECTOR(32) | LVT_TIMER_PERIODIC;
 
-       mmio_write(mmio_mem, MMIO_XAPIC_LVT_TIMER, lvt);
+       apic->write(APIC_LVT_TIMER, lvt);
 
        /* Set the initial count, which will start the timer */
-       mmio_write(mmio_mem, MMIO_XAPIC_TIMER_INIT_CT, 0x1000);
+       apic->write(APIC_TIMER_INIT_CT, 0x1000);
 }
 
-static void xapic_eoi(void)
+static void apic_eoi(void)
 {
        /* Allow another interrupt to come through when we re-enable
         * them (exiting through exception)
         */
-       mmio_write(mmio_mem, MMIO_XAPIC_EOI, 0);
+       apic->write(APIC_EOI, 0);
+}
+
+static void apic_wake_secondary(void)
+{
+       u32 val = ICRL_DSH(DSH_ALL_IN_SELF) | ICRL_VEC(33) | ICRL_MT(MT_STARTUP) | ICRL_DM | ICRL_L;
+
+       apic->write(APIC_ICRL, val);
+}
+
+static void xapic_init(void)
+{
+       u64 msr;
+
+       /* Make sure the APIC is actually enabled */
+       msr = rdmsr(MSR_IA32_APIC_BASE);
+       wrmsr(MSR_IA32_APIC_BASE, msr | XAPIC_ENABLE);
+
+       /* Get a nocache virtual memory of the base memory */
+       mmio_mem = vmalloc("APIC", VMALLOC_HEAP_VIRTUAL, PAGE_SIZE);
+
+       map_page_nocache((u64) mmio_mem, msr & ~(0xFFFUL));
+}
+
+static void xapic_mmio_write(u32 offset, u32 val)
+{
+       mmio_write(mmio_mem, MMIO_OFFSET(offset), val);
+}
+
+static u32 xapic_mmio_read(u32 offset)
+{
+       return mmio_read(mmio_mem, MMIO_OFFSET(offset));
 }
 
 struct apic xapic = {
        .init = xapic_init,
-       .eoi = xapic_eoi,
+       .eoi = apic_eoi,
+       .wake_secondary = apic_wake_secondary,
+       .start_timer = apic_start_timer,
+       .write = xapic_mmio_write,
+       .read = xapic_mmio_read,
 };
 
-/* Virtually the same as above */
-
 static void x2apic_init(void)
 {
-       u64 msr = rdmsr(MSR_IA32_APIC_BASE);
+       u64 msr;
+
+       /* Make sure the APIC is actually enabled */
+       msr = rdmsr(MSR_IA32_APIC_BASE);
 
        /* Need EXTD flag to use MSR interface */
        wrmsr(MSR_IA32_APIC_BASE, msr | XAPIC_ENABLE | XAPIC_EXTD);
+}
 
-       wrmsr(MSR_X2APIC_TIMER_DIVIDE, 0x11);
-
-       msr = rdmsr(MSR_X2APIC_LVT_TIMER);
-
-       msr &= ~LVT_MASKED;
-       msr |= LVT_TIMER_PERIODIC | LVT_TIMER_VECTOR(32);
-
-       wrmsr(MSR_X2APIC_LVT_TIMER, msr);
-
-       wrmsr(MSR_X2APIC_TIMER_INIT_CT, 0x1000);
+static void x2apic_msr_write(u32 offset, u32 val)
+{
+       wrmsr(MSR_OFFSET(offset), val);
 }
 
-static void x2apic_eoi(void)
+static u32 x2apic_msr_read(u32 offset)
 {
-       wrmsr(MSR_X2APIC_EOI, 0);
+       return rdmsr(MSR_OFFSET(offset));
 }
 
 struct apic x2apic = {
        .init = x2apic_init,
-       .eoi = x2apic_eoi,
+       .eoi = apic_eoi,
+       .wake_secondary = apic_wake_secondary,
+       .start_timer = apic_start_timer,
+       .read = x2apic_msr_read,
+       .write = x2apic_msr_write,
 };
 
 int apic_init(void)
@@ -140,5 +176,9 @@ int apic_init(void)
 
        apic->init();
 
+       apic->wake_secondary();
+
+       apic->start_timer();
+
     return 0;
 }