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
