x86_64: GS base work
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 28 Jun 2013 00:08:21 +0000 (17:08 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 28 Jun 2013 00:22:12 +0000 (17:22 -0700)
The kernel uses GS base to point to pcpu info, and will make sure this is set
up on entry.  It's also important to reset KERN_GS_BASE before we reenable
interrupts.  The assumption is that on entry from any trap/irq, that the
correct kernel GS base is in KERN_GS_BASE.

We don't actually need to swapgs in trap and irq code, but it'll make it easier
to save the user contexts directly into pcpui->cur_tf in the future.

Also note that we're not even going to bother saving and restoring fs and gs -
just the base registers.  This also goes for ds and es, meaning it is possible
for one application to see the ds of a previous program running on that core.
AFAIK, the hardware just ignores DS and ES's internal registers, so if a
program randomly sets DS, it shouldn't hurt the kernel or another process.

kern/arch/x86/cpuinfo.c
kern/arch/x86/entry64.S
kern/arch/x86/process64.c
kern/arch/x86/ros/trapframe64.h
kern/arch/x86/smp_boot.c
kern/arch/x86/trap.c
kern/arch/x86/trap64.c
kern/arch/x86/trapentry64.S
kern/arch/x86/x86.h

index e110bf9..0ef9a1e 100644 (file)
@@ -130,6 +130,13 @@ void print_cpuinfo(void)
        else
                printk("RDTSCP not supported: don't trust detailed measurements\n");
        printk("FS/GS MSRs %ssupported\n", edx & (1 << 29) ? "" : "not ");
        else
                printk("RDTSCP not supported: don't trust detailed measurements\n");
        printk("FS/GS MSRs %ssupported\n", edx & (1 << 29) ? "" : "not ");
+       #ifdef CONFIG_X86_64
+       if (!(edx & (1 << 29))) {
+               printk("Can't handle no FS/GS MSRs!\n");
+               while (1)
+                       asm volatile ("hlt");
+       }
+       #endif
        msr_val = read_msr(IA32_MISC_ENABLE);
        /* we want this to be not set for cpuid.6h to work. */
        if (msr_val & (1 << 22))
        msr_val = read_msr(IA32_MISC_ENABLE);
        /* we want this to be not set for cpuid.6h to work. */
        if (msr_val & (1 << 22))
index fb52b47..fe911f2 100644 (file)
@@ -126,6 +126,15 @@ long_mode:
        movl    $0x0, (%rax)
        movabs  $(hw_coreid_lookup), %rax
        movl    $0x0, (%rax)
        movl    $0x0, (%rax)
        movabs  $(hw_coreid_lookup), %rax
        movl    $0x0, (%rax)
+       # set up gs to point to our pcpu info (both GS base and KERN GS base)
+       movabs  $(per_cpu_info), %rdx
+       movq    %rdx, %rax
+       shrq    $32, %rdx
+       andl    $0xffffffff, %eax
+       movl    $MSR_GS_BASE, %ecx
+       wrmsr
+       movl    $MSR_KERN_GS_BASE, %ecx
+       wrmsr
        # Clear the frame pointer for proper backtraces
        movq    $0x0, %rbp
        movabs  $(bootstacktop), %rsp
        # Clear the frame pointer for proper backtraces
        movq    $0x0, %rbp
        movabs  $(bootstacktop), %rsp
index acbfeb7..145320e 100644 (file)
@@ -36,6 +36,8 @@ void proc_pop_ctx(struct user_context *ctx)
        /* In case they are enabled elsewhere.  We can't take an interrupt in these
         * routines, due to how they play with the kernel stack pointer. */
        disable_irq();
        /* In case they are enabled elsewhere.  We can't take an interrupt in these
         * routines, due to how they play with the kernel stack pointer. */
        disable_irq();
+       write_msr(MSR_GS_BASE, (uint64_t)tf->tf_gsbase);
+       write_msr(MSR_FS_BASE, (uint64_t)tf->tf_fsbase);
        /* If the process entered the kernel via sysenter, we need to leave via
         * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
         * sysenter_handler. */
        /* If the process entered the kernel via sysenter, we need to leave via
         * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
         * sysenter_handler. */
@@ -56,11 +58,9 @@ void proc_pop_ctx(struct user_context *ctx)
                              "popq %%r13;              "
                              "popq %%r14;              "
                              "popq %%r15;              "
                              "popq %%r13;              "
                              "popq %%r14;              "
                              "popq %%r15;              "
-                             "movw 0x4(%%rsp), %%gs;   "
-                             "movw 0x6(%%rsp), %%fs;   "
                              "addq $0x10, %%rsp;       "
                              "iretq                    "
                              "addq $0x10, %%rsp;       "
                              "iretq                    "
-                             : : "g" (tf) : "memory");
+                             : : "g" (&tf->tf_rax) : "memory");
                panic("iret failed");  /* mostly to placate the compiler */
        } else {
                /* Return path of sysexit.  See sysenter_handler's asm for details.
                panic("iret failed");  /* mostly to placate the compiler */
        } else {
                /* Return path of sysexit.  See sysenter_handler's asm for details.
@@ -90,7 +90,9 @@ void proc_pop_ctx(struct user_context *ctx)
 //                           "popl %%esp;              "
 //                           "sti;                     "
 //                           "sysexit                  "
 //                           "popl %%esp;              "
 //                           "sti;                     "
 //                           "sysexit                  "
-//                           : : "g" (tf) : "memory");
+//                           : : "g" (&tf->tf_rax) : "memory");
+               // keep in mind, we can take an interrupt in here (depending on what GS
+               // tricks there are)
                panic("sysexit failed");  /* mostly to placate your mom */
        }
 }
                panic("sysexit failed");  /* mostly to placate your mom */
        }
 }
