:: commit 7637f94efa888443123ec59ef170f6d0d460dbf4

mintsuki <mintsuki@protonmail.com> — 2021-09-21 15:28

parents: 289030a105

apic: Improve pending IRQ flushing mechanism

diff --git a/stage23/entry.s3.c b/stage23/entry.s3.c
index 0be9473f..81f11843 100644
--- a/stage23/entry.s3.c
+++ b/stage23/entry.s3.c
@@ -7,6 +7,7 @@
 #include <lib/trace.h>
 #include <sys/e820.h>
 #include <sys/a20.h>
+#include <sys/idt.h>
 #include <lib/print.h>
 #include <fs/file.h>
 #include <lib/elf.h>
@@ -64,8 +65,6 @@ void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
 
     disk_create_index();
 
-    init_io_apics();
-
     boot_volume = NULL;
 
     EFI_HANDLE current_handle = ImageHandle;
@@ -126,6 +125,9 @@ __attribute__((section(".stage3_entry")))
 #endif
 __attribute__((noreturn))
 void stage3_common(void) {
+    init_flush_irqs();
+    init_io_apics();
+
     volume_iterate_parts(boot_volume,
         if (!init_config_disk(_PART)) {
             boot_volume = _PART;
diff --git a/stage23/lib/spinup.asm b/stage23/lib/spinup.asm
index 9d832854..3bfbdb8d 100644
--- a/stage23/lib/spinup.asm
+++ b/stage23/lib/spinup.asm
@@ -5,6 +5,8 @@ invalid_idt:
 
 section .text
 
+extern flush_irqs
+
 global common_spinup
 bits 32
 common_spinup:
@@ -12,6 +14,8 @@ common_spinup:
 
     lidt [invalid_idt]
 
+    call flush_irqs
+
     xor eax, eax
     lldt ax
 
diff --git a/stage23/lib/spinup.asm32 b/stage23/lib/spinup.asm32
index d37acb74..74a63388 100644
--- a/stage23/lib/spinup.asm32
+++ b/stage23/lib/spinup.asm32
@@ -4,6 +4,8 @@ extern gdt
 
 section .text
 
+extern flush_irqs
+
 global common_spinup
 bits 32
 common_spinup:
@@ -37,6 +39,8 @@ common_spinup:
     mov gs, eax
     mov ss, eax
 
+    call flush_irqs
+
     xor eax, eax
     lldt ax
 
diff --git a/stage23/lib/spinup.asm64 b/stage23/lib/spinup.asm64
index f41452d2..078bd178 100644
--- a/stage23/lib/spinup.asm64
+++ b/stage23/lib/spinup.asm64
@@ -5,6 +5,8 @@ invalid_idt:
 
 section .text
 
+extern flush_irqs
+
 %macro push32 1
     sub rsp, 4
     mov dword [rsp], %1
@@ -20,6 +22,21 @@ common_spinup:
     lgdt [rel gdt]
     lidt [rel invalid_idt]
 
+    lea rbx, [rel .reload_cs]
+
+    push 0x28
+    push rbx
+    retfq
+.reload_cs:
+    mov eax, 0x30
+    mov ds, eax
+    mov es, eax
+    mov fs, eax
+    mov gs, eax
+    mov ss, eax
+
+    call flush_irqs
+
     mov rbp, rsp
 
     cmp esi, 4
diff --git a/stage23/protos/stivale.c b/stage23/protos/stivale.c
index 2fe80404..608c9c19 100644
--- a/stage23/protos/stivale.c
+++ b/stage23/protos/stivale.c
@@ -16,6 +16,7 @@
 #include <sys/pic.h>
 #include <sys/cpu.h>
 #include <sys/gdt.h>
+#include <sys/idt.h>
 #include <sys/lapic.h>
 #include <fs/file.h>
 #include <mm/vmm.h>
@@ -423,8 +424,6 @@ __attribute__((noreturn)) void stivale_spinup(
     }
 
     pic_mask_all();
-    pic_flush();
-
     io_apic_mask_all();
 
     common_spinup(stivale_spinup_32, 9,
diff --git a/stage23/sys/cpu.h b/stage23/sys/cpu.h
index 354bf14e..1979a158 100644
--- a/stage23/sys/cpu.h
+++ b/stage23/sys/cpu.h
@@ -160,6 +160,12 @@ inline uint64_t rdtsc(void) {
     return ((uint64_t)edx << 32) | eax;
 }
 
+static inline void delay(uint64_t cycles) {
+    uint64_t next_stop = rdtsc() + cycles;
+
+    while (rdtsc() < next_stop);
+}
+
 #define rdrand(type) ({ \
     type ret; \
     asm volatile ( \
diff --git a/stage23/sys/idt.h b/stage23/sys/idt.h
index 002e74ab..ee015b22 100644
--- a/stage23/sys/idt.h
+++ b/stage23/sys/idt.h
@@ -3,7 +3,7 @@
 
 #include <stdint.h>
 
-#if bios == 1
+#if defined (__i386__)
 
 struct idtr {
     uint16_t limit;
@@ -18,10 +18,34 @@ struct idt_entry {
     uint16_t offset_hi;
 } __attribute__((packed));
 
+#elif defined (__x86_64__)
+
+struct idtr {
+    uint16_t limit;
+    uint64_t ptr;
+} __attribute__((packed));
+
+struct idt_entry {
+    uint16_t offset_lo;
+    uint16_t selector;
+    uint8_t  ist;
+    uint8_t  type_attr;
+    uint16_t offset_mid;
+    uint32_t offset_hi;
+    uint32_t reserved;
+} __attribute__((packed));
+
+#endif
+
+#if bios == 1
+
 extern struct idtr idt;
 
 void init_idt(void);
 
 #endif
 
+void init_flush_irqs(void);
+void flush_irqs(void);
+
 #endif
diff --git a/stage23/sys/idt.s2.c b/stage23/sys/idt.s2.c
index e5baf3e9..b8a59419 100644
--- a/stage23/sys/idt.s2.c
+++ b/stage23/sys/idt.s2.c
@@ -1,7 +1,11 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <sys/idt.h>
+#include <sys/cpu.h>
+#include <sys/pic.h>
+#include <sys/lapic.h>
 #include <lib/blib.h>
+#include <mm/pmm.h>
 
 #if bios == 1
 
@@ -33,3 +37,51 @@ void init_idt(void) {
 }
 
 #endif
+
+static struct idt_entry *dummy_idt = NULL;
+
+__attribute__((interrupt))
+static void dummy_isr(void *p) {
+    (void)p;
+    lapic_eoi();
+}
+
+void init_flush_irqs(void) {
+    dummy_idt = ext_mem_alloc(256 * sizeof(struct idt_entry));
+
+    for (size_t i = 0; i < 256; i++) {
+        dummy_idt[i].offset_lo = (uint16_t)(uintptr_t)dummy_isr;
+        dummy_idt[i].type_attr = 0x8e;
+#if defined (__i386__)
+        dummy_idt[i].selector = 0x18;
+        dummy_idt[i].offset_hi = (uint16_t)((uintptr_t)dummy_isr >> 16);
+#elif defined (__x86_64__)
+        dummy_idt[i].selector = 0x28;
+        dummy_idt[i].offset_mid = (uint16_t)((uintptr_t)dummy_isr >> 16);
+        dummy_idt[i].offset_hi = (uint32_t)((uintptr_t)dummy_isr >> 32);
+#endif
+    }
+}
+
+void flush_irqs(void) {
+    struct idtr old_idt;
+    asm volatile ("sidt %0" : "=m"(old_idt) :: "memory");
+
+    struct idtr new_idt = {
+        256 * sizeof(struct idt_entry) - 1,
+        (uintptr_t)dummy_idt
+    };
+    asm volatile ("lidt %0" :: "m"(new_idt) : "memory");
+
+    // Flush the legacy PIC so we know the remaining ints come from the LAPIC
+    pic_flush();
+
+    asm volatile ("sti" ::: "memory");
+
+    // Delay a while to make sure we catch ALL pending IRQs
+    delay(10000000);
+
+    asm volatile ("cli" ::: "memory");
+
+    asm volatile ("lidt %0" :: "m"(old_idt) : "memory");
+}
diff --git a/stage23/sys/lapic.c b/stage23/sys/lapic.c
index 8211f970..d52a1634 100644
--- a/stage23/sys/lapic.c
+++ b/stage23/sys/lapic.c
@@ -74,6 +74,8 @@ bool x2apic_check(void) {
     return true;
 }
 
+static bool x2apic_mode = false;
+
 bool x2apic_enable(void) {
     if (!x2apic_check())
         return false;
@@ -82,9 +84,19 @@ bool x2apic_enable(void) {
     ia32_apic_base |= (1 << 10);
     wrmsr(0x1b, ia32_apic_base);
 
+    x2apic_mode = true;
+
     return true;
 }
 
+void lapic_eoi(void) {
+    if (!x2apic_mode) {
+        lapic_write(0xb0, 0);
+    } else {
+        x2apic_write(0xb0, 0);
+    }
+}
+
 uint64_t x2apic_read(uint32_t reg) {
     return rdmsr(0x800 + (reg >> 4));
 }
@@ -122,7 +134,6 @@ void init_io_apics(void) {
     io_apics = ext_mem_alloc(max_io_apics * sizeof(struct madt_io_apic *));
     max_io_apics = 0;
 
-    // Try to start all APs
     for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
       (uintptr_t)madt_ptr < (uintptr_t)madt + madt->header.length;
       madt_ptr += *(madt_ptr + 1)) {
diff --git a/stage23/sys/lapic.h b/stage23/sys/lapic.h
index aba64ad3..992aee98 100644
--- a/stage23/sys/lapic.h
+++ b/stage23/sys/lapic.h
@@ -11,6 +11,7 @@
 #define LAPIC_REG_EOI      0x0b0
 
 bool lapic_check(void);
+void lapic_eoi(void);
 uint32_t lapic_read(uint32_t reg);
 void lapic_write(uint32_t reg, uint32_t data);
 
diff --git a/stage23/sys/smp.c b/stage23/sys/smp.c
index 14c3d777..963adf2d 100644
--- a/stage23/sys/smp.c
+++ b/stage23/sys/smp.c
@@ -39,12 +39,6 @@ struct madt_x2apic {
     uint32_t acpi_processor_uid;
 } __attribute__((packed));
 
-static void delay(uint64_t cycles) {
-    uint64_t next_stop = rdtsc() + cycles;
-
-    while (rdtsc() < next_stop);
-}
-
 extern symbol _binary_smp_trampoline_bin_start;
 extern symbol _binary_smp_trampoline_bin_end;
 
tab: 248 wrap: offon