From e886a6fa76dd070501c55a45f54a17186d8bdb12 Mon Sep 17 00:00:00 2001 From: mintsuki Date: Sun, 27 Sep 2020 01:32:47 +0200 Subject: [PATCH] stivale2: Finish implementing x2APIC support --- STIVALE2.md | 6 ++- limine.bin | Bin 32768 -> 32768 bytes stage2/protos/stivale2.c | 3 ++ stage2/sys/lapic.h | 31 +++++++++++++ stage2/sys/smp.c | 83 +++++++++++++++++++++++++++++----- stage2/sys/smp_trampoline.asm | 10 ++++ stivale/stivale2.h | 1 + test/test.asm | 2 +- 8 files changed, 122 insertions(+), 14 deletions(-) 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 505e16d07156e75b0f8c9b09af4b4c1174cd4040..34171bfca9eddb78f92d8a06050db003a4587f13 100644 GIT binary patch delta 20440 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 QvJ^;@v$0xFC6mBf*tnK@3jhEB 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 #include #include +#include #include #include #include @@ -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 #include +#include #include +#include #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