File 5a5e3a4e-2-x86-support-indirect-thunks-from-asm.patch of Package xen.11298
# Commit 7c508612f7a5096b4819d4ef2ce566e01bd66c0c
# Date 2018-01-16 17:45:50 +0000
# Author Andrew Cooper <andrew.cooper3@citrix.com>
# Committer Andrew Cooper <andrew.cooper3@citrix.com>
x86: Support indirect thunks from assembly code
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>
--- a/xen/Rules.mk
+++ b/xen/Rules.mk
@@ -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)
--- a/xen/arch/x86/Rules.mk
+++ b/xen/arch/x86/Rules.mk
@@ -55,3 +55,9 @@ CFLAGS += -mindirect-branch=thunk-extern
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
--- a/xen/arch/x86/boot/trampoline.S
+++ b/xen/arch/x86/boot/trampoline.S
@@ -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:
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -1956,7 +1956,7 @@ static int emulate_privileged_op(struct
: (*(u16 *)®s->reg = (val)))
unsigned long code_base, code_limit;
char io_emul_stub[32];
- void (*io_emul)(struct cpu_user_regs *) __attribute__((__regparm__(1)));
+ void (*io_emul)(struct cpu_user_regs *, typeof(host_to_guest_gpr_switch) *);
uint64_t val, msr_content;
if ( !read_descriptor(regs->cs, v, regs,
@@ -2144,27 +2144,41 @@ static int emulate_privileged_op(struct
* GPR context. This is needed for some systems which (ab)use IN/OUT
* to communicate with BIOS code in system-management mode.
*/
- /* movq $host_to_guest_gpr_switch,%rcx */
+
+ /* lea 1f(%rip),%rcx */
io_emul_stub[0] = 0x48;
- io_emul_stub[1] = 0xb9;
- *(void **)&io_emul_stub[2] = (void *)host_to_guest_gpr_switch;
- /* callq *%rcx */
- io_emul_stub[10] = 0xff;
- io_emul_stub[11] = 0xd1;
+ io_emul_stub[1] = 0x8d;
+ io_emul_stub[2] = 0x0d;
+ io_emul_stub[3] = 0x06;
+ io_emul_stub[4] = 0x00;
+ io_emul_stub[5] = 0x00;
+ io_emul_stub[6] = 0x00;
+ /* mov %cs,%eax */
+ io_emul_stub[7] = 0x8c;
+ io_emul_stub[8] = 0xc8;
+ /* push %rax */
+ io_emul_stub[9] = 0x50;
+ /* push %rsi */
+ io_emul_stub[10] = 0x56;
+ /* lretq */
+ io_emul_stub[11] = 0x48;
+ io_emul_stub[12] = 0xcb;
+ /* 1: */
+
/* data16 or nop */
- io_emul_stub[12] = (op_bytes != 2) ? 0x90 : 0x66;
+ io_emul_stub[13] = (op_bytes != 2) ? 0x90 : 0x66;
/* <io-access opcode> */
- io_emul_stub[13] = opcode;
+ io_emul_stub[14] = opcode;
/* imm8 or nop */
- io_emul_stub[14] = 0x90;
+ io_emul_stub[15] = 0x90;
/* ret (jumps to guest_to_host_gpr_switch) */
- io_emul_stub[15] = 0xc3;
+ io_emul_stub[16] = 0xc3;
/* Handy function-typed pointer to the stub. */
io_emul = (void *)io_emul_stub;
if ( ioemul_handle_quirk )
- ioemul_handle_quirk(opcode, &io_emul_stub[12], regs);
+ ioemul_handle_quirk(opcode, &io_emul_stub[13], regs);
/* I/O Port and Interrupt Flag instructions. */
switch ( opcode )
@@ -2173,13 +2187,13 @@ static int emulate_privileged_op(struct
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[15] = port; /* imm8 */
exec_in:
if ( !guest_io_okay(port, op_bytes, v, regs) )
goto fail;
if ( admin_io_okay(port, op_bytes, v, regs) )
{
- io_emul(regs);
+ io_emul(regs, host_to_guest_gpr_switch);
}
else
{
@@ -2202,13 +2216,13 @@ static int emulate_privileged_op(struct
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[15] = port; /* imm8 */
exec_out:
if ( !guest_io_okay(port, op_bytes, v, regs) )
goto fail;
if ( admin_io_okay(port, op_bytes, v, regs) )
{
- io_emul(regs);
+ io_emul(regs, host_to_guest_gpr_switch);
if ( (op_bytes == 1) && pv_post_outb_hook )
pv_post_outb_hook(port, regs->eax);
}
--- a/xen/arch/x86/x86_64/entry.S
+++ b/xen/arch/x86/x86_64/entry.S
@@ -226,7 +226,8 @@ UNLIKELY_START(ne, trace)
UNLIKELY_END(trace)
leaq hypercall_table(%rip),%r10
PERFC_INCR(hypercalls, %rax, %rbx)
- callq *(%r10,%rax,8)
+ mov (%r10, %rax, 8), %rax
+ INDIRECT_CALL %rax
#ifndef NDEBUG
/* Deliberately corrupt parameter regs used by this hypercall. */
popq %r10 # Shadow RIP
@@ -679,7 +680,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
@@ -849,7 +851,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
--- a/xen/arch/x86/x86_64/gpr_switch.S
+++ b/xen/arch/x86/x86_64/gpr_switch.S
@@ -9,8 +9,7 @@
#include <asm/asm_defns.h>
ENTRY(host_to_guest_gpr_switch)
- movq (%rsp), %rcx
- movq %rdi, (%rsp)
+ pushq %rdi
movq UREGS_rdx(%rdi), %rdx
pushq %rbx
movq UREGS_rax(%rdi), %rax
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -4212,8 +4212,8 @@ x86_emulate(
if ( !rc )
{
copy_REX_VEX(stub, rex_prefix, vex);
- asm volatile ( "call *%0" : : "r" (stub), "a" (mmvalp)
- : "memory" );
+ asm volatile ( "INDIRECT_CALL %0" : : "r" (stub), "a" (mmvalp)
+ : "memory" );
}
put_fpu(&fic);
if ( !rc && (b & 1) && (ea.type == OP_MEM) )
@@ -4460,8 +4460,8 @@ x86_emulate(
if ( !rc )
{
copy_REX_VEX(stub, rex_prefix, vex);
- asm volatile ( "call *%0" : : "r" (stub), "a" (mmvalp)
- : "memory" );
+ asm volatile ( "INDIRECT_CALL %0" : : "r" (stub), "a" (mmvalp)
+ : "memory" );
}
put_fpu(&fic);
if ( !rc && (b != 0x6f) && (ea.type == OP_MEM) )
--- a/xen/arch/x86/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate.c
@@ -10,6 +10,7 @@
*/
#include <asm/x86_emulate.h>
+#include <asm/asm_defns.h> /* indirect_call */
#include <asm/processor.h> /* current_cpu_info */
#include <asm/amd.h> /* cpu_has_amd_erratum() */
--- a/xen/common/wait.c
+++ b/xen/common/wait.c
@@ -205,12 +205,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();
}
--- a/xen/include/asm-x86/asm_defns.h
+++ b/xen/include/asm-x86/asm_defns.h
@@ -12,6 +12,17 @@
#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
--- /dev/null
+++ b/xen/include/asm-x86/indirect_thunk_asm.h
@@ -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
--- a/xen/include/asm-x86/multicall.h
+++ b/xen/include/asm-x86/multicall.h
@@ -34,7 +34,7 @@ enum mc_disposition {
" movq %c2+3*%c3(%0),%%rcx; " \
" movq %c2+4*%c3(%0),%%r8; " \
" movq %c2+5*%c3(%0),%%r9; " \
- " callq *%%rax; " \
+ " INDIRECT_CALL %%rax; " \
"1: movq %%rax,%c4(%0)\n" \
".section .fixup,\"ax\"\n" \
"2: movq $-"STR(ENOSYS)",%%rax\n" \
@@ -66,7 +66,7 @@ enum mc_disposition {
" movl %c2+3*%c3(%0),%%ecx; " \
" movl %c2+4*%c3(%0),%%r8d; " \
" movl %c2+5*%c3(%0),%%r9d; " \
- " callq *%%rax; " \
+ " INDIRECT_CALL %%rax; " \
"1: movl %%eax,%c4(%0)\n" \
".section .fixup,\"ax\"\n" \
"2: movl $-"STR(ENOSYS)",%%eax\n" \