:: commit e886a6fa76dd070501c55a45f54a17186d8bdb12

mintsuki <mintsuki@protonmail.com> — 2020-09-26 23:32

parents: b830c71d90

stivale2: Finish implementing x2APIC support

diff --git a/STIVALE2.md b/STIVALE2.md
index ec34a13c..5db165fa 100644
--- a/STIVALE2.md
+++ b/STIVALE2.md
@@ -219,7 +219,7 @@ struct stivale2_header_tag_smp {
     uint64_t identifier;          // Identifier: 0x1ab015085f3273df
     uint64_t next;
     uint64_t flags;               // Flags:
-                                  //   bit 0: 0 = use xAPIC, 1 = use x2APIC
+                                  //   bit 0: 0 = use xAPIC, 1 = use x2APIC (if available)
                                   // All other flags are undefined.
 } __attribute__((packed));
 ```
@@ -395,6 +395,10 @@ This tag reports to the kernel info about the firmware.
 struct stivale2_struct_tag_smp {
     uint64_t identifier;        // Identifier: 0x34d1d96339647025
     uint64_t next;
+    uint64_t flags;             // Flags:
+                                //   bit 0: Set if x2APIC was requested and it
+                                //          was supported and enabled.
+                                //  All other bits undefined.
     uint64_t cpu_count;         // Total number of logical CPUs (including BSP)
     struct stivale2_smp_info smp_info[];  // Array of smp_info structs, one per
                                           // logical processor, including BSP.
diff --git a/limine.bin b/limine.bin
index 505e16d0..34171bfc 100644
Binary files a/limine.bin and b/limine.bin differ
diff --git a/stage2/protos/stivale2.c b/stage2/protos/stivale2.c
index 6bb8e7e3..8a3a0db8 100644
--- a/stage2/protos/stivale2.c
+++ b/stage2/protos/stivale2.c
@@ -17,6 +17,7 @@
 #include <drivers/vbe.h>
 #include <lib/term.h>
 #include <sys/pic.h>
+#include <sys/lapic.h>
 #include <fs/file.h>
 #include <mm/pmm.h>
 #include <stivale/stivale2.h>
@@ -328,6 +329,8 @@ void stivale2_load(char *cmdline, int boot_drive) {
         struct stivale2_struct_tag_smp *tag = conv_mem_alloc(sizeof(struct stivale2_struct_tag_smp));
         tag->tag.identifier = STIVALE2_STRUCT_TAG_SMP_ID;
 
+        tag->flags |= (smp_hdr_tag->flags & 1) && x2apic_check();
+
         init_smp((size_t*)&tag->cpu_count, bits == 64, level5pg && level5pg_requested,
                  pagemap, smp_hdr_tag->flags & 1);
 
diff --git a/stage2/sys/lapic.h b/stage2/sys/lapic.h
index 6142f7c2..1c30436a 100644
--- a/stage2/sys/lapic.h
+++ b/stage2/sys/lapic.h
@@ -3,7 +3,9 @@
 
 #include <stdint.h>
 #include <stddef.h>
+#include <stdbool.h>
 #include <sys/cpu.h>
+#include <lib/blib.h>
 
 #define LAPIC_REG_ICR0     0x300
 #define LAPIC_REG_ICR1     0x310
@@ -20,4 +22,33 @@ static inline void lapic_write(uint32_t reg, uint32_t data) {
     mmoutd((void *)(lapic_mmio_base + reg), data);
 }
 
+static inline bool x2apic_check(void) {
+    uint32_t eax, ebx, ecx, edx;
+    cpuid(1, 0, &eax, &ebx, &ecx, &edx);
+
+    if (!(ecx & (1 << 21)))
+        return false;
+
+    return true;
+}
+
+static inline bool x2apic_enable(void) {
+    if (!x2apic_check())
+        return false;
+
+    uint64_t ia32_apic_base = rdmsr(0x1b);
+    ia32_apic_base |= (1 << 10);
+    wrmsr(0x1b, ia32_apic_base);
+
+    return true;
+}
+
+static inline uint64_t x2apic_read(uint32_t reg) {
+    return rdmsr(0x800 + (reg >> 4));
+}
+
+static inline void x2apic_write(uint32_t reg, uint64_t data) {
+    wrmsr(0x800 + (reg >> 4), data);
+}
+
 #endif
diff --git a/stage2/sys/smp.c b/stage2/sys/smp.c
index 222720bb..9257db88 100644
--- a/stage2/sys/smp.c
+++ b/stage2/sys/smp.c
@@ -29,6 +29,14 @@ struct madt_lapic {
     uint32_t flags;
 } __attribute__((packed));
 
+struct madt_x2apic {
+    struct madt_header;
+    uint8_t  reserved[2];
+    uint32_t x2apic_id;
+    uint32_t flags;
+    uint32_t acpi_processor_uid;
+} __attribute__((packed));
+
 struct gdtr {
     uint16_t limit;
     uint32_t ptr;
@@ -46,24 +54,36 @@ uint8_t  smp_tpl_booted_flag;
 uint32_t smp_tpl_pagemap;
 uint8_t  smp_tpl_target_mode;
 
-static bool smp_start_ap(uint8_t lapic_id, struct gdtr *gdtr,
+static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr,
                          struct smp_information *info_struct,
-                         bool longmode, bool lv5, uint32_t pagemap) {
+                         bool longmode, bool lv5, uint32_t pagemap,
+                         bool x2apic) {
     // Prepare the trampoline
     smp_tpl_info_struct = info_struct;
     smp_tpl_booted_flag = 0;
     smp_tpl_pagemap     = pagemap;
-    smp_tpl_target_mode = ((uint32_t)lv5 << 1) | (uint32_t)longmode;
+    smp_tpl_target_mode = ((uint32_t)x2apic << 2)
+                        | ((uint32_t)lv5 << 1)
+                        | (uint32_t)longmode;
     smp_tpl_gdt         = *gdtr;
 
     // Send the INIT IPI
-    lapic_write(LAPIC_REG_ICR1, lapic_id << 24);
-    lapic_write(LAPIC_REG_ICR0, 0x500);
+    if (x2apic) {
+        x2apic_write(LAPIC_REG_ICR0, ((uint64_t)lapic_id << 32) | 0x500);
+    } else {
+        lapic_write(LAPIC_REG_ICR1, lapic_id << 24);
+        lapic_write(LAPIC_REG_ICR0, 0x500);
+    }
     delay(5000);
 
     // Send the Startup IPI
-    lapic_write(LAPIC_REG_ICR1, lapic_id << 24);
-    lapic_write(LAPIC_REG_ICR0, ((size_t)smp_trampoline / 4096) | 0x600);
+    if (x2apic) {
+        x2apic_write(LAPIC_REG_ICR0, ((uint64_t)lapic_id << 32) |
+                                     ((size_t)smp_trampoline / 4096) | 0x600);
+    } else {
+        lapic_write(LAPIC_REG_ICR1, lapic_id << 24);
+        lapic_write(LAPIC_REG_ICR0, ((size_t)smp_trampoline / 4096) | 0x600);
+    }
 
     for (int i = 0; i < 100; i++) {
         if (locked_read(&smp_tpl_booted_flag) == 1) {
@@ -92,6 +112,8 @@ struct smp_information *init_smp(size_t   *cpu_count,
     struct smp_information *ret = conv_mem_alloc_aligned(0, 1);
     *cpu_count = 0;
 
+    x2apic = x2apic && x2apic_enable();
+
     // Parse the MADT entries
     for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
       (uintptr_t)madt_ptr < (uintptr_t)madt + madt->length;
@@ -99,9 +121,6 @@ struct smp_information *init_smp(size_t   *cpu_count,
         switch (*madt_ptr) {
             case 0: {
                 // Processor local xAPIC
-                if (x2apic)
-                    continue;
-
                 struct madt_lapic *lapic = (void *)madt_ptr;
 
                 // Check if we can actually try to start the AP
@@ -120,11 +139,51 @@ struct smp_information *init_smp(size_t   *cpu_count,
                     continue;
                 }
 
-                print("smp: Found candidate AP for bring-up. LAPIC ID: %u\n", lapic->lapic_id);
+                print("smp: [xAPIC] Found candidate AP for bring-up. LAPIC ID: %u\n", lapic->lapic_id);
 
                 // Try to start the AP
                 if (!smp_start_ap(lapic->lapic_id, &gdtr, info_struct,
-                                  longmode, lv5, (uint32_t)pagemap.top_level)) {
+                                  longmode, lv5, (uint32_t)pagemap.top_level,
+                                  x2apic)) {
+                    print("smp: FAILED to bring-up AP\n");
+                    conv_mem_rewind(sizeof(struct smp_information));
+                    continue;
+                }
+
+                print("smp: Successfully brought up AP\n");
+
+                (*cpu_count)++;
+                continue;
+            }
+            case 9: {
+                // Processor local x2APIC
+                if (!x2apic)
+                    continue;
+
+                struct madt_x2apic *x2apic = (void *)madt_ptr;
+
+                // Check if we can actually try to start the AP
+                if (!((x2apic->flags & 1) ^ ((x2apic->flags >> 1) & 1)))
+                    continue;
+
+                struct smp_information *info_struct =
+                        conv_mem_alloc_aligned(sizeof(struct smp_information), 1);
+
+                info_struct->acpi_processor_uid = x2apic->acpi_processor_uid;
+                info_struct->lapic_id           = x2apic->x2apic_id;
+
+                // Do not try to restart the BSP
+                if (x2apic->x2apic_id == 0) {
+                    (*cpu_count)++;
+                    continue;
+                }
+
+                print("smp: [x2APIC] Found candidate AP for bring-up. LAPIC ID: %u\n", x2apic->x2apic_id);
+
+                // Try to start the AP
+                if (!smp_start_ap(x2apic->x2apic_id, &gdtr, info_struct,
+                                  longmode, lv5, (uint32_t)pagemap.top_level,
+                                  true)) {
                     print("smp: FAILED to bring-up AP\n");
                     conv_mem_rewind(sizeof(struct smp_information));
                     continue;
diff --git a/stage2/sys/smp_trampoline.asm b/stage2/sys/smp_trampoline.asm
index 769ecd97..2cf7ed60 100644
--- a/stage2/sys/smp_trampoline.asm
+++ b/stage2/sys/smp_trampoline.asm
@@ -36,6 +36,16 @@ smp_trampoline:
     btr eax, 30
     mov cr0, eax
 
+    test dword [smp_tpl_target_mode], (1 << 2)
+    jz .nox2apic
+
+    mov ecx, 0x1b
+    rdmsr
+    bts eax, 10
+    bts eax, 11
+    wrmsr
+
+  .nox2apic:
     test dword [smp_tpl_target_mode], (1 << 0)
     jz parking32
 
diff --git a/stivale/stivale2.h b/stivale/stivale2.h
index 54cbde6f..569e8d13 100644
--- a/stivale/stivale2.h
+++ b/stivale/stivale2.h
@@ -141,6 +141,7 @@ struct stivale2_smp_info {
 
 struct stivale2_struct_tag_smp {
     struct stivale2_tag tag;
+    uint64_t flags;
     uint64_t cpu_count;
     struct stivale2_smp_info smp_info[];
 } __attribute__((packed));
diff --git a/test/test.asm b/test/test.asm
index e1f1aa33..69bde7c6 100644
--- a/test/test.asm
+++ b/test/test.asm
@@ -18,7 +18,7 @@ lv5:
 smp:
     dq 0x1ab015085f3273df
     dq 0
-    dq 0
+    dq 1
 
 section .bss
 
tab: 248 wrap: offon