]> xenbits.xensource.com Git - xen.git/commitdiff
x86: Support indirect thunks from assembly code
authorAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 14 Feb 2018 11:41:00 +0000 (12:41 +0100)
committerJan Beulich <jbeulich@suse.com>
Wed, 14 Feb 2018 11:41:00 +0000 (12:41 +0100)
Introduce INDIRECT_CALL and INDIRECT_JMP which either degrade to a normal
indirect branch, or dispatch to the __x86_indirect_thunk_* symbols.

Update all the manual indirect branches in to use the new thunks.  The
indirect branches in the early boot and kexec path are left intact as we can't
use the compiled-in thunks at those points.

This is part of XSA-254.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
master commit: 7c508612f7a5096b4819d4ef2ce566e01bd66c0c
master date: 2018-01-16 17:45:50 +0000

xen/Rules.mk
xen/arch/x86/Rules.mk
xen/arch/x86/boot/trampoline.S
xen/arch/x86/traps.c
xen/arch/x86/x86_64/entry.S
xen/arch/x86/x86_emulate/x86_emulate.c
xen/common/wait.c
xen/include/asm-x86/asm_defns.h
xen/include/asm-x86/indirect_thunk_asm.h [new file with mode: 0644]

index feb08d689ac91c49ec7a3ae81d08f9bd8353da72..3c79b27808c207654b9c3412ec97575ee050839c 100644 (file)
@@ -74,8 +74,8 @@ endif
 
 AFLAGS-y                += -D__ASSEMBLY__ -include $(BASEDIR)/include/xen/config.h
 
-# Clang's built-in assembler can't handle .code16/.code32/.code64 yet
-AFLAGS-$(clang)         += -no-integrated-as
+# Clang's built-in assembler can't handle embedded .include's
+CFLAGS-$(clang)         += -no-integrated-as
 
 ALL_OBJS := $(ALL_OBJS-y)
 
index 4f79a5b0820a4993f9a916e5b0378215c47cbdcd..faf08ea14b1f51961b5361c3dad09bf838f3ac04 100644 (file)
@@ -49,5 +49,11 @@ CFLAGS += -DCONFIG_INDIRECT_THUNK
 export CONFIG_INDIRECT_THUNK=y
 endif
 
+# Set up the assembler include path properly for older GCC toolchains.  Clang
+# objects to the agument being passed however.
+ifneq ($(clang),y)
+CFLAGS += -Wa,-I$(BASEDIR)/include
+endif
+
 CFLAGS-$(shadow-paging) += -DCONFIG_SHADOW_PAGING
 CFLAGS-$(bigmem)        += -DCONFIG_BIGMEM
index ccb40fb4d8aabf7a9c6e66a0513675ea5802591c..f46681c48a991b4cc210fc9a2ffa9c1ebbfed25d 100644 (file)
@@ -109,8 +109,28 @@ trampoline_protmode_entry:
         .code64
 start64:
         /* Jump to high mappings. */
-        movabs  $__high_start,%rax
-        jmpq    *%rax
+        movabs  $__high_start, %rdi
+
+#ifdef CONFIG_INDIRECT_THUNK
+        /*
+         * If booting virtualised, or hot-onlining a CPU, sibling threads can
+         * attempt Branch Target Injection against this jmp.
+         *
+         * We've got no usable stack so can't use a RETPOLINE thunk, and are
+         * further than disp32 from the high mappings so couldn't use
+         * JUMP_THUNK even if it was a non-RETPOLINE thunk.  Furthermore, an
+         * LFENCE isn't necessarily safe to use at this point.
+         *
+         * As this isn't a hotpath, use a fully serialising event to reduce
+         * the speculation window as much as possible.  %ebx needs preserving
+         * for __high_start.
+         */
+        mov     %ebx, %esi
+        cpuid
+        mov     %esi, %ebx
+#endif
+
+        jmpq    *%rdi
 
         .code32
 trampoline_boot_cpu_entry:
index 16c63727a2026a1e508692db4ef3f636730acd20..cd63a4df192cc43fa63cf0f3feca0f86db8deba9 100644 (file)
@@ -1974,6 +1974,8 @@ static inline uint64_t guest_misc_enable(uint64_t val)
     return val;
 }
 