index 125da6a..8513ecb 100644 (file)
@@ -6,6 +6,8 @@
 #endif
 
 struct hw_trapframe {
 #endif
 
 struct hw_trapframe {
+       uint64_t tf_gsbase;
+       uint64_t tf_fsbase;
        uint64_t tf_rax;
        uint64_t tf_rbx;
        uint64_t tf_rcx;
        uint64_t tf_rax;
        uint64_t tf_rbx;
        uint64_t tf_rcx;
@@ -22,8 +24,7 @@ struct hw_trapframe {
        uint64_t tf_r14;
        uint64_t tf_r15;
        uint32_t tf_trapno;
        uint64_t tf_r14;
        uint64_t tf_r15;
        uint32_t tf_trapno;
-       uint16_t tf_gs;
-       uint16_t tf_fs;
+       uint32_t tf_padding5;
        /* below here defined by x86 hardware (error code optional) */
        uint32_t tf_err;
        uint32_t tf_padding4;
        /* below here defined by x86 hardware (error code optional) */
        uint32_t tf_err;
        uint32_t tf_padding4;
index 9b5a34f..b9bba08 100644 (file)
@@ -280,6 +280,7 @@ uintptr_t smp_main(void)
 void __arch_pcpu_init(uint32_t coreid)
 {
        uintptr_t my_stack_bot;
 void __arch_pcpu_init(uint32_t coreid)
 {
        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) */
 
        /* Flushes any potentially old mappings from smp_boot() (note the page table
         * removal) */
@@ -294,14 +295,24 @@ void __arch_pcpu_init(uint32_t coreid)
 
        /* core 0 sets up via the global gdt symbol */
        if (!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);
        } 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));
+               pcpui->tss = (taskstate_t*)(*(uintptr_t*)my_stack_bot);
+               pcpui->gdt = (segdesc_t*)(*(uintptr_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
        /* need to init perfctr before potentiall using it in timer handler */
        perfmon_init();
 }
        /* need to init perfctr before potentiall using it in timer handler */
        perfmon_init();
 }
index 0d31fe4..583ac16 100644 (file)
@@ -126,10 +126,8 @@ void idt_init(void)
                SETGATE(idt[trap_tbl[i].trapnumber], 0, GD_KT, trap_tbl[i].trapaddr, 0);
 
        /* turn on trap-based syscall handling and other user-accessible ints
                SETGATE(idt[trap_tbl[i].trapnumber], 0, GD_KT, trap_tbl[i].trapaddr, 0);
 
        /* turn on trap-based syscall handling and other user-accessible ints
-        * DPL 3 means this can be triggered by the int instruction
-        * STS_TG32 sets the IDT type to a Interrupt Gate (interrupts disabled) */
+        * DPL 3 means this can be triggered by the int instruction */
        idt[T_SYSCALL].gd_dpl = SINIT(3);
        idt[T_SYSCALL].gd_dpl = SINIT(3);
-       idt[T_SYSCALL].gd_type = SINIT(STS_IG32);
        idt[T_BRKPT].gd_dpl = SINIT(3);
 
        /* Set up our kernel stack when changing rings */
        idt[T_BRKPT].gd_dpl = SINIT(3);
 
        /* Set up our kernel stack when changing rings */
index 9f08438..783b02a 100644 (file)
@@ -73,8 +73,9 @@ void print_trapframe(struct hw_trapframe *hw_tf)
        printk("  r15  0x%016lx\n",           hw_tf->tf_r15);
        printk("  trap 0x%08x %s\n",          hw_tf->tf_trapno,
                                              x86_trapname(hw_tf->tf_trapno));
        printk("  r15  0x%016lx\n",           hw_tf->tf_r15);
        printk("  trap 0x%08x %s\n",          hw_tf->tf_trapno,
                                              x86_trapname(hw_tf->tf_trapno));
-       printk("  gs   0x%04x\n",             hw_tf->tf_gs);
-       printk("  fs   0x%04x\n",             hw_tf->tf_fs);
+       /* FYI: these aren't physically adjacent to trap and err */
+       printk("  gsbs 0x%016lx\n",           hw_tf->tf_gsbase);
+       printk("  fsbs 0x%016lx\n",           hw_tf->tf_fsbase);
        printk("  err  0x--------%08x\n",     hw_tf->tf_err);
        printk("  rip  0x%016lx\n",           hw_tf->tf_rip);
        printk("  cs   0x------------%04x\n", hw_tf->tf_cs);
        printk("  err  0x--------%08x\n",     hw_tf->tf_err);
        printk("  rip  0x%016lx\n",           hw_tf->tf_rip);
        printk("  cs   0x------------%04x\n", hw_tf->tf_cs);
@@ -83,6 +84,10 @@ void print_trapframe(struct hw_trapframe *hw_tf)
        printk("  ss   0x------------%04x\n", hw_tf->tf_ss);
        spin_unlock_irqsave(&ptf_lock);
        pcpui->__lock_depth_disabled--;
        printk("  ss   0x------------%04x\n", hw_tf->tf_ss);
        spin_unlock_irqsave(&ptf_lock);
        pcpui->__lock_depth_disabled--;
+
+       /* Used in trapentry64.S */
+       static_assert(offsetof(struct hw_trapframe, tf_cs) - 
+                     offsetof(struct hw_trapframe, tf_rax) == 0x90);
 }
 
 void page_fault_handler(struct hw_trapframe *hw_tf)
 }
 
 void page_fault_handler(struct hw_trapframe *hw_tf)
