x86_64 boot up
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 14 Jun 2013 08:35:10 +0000 (01:35 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Sat, 22 Jun 2013 17:29:30 +0000 (10:29 -0700)
Gets into long mode, maps 512GB of KERNBASE in 1 GB pages.

kern/arch/x86/cpuinfo.c
kern/arch/x86/entry32.S
kern/arch/x86/entry64.S
kern/arch/x86/ros/mmu64.h
kern/arch/x86/x86.h

index 013d844..fe42d88 100644 (file)
@@ -27,6 +27,10 @@ void print_cpuinfo(void)
        char vendor_id[13];
        extern char (SNT RO _start)[];
 
+       if (sizeof(long) == 8)
+               printk("64 bit Kernel Booting...\n");
+       else
+               printk("32 bit Kernel Booting...\n");
        asm volatile ("cpuid;"
                  "movl    %%ebx, (%2);"
                  "movl    %%edx, 4(%2);"
index 7852761..1e6544e 100644 (file)
@@ -50,8 +50,8 @@ newcs:
        movl    $boot_pdt, %edx
        # identity map the first jumbo PTE from 0x0 -> 0x0
        movl    $(PTE_P | PTE_W | PTE_PS), (%edx)
-       # map KERNBASE -> 0 for 200 MB
-       movl    $50, %ecx
+       # map KERNBASE -> 0 for 1GB (1/4 of the 1024 entries)
+       movl    $256, %ecx
        # init loop, eax at paddr 0, and edx is advanced by KERNBASE mapping slots
        # (with 4 bytes per PTE).
        addl    $((KERNBASE >> PTSHIFT) << 2), %edx
index 41cf0cb..031a0ef 100644 (file)
@@ -1,17 +1,15 @@
-/* Copyright (c) 2009-13 The Regents of the University of California
+/* Copyright (c) 2013 The Regents of the University of California
  * Barret Rhoden <brho@cs.berkeley.edu>
  * See LICENSE for details. */
 
 #include <arch/mmu.h>
 #include <arch/trap.h>
+#include <arch/x86.h>
 #include <ros/memlayout.h>
 
 # Shift Right Logical 
 #define SRL(val, shamt)                (((val) >> (shamt)) & ~(-1 << (32 - (shamt))))
 
-.set CODE_SEL,0x8              # index of code seg within mygdt
-.set DATA_SEL,0x10             # index of data seg within mygdt
-
 #define MULTIBOOT_PAGE_ALIGN  (1<<0)
 #define MULTIBOOT_MEMORY_INFO (1<<1)
 #define MULTIBOOT_HEADER_MAGIC (0x1BADB002)
@@ -32,70 +30,104 @@ multiboot_header:
 .long MULTIBOOT_HEADER_FLAGS
 .long CHECKSUM
 
+/* Helper: creates count mappings in the PML3 for 1GB jumbo pages for the given
+ * vaddr to paddr range in physical memory.  Then it puts that PML3's addr in
+ * the PML4's appropriate slot.  A few notes:
+ *     - PML3 is responsible for the 9 bits from 30-38, hence the >> 30 and mask
+ *     - PML4 is responsible for the 9 bits from 47-39, hence the >> 39 and mask
+ *     - We use the jumbo PTE_PS flag only on PML3 - can't do it for PML4.
+ *     - PTEs are 8 bytes each, hence the scale = 8 in the indirect addressing
+ *     - The top half of all of PML4's PTEs are set to 0.  This includes the top 20
+ *     bits of the physical address of the page tables - which are 0 in our case.
+ *     - The paddr for the PML3 PTEs is split across two 32-byte halves of the PTE.
+ *     We drop off the lower 30 bits, since we're dealing with 1GB pages.  The 2
+ *     LSBs go at the top of the first half of the PTE, and the remaining 30 are
+ *     the lower 30 of the top half.
+ *     - In general, I use eax as the offset in a PML (the entry is determined by
+ *     the vaddr), edx for the entry I'm writing, and ecx for the value I'm
+ *     writing.  For PML3's mappings, esi tracks which paddr we are on.  Finally,
+ *     I'm using edi for loop control. */
+#define MAP_GB_PAGES(pml3, vaddr, paddr, count)                                \
+       movl    $(pml3), %edx;                                                     \
+       movl    $(((vaddr) >> 30) & 0x1ff), %eax;                                  \
+       movl    $((paddr) >> 30), %esi;                                            \
+       movl    $(count), %edi;                                                    \
+1:;                                                                            \
+       movl    %esi, %ecx;                                                        \
+       shll    $30, %ecx;                       /* lower part of PTE ADDR */      \
+       orl             $(PTE_P | PTE_W | PTE_PS), %ecx;                                   \
+       movl    %ecx, (%edx, %eax, 8);                                             \
+       movl    %esi, %ecx;                                                        \
+       shrl    $2, %ecx;                        /* upper part of PTE ADDR */      \
+       movl    %ecx, 4(%edx, %eax, 8);                                            \
+       /* prep for next loop */;                                                  \
+       incl    %eax;                                                              \
+       incl    %esi;                                                              \
+       /* could test eax against count, if we're short on regs */;                \
+       decl    %edi;                                                              \
+       jnz             1b;                                                                \
+       /* now insert the PML3 into pml4 */;                                       \
+       movl    $(((vaddr) >> 39) & 0x1ff), %eax;                                  \
+       movl    $boot_pml4, %edx;                                                  \
+       movl    $(pml3), %ecx;                                                     \
+       orl             $(PTE_P | PTE_W), %ecx;                                            \
+       movl    %ecx, (%edx, %eax, 8);                                             \
+       movl    $0x0, 4(%edx, %eax, 8)
+
 .globl         _start
 _start:
        movw    $0x1234,0x472                   # warm boot
-       # Reload all segment registers (including CS!) with flag segment selectors
-       # from our boot GDT.
-       lgdt    mygdtdesc
-       movl    $DATA_SEL, %eax
-       movw    %ax,%ds
-       movw    %ax,%es
-       movw    %ax,%ss
-       ljmp    $CODE_SEL,$newcs                # reload CS by jumping
-newcs:
-       # build page table.  need a mapping for current code at 0x00100000 and a
-       # basic kernbase mapping.  we're using the 32 bit second PT (aka, pg_dir),
-       # which covers 4MB per entry
-       movl    $boot_pdt, %edx
-       # identity map the first jumbo PTE from 0x0 -> 0x0
-       movl    $(PTE_P | PTE_W | PTE_PS), (%edx)
-       # map KERNBASE -> 0 for 200 MB
-       movl    $50, %ecx
-       # init loop, eax at paddr 0, and edx is advanced by KERNBASE mapping slots
-       # (with 4 bytes per PTE).
-       # XXX addl      $((KERNBASE >> PTSHIFT) << 2), %edx
-       movl    $(PTE_P | PTE_W | PTE_PS), %eax
-loop:
-       movl    %eax, (%edx)
-       addl    $PTSIZE, %eax
-       addl    $4, %edx
-       decl    %ecx
-       jnz             loop
-
-# hack to compile / link
-spin2:
-       jmp spin2
-
-       # load cr3 and turn on paging.  note we assume PSE support.  if we didn't
-       # have it, then our jumbo page mappings are going to fail.
-       movl    $boot_pdt, %eax
+       # build page table.  need mappings for
+       #       - current code/data at 0x00100000 -> 0x00100000
+       #       - kernel load location: 0xffffffffc0000000 -> 0x0000000000000000
+       #       - kernbase: 0xffff80000000 -> 0x0000000000000000
+       # we'll need one table for the PML4, and three PML3 (PDPE)'s.  1GB will
+       # suffice for lo and hi (til we do the VPT and LAPIC mappings).  For
+       # kernbase, we'll do all 512 PML3 entries (covers 512GB)
+       MAP_GB_PAGES(boot_pml3_lo,       0x0000000000000000, 0x0, 1)
+       MAP_GB_PAGES(boot_pml3_hi,       0xffffffffc0000000, 0x0, 1)
+       MAP_GB_PAGES(boot_pml3_kernbase, 0xffff800000000000, 0x0, 512)
+       # load cr3 - note that in long mode, cr3 is 64 bits wide.  our boot pml4 is
+       # in lower memory, so it'll be fine if the HW 0 extends.
+       movl    $boot_pml4, %eax
        movl    %eax, %cr3
+       # 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_PSE | CR4_PGE), %eax
+       orl             $(CR4_PSE | CR4_PGE | CR4_PAE), %eax
        movl    %eax, %cr4
+       # 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_AM | CR0_WP | CR0_NE | CR0_MP), %eax  
-       andl    $(~(CR0_TS | CR0_EM | CR0_CD | CR0_NW)), %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
-       # 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.  this is the only arch-dependent code called before then.
-.code64 # temp hack for linkage.  also, this symbol is too far at link time
+       # load the 64bit GDT and jump to long mode
+       lgdt    gdt64desc
+       ljmp    $0x08, $long_mode
+.code64
+long_mode:
+       # 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. 
        movabs  $(os_coreid_lookup), %rax
        movl    $0x0, (%rax)
        movabs  $(hw_coreid_lookup), %rax
        movl    $0x0, (%rax)
-       # Clear the frame pointer register (EBP)
-       # so that once we get into debugging C code,
-       # stack backtraces will be terminated properly.
-       movl    $0x0,%ebp
-       movabs  $(bootstacktop),%rsp
-       # Save multiboot info
-       push    %rbx
+       # Clear the frame pointer for proper backtraces
+       movq    $0x0, %rbp
+       movabs  $(bootstacktop), %rsp
        movabs  $(num_cpus), %rax
        movl    $0x1, (%rax)
-
+       # Pass multiboot info to kernel_init (%rdi == arg1)
+       movq    %rbx, %rdi
        movabs  $(kernel_init), %rax
        call    *%rax
        # Should never get here, but in case we do, just spin.
@@ -103,25 +135,26 @@ spin:     jmp     spin
 
 .section .bootdata, "aw"
        .p2align        2               # force 4 byte alignment
-mygdt:
-       SEG_NULL                        # null seg
-       SEG(STA_X|STA_R, 0, 0xffffffff) # code seg
-       SEG(STA_W, 0, 0xffffffff)       # data seg
-mygdtdesc:
-       .word   0x17            # sizeof(mygdt) - 1
-       .long   mygdt           # address mygdt
-# boot page directory.  going to use jumbo page entries
+gdt64:
+       SEG_NULL
+       SEG_CODE_64(0)
+gdt64desc:
+       .word   (gdt64desc - gdt64 - 1)         # sizeof(gdt64) - 1
+       .long   gdt64           # HW 0-extends this to 64 bit when loading (i think)
+# boot page tables
        .align PGSIZE
-boot_pdt:
+boot_pml4:
+       .space  PGSIZE
+boot_pml3_lo:
+       .space  PGSIZE
+boot_pml3_hi:
+       .space  PGSIZE
+boot_pml3_kernbase:
        .space  PGSIZE
-
 
 # From here down is linked for KERNBASE
-
-###################################################################    
-# See <inc/memlayout.h> for a complete description of these two symbols.
-###################################################################
 .data
+       # TODO: do we need these in asm?
        .globl  vpt
        .quad   vpt
        .set    vpt, VPT
@@ -129,13 +162,9 @@ boot_pdt:
        .quad   vpd
        .set    vpd, (VPT + SRL(VPT, 10))
 
-###################################################################
-# boot stack
-###################################################################
        .p2align        PGSHIFT         # force page alignment
        .globl          bootstack
 bootstack:
        .space          KSTKSIZE
        .globl          bootstacktop   
 bootstacktop:
-
index fd2dbe8..1a98a29 100644 (file)
@@ -230,16 +230,36 @@ typedef unsigned long pde_t;
 
 #ifdef __ASSEMBLER__
 
-/*
- * Macros to build GDT entries in assembly.
- */
+/* Macros to build GDT entries in assembly. */
 #define SEG_NULL                                               \
        .word 0, 0;                                             \
        .byte 0, 0, 0, 0
-#define SEG(type,base,lim)                                     \
-       .word (((lim) >> 12) & 0xffff), ((base) & 0xffff);      \
-       .byte (((base) >> 16) & 0xff), (0x90 | (type)),         \
-               (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
+
+/* 64 bit code segment.  This is for long mode, no compatibility.  If we want
+ * to support 32 bit apps later, we'll want to adjust this. */
+#define SEG_CODE_64(dpl)                                                    \
+       .word 0, 0;                                                             \
+       .byte 0;                                                                \
+       .byte (((1/*p*/) << 7) | ((dpl) << 5) | 0x18 | ((0/*c*/) << 2));        \
+       .byte (((0/*d*/) << 6) | ((1/*l*/) << 5));                              \
+       .byte 0;
+
+/* 64 bit data segment.  These are pretty much completely ignored (except if we
+ * use them for fs/gs, or compatibility mode */
+#define SEG_DATA_64                                                         \
+       .word 0, 0;                                                             \
+       .byte 0;                                                                \
+       .byte 0x90;                                                             \
+       .word 0;
+
+/* Default segment (32 bit style).  Would work for fs/gs, if needed */
+#define SEG(type, base, lim)                                                \
+       .word (((lim) >> 12) & 0xffff);                                         \
+       .word ((base) & 0xffff);                                                \
+       .byte (((base) >> 16) & 0xff);                                          \
+       .byte (0x90 | (type));                                                  \
+       .byte (0xC0 | (((lim) >> 28) & 0xf));                                   \
+       .byte (((base) >> 24) & 0xff)
 
 #else  // not __ASSEMBLER__
 
index 1537f99..cdaa06e 100644 (file)
 #define MSR_APIC_ENABLE                                0x00000800
 #define MSR_APIC_BASE_ADDRESS          0x0000000FFFFFF000
 
+#define IA32_EFER_MSR                          0xc0000080
+# define IA32_EFER_SYSCALL                     (1 << 0)
+# define IA32_EFER_IA32E_EN                    (1 << 8)
+# define IA32_EFER_IA32E_ACT           (1 << 10)
+# define IA32_EFER_EXE_DIS_BIT         (1 << 11)
+
 /* CPUID */
 #define CPUID_PSE_SUPPORT                      0x00000008
 
@@ -58,6 +64,7 @@
 
 #endif /* 64bit / 32bit */
 
+#ifndef __ASSEMBLER__
 static inline uint8_t inb(int port) __attribute__((always_inline));
 static inline void insb(int port, void *addr, int cnt)
               __attribute__((always_inline));
@@ -340,5 +347,6 @@ static inline void __cpu_relax(void)
 #ifndef UNUSED_ARG
 #define UNUSED_ARG(x) (void)x
 #endif /* This prevents compiler warnings for UNUSED_ARG */ 
+#endif /* !__ASSEMBLER__ */
 
 #endif /* !ROS_INC_X86_H */