x86_64: smp_boot
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 25 Jun 2013 20:44:34 +0000 (13:44 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 26 Jun 2013 05:20:24 +0000 (22:20 -0700)
Also fixes an old bug where smp_boot was treating the boot lock and sem as if
they were 32 bit (they are 16).

kern/arch/x86/entry64.S
kern/arch/x86/smp_boot.c
kern/arch/x86/smp_entry32.S
kern/arch/x86/smp_entry64.S
kern/src/init.c

index 876e840..cfc3278 100644 (file)
@@ -112,12 +112,13 @@ _start:
 .code64
 long_mode:
        # zero the data segments.  Not sure if this is legit or not.
-       xor     %rax, %rax
-       mov     %ax, %ds
-       mov     %ax, %es
-       mov     %ax, %ss
-       mov     %ax, %fs
-       mov     %ax, %gs
+       xor             %rax, %rax
+       mov             %ax, %ds
+       mov             %ax, %es
+       mov             %ax, %ss
+       mov             %ax, %fs
+       mov             %ax, %gs
+       lldt    %ax
        # paging is on, and our code is still running at 0x00100000.
        # do some miscellaneous OS setup.  the coreid stuff is so we can call
        # core_id() before smp_boot. 
index e70a62d..9b5a34f 100644 (file)
@@ -73,16 +73,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 +123,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 +152,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 +164,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 +172,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);
 
@@ -193,7 +199,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.
index 6382467..dda7cc7 100644 (file)
@@ -88,6 +88,9 @@ here:
        sti                     # so we can get the IPI
        hlt                     # wait for the IPI to run smp_pcu_init()
        call    smp_idle                # idle loop, will have interrupts turned on
+       # smp_idle should never return
+spin:
+       jmp spin
 
        # Below here is just data, stored with the code text
        .p2align        2                                               # force 4 byte alignment
index f2a8210..a0360ba 100644 (file)
@@ -1,6 +1,7 @@
 #include <arch/mmu.h>
 #include <ros/memlayout.h>
 #include <arch/trap.h>
+#include <arch/x86.h>
 
 #define        RELOC(x) ((x) - KERNBASE)
 #define        CPUID_PSE_SUPPORT       0x00000008
@@ -15,7 +16,6 @@ spin_start:                                           # grab lock in real mode
        xchgw   %ax, smp_boot_lock - smp_entry + 0x1000
        test    %ax, %ax
        jne             spin_start
-
        # Set up rudimentary segmentation
        xorw    %ax, %ax                        # Segment number zero
        movw    %ax, %ds                        # -> Data Segment
@@ -24,15 +24,12 @@ spin_start:                                         # grab lock in real mode
        # Would like to patch all of these 0x1000's at trampoline relocation time
        # There's three of them, so we could patch the trampoline code when we load,
        # once we're sure the entry code will not change anymore
-       # Note that this GDT is straight through, with no KERNBASE translation
        lgdt    gdtdesc - smp_entry + 0x1000
-
        # Turn on protected mode
        movl    %cr0, %eax
        orl             $CR0_PE, %eax
        movl    %eax, %cr0
        ljmp    $GD_KT, $(protcseg - smp_entry + 0x1000)
-       
 .code32
 protcseg:
        # Set up the protected-mode data segment registers
@@ -42,70 +39,67 @@ protcseg:
        movw    %ax, %ss                # -> SS: Stack Segment
        movw    %ax, %fs                # -> FS
        movw    %ax, %gs                # -> GS
-
-       # Turn on Paging
-       # crap, how do we sort out the relocs such that this is reachable from 32
-       # bit code?  need to put these in a magic location?
-       # XXX movl      RELOC(boot_cr3), %eax
+       # Turn on Paging.  We're using the symbol from entry64, which we'll have no
+       # problem linking against (compared to boot_cr3).  this assumes we use the
+       # boot stuff at least through smp_boot.
+       movl    $boot_pml4, %eax
        movl    %eax, %cr3
-       # Enable PSE, if available
-       movl    $1, %eax
-       cpuid
-       test    $CPUID_PSE_SUPPORT, %edx
-       jz              past_pse
-       movl    %cr4, %eax
-       orl             $CR4_PSE, %eax
-       movl    %eax, %cr4
-past_pse:
-       # Turn on PGE, no matter what.  Ghetto, but we panic if it's not supported.
+       # turn on paging option in cr4.  note we assume PSE support.  if we didn't
+       # have it, then our jumbo page mappings are going to fail.  we also want
+       # global pages (for performance).  PAE is the basics needed for long paging
        movl    %cr4, %eax
-       orl             $CR4_PGE, %eax
+       orl             $(CR4_PSE | CR4_PGE | CR4_PAE), %eax
        movl    %eax, %cr4
