bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 6/6 gnumach] smp: Fix INIT/STARTUP IPI sequence


From: Damien Zammit
Subject: [PATCH 6/6 gnumach] smp: Fix INIT/STARTUP IPI sequence
Date: Mon, 05 Feb 2024 11:34:09 +0000

Outstanding: Find a way to allocate memory below 1MiB.
Otherwise, this hardcodes 0x3000 as the starting eip.

TESTED: works in qemu
TESTED: works hardware with AMD cpu
---
 i386/i386/mp_desc.c     |  15 ++++--
 i386/i386/smp.c         | 114 +++++++++++++++++++++++++++++-----------
 i386/i386/smp.h         |   2 +-
 i386/i386at/cram.h      |   5 ++
 i386/i386at/model_dep.c |   2 +-
 5 files changed, 100 insertions(+), 38 deletions(-)

diff --git a/i386/i386/mp_desc.c b/i386/i386/mp_desc.c
index 071aa292..6a040ab8 100644
--- a/i386/i386/mp_desc.c
+++ b/i386/i386/mp_desc.c
@@ -292,17 +292,24 @@ cpu_ap_main()
 kern_return_t
 cpu_start(int cpu)
 {
+    int err, i, vec;
+
     assert(machine_slot[cpu].running != TRUE);
 
     uint16_t apic_id = apic_get_cpu_apic_id(cpu);
 
-    printf("Trying to enable: %d\n", apic_id);
+    vec = apboot_addr >> 12;
 
-    smp_startup_cpu(apic_id, apboot_addr);
+    printf("Trying to enable: %d at 0x%x\n", apic_id, vec);
 
-    printf("Started cpu %d (lapic id %04x)\n", cpu, apic_id);
+    err = smp_startup_cpu(apic_id, vec);
 
-    return KERN_SUCCESS;
+    if (!err) {
+        printf("Started cpu %d (lapic id %04x)\n", cpu, apic_id);
+        return KERN_SUCCESS;
+    }
+    printf("FATAL: Cannot init AP %d\n", cpu);
+    for (;;);
 }
 
 void
diff --git a/i386/i386/smp.c b/i386/i386/smp.c
index 87f59913..397defa6 100644
--- a/i386/i386/smp.c
+++ b/i386/i386/smp.c
@@ -18,10 +18,14 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
 
+#include <string.h>
 #include <i386/apic.h>
 #include <i386/smp.h>
 #include <i386/cpu.h>
+#include <i386/pio.h>
+#include <i386/vm_param.h>
 #include <i386at/idt.h>
+#include <i386at/cram.h>
 #include <i386at/acpi_parse_apic.h>
 #include <kern/printf.h>
 #include <mach/machine.h>
@@ -75,59 +79,105 @@ void smp_pmap_update(unsigned apic_id)
     smp_send_ipi(apic_id, CALL_PMAP_UPDATE);
 }
 
-/* See Intel IA32/64 Software Developer's Manual 3A Section 8.4.4.1 */
-void smp_startup_cpu(unsigned apic_id, unsigned vector)
+static void
+wait_for_ipi(void)
 {
-    /* Clear APIC errors */
-    lapic->error_status.r = 0;
+    int loops = 100000;
+
+    while (lapic->icr_low.delivery_status == SEND_PENDING) {
+       cpu_pause();
+       loops--;
+       if (loops == 0) {
+           printf("FATAL: wait_for_ipi: busy\n");
+           for (;;);
+       }
+    }
+}
+
+static int
+smp_send_ipi_init(int apic_id)
+{
+    int err;
 
-    printf("Sending IPIs to APIC ID %u...", apic_id);
+    lapic->error_status.r = 0;
 
     /* Assert INIT IPI */
-    apic_send_ipi(NO_SHORTHAND, INIT, PHYSICAL, ASSERT, LEVEL, 0, apic_id);
+    apic_send_ipi(NO_SHORTHAND, INIT, PHYSICAL, ASSERT, EDGE, 0, apic_id);
 
     /* Wait for delivery */
-    do {
-        cpu_pause();
-    } while(lapic->icr_low.delivery_status == SEND_PENDING);
+    wait_for_ipi();
+    hpet_mdelay(10);
 
     /* Deassert INIT IPI */
-    apic_send_ipi(NO_SHORTHAND, INIT, PHYSICAL, DE_ASSERT, LEVEL, 0, apic_id);
+    apic_send_ipi(NO_SHORTHAND, INIT, PHYSICAL, DE_ASSERT, EDGE, 0, apic_id);
 
     /* Wait for delivery */
-    do {
-        cpu_pause();
-    } while(lapic->icr_low.delivery_status == SEND_PENDING);
+    wait_for_ipi();
 
-    /* Wait 10 msec */
-    hpet_mdelay(10);
+    err = lapic->error_status.r;
+    if (err) {
+        printf("ESR error upon INIT 0x%x\n", err);
+    }
+    return 0;
+}
 