index 8bfc157..3203f6e 100644 (file)
@@ -7,6 +7,7 @@
  */
 #include <arch/mmu.h>
 #include <arch/trap.h>
  */
 #include <arch/mmu.h>
 #include <arch/trap.h>
+#include <arch/x86.h>
 #include <ros/memlayout.h>
 
 ###################################################################
 #include <ros/memlayout.h>
 
 ###################################################################
@@ -146,14 +147,10 @@ TRAPHANDLER_NOEC(ISR_default, T_DEFAULT)
 .globl trap_tbl_end
 trap_tbl_end:
 
 .globl trap_tbl_end
 trap_tbl_end:
 
-/* Keep the exit paths of _alltraps, _allirqs, and sysenter_handler in sync
- * with the corrrsponding pop_tf's.  */
 .text
 _alltraps:
        cld
 .text
 _alltraps:
        cld
-       /* trapno was pushed, taking up 64 bits.  we're using the upper 32 bits */
-       movw %gs, 0x4(%rsp)
-       movw %fs, 0x6(%rsp)
+       swapgs                  # harmless if we were already in the kernel
        pushq %r15
        pushq %r14
        pushq %r13
        pushq %r15
        pushq %r14
        pushq %r13
@@ -169,9 +166,38 @@ _alltraps:
        pushq %rcx
        pushq %rbx
        pushq %rax
        pushq %rcx
        pushq %rbx
        pushq %rax
+       cmpw $GD_KT, 0x90(%rsp) # 0x90 - diff btw tf_cs and tf_rax
+       je trap_kernel_tf
+       # this is a user TF, so we need to save their fs/gsbase and load gs base for
+       # the kernel.
+       movl $MSR_FS_BASE, %ecx
+       rdmsr
+       shl $32, %rdx
+       orq %rax, %rdx
+       pushq %rdx
+       # because we swapped gs earlier, the TF in use is now in KERN_GS_BASE
+       movl $MSR_KERN_GS_BASE, %ecx
+       rdmsr
+       shl $32, %rdx
+       orq %rax, %rdx
+       pushq %rdx
+       # make sure the kernel's gs base is loaded into the KERN slot at all times
+       movl $MSR_GS_BASE, %ecx
+       rdmsr
+       movl $MSR_KERN_GS_BASE, %ecx
+       wrmsr
+       jmp trap_all_tf
+trap_kernel_tf:
+       # we don't muck with fs/gsbase, push placeholders
+       movq $0xdeadbeef, %rax
+       pushq %rax
+       pushq %rax
+trap_all_tf:
        movq $0, %rbp                   # so we can backtrace to this point
        movq %rsp, %rdi
        call trap
        movq $0, %rbp                   # so we can backtrace to this point
        movq %rsp, %rdi
        call trap
