Clear x86_default_fpu STX/MMX register state at boot
[akaros.git] / kern / arch / x86 / smp_boot.c
index 54935c5..efb11e2 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 <env.h>
 #include <trap.h>
 #include <kmalloc.h>
+#include <cpu_feat.h>
+#include <arch/fsgsbase.h>
+#include <ros/procinfo.h>
+
+#include "vmm/vmm.h"
 
-extern handler_wrapper_t (RO handler_wrappers)[NUM_HANDLER_WRAPPERS];
-volatile uint32_t num_cpus = 0xee;
-uintptr_t RO smp_stack_top;
+extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
+int x86_num_cores_booted = 1;
+uintptr_t smp_stack_top;
 barrier_t generic_barrier;
 
 #define DECLARE_HANDLER_CHECKLISTS(vector)                          \
-       INIT_CHECKLIST(f##vector##_cpu_list, MAX_NUM_CPUS);
+       INIT_CHECKLIST(f##vector##_cpu_list, MAX_NUM_CORES);
 
 #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;           \
+       handler_wrappers[(v)].cpu_list->mask.size = num_cores;          \
 }
 
 DECLARE_HANDLER_CHECKLISTS(0);
@@ -60,25 +60,41 @@ static void init_smp_call_function(void)
 
 /******************************************************************************/
 
-void smp_final_core_init(void)
-{
-#ifdef CONFIG_FAST_COREID
-       /* Need to bootstrap the rdtscp MSR with our OS coreid */
-       int coreid = get_os_coreid(hw_core_id());
-       write_msr(MSR_TSC_AUX, coreid);
+bool core_id_ready = FALSE;
 
-       /* Busted versions of qemu bug out here (32 bit) */
+static void setup_rdtscp(int coreid)
+{
+       uint32_t edx;
        int rdtscp_ecx;
-       asm volatile ("rdtscp" : "=c"(rdtscp_ecx) : : "eax", "edx");
-       if (read_msr(MSR_TSC_AUX) != rdtscp_ecx) {
-               printk("Broken rdtscp detected!  Rebuild without CONFIG_FAST_COREID\n");
-               if (coreid)
-                       while(1);
-               /* note this panic may think it is not core 0, and core 0 might not have
-                * an issue (seems random) */
-               panic("");
+       /* 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");
        }
-#endif
+}
+
+/* TODO: consider merging __arch_pcpu with parts of this (sync with RISCV) */
+void smp_final_core_init(void)
+{
+       /* Set the coreid in pcpui for fast access to it through TLS. */
+       int coreid = get_os_coreid(hw_core_id());
+       struct per_cpu_info *pcpui = &per_cpu_info[coreid];
+       pcpui->coreid = coreid;
+       write_msr(MSR_GS_BASE, (uintptr_t)pcpui);       /* our cr4 isn't set yet */
+       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(). */
+       waiton_barrier(&generic_barrier);
+       if (coreid == 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(&generic_barrier);
@@ -86,10 +102,10 @@ void smp_final_core_init(void)
 
 // 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()
 {
@@ -107,52 +123,29 @@ static void __spin_bootlock_raw(void)
                      "jne 1b;" : : "m"(*bootlock) : "eax", "cc", "memory");
 }
 
-/* hw_coreid_lookup will get packed, but keep it's hw values.  
- * os_coreid_lookup will remain sparse, but it's values will be consecutive.
- * for both arrays, -1 means an empty slot.  hw_step tracks the next valid entry
- * in hw_coreid_lookup, jumping over gaps of -1's. */
-static void smp_remap_coreids(void)
-{
-       for (int i = 0, hw_step = 0; i < num_cpus; i++, hw_step++) {
-               if (hw_coreid_lookup[i] == -1) {
-                       while (hw_coreid_lookup[hw_step] == -1) {
-                               hw_step++;
-                               if (hw_step == MAX_NUM_CPUS)
-                                       panic("Mismatch in num_cpus and hw_step");
-                       }
-                       hw_coreid_lookup[i] = hw_coreid_lookup[hw_step];
-                       hw_coreid_lookup[hw_step] = -1;
-                       os_coreid_lookup[hw_step] = i;
-               }
-       }
-}
-
 void smp_boot(void)
 {
-       /* set core0's mappings */
-       assert(lapic_get_id() == 0);
-       os_coreid_lookup[0] = 0;
-       hw_coreid_lookup[0] = 0;
-
+       struct per_cpu_info *pcpui0 = &per_cpu_info[0];
        page_t *smp_stack;
+
        // 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.
@@ -183,20 +176,23 @@ void smp_boot(void)
        // mapping pulled out from under them.  Now, if a core loses, it will spin
        // on the trampoline (which we must be careful to not deallocate)
        __spin_bootlock_raw();
-       printk("Number of Cores Detected: %d\n", num_cpus);
+       printk("Number of Cores Detected: %d\n", x86_num_cores_booted);
 #ifdef CONFIG_DISABLE_SMT
-       assert(!(num_cpus % 2));
-       printk("Using only %d Idlecores (SMT Disabled)\n", num_cpus >> 1);
+       assert(!(num_cores % 2));
+       printk("Using only %d Idlecores (SMT Disabled)\n", num_cores >> 1);
 #endif /* CONFIG_DISABLE_SMT */
-       smp_remap_coreids();
 
        /* cleans up the trampoline page, and any other low boot mem mappings */
        x86_cleanup_bootmem();
-       // It had a refcount of 2 earlier, so we need to dec once more to free it
-       // but only if all cores are in (or we reset / reinit those that failed)
-       // TODO after we parse ACPI tables
-       if (num_cpus == 8) // TODO - ghetto coded for our 8 way SMPs
+       /* trampoline_pg had a refcount of 2 earlier, so we need to dec once more to free it
+        * but only if all cores are in (or we reset / reinit those that failed) */
+       if (x86_num_cores_booted == num_cores) {
                page_decref(pa2page(trampoline_pg));
+       } else {
+               warn("ACPI/MP found %d cores, smp_boot initialized %d, using %d\n",
+                    num_cores, x86_num_cores_booted, x86_num_cores_booted);
+               num_cores = x86_num_cores_booted;
+       }
        // Dealloc the temp shared stack
        page_decref(smp_stack);
 
@@ -204,9 +200,9 @@ void smp_boot(void)
        init_smp_call_function();
 
        /* Final core initialization */
-       init_barrier(&generic_barrier, num_cpus);
+       init_barrier(&generic_barrier, num_cores);
        /* This will break the cores out of their hlt in smp_entry.S */
-       send_broadcast_ipi(254);
+       send_broadcast_ipi(I_POKE_CORE);
        smp_final_core_init();  /* need to init ourselves as well */
 }
 
@@ -228,19 +224,11 @@ uintptr_t smp_main(void)
                cprintf("I am the Boot Strap Processor\n");
        else
                cprintf("I am an Application Processor\n");
-       cprintf("Num_Cpus: %d\n\n", num_cpus);
+       cprintf("Num_Cores: %d\n\n", num_cores);
        */
-       /* set up initial mappings.  core0 will adjust it later */
-       unsigned long my_hw_id = lapic_get_id();
-       os_coreid_lookup[my_hw_id] = my_hw_id;
-       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 +
@@ -254,7 +242,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);
@@ -274,17 +262,8 @@ 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());
 
        return my_stack_top; // will be loaded in smp_entry.S
 }
@@ -296,42 +275,53 @@ 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];
+       uint32_t eax, edx;
 
        /* Flushes any potentially old mappings from smp_boot() (note the page table
         * removal) */
        tlbflush();
-       /* Ensure the FPU units are initialized */
-       asm volatile ("fninit");
 
-       /* Enable SSE instructions.  We might have to do more, like masking certain
-        * flags or exceptions in the MXCSR, or at least handle the SIMD exceptions.
-        * We don't do it for FP yet either, so YMMV. */
+       if (cpu_has_feat(CPU_FEAT_X86_FSGSBASE))
+               lcr4(rcr4() | CR4_FSGSBASE);
+
+       /*
+        * Enable SSE instructions.
+        * CR4.OSFXSR enables SSE and ensures that MXCSR/XMM gets saved with FXSAVE
+        * CR4.OSXSAVE enables XSAVE instructions. Only set if XSAVE supported.
+        * CR4.OSXMME indicates OS support for software exception handlers for
+        * SIMD floating-point exceptions (turn it on to get #XM exceptions
+        * in the event of a SIMD error instead of #UD exceptions).
+        */
        lcr4(rcr4() | CR4_OSFXSR | CR4_OSXMME);
 
+       if (cpu_has_feat(CPU_FEAT_X86_XSAVE)) {
+               // You MUST set CR4.OSXSAVE before loading xcr0
+               lcr4(rcr4() | CR4_OSXSAVE);
+               // Set xcr0 to the Akaros-wide default
+               lxcr0(__proc_global_info.x86_default_xcr0);
+       }
+
+       // Initialize fpu and extended state by restoring our default XSAVE area.
+       init_fp_state();
+
        /* core 0 sets up via the global gdt symbol */
        if (!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_gsbase() == (uintptr_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();
+       /* need to init perfctr before potentially using it in timer handler */
+       perfmon_pcpu_init();
+       vmm_pcpu_init();
+       lcr4(rcr4() & ~CR4_TSD);
 }