-    /* Clear APIC errors */
-    lapic->error_status.r = 0;
+static int
+smp_send_ipi_startup(int apic_id, int vector)
+{
+    int err;
 
-    /* First StartUp IPI */
-    apic_send_ipi(NO_SHORTHAND, STARTUP, PHYSICAL, ASSERT, LEVEL, vector >> 
12, apic_id);
+    lapic->error_status.r = 0;
 
-    /* Wait 200 usec */
-    hpet_udelay(200);
+    /* StartUp IPI */
+    apic_send_ipi(NO_SHORTHAND, STARTUP, PHYSICAL, ASSERT, EDGE, vector, 
apic_id);
 
     /* Wait for delivery */
-    do {
-        cpu_pause();
-    } while(lapic->icr_low.delivery_status == SEND_PENDING);
+    wait_for_ipi();
 
-    /* Second StartUp IPI */
-    apic_send_ipi(NO_SHORTHAND, STARTUP, PHYSICAL, ASSERT, LEVEL, vector >> 
12, apic_id);
+    err = lapic->error_status.r;
+    if (err) {
+        printf("ESR error upon STARTUP 0x%x\n", err);
+    }
+    return 0;
+}
 
-    /* Wait 200 usec */
+/* See Intel IA32/64 Software Developer's Manual 3A Section 8.4.4.1 */
+int smp_startup_cpu(unsigned apic_id, unsigned vector)
+{
+    int err;
+
+#if 0
+    /* This is a legacy method of INIT that only works with
+     * old hardware that does not support SIPIs.
+     * Must use INIT DEASSERT LEVEL triggered IPI to use this block.
+     * (At least one AMD FCH does not support this IPI mode,
+     * See AMD BKDG FAM16h document # 48751 page 461).
+     */
+
+    /* Tell CMOS to warm reset through through 40:67 */
+    outb(CMOS_ADDR, CMOS_SHUTDOWN);
+    outb(CMOS_DATA, CM_JMP_467);
+
+    /* Set warm reset vector to point to AP startup code */
+    uint16_t dword[2];
+    dword[0] = 0;
+    dword[1] = vector << 8;
+    memcpy((uint8_t *)phystokv(0x467), dword, 4);
+#endif
+
+    /* Local cache flush */
+    asm("wbinvd":::"memory");
+
+    printf("Sending IPIs to APIC ID %u...\n", apic_id);
+    err = smp_send_ipi_init(apic_id);
+    hpet_mdelay(10);
+    err = smp_send_ipi_startup(apic_id, vector);
+    hpet_udelay(200);
+    err = smp_send_ipi_startup(apic_id, vector);
     hpet_udelay(200);
-
-    /* Wait for delivery */
-    do {
-        cpu_pause();
-    } while(lapic->icr_low.delivery_status == SEND_PENDING);
 
     printf("done\n");
+    return 0;
 }
 
 /*
diff --git a/i386/i386/smp.h b/i386/i386/smp.h
index 784936ea..a2b41253 100644
--- a/i386/i386/smp.h
+++ b/i386/i386/smp.h
@@ -24,7 +24,7 @@
 int smp_init(void);
 void smp_remote_ast(unsigned apic_id);
 void smp_pmap_update(unsigned apic_id);
-void smp_startup_cpu(unsigned apic_id, unsigned vector);
+int smp_startup_cpu(unsigned apic_id, unsigned vector);
 
 #define cpu_pause() asm volatile ("pause" : : : "memory")
 
diff --git a/i386/i386at/cram.h b/i386/i386at/cram.h
index 8a3a6ec9..ac40cf13 100644
--- a/i386/i386at/cram.h
+++ b/i386/i386at/cram.h
@@ -71,6 +71,11 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 /* Addresses, related masks, and potential results */
 
+#define CMOS_SHUTDOWN  0xf
+#define CM_NORM_RST    0x0
+#define CM_LOAD_SYS    0x4
+#define CM_JMP_467     0xa
+
 #define CMOS_EB                0x14            /* read Equipment Byte */
 #define CM_SCRMSK      0x30            /* mask for EB query to get screen */
 #define CM_EGA_VGA     0x00            /* "not CGA or MONO" */
diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c
index 676d8751..74be13e1 100644
--- a/i386/i386at/model_dep.c
+++ b/i386/i386at/model_dep.c
@@ -217,7 +217,7 @@ void machine_init(void)
         * Grab an early page for AP boot code
         */
        /* FIXME: this may not allocate from below 1MB, if within first 16MB */
-       apboot_addr = vm_page_to_pa(vm_page_grab_contig(PAGE_SIZE, 
VM_PAGE_SEL_DMA));
+       apboot_addr = 0x3000; //vm_page_to_pa(vm_page_grab_contig(PAGE_SIZE, 
VM_PAGE_SEL_DMA));
        assert (apboot_addr < 0x100000);
 
        /*
-- 
2.43.0





reply via email to

[Prev in Thread] Current Thread [Next in Thread]