x86: Uses rbx for the initial vcoreid (XCC)
[akaros.git] / kern / arch / x86 / smp_boot.c
index 2010a3e..96bdba6 100644 (file)
@@ -4,11 +4,6 @@
  * See LICENSE for details.
  */
 
-#ifdef __SHARC__
-#pragma nosharc
-#define SINIT(x) x
-#endif
-
 #include <arch/x86.h>
 #include <arch/arch.h>
 #include <smp.h>
 #include <trap.h>
 #include <kmalloc.h>
 
-extern handler_wrapper_t (RO handler_wrappers)[NUM_HANDLER_WRAPPERS];
+#include "vmm/vmm.h"
+
+extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
 volatile uint32_t num_cpus = 0xee;
-uintptr_t RO smp_stack_top;
+uintptr_t smp_stack_top;
+barrier_t generic_barrier;
 
 #define DECLARE_HANDLER_CHECKLISTS(vector)                          \
        INIT_CHECKLIST(f##vector##_cpu_list, MAX_NUM_CPUS);
 
 #define INIT_HANDLER_WRAPPER(v)                                     \
 {                                                                   \
-       handler_wrappers[(v)].vector = 0xf##v;                          \
+       handler_wrappers[(v)].vector = 0xe##v;                          \
        handler_wrappers[(v)].cpu_list = &f##v##_cpu_list;              \
        handler_wrappers[(v)].cpu_list->mask.size = num_cpus;           \
 }
@@ -59,24 +57,65 @@ static void init_smp_call_function(void)
 
 /******************************************************************************/
 
-static void smp_final_core_init(struct hw_trapframe *hw_tf, void *data)
+bool core_id_ready = FALSE;
+
+static void setup_rdtscp(int coreid)
+{
+       uint32_t edx;
+       int rdtscp_ecx;
+       /* TODO: have some sort of 'cpu info structure' with flags */
+       cpuid(0x80000001, 0x0, 0, 0, 0, &edx);
+       if (edx & (1 << 27)) {
+               write_msr(MSR_TSC_AUX, coreid);
+               /* Busted versions of qemu bug out here (32 bit) */
+               asm volatile ("rdtscp" : "=c"(rdtscp_ecx) : : "eax", "edx");
+               if (!coreid && (read_msr(MSR_TSC_AUX) != rdtscp_ecx))
+                       printk("\nBroken rdtscp detected, don't trust it for pcoreid!\n\n");
+       }
+}
+
+/* TODO: consider merging __arch_pcpu with parts of this (sync with RISCV) */
+void smp_final_core_init(void)
 {
-#ifdef CONFIG_FAST_COREID
-       /* Need to bootstrap the rdtscp MSR with our OS coreid */
+       /* It is possible that the non-0 cores will wake up before the broadcast
+        * ipi.  this can be due to spurious IRQs or some such.  anyone other than
+        * core 0 that comes in here will wait til core 0 has set everything up.
+        * those other cores might have come up before core 0 remapped the coreids,
+        * so we can only look at the HW coreid, which is only 0 for core 0. */
+       static bool wait = TRUE;
+       if (hw_core_id() == 0)
+               wait = FALSE;
+       while (wait)
+               cpu_relax();
+       /* at this point, it is safe to get the OS coreid */
        int coreid = get_os_coreid(hw_core_id());
-       write_msr(MSR_TSC_AUX, coreid);
-#endif
-       setup_default_mtrrs(data);
+       struct per_cpu_info *pcpui = &per_cpu_info[coreid];
+       pcpui->coreid = coreid;
+       write_msr(MSR_GS_BASE, (uint64_t)pcpui);
+       write_msr(MSR_KERN_GS_BASE, (uint64_t)pcpui);
+       /* don't need this for the kernel anymore, but userspace can still use it */
+       setup_rdtscp(coreid);
+       /* After this point, all cores have set up their segmentation and whatnot to
+        * be able to do a proper core_id().  As a note to posterity, using the
+        * LAPIC coreid (like get_hw_coreid()) needs the LAPIC set up, which happens
+        * by the end of vm_init() */
+       waiton_barrier(&generic_barrier);
+       if (hw_core_id() == 0) {
+               core_id_ready = TRUE;
+       }
+       /* being paranoid with this, it's all a bit ugly */
+       waiton_barrier(&generic_barrier);
+       setup_default_mtrrs(&generic_barrier);
        smp_percpu_init();
-       waiton_barrier(data);
+       waiton_barrier(&generic_barrier);
 }
 
 // this needs to be set in smp_entry too...
 #define trampoline_pg 0x00001000UL
-extern char (SNT SREADONLY smp_entry)[];
-extern char (SNT SREADONLY smp_entry_end)[];
-extern char (SNT SREADONLY smp_boot_lock)[];
-extern char (SNT SREADONLY smp_semaphore)[];
+extern char smp_entry[];
+extern char smp_entry_end[];
+extern char smp_boot_lock[];
+extern char smp_semaphore[];
 
 static inline uint16_t *get_smp_semaphore()
 {
@@ -116,6 +155,7 @@ static void smp_remap_coreids(void)
 
 void smp_boot(void)
 {
+       struct per_cpu_info *pcpui0 = &per_cpu_info[0];
        /* set core0's mappings */
        assert(lapic_get_id() == 0);
        os_coreid_lookup[0] = 0;
@@ -125,21 +165,21 @@ void smp_boot(void)
        // NEED TO GRAB A LOWMEM FREE PAGE FOR AP BOOTUP CODE
        // page1 (2nd page) is reserved, hardcoded in pmap.c
        memset(KADDR(trampoline_pg), 0, PGSIZE);
-       memcpy(KADDR(trampoline_pg), (void *COUNT(PGSIZE))TC(smp_entry),
+       memcpy(KADDR(trampoline_pg), (void *)smp_entry,
            smp_entry_end - smp_entry);
 
-       /* 64 bit already has the tramp pg mapped (1 GB of lowmem)  */
-#ifndef CONFIG_X86_64
-       // This mapping allows access to the trampoline with paging on and off
-       // via trampoline_pg
-       page_insert(boot_pgdir, pa2page(trampoline_pg), (void*SNT)trampoline_pg, PTE_W);
-#endif
+       /* Make sure the trampoline page is mapped.  64 bit already has the tramp pg
+        * mapped (1 GB of lowmem), so this is a nop. */
 
        // Allocate a stack for the cores starting up.  One for all, must share
        if (kpage_alloc(&smp_stack))
                panic("No memory for SMP boot stack!");
-       smp_stack_top = SINIT((uintptr_t)(page2kva(smp_stack) + PGSIZE));
+       smp_stack_top = (uintptr_t)(page2kva(smp_stack) + PGSIZE);
 
+       /* During SMP boot, core_id_early() returns 0, so all of the cores, which
+        * grab locks concurrently, share the same pcpui and thus the same
+        * lock_depth.  We need to disable checking until core_id works properly. */
+       pcpui0->__lock_checking_enabled = 0;
        // Start the IPI process (INIT, wait, SIPI, wait, SIPI, wait)
        send_init_ipi();
        // SDM 3A is a little wonky wrt the proper delays.  These are my best guess.
@@ -191,10 +231,10 @@ void smp_boot(void)
        init_smp_call_function();
 
        /* Final core initialization */
-       barrier_t generic_barrier;
        init_barrier(&generic_barrier, num_cpus);
        /* This will break the cores out of their hlt in smp_entry.S */
-       smp_call_function_all(smp_final_core_init, &generic_barrier, 0);
+       send_broadcast_ipi(I_POKE_CORE);
+       smp_final_core_init();  /* need to init ourselves as well */
 }
 
 /* This is called from smp_entry by each core to finish the core bootstrapping.
@@ -223,11 +263,7 @@ uintptr_t smp_main(void)
        hw_coreid_lookup[my_hw_id] = my_hw_id;
 
        // Get a per-core kernel stack
-       page_t *my_stack;
-       if (kpage_alloc(&my_stack))
-               panic("Unable to alloc a per-core stack!");
-       memset(page2kva(my_stack), 0, PGSIZE);
-       uintptr_t my_stack_top = (uintptr_t)page2kva(my_stack) + PGSIZE;
+       uintptr_t my_stack_top = get_kstack();
 
        /* This blob is the GDT, the GDT PD, and the TSS. */
        unsigned int blob_size = sizeof(segdesc_t) * SEG_COUNT +
@@ -241,7 +277,7 @@ uintptr_t smp_main(void)
         * to smp_percpu_init(), but we can't trust our coreid (since they haven't
         * been remapped yet (so we can't write it directly to per_cpu_info)).  So
         * we use the bottom of the stack page... */
-       *(uintptr_t*)page2kva(my_stack) = (uintptr_t)gdt_etc;
+       *kstack_bottom_addr(my_stack_top) = (uintptr_t)gdt_etc;
 
        // Build and load the gdt / gdt_pd
        memcpy(my_gdt, gdt, sizeof(segdesc_t)*SEG_COUNT);
@@ -261,14 +297,7 @@ uintptr_t smp_main(void)
        // Loads the same IDT used by the other cores
        asm volatile("lidt %0" : : "m"(idt_pd));
 
-       // APIC setup
-       // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
-       write_mmreg32(LAPIC_LVT_LINT0, 0x700);
-       // mask it to shut it up for now.  Doesn't seem to matter yet, since both
-       // KVM and Bochs seem to only route the PIC to core0.
-       mask_lapic_lvt(LAPIC_LVT_LINT0);
-       // and then turn it on
-       lapic_enable();
+       apiconline();
 
        // set a default logical id for now
        lapic_set_logid(lapic_get_id());
@@ -283,7 +312,7 @@ uintptr_t smp_main(void)
  * to call it on too deep of a stack frame. */
 void __arch_pcpu_init(uint32_t coreid)
 {
-       uintptr_t my_stack_bot;
+       uintptr_t *my_stack_bot;
        struct per_cpu_info *pcpui = &per_cpu_info[coreid];
 
        /* Flushes any potentially old mappings from smp_boot() (note the page table
@@ -302,23 +331,16 @@ void __arch_pcpu_init(uint32_t coreid)
                pcpui->tss = &ts;
                pcpui->gdt = gdt;
        } else {
-               my_stack_bot = ROUNDDOWN(read_sp(), PGSIZE);
-               pcpui->tss = (taskstate_t*)(*(uintptr_t*)my_stack_bot);
-               pcpui->gdt = (segdesc_t*)(*(uintptr_t*)my_stack_bot +
+               my_stack_bot = kstack_bottom_addr(ROUNDUP(read_sp() - 1, PGSIZE));
+               pcpui->tss = (taskstate_t*)(*my_stack_bot);
+               pcpui->gdt = (segdesc_t*)(*my_stack_bot +
                                          sizeof(taskstate_t) + sizeof(pseudodesc_t));
        }
-#ifdef CONFIG_X86_64
-       /* Core 0 set up the base MSRs in entry64 */
-       if (!coreid) {
-               assert(read_msr(MSR_GS_BASE) == (uint64_t)pcpui);
-               assert(read_msr(MSR_KERN_GS_BASE) == (uint64_t)pcpui);
-       } else {
-               write_msr(MSR_GS_BASE, (uint64_t)pcpui);
-               write_msr(MSR_KERN_GS_BASE, (uint64_t)pcpui);
-       }
-#endif
+       assert(read_msr(MSR_GS_BASE) == (uint64_t)pcpui);
+       assert(read_msr(MSR_KERN_GS_BASE) == (uint64_t)pcpui);
        /* Don't try setting up til after setting GS */
        x86_sysenter_init(x86_get_stacktop_tss(pcpui->tss));
        /* need to init perfctr before potentiall using it in timer handler */
        perfmon_init();
+       vmm_pcpu_init();
 }