x86: Use partial contexts for all entries [2/2]
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 2 Dec 2015 18:39:28 +0000 (13:39 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 8 Dec 2015 20:59:08 +0000 (15:59 -0500)
Syscalls, traps, and IRQs all use partial contexts when entering from
userspace.  Note that the traps and IRQs that occur while a kernel context
is running does not mess with swapgs or partial contexts.  Partial contexts
(and pcpui current_ctx in general) has nothing to do with the kernel's
context / HW TF.  They are for user contexts.

This change takes about 260ns off of a sys_null() call, and we're down to
about 220ns (on my devbox).  Quite an improvement.  The expensive bit was
messing with the TLS MSRs, since those operations are unfortunately slow.
This isn't true on more recent x86 machines, which have support for fast
instructions to change the GS and FS base.  However, many machines,
(notable Qemu) do not support those instructions, and we'd need the kernel
and userland to decide which method of changing GS/FS to use, either during
compilation or at runtime.

In the long run, with support for quickly changing the TLS, the hardware
and software partial contexts savings won't be as important.  But partial
contexts are likely to be important for the upcoming virtual machine
context type.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/trap64.c
kern/arch/x86/trapentry64.S

index 20f89dd..5fffe6b 100644 (file)
@@ -71,6 +71,8 @@ void print_trapframe(struct hw_trapframe *hw_tf)
        /* Used in trapentry64.S */
        static_assert(offsetof(struct hw_trapframe, tf_cs) - 
                      offsetof(struct hw_trapframe, tf_rax) == 0x90);
+       static_assert(offsetof(struct hw_trapframe, tf_padding0) -
+                     offsetof(struct hw_trapframe, tf_rax) == 0xac);
        /* Used in trap64.h */
        static_assert(offsetof(struct per_cpu_info, stacktop) == 0);
 }
index 3572010..5e4f19c 100644 (file)
@@ -350,7 +350,6 @@ trap_tbl_end:
 .text
 _alltraps:
        cld
-       swapgs                  # harmless if we were already in the kernel
        pushq %r15
        pushq %r14
        pushq %r13
@@ -367,32 +366,14 @@ _alltraps:
        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 user GS 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
+       je trap_all_tf
+       # this is a user TF.  we need to swapgs to get the kernel's gs and mark the
+       # context as partial
+       swapgs                                  # user's GS is now in MSR_KERNEL_GS_BASE
+       movl $0x1, 0xac(%rsp)   # 0xac - diff btw tf_padding0 and tf_rax
 trap_all_tf:
+       pushq $0                                # fsbase space
+       pushq $0                                # gsbase space
        movq $0, %rbp                   # so we can backtrace to this point
        movq %rsp, %rdi
        call trap
@@ -419,7 +400,6 @@ trap_all_tf:
 # might merge this with _alltraps
 _allirqs:
        cld
-       swapgs                  # harmless if we were already in the kernel
        pushq %r15
        pushq %r14
        pushq %r13
@@ -436,32 +416,14 @@ _allirqs:
        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 user GS 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
+       je irq_all_tf
+       # this is a user TF.  we need to swapgs to get the kernel's gs and mark the
+       # context as partial
+       swapgs                                  # user's GS is now in MSR_KERNEL_GS_BASE
+       movl $0x1, 0xac(%rsp)   # 0xac - diff btw tf_padding0 and tf_rax
 irq_all_tf:
+       pushq $0                                # fsbase space
+       pushq $0                                # gsbase space
        movq $0, %rbp                   # so we can backtrace to this point
        movq %rsp, %rdi
        call handle_irq
@@ -515,11 +477,12 @@ fastcall_pop:
 normal_syscall:
 #endif
        # cld is handled by the SFMASK
-       swapgs
+       swapgs                                  # user's GS is now in MSR_KERNEL_GS_BASE
        movq %gs:0, %rsp
        # Saving the FPU callee-saved state for now.  Might be able to have the
        # preempt handler deal with it.
-       pushq $0                        # space for mxcsr and fpucw
+       pushq $0                                # space for mxcsr, fpucw, and padding0
+       movw $0x1, 0x6(%rsp)    # tf_padding0 = 1, partial context
        fnstcw 0x4(%rsp)
        stmxcsr (%rsp)
        pushq %rdx                      # rsp, saved by userspace
@@ -530,23 +493,8 @@ normal_syscall:
        pushq %r12
        pushq %rbp
        pushq %rbx
-       # save fs and gs base
-       movl $MSR_FS_BASE, %ecx
-       rdmsr
-       shl $32, %rdx
-       orq %rax, %rdx
-       pushq %rdx
-       # because we swapped gs earlier, the user GS 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
+       pushq $0                        # fsbase space
+       pushq $0                        # gsbase space
        movq $0, %rbp                   # so we can backtrace to this point
        movq %rsp, %rdx
        # arg0, rdi: struct sysc*.  arg1, rsi: count.  arg2, rdx: sw_tf