x86_64: TLS FS base changing 'fast call' (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 2 Jul 2013 18:33:01 +0000 (11:33 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 11 Jul 2013 02:29:46 +0000 (19:29 -0700)
For systems that don't support reading/writing the FS base from userspace, we
have a fast path in the kernel to change the FS base.  We don't save or restore
anything, nor do we enter the main syscall path.  Hopefully that will be
reasonably fast.

This does impact the normal syscall path, albeit slightly.  If you know you
have hardware (Ivy Bridge or later) that supports WRFSBASE, then you can
disable this option.  Be sure your userspace knows what to do.

Reinstall your kernel headers.

kern/arch/x86/Kconfig
kern/arch/x86/cpuinfo.c
kern/arch/x86/ros/mmu64.h
kern/arch/x86/ros/syscall64.h
kern/arch/x86/trapentry64.S

index 9ad1e21..bc112c4 100644 (file)
@@ -7,7 +7,7 @@ config X86_32
 config X86_64
        def_bool 64BIT
 
-menu "x86 Hacks"
+menu "x86 Options"
 
 config PCI_VERBOSE
        bool "Verbose PCI Output"
@@ -15,6 +15,19 @@ config PCI_VERBOSE
        help
                Will print out extra information related to PCI.
 
+config NOFASTCALL_FSBASE
+       depends on X86_64
+       bool "Disable fastcall to set FS base"
+       default n
+       help
+               Disable the fast path syscall to set FS base.  If your hardware allows
+               setting FS base from userspace, you can say y to disable the fastcall
+               for a slight improvement for all syscalls.  If unsure, say n.
+
+endmenu
+
+menu "x86 Hacks"
+
 config NOMTRRS
        bool "Disable MTRRs"
        default n
index 0ef9a1e..d3b7884 100644 (file)
@@ -120,10 +120,19 @@ void print_cpuinfo(void)
        else
                printk("Invariant TSC not present\n");
        cpuid(0x07, 0x0, &eax, &ebx, &ecx, &edx);
-       if (ebx & 0x00000001)
+       if (ebx & 0x00000001) {
                printk("FS/GS Base RD/W supported\n");
-       else
+               /* Untested, since we don't have a machine that supports this.  Email us
+                * if this fails. */
+               printk("Attempting to enable WRFSBASE...\n");
+               lcr4(rcr4() | (1 << 16));
+       } else {
                printk("FS/GS Base RD/W not supported\n");
+               #ifdef CONFIG_NOFASTCALL_FSBASE
+               printk("\nGIANT WARNING: Can't write FS Base from userspace, "
+                      "and no FASTCALL support!\n\n");
+               #endif
+       }
        cpuid(0x80000001, 0x0, &eax, &ebx, &ecx, &edx);
        if (edx & (1 << 27))
                printk("RDTSCP supported\n");
index b905b6d..6164185 100644 (file)
@@ -508,4 +508,7 @@ typedef struct Pseudodesc {
 /* TODO: Probably won't use this */
 #define LDT_SIZE       (8192 * sizeof(segdesc_t))
 
+/* TLS 'syscall', coupled to trapentry64.S.  Needed a non-canon 'addr' */
+#define FASTCALL_SETFSBASE 0xf0f0000000000001
+
 #endif /* ROS_INC_ARCH_MMU64_H */
index 421321b..9353f58 100644 (file)
@@ -12,6 +12,7 @@
 #include <sys/types.h>
 #include <stdint.h>
 #include <ros/common.h>
+#include <ros/arch/mmu.h>
 #include <assert.h>
 
 static inline intreg_t __syscall_sysenter(uintreg_t a0, uintreg_t a1)
@@ -47,6 +48,15 @@ static inline intreg_t __syscall_trap(uintreg_t a0, uintreg_t a1)
        return ret;
 }
 
+/* The kernel has a fast path for setting the fs base, used for TLS changes on
+ * machines that can't do it from user space.  The magic value for rdi (D) is a
+ * non-canonical address, which should never be a legitamate syscall. */
+static inline void __fastcall_setfsbase(uintptr_t fsbase)
+{
+       asm volatile ("syscall" : : "D"(FASTCALL_SETFSBASE), "S"(fsbase)
+                               : "rax", "r11", "rcx", "rdx", "memory");
+}
+
 #endif
 
 #endif /* ROS_INC_ARCH_SYSCALL64_H */
index 7ae67b9..880d0d7 100644 (file)
@@ -289,14 +289,31 @@ irq_all_tf:
 .type sysenter_handler, @function;
 
 sysenter_handler:
-       # Rough plan to do a quick TLS / FS base change, never changing stacks
-#      compare rdi with a non-canon magic number (never a sysc)
-#      mov rcx to rdi (save it)
-#      cx, dx, and ax are available, can do an wrmsr on MSR_FS_BASE
-#      rsi has the new FS base
-#      mov rdi back to rcx
-#      sysret
-
+#ifndef CONFIG_NOFASTCALL_FSBASE
+       # Do a quick TLS / FS base change, never changing stacks.
+       # When rdi has the magic number, rsi has the new base
+       movabs $FASTCALL_SETFSBASE, %rax
+       cmp %rax, %rdi
+       jne normal_syscall      # could profile this and handle the jump differently
+       # need to check rsi, make sure it is canonical (will enfore below ULIM).
+       # need to either do this check, or handle the kernel GP fault on wrmsr.
+       movq %rsi, %rdi
+       shrq $47, %rdi
+       cmp $0, %rdi
+       jne fastcall_pop
+       # need to use cx, dx, and ax for the wrmsr.  dx and ax are free.
+       movq %rcx, %rdi         # save rcx, the retaddr
+       movq %rsi, %rdx
+       movq %rsi, %rax
+       shrq $32, %rdx
+       andl $0xffffffff, %eax
+       movl $MSR_FS_BASE, %ecx
+       wrmsr
+       movq %rdi, %rcx         # restore retaddr
+fastcall_pop:
+       rex.w sysret
+normal_syscall:
+#endif
        # cld is handled by the SFMASK
        swapgs
        movq %gs:0, %rsp