+       # the return paths are only used by the kernel
+       addq $0x10, %rsp                        # skip fs/gs base
        popq %rax
        popq %rbx
        popq %rcx
        popq %rax
        popq %rbx
        popq %rcx
@@ -187,16 +213,13 @@ _alltraps:
        popq %r13
        popq %r14
        popq %r15
        popq %r13
        popq %r14
        popq %r15
-       movw 0x4(%rsp), %gs
-       movw 0x6(%rsp), %fs
-       addq $0x10, %rsp                        # skip gs, fs, trapno, and err
+       addq $0x10, %rsp                        # skip trapno and err
        iretq
 
        iretq
 
+# might merge this with _alltraps
 _allirqs:
        cld
 _allirqs:
        cld
-       /* trapno was pushed, taking up 64 bits.  we're using the upper 32 bits */
-       movw %gs, 0x4(%rsp)
-       movw %fs, 0x6(%rsp)
+       swapgs                  # harmless if we were already in the kernel
        pushq %r15
        pushq %r14
        pushq %r13
        pushq %r15
        pushq %r14
        pushq %r13
@@ -212,9 +235,38 @@ _allirqs:
        pushq %rcx
        pushq %rbx
        pushq %rax
        pushq %rcx
        pushq %rbx
        pushq %rax
+       cmpw $GD_KT, 0x90(%rsp) # 0x90 - diff btw tf_cs and tf_rax
+       je irq_kernel_tf
+       # this is a user TF, so we need to save their fs/gsbase and load gs base for
+       # the kernel.
+       movl $MSR_FS_BASE, %ecx
+       rdmsr
+       shl $32, %rdx
+       orq %rax, %rdx
+       pushq %rdx
+       # because we swapped gs earlier, the TF in use is now in KERN_GS_BASE
+       movl $MSR_KERN_GS_BASE, %ecx
+       rdmsr
+       shl $32, %rdx
+       orq %rax, %rdx
+       pushq %rdx
+       # make sure the kernel's gs base is loaded into the KERN slot at all times
+       movl $MSR_GS_BASE, %ecx
+       rdmsr
+       movl $MSR_KERN_GS_BASE, %ecx
+       wrmsr
+       jmp irq_all_tf
+irq_kernel_tf:
+       # we don't muck with fs/gsbase, push placeholders
+       movq $0xdeadbeef, %rax
+       pushq %rax
+       pushq %rax
+irq_all_tf:
        movq $0, %rbp                   # so we can backtrace to this point
        movq %rsp, %rdi
        call irq_handler
        movq $0, %rbp                   # so we can backtrace to this point
        movq %rsp, %rdi
        call irq_handler
+       # the return paths are only used by the kernel
+       addq $0x10, %rsp                        # skip fs/gs base
        popq %rax
        popq %rbx
        popq %rcx
        popq %rax
        popq %rbx
        popq %rcx
@@ -230,9 +282,7 @@ _allirqs:
        popq %r13
        popq %r14
        popq %r15
        popq %r13
        popq %r14
        popq %r15
-       movw 0x4(%rsp), %gs
-       movw 0x6(%rsp), %fs
-       addq $0x10, %rsp                        # skip gs, fs, trapno, and err (which is 0)
+       addq $0x10, %rsp                        # skip trapno and err
        iretq
 
 .globl sysenter_handler;
        iretq
 
 .globl sysenter_handler;
index cdaa06e..8aba0b1 100644 (file)
 # define IA32_EFER_IA32E_ACT           (1 << 10)
 # define IA32_EFER_EXE_DIS_BIT         (1 << 11)
 
 # define IA32_EFER_IA32E_ACT           (1 << 10)
 # define IA32_EFER_EXE_DIS_BIT         (1 << 11)
 
+#define MSR_FS_BASE                                    0xc0000100
+#define MSR_GS_BASE                                    0xc0000101
+#define MSR_KERN_GS_BASE                       0xc0000102
+
 /* CPUID */
 #define CPUID_PSE_SUPPORT                      0x00000008
 
 /* CPUID */
 #define CPUID_PSE_SUPPORT                      0x00000008