diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index b7f25a87ab..60b7c92b19 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -2246,6 +2246,8 @@ static void pi_notification_interrupt(struct cpu_user_regs *regs) raise_softirq(VCPU_KICK_SOFTIRQ); } +static void __init lbr_tsx_fixup_check(void); + const struct hvm_function_table * __init start_vmx(void) { set_in_cr4(X86_CR4_VMXE); @@ -2315,6 +2317,8 @@ const struct hvm_function_table * __init start_vmx(void) setup_vmcs_dump(); + lbr_tsx_fixup_check(); + return &vmx_function_table; } @@ -2475,6 +2479,14 @@ static int vmx_cr_access(unsigned long exit_qualification) return X86EMUL_OKAY; } +/* This defines the layout of struct lbr_info[] */ +#define LBR_LASTINT_FROM_IDX 0 +#define LBR_LASTINT_TO_IDX 1 +#define LBR_LASTBRANCH_TOS_IDX 2 +#define LBR_LASTBRANCH_FROM_IDX 3 +#define LBR_LASTBRANCH_TO_IDX 4 +#define LBR_LASTBRANCH_INFO 5 + static const struct lbr_info { u32 base, count; } p4_lbr[] = { @@ -2580,6 +2592,56 @@ static const struct lbr_info *last_branch_msr_get(void) return NULL; } +enum +{ + LBR_FORMAT_32 = 0x0, /* 32-bit record format */ + LBR_FORMAT_LIP = 0x1, /* 64-bit LIP record format */ + LBR_FORMAT_EIP = 0x2, /* 64-bit EIP record format */ + LBR_FORMAT_EIP_FLAGS = 0x3, /* 64-bit EIP, Flags */ + LBR_FORMAT_EIP_FLAGS_TSX = 0x4, /* 64-bit EIP, Flags, TSX */ + LBR_FORMAT_EIP_FLAGS_TSX_INFO = 0x5, /* 64-bit EIP, Flags, TSX, LBR_INFO */ + LBR_FORMAT_EIP_FLAGS_CYCLES = 0x6, /* 64-bit EIP, Flags, Cycles */ +}; + +#define LBR_FROM_SIGNEXT_2MSB ((1ULL << 59) | (1ULL << 60)) + +static bool __read_mostly lbr_tsx_fixup_needed; +static uint32_t __read_mostly lbr_from_start; +static uint32_t __read_mostly lbr_from_end; +static uint32_t __read_mostly lbr_lastint_from; + +static void __init lbr_tsx_fixup_check(void) +{ + bool tsx_support = cpu_has_hle || cpu_has_rtm; + uint64_t caps; + uint32_t lbr_format; + + /* Fixup is needed only when TSX support is disabled ... */ + if ( tsx_support ) + return; + + if ( !cpu_has_pdcm ) + return; + + rdmsrl(MSR_IA32_PERF_CAPABILITIES, caps); + lbr_format = caps & MSR_IA32_PERF_CAP_LBR_FORMAT; + + /* ... and the address format of LBR includes TSX bits 61:62 */ + if ( lbr_format == LBR_FORMAT_EIP_FLAGS_TSX ) + { + const struct lbr_info *lbr = last_branch_msr_get(); + + if ( lbr == NULL ) + return; + + lbr_lastint_from = lbr[LBR_LASTINT_FROM_IDX].base; + lbr_from_start = lbr[LBR_LASTBRANCH_FROM_IDX].base; + lbr_from_end = lbr_from_start + lbr[LBR_LASTBRANCH_FROM_IDX].count; + + lbr_tsx_fixup_needed = true; + } +} + static int is_last_branch_msr(u32 ecx) { const struct lbr_info *lbr = last_branch_msr_get(); @@ -2837,7 +2899,11 @@ static int vmx_msr_write_intercept(unsigned int msr, uint64_t msr_content) for ( ; (rc == 0) && lbr->count; lbr++ ) for ( i = 0; (rc == 0) && (i < lbr->count); i++ ) if ( (rc = vmx_add_guest_msr(lbr->base + i)) == 0 ) + { vmx_disable_intercept_for_msr(v, lbr->base + i, MSR_TYPE_R | MSR_TYPE_W); + v->arch.hvm_vmx.lbr_tsx_fixup_enabled = + lbr_tsx_fixup_needed; + } } if ( (rc < 0) || @@ -3872,6 +3938,27 @@ out: } } +static void lbr_tsx_fixup(void) +{ + struct vcpu *curr = current; + unsigned int msr_count = curr->arch.hvm_vmx.msr_count; + struct vmx_msr_entry *msr_area = curr->arch.hvm_vmx.msr_area; + struct vmx_msr_entry *msr; + + if ( (msr = vmx_find_msr(lbr_from_start, VMX_GUEST_MSR)) != NULL ) + { + /* + * Sign extend into bits 61:62 while preserving bit 63 + * The loop relies on the fact that MSR array is sorted. + */ + for ( ; msr < msr_area + msr_count && msr->index < lbr_from_end; msr++ ) + msr->data |= ((LBR_FROM_SIGNEXT_2MSB & msr->data) << 2); + } + + if ( (msr = vmx_find_msr(lbr_lastint_from, VMX_GUEST_MSR)) != NULL ) + msr->data |= ((LBR_FROM_SIGNEXT_2MSB & msr->data) << 2); +} + void vmx_vmenter_helper(const struct cpu_user_regs *regs) { struct vcpu *curr = current; @@ -3928,6 +4015,9 @@ void vmx_vmenter_helper(const struct cpu_user_regs *regs) } out: + if ( unlikely(curr->arch.hvm_vmx.lbr_tsx_fixup_enabled) ) + lbr_tsx_fixup(); + HVMTRACE_ND(VMENTRY, 0, 1/*cycles*/, 0, 0, 0, 0, 0, 0, 0); __vmwrite(GUEST_RIP, regs->rip); diff --git a/xen/include/asm-x86/cpufeature.h b/xen/include/asm-x86/cpufeature.h index 39ad582d53..3b187ac505 100644 --- a/xen/include/asm-x86/cpufeature.h +++ b/xen/include/asm-x86/cpufeature.h @@ -78,6 +78,9 @@ #define cpu_has_cmp_legacy boot_cpu_has(X86_FEATURE_CMP_LEGACY) #define cpu_has_tbm boot_cpu_has(X86_FEATURE_TBM) #define cpu_has_itsc boot_cpu_has(X86_FEATURE_ITSC) +#define cpu_has_hle boot_cpu_has(X86_FEATURE_HLE) +#define cpu_has_rtm boot_cpu_has(X86_FEATURE_RTM) +#define cpu_has_pdcm boot_cpu_has(X86_FEATURE_PDCM) enum _cache_type { CACHE_TYPE_NULL = 0, diff --git a/xen/include/asm-x86/hvm/vmx/vmcs.h b/xen/include/asm-x86/hvm/vmx/vmcs.h index 14b3ead62d..58008051ae 100644 --- a/xen/include/asm-x86/hvm/vmx/vmcs.h +++ b/xen/include/asm-x86/hvm/vmx/vmcs.h @@ -135,6 +135,9 @@ struct arch_vmx_struct { uint8_t vmx_realmode; /* Are we emulating rather than VMENTERing? */ uint8_t vmx_emulate; + + bool lbr_tsx_fixup_enabled; + /* Bitmask of segments that we can't safely use in virtual 8086 mode */ uint16_t vm86_segment_mask; /* Shadow CS, SS, DS, ES, FS, GS, TR while in virtual 8086 mode */ diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h index 98dbff1337..771e7500af 100644 --- a/xen/include/asm-x86/msr-index.h +++ b/xen/include/asm-x86/msr-index.h @@ -55,6 +55,8 @@ #define MSR_IA32_PEBS_ENABLE 0x000003f1 #define MSR_IA32_DS_AREA 0x00000600 #define MSR_IA32_PERF_CAPABILITIES 0x00000345 +/* Lower 6 bits define the format of the address in the LBR stack */ +#define MSR_IA32_PERF_CAP_LBR_FORMAT 0x3f #define MSR_IA32_BNDCFGS 0x00000d90 #define IA32_BNDCFGS_ENABLE 0x00000001