Skip the shitty e1000s on c89
[akaros.git] / kern / arch / x86 / smp_boot.c
index eb94bdb..f08b997 100644 (file)
@@ -31,6 +31,7 @@
 extern handler_wrapper_t (RO handler_wrappers)[NUM_HANDLER_WRAPPERS];
 volatile uint32_t num_cpus = 0xee;
 uintptr_t RO smp_stack_top;
+barrier_t generic_barrier;
 
 #define DECLARE_HANDLER_CHECKLISTS(vector)                          \
        INIT_CHECKLIST(f##vector##_cpu_list, MAX_NUM_CPUS);
@@ -59,11 +60,36 @@ static void init_smp_call_function(void)
 
 /******************************************************************************/
 
-static void smp_final_core_init(struct hw_trapframe *hw_tf, void *data)
+void smp_final_core_init(void)
 {
-       setup_default_mtrrs(data);
+       /* 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 */
+       static bool wait = TRUE;
+       if (get_os_coreid(hw_core_id()) == 0)
+               wait = FALSE;
+       while (wait)
+               cpu_relax();
+#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);
+
+       /* Busted versions of qemu bug out here (32 bit) */
+       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("");
+       }
+#endif
+       setup_default_mtrrs(&generic_barrier);
        smp_percpu_init();
-       waiton_barrier(data);
+       waiton_barrier(&generic_barrier);
 }
 
 // this needs to be set in smp_entry too...
@@ -73,16 +99,20 @@ extern char (SNT SREADONLY smp_entry_end)[];
 extern char (SNT SREADONLY smp_boot_lock)[];
 extern char (SNT SREADONLY smp_semaphore)[];
 
-static inline volatile uint32_t *COUNT(1)
-get_smp_semaphore()
+static inline uint16_t *get_smp_semaphore()
 {
-       return (volatile uint32_t *COUNT(1))TC(smp_semaphore - smp_entry + trampoline_pg);
+       return (uint16_t *)(smp_semaphore - smp_entry + trampoline_pg);
 }
 
-static inline uint32_t *COUNT(1)
-get_smp_bootlock()
+static void __spin_bootlock_raw(void)
 {
-       return (uint32_t *COUNT(1))TC(smp_boot_lock - smp_entry + trampoline_pg);
+       uint16_t *bootlock = (uint16_t*)(smp_boot_lock - smp_entry + trampoline_pg);
+       /* Same lock code as in smp_entry */
+       asm volatile ("movw $1, %%ax;   "
+                                 "1:               "
+                     "xchgw %%ax, %0;  "
+                     "test %%ax, %%ax; "
+                     "jne 1b;" : : "m"(*bootlock) : "eax", "cc", "memory");
 }
 
 /* hw_coreid_lookup will get packed, but keep it's hw values.  
@@ -119,9 +149,12 @@ void smp_boot(void)
        memcpy(KADDR(trampoline_pg), (void *COUNT(PGSIZE))TC(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
 
        // Allocate a stack for the cores starting up.  One for all, must share
        if (kpage_alloc(&smp_stack))
@@ -145,7 +178,8 @@ void smp_boot(void)
        // all in smp_entry.  It's purpose is to keep Core0 from competing for the
        // smp_boot_lock.  So long as one AP increments the sem before the final
        // LAPIC timer goes off, all available cores will be initialized.
-       while(*get_smp_semaphore());
+       while (*get_smp_semaphore())
+               cpu_relax();
 
        // From here on, no other cores are coming up.  Grab the lock to ensure it.
        // Another core could be in it's prelock phase and be trying to grab the lock
@@ -156,7 +190,7 @@ void smp_boot(void)
        // booting.  Specifically, it's when they turn on paging and have that temp
        // 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_lock_raw(get_smp_bootlock());
+       __spin_bootlock_raw();
        printk("Number of Cores Detected: %d\n", num_cpus);
 #ifdef CONFIG_DISABLE_SMT
        assert(!(num_cpus % 2));
@@ -164,15 +198,13 @@ void smp_boot(void)
 #endif /* CONFIG_DISABLE_SMT */
        smp_remap_coreids();
 
-       // Remove the mapping of the page used by the trampoline
-       page_remove(boot_pgdir, (void*SNT)trampoline_pg);
+       /* 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
                page_decref(pa2page(trampoline_pg));
-       // Remove the page table used for that mapping
-       pagetable_remove(boot_pgdir, (void*SNT)trampoline_pg);
        // Dealloc the temp shared stack
        page_decref(smp_stack);
 
@@ -180,10 +212,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(254);
+       smp_final_core_init();  /* need to init ourselves as well */
 }
 
 /* This is called from smp_entry by each core to finish the core bootstrapping.
@@ -193,7 +225,7 @@ void smp_boot(void)
  *
  * Do not use per_cpu_info in here.  Do whatever you need in smp_percpu_init().
  */
-uint32_t smp_main(void)
+uintptr_t smp_main(void)
 {
        /*
        // Print some diagnostics.  Uncomment if there're issues.
@@ -212,11 +244,7 @@ uint32_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 +
@@ -230,13 +258,7 @@ uint32_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;
-
-       /* TODO: 64b is diff, use a helper */
-       // Set up MSR for SYSENTER 
-       write_msr(MSR_IA32_SYSENTER_CS, GD_KT);
-       write_msr(MSR_IA32_SYSENTER_ESP, my_stack_top);
-       write_msr(MSR_IA32_SYSENTER_EIP, (uintptr_t) &sysenter_handler);
+       *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);
@@ -244,7 +266,7 @@ uint32_t smp_main(void)
                sizeof(segdesc_t)*SEG_COUNT - 1, (uintptr_t) my_gdt };
        asm volatile("lgdt %0" : : "m"(*my_gdt_pd));
 
-       // Need to set the TSS so we know where to trap on this core
+       /* Set up our kernel stack when changing rings */
        x86_set_stacktop_tss(my_ts, my_stack_top);
        // Initialize the TSS field of my_gdt.
        syssegdesc_t *ts_slot = (syssegdesc_t*)&my_gdt[GD_TSS >> 3];
@@ -256,6 +278,9 @@ uint32_t smp_main(void)
        // Loads the same IDT used by the other cores
        asm volatile("lidt %0" : : "m"(idt_pd));
 
+#ifdef CONFIG_ENABLE_MPTABLES
+       apiconline();
+#else
        // APIC setup
        // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
        write_mmreg32(LAPIC_LVT_LINT0, 0x700);
@@ -264,6 +289,7 @@ uint32_t smp_main(void)
        mask_lapic_lvt(LAPIC_LVT_LINT0);
        // and then turn it on
        lapic_enable();
+#endif
 
        // set a default logical id for now
        lapic_set_logid(lapic_get_id());
@@ -278,7 +304,8 @@ uint32_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
         * removal) */
@@ -293,14 +320,26 @@ void __arch_pcpu_init(uint32_t coreid)
 
        /* core 0 sets up via the global gdt symbol */
        if (!coreid) {
-               per_cpu_info[0].tss = &ts;
-               per_cpu_info[0].gdt = gdt;
+               pcpui->tss = &ts;
+               pcpui->gdt = gdt;
        } else {
-               my_stack_bot = ROUNDDOWN(read_sp(), PGSIZE);
-               per_cpu_info[coreid].tss = (taskstate_t*)(*(uintptr_t*)my_stack_bot);
-               per_cpu_info[coreid].gdt = (segdesc_t*)(*(uintptr_t*)my_stack_bot +
-                                          sizeof(taskstate_t) + sizeof(pseudodesc_t));
+               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
+       /* 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();
 }