+void __x86_indirect_thunk_rcx(void);
+
 /* Instruction fetch with error handling. */
 #define insn_fetch(type, base, eip, limit)                                  \
 ({  unsigned long _rc, _ptr = (base) + (eip);                               \
@@ -2022,6 +2024,8 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
     unsigned long code_base, code_limit;
     char *io_emul_stub = NULL;
     void (*io_emul)(struct cpu_user_regs *) __attribute__((__regparm__(1)));
+    struct stubs *this_stubs = &this_cpu(stubs);
+    unsigned long stub_va = this_stubs->addr + STUB_BUF_SIZE / 2;
     uint64_t val;
     bool_t vpmu_msr;
 
@@ -2210,31 +2214,44 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
      * context. This is needed for some systems which (ab)use IN/OUT
      * to communicate with BIOS code in system-management mode.
      */
-    io_emul_stub = map_domain_page(_mfn(this_cpu(stubs.mfn))) +
-                   (this_cpu(stubs.addr) & ~PAGE_MASK) +
-                   STUB_BUF_SIZE / 2;
+    io_emul_stub = map_domain_page(_mfn(this_stubs->mfn)) +
+                   (stub_va & ~PAGE_MASK);
     /* movq $host_to_guest_gpr_switch,%rcx */
     io_emul_stub[0] = 0x48;
     io_emul_stub[1] = 0xb9;
     *(void **)&io_emul_stub[2] = (void *)host_to_guest_gpr_switch;
+
+#ifdef CONFIG_INDIRECT_THUNK
+    /* callq __x86_indirect_thunk_rcx */
+    io_emul_stub[10] = 0xe8;
+    *(int32_t *)&io_emul_stub[11] =
+        (long)__x86_indirect_thunk_rcx - (stub_va + 11 + 4);
+#else
     /* callq *%rcx */
     io_emul_stub[10] = 0xff;
     io_emul_stub[11] = 0xd1;
+    /* TODO: untangle ideal_nops from init/livepatch Kconfig options. */
+    memcpy(&io_emul_stub[12], "\x0f\x1f\x00", 3); /* P6_NOP3 */
+#endif
+
     /* data16 or nop */
-    io_emul_stub[12] = (op_bytes != 2) ? 0x90 : 0x66;
+    io_emul_stub[15] = (op_bytes != 2) ? 0x90 : 0x66;
     /* <io-access opcode> */
-    io_emul_stub[13] = opcode;
+    io_emul_stub[16] = opcode;
     /* imm8 or nop */
-    io_emul_stub[14] = 0x90;
+    io_emul_stub[17] = 0x90;
     /* ret (jumps to guest_to_host_gpr_switch) */
-    io_emul_stub[15] = 0xc3;
-    BUILD_BUG_ON(STUB_BUF_SIZE / 2 < 16);
+    io_emul_stub[18] = 0xc3;
+    BUILD_BUG_ON(STUB_BUF_SIZE / 2 < 19);
 
     /* Handy function-typed pointer to the stub. */
-    io_emul = (void *)(this_cpu(stubs.addr) + STUB_BUF_SIZE / 2);
+    io_emul = (void *)stub_va;
 
     if ( ioemul_handle_quirk )
-        ioemul_handle_quirk(opcode, &io_emul_stub[12], regs);
+    {
+        BUILD_BUG_ON(STUB_BUF_SIZE / 2 < 15 + 10);
+        ioemul_handle_quirk(opcode, &io_emul_stub[15], regs);
+    }
 
     /* I/O Port and Interrupt Flag instructions. */
     switch ( opcode )
@@ -2243,7 +2260,7 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
         op_bytes = 1;
     case 0xe5: /* IN imm8,%eax */
         port = insn_fetch(u8, code_base, eip, code_limit);
-        io_emul_stub[14] = port; /* imm8 */
+        io_emul_stub[17] = port; /* imm8 */
     exec_in:
         if ( !guest_io_okay(port, op_bytes, v, regs) )
             goto fail;
@@ -2272,7 +2289,7 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
         op_bytes = 1;
     case 0xe7: /* OUT %eax,imm8 */
         port = insn_fetch(u8, code_base, eip, code_limit);
-        io_emul_stub[14] = port; /* imm8 */
+        io_emul_stub[17] = port; /* imm8 */
     exec_out:
         if ( !guest_io_okay(port, op_bytes, v, regs) )
             goto fail;
index b5188e49df813f3a162b968e4f9bb2c0a8c27838..008e22d4f07fb31154bc390b92e12f1d5bf169aa 100644 (file)
@@ -677,7 +677,8 @@ handle_exception_saved:
         movzbl UREGS_entry_vector(%rsp),%eax
         leaq  exception_table(%rip),%rdx
         PERFC_INCR(exceptions, %rax, %rbx)
-        callq *(%rdx,%rax,8)
+        mov   (%rdx, %rax, 8), %rdx
+        INDIRECT_CALL %rdx
         mov   %r15, STACK_CPUINFO_FIELD(xen_cr3)(%r14)
         testb $3,UREGS_cs(%rsp)
         jz    restore_all_xen
@@ -848,7 +849,8 @@ handle_ist_exception:
 1:      movq  %rsp,%rdi
         movzbl UREGS_entry_vector(%rsp),%eax
         leaq  exception_table(%rip),%rdx
-        callq *(%rdx,%rax,8)
+        mov   (%rdx, %rax, 8), %rdx
+        INDIRECT_CALL %rdx
         mov   %r15, STACK_CPUINFO_FIELD(xen_cr3)(%r14)
         cmpb  $TRAP_nmi,UREGS_entry_vector(%rsp)
         jne   ret_from_intr
index 63fb761fbaa71c475a5105b841a71745e9da5ed5..f395534bf502dd041ebdc60cd474f4ddf338b24e 100644 (file)
@@ -4263,8 +4263,8 @@ x86_emulate(
         if ( !rc )
         {
            copy_REX_VEX(buf, rex_prefix, vex);
-           asm volatile ( "call *%0" : : "r" (stub.func), "a" (mmvalp)
-                                     : "memory" );
+           asm volatile ( "INDIRECT_CALL %0" : : "r" (stub.func), "a" (mmvalp)
+                                             : "memory" );
         }
         put_fpu(&fic);
         put_stub(stub);
@@ -4518,8 +4518,8 @@ x86_emulate(
         if ( !rc )
         {
            copy_REX_VEX(buf, rex_prefix, vex);
-           asm volatile ( "call *%0" : : "r" (stub.func), "a" (mmvalp)
-                                     : "memory" );
+           asm volatile ( "INDIRECT_CALL %0" : : "r" (stub.func), "a" (mmvalp)
+                                             : "memory" );
         }
         put_fpu(&fic);
         put_stub(stub);
index 877ef1963838847a1848d5b8ecb4dec9d550b246..6bb65c60eb87426f3a3566d9f992a4d35e5bb7f8 100644 (file)
@@ -204,12 +204,14 @@ void check_wakeup_from_wait(void)
 
     /*
      * Hand-rolled longjmp().  Returns to the pointer on the top of
-     * wqv->stack, and lands on a `rep movs` instruction.
+     * wqv->stack, and lands on a `rep movs` instruction.  All other GPRs are
+     * restored from the stack, so are available for use here.
      */
     asm volatile (
-        "mov %1,%%"__OP"sp; jmp *(%0)"
+        "mov %1,%%"__OP"sp; INDIRECT_JMP %[ip]"
         : : "S" (wqv->stack), "D" (wqv->esp),
-        "c" ((char *)get_cpu_info() - (char *)wqv->esp)
+          "c" ((char *)get_cpu_info() - (char *)wqv->esp),
+          [ip] "r" (*(unsigned long *)wqv->stack)
         : "memory" );
     unreachable();
 }
index 0bb058977e61d844f08bb6ccee72d0e190d90823..1d6920ca4d5ce6e51b2904fd188b6f62aac04821 100644 (file)
 #include <asm/cpufeature.h>
 #include <asm/alternative.h>
 
+#ifdef __ASSEMBLY__
+# include <asm/indirect_thunk_asm.h>
+#else
+# ifdef CONFIG_INDIRECT_THUNK
+asm ( "\t.equ CONFIG_INDIRECT_THUNK, 1" );
+# else
+asm ( "\t.equ CONFIG_INDIRECT_THUNK, 0" );
+# endif
+asm ( "\t.include \"asm/indirect_thunk_asm.h\"" );
+#endif
+
 #ifndef __ASSEMBLY__
 void ret_from_intr(void);
 #endif
diff --git a/xen/include/asm-x86/indirect_thunk_asm.h b/xen/include/asm-x86/indirect_thunk_asm.h
new file mode 100644 (file)
index 0000000..96bcc25
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Warning!  This file is included at an assembler level for .c files, causing
+ * usual #ifdef'ary to turn into comments.
+ */
+
+.macro INDIRECT_BRANCH insn:req arg:req
+/*
+ * Create an indirect branch.  insn is one of call/jmp, arg is a single
+ * register.
+ *
+ * With no compiler support, this degrades into a plain indirect call/jmp.
+ * With compiler support, dispatch to the correct __x86_indirect_thunk_*
+ */
+    .if CONFIG_INDIRECT_THUNK == 1
+
+        $done = 0
+        .irp reg, ax, cx, dx, bx, bp, si, di, 8, 9, 10, 11, 12, 13, 14, 15
+        .ifeqs "\arg", "%r\reg"
+            \insn __x86_indirect_thunk_r\reg
+            $done = 1
+           .exitm
+        .endif
+        .endr
+
+        .if $done != 1
+            .error "Bad register arg \arg"
+        .endif
+
+    .else
+        \insn *\arg
+    .endif
+.endm
+
+/* Convenience wrappers. */
+.macro INDIRECT_CALL arg:req
+    INDIRECT_BRANCH call \arg
+.endm
+
+.macro INDIRECT_JMP arg:req
+    INDIRECT_BRANCH jmp \arg
+.endm