-       movl    %cr0, %eax      
-       # These cr0 flags are the same as in pmap.c.  Keep them in sync
-       orl             $(CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_MP), %eax  
-       andl    $(~(CR0_TS|CR0_EM|CR0_CD|CR0_NW)), %eax  
+       # Turn on the IA32E enabled bit.
+       # rd/wrmsr use ecx for the addr, and eax as the in/out register.
+       movl    $IA32_EFER_MSR, %ecx
+       rdmsr
+       orl             $IA32_EFER_IA32E_EN, %eax
+       wrmsr
+       # Setup cr0.  PE and PG are critical for now.  The others are similar to
+       # what we want in general (-AM with 64 bit, it's useless).
+       movl    %cr0, %eax
+       orl             $(CR0_PE | CR0_PG | CR0_WP | CR0_NE | CR0_MP), %eax  
+       andl    $(~(CR0_AM | CR0_TS | CR0_EM | CR0_CD | CR0_NW)), %eax  
        movl    %eax, %cr0
-
-       # Reload Segments, using the same gdt_pd as Core 0
-       # XXX lgdt      gdt_pd
-       movw    $GD_KD, %ax             # Kernel segment selector
-       movw    %ax, %ds                # -> DS: Data Segment
-       movw    %ax, %es                # -> ES: Extra Segment
-       movw    %ax, %ss                # -> SS: Stack Segment
-       movw    $GD_UD|3, %ax   # User segment selector, with RPL=3
-       movw    %ax, %fs                # -> FS
-       movw    %ax, %gs                # -> GS
-       # TODO: this reloc is mucking up.  it assembles for 32 bit okay, but if you asm
-       # for 64 bit, or if you objcopy ther 32 to 64, it can't find 'here'.  tried
-       # other names too.  is it because the linker doesn't know where smp entry
-       # is going to be, or that it later becomes a 64 bit reloc (unlike in
-       # _start, where we know it is linked for low mem).  Subtracting them gives
-       # the linker the ability to do the math now. (don't forget you can't trust
-       # the disassembly when the format doesn't match the code type)
-       # 
-       # we're going to have issues with all of the relocs like that - trying to
-       # find 64 bit addresses in 32 bit code.  for all of the external ones,
-       # we're going to need to either put the memory in here, so we don't need to
-       # reloc, or do it after we turn on 64b mode
-
-
-       # ljmp  $GD_KT, $(here) # jumping to original location of trampoline!
-       ljmp    $GD_KT, $(here - smp_entry + 0x1000)
-here:
-       xorl    %eax, %eax
+       # load the 64bit GDT and jump to long mode (symbol from entry64)
+       lgdt    gdt64desc
+       # Want to jump to the label long_mode, but we need to relocate to code
+       # reachable by 32 bit code: on our trampoline page.
+       ljmp    $0x08, $(long_mode - smp_entry + 0x1000)
+.code64
+long_mode:
+       # Note: we are still running code on the trampoline
+       # zero the data segments.  Not sure if this is legit or not.
+       xor             %rax, %rax
+       mov             %ax, %ds
+       mov             %ax, %es
+       mov             %ax, %ss
+       mov             %ax, %fs
+       mov             %ax, %gs
        lldt    %ax
-       # XXX incl      num_cpus
-       # XXX movl      (smp_stack_top), %esp
-       movl    $0, %ebp                # so backtrace works
+       incl    num_cpus                # uint32_t
+       movq    (smp_stack_top), %rsp
+       movq    $0, %rbp                # so backtrace works
+       # We're on the trampoline, but want to be in the real location of the smp
+       # code (somewhere above KERN_LOAD_ADDR).  This allows us to easily unmap
+       # the boot up memory, which the trampoline is part of.
+       movabs  $(non_trampoline), %rax
+       call    *%rax
+non_trampoline:
        call    smp_main
-       movl    %eax, %esp              # use our new stack, value returned from smp_main
-       # note the next two lines are using the direct mapping from smp_boot()
+       movq    %rax, %rsp              # use our new stack, value returned from smp_main
+       # note the next two lines are using the direct mapping from smp_boot().
+       # Remember, the stuff at 0x1000 is a *copy* of the code and data at
+       # KERN_LOAD_ADDR.
        movw    $0, smp_boot_lock - smp_entry + 0x1000  # release lock
        lock decw       smp_semaphore - smp_entry + 0x1000  # show we are done
        sti                     # so we can get the IPI
        hlt                     # wait for the IPI to run smp_pcu_init()
-       call    smp_idle                # idle loop, will have interrupts turned on
+       call    smp_idle                # idle loop, will have interrupts turned on
+       # smp_idle should never return
+spin:
+       jmp spin
 
        # Below here is just data, stored with the code text
        .p2align        2                                               # force 4 byte alignment
index 9b8ea0f..63fa4c4 100644 (file)
@@ -83,14 +83,6 @@ void kernel_init(multiboot_info_t *mboot_info)
        timer_init();
        train_timing();
        kb_buf_init(&cons_buf);
-       
-#ifdef CONFIG_X86_64
-monitor(0);
-printk("Halting/spinning...\n");
-while (1)
-       asm volatile("hlt");
-#endif
-
        arch_init();
        block_init();
        enable_irq();