Using vmx msr store/load mechanism and msr intercept bitmap to implement LBR virtualization. Signed-off-by: Jian Zhou <jianjay.zhou@xxxxxxxxxx> Signed-off-by: Stephen He <herongguang.he@xxxxxxxxxx> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 2beee03..244f68c 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -887,6 +887,12 @@ struct kvm_x86_ops { gfn_t offset, unsigned long mask); /* pmu operations of sub-arch */ const struct kvm_pmu_ops *pmu_ops; + + void (*vmcs_write64)(unsigned long field, u64 value); + u64 (*vmcs_read64)(unsigned long field); + + int (*add_atomic_switch_msr)(struct kvm_vcpu *vcpu, u32 msr, u64 guest_val, u64 host_val); + void (*disable_intercept_guest_msr)(struct kvm_vcpu *vcpu, u32 msr); }; struct kvm_arch_async_pf { diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 06ef490..2305308 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -159,7 +159,7 @@ module_param(ple_window_max, int, S_IRUGO); extern const ulong vmx_return; -#define NR_AUTOLOAD_MSRS 8 +#define NR_AUTOLOAD_MSRS 256 #define VMCS02_POOL_SIZE 1 struct vmcs { @@ -1630,6 +1630,7 @@ static void clear_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr) --m->nr; m->guest[i] = m->guest[m->nr]; m->host[i] = m->host[m->nr]; + vmcs_write32(VM_EXIT_MSR_STORE_COUNT, m->nr); vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, m->nr); vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, m->nr); } @@ -1645,7 +1646,7 @@ static void add_atomic_switch_msr_special(struct vcpu_vmx *vmx, vm_exit_controls_setbit(vmx, exit); } -static void add_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr, +static int add_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr, u64 guest_val, u64 host_val) { unsigned i; @@ -1660,7 +1661,7 @@ static void add_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr, GUEST_IA32_EFER, HOST_IA32_EFER, guest_val, host_val); - return; + return 0; } break; case MSR_CORE_PERF_GLOBAL_CTRL: @@ -1671,7 +1672,7 @@ static void add_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr, GUEST_IA32_PERF_GLOBAL_CTRL, HOST_IA32_PERF_GLOBAL_CTRL, guest_val, host_val); - return; + return 0; } break; } @@ -1683,9 +1684,10 @@ static void add_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr, if (i == NR_AUTOLOAD_MSRS) { printk_once(KERN_WARNING "Not enough msr switch entries. " "Can't add msr %x\n", msr); - return; + return -ENOSPC; } else if (i == m->nr) { ++m->nr; + vmcs_write32(VM_EXIT_MSR_STORE_COUNT, m->nr); vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, m->nr); vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, m->nr); } @@ -1694,6 +1696,15 @@ static void add_atomic_switch_msr(struct vcpu_vmx *vmx, unsigned msr, m->guest[i].value = guest_val; m->host[i].index = msr; m->host[i].value = host_val; + + return 0; +} + +static int vmx_add_atomic_switch_msr(struct kvm_vcpu *vcpu, u32 msr, u64 guest_val, u64 host_val) +{ + struct vcpu_vmx *vmx = to_vmx(vcpu); + + return add_atomic_switch_msr(vmx, msr, guest_val, host_val); } static void reload_tss(void) @@ -4332,6 +4343,20 @@ static void vmx_disable_intercept_msr_write_x2apic(u32 msr) msr, MSR_TYPE_W); } +static void vmx_disable_intercept_guest_msr(struct kvm_vcpu *vcpu, u32 msr) +{ + if (irqchip_in_kernel(vcpu->kvm) && apic_x2apic_mode(vcpu->arch.apic)) { + vmx_disable_intercept_msr_read_x2apic(msr); + vmx_disable_intercept_msr_write_x2apic(msr); + } + else { + if (is_long_mode(vcpu)) + vmx_disable_intercept_for_msr(msr, true); + else + vmx_disable_intercept_for_msr(msr, false); + } +} + static int vmx_vm_has_apicv(struct kvm *kvm) { return enable_apicv && irqchip_in_kernel(kvm); @@ -4654,6 +4679,7 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx) #endif vmcs_write32(VM_EXIT_MSR_STORE_COUNT, 0); + vmcs_write64(VM_EXIT_MSR_STORE_ADDR, __pa(vmx->msr_autoload.guest)); vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, 0); vmcs_write64(VM_EXIT_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.host)); vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, 0); @@ -10409,6 +10435,12 @@ static struct kvm_x86_ops vmx_x86_ops = { .enable_log_dirty_pt_masked = vmx_enable_log_dirty_pt_masked, .pmu_ops = &intel_pmu_ops, + + .vmcs_write64 = vmcs_write64, + .vmcs_read64 = vmcs_read64, + + .add_atomic_switch_msr = vmx_add_atomic_switch_msr, + .disable_intercept_guest_msr = vmx_disable_intercept_guest_msr, }; static int __init vmx_init(void) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 92511d4..f1fcd7c 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -176,6 +176,113 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { u64 __read_mostly host_xcr0; +/* Netburst (P4) last-branch recording */ +#define MSR_P4_LER_FROM_LIP 0x000001d7 +#define MSR_P4_LER_TO_LIP 0x000001d8 +#define MSR_P4_LASTBRANCH_TOS 0x000001da +#define MSR_P4_LASTBRANCH_0 0x000001db +#define NUM_MSR_P4_LASTBRANCH 4 +#define MSR_P4_LASTBRANCH_0_FROM_LIP 0x00000680 +#define MSR_P4_LASTBRANCH_0_TO_LIP 0x000006c0 +#define NUM_MSR_P4_LASTBRANCH_FROM_TO 16 + +/* Pentium M (and Core) last-branch recording */ +#define MSR_PM_LASTBRANCH_TOS 0x000001c9 +#define MSR_PM_LASTBRANCH_0 0x00000040 +#define NUM_MSR_PM_LASTBRANCH 8 + +/* Core 2 and Atom last-branch recording */ +#define MSR_C2_LASTBRANCH_TOS 0x000001c9 +#define MSR_C2_LASTBRANCH_0_FROM_IP 0x00000040 +#define MSR_C2_LASTBRANCH_0_TO_IP 0x00000060 +#define NUM_MSR_C2_LASTBRANCH_FROM_TO 4 +#define NUM_MSR_ATOM_LASTBRANCH_FROM_TO 8 + +struct lbr_info { + u32 base, count; +} p4_lbr[] = { + { MSR_LBR_SELECT, 1 }, + { MSR_P4_LER_FROM_LIP, 1 }, + { MSR_P4_LER_TO_LIP, 1 }, + { MSR_P4_LASTBRANCH_TOS, 1 }, + { MSR_P4_LASTBRANCH_0_FROM_LIP, NUM_MSR_P4_LASTBRANCH_FROM_TO }, + { MSR_P4_LASTBRANCH_0_TO_LIP, NUM_MSR_P4_LASTBRANCH_FROM_TO }, + { 0, 0 } +}, c2_lbr[] = { + { MSR_LBR_SELECT, 1 }, + { MSR_IA32_LASTINTFROMIP, 1 }, + { MSR_IA32_LASTINTTOIP, 1 }, + { MSR_C2_LASTBRANCH_TOS, 1 }, + { MSR_C2_LASTBRANCH_0_FROM_IP, NUM_MSR_C2_LASTBRANCH_FROM_TO }, + { MSR_C2_LASTBRANCH_0_TO_IP, NUM_MSR_C2_LASTBRANCH_FROM_TO }, + { 0, 0 } +}, nh_lbr[] = { + { MSR_LBR_SELECT, 1 }, + { MSR_IA32_LASTINTFROMIP, 1 }, + { MSR_IA32_LASTINTTOIP, 1 }, + { MSR_C2_LASTBRANCH_TOS, 1 }, + { MSR_P4_LASTBRANCH_0_FROM_LIP, NUM_MSR_P4_LASTBRANCH_FROM_TO }, + { MSR_P4_LASTBRANCH_0_TO_LIP, NUM_MSR_P4_LASTBRANCH_FROM_TO }, + { 0, 0 } +}, at_lbr[] = { + { MSR_LBR_SELECT, 1 }, + { MSR_IA32_LASTINTFROMIP, 1 }, + { MSR_IA32_LASTINTTOIP, 1 }, + { MSR_C2_LASTBRANCH_TOS, 1 }, + { MSR_C2_LASTBRANCH_0_FROM_IP, NUM_MSR_ATOM_LASTBRANCH_FROM_TO }, + { MSR_C2_LASTBRANCH_0_TO_IP, NUM_MSR_ATOM_LASTBRANCH_FROM_TO }, + { 0, 0 } +}; + +static const struct lbr_info *last_branch_msr_get(void) +{ + switch ( boot_cpu_data.x86 ) + { + case 6: + switch ( boot_cpu_data.x86_model ) + { + /* Core2 Duo */ + case 15: + /* Enhanced Core */ + case 23: + return c2_lbr; + break; + /* Nehalem */ + case 26: case 30: case 31: case 46: + /* Westmere */ + case 37: case 44: case 47: + /* Sandy Bridge */ + case 42: case 45: + /* Ivy Bridge */ + case 58: case 62: + /* Haswell */ + case 60: case 63: case 69: case 70: + /* future */ + case 61: case 78: + return nh_lbr; + break; + /* Atom */ + case 28: case 38: case 39: case 53: case 54: + /* Silvermont */ + case 55: case 74: case 77: case 90: case 93: + return at_lbr; + break; + } + break; + case 15: + switch ( boot_cpu_data.x86_model ) + { + /* Pentium4/Xeon with em64t */ + case 3: case 4: case 6: + return p4_lbr; + break; + } + break; + } + + return NULL; +} + static int emulator_fix_hypercall(struct x86_emulate_ctxt *ctxt); static inline void kvm_async_pf_hash_reset(struct kvm_vcpu *vcpu) @@ -1917,6 +2024,10 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) bool pr = false; u32 msr = msr_info->index; u64 data = msr_info->data; + u64 supported = 0; + static const struct lbr_info *lbr = NULL; + int i = 0; + int value = 0; switch (msr) { case MSR_AMD64_NB_CFG: @@ -1948,16 +2059,34 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) } break; case MSR_IA32_DEBUGCTLMSR: - if (!data) { - /* We support the non-activated case already */ - break; - } else if (data & ~(DEBUGCTLMSR_LBR | DEBUGCTLMSR_BTF)) { - /* Values other than LBR and BTF are vendor-specific, - thus reserved and should throw a #GP */ + supported = DEBUGCTLMSR_LBR | DEBUGCTLMSR_BTF | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI; + + if (data & ~supported) { + /* Values other than LBR, BTF and FREEZE_LBRS_ON_PMI are not supported, + * thus reserved and should throw a #GP */ + vcpu_unimpl(vcpu, "unsupported MSR_IA32_DEBUGCTLMSR wrmsr: 0x%llx\n", data); return 1; } - vcpu_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTLMSR 0x%llx, nop\n", - __func__, data); + + if (data & DEBUGCTLMSR_LBR) { + lbr = last_branch_msr_get(); + if (lbr == NULL) + break; + + for (; (value == 0) && lbr->count; lbr++) + for (i = 0; (value == 0) && (i < lbr->count); i++) + if ((value = kvm_x86_ops->add_atomic_switch_msr(vcpu, lbr->base + i, 0, 0)) == 0) + kvm_x86_ops->disable_intercept_guest_msr(vcpu, lbr->base + i); + } + + if (value == 0) { + kvm_x86_ops->vmcs_write64(GUEST_IA32_DEBUGCTL, data); + } + else { + /* throw a #GP */ + return 1; + } + break; case 0x200 ... 0x2ff: return kvm_mtrr_set_msr(vcpu, msr, data); @@ -2178,9 +2307,11 @@ static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) { switch (msr_info->index) { + case MSR_IA32_DEBUGCTLMSR: + msr_info->data = kvm_x86_ops->vmcs_read64(GUEST_IA32_DEBUGCTL); + break; case MSR_IA32_PLATFORM_ID: case MSR_IA32_EBL_CR_POWERON: - case MSR_IA32_DEBUGCTLMSR: case MSR_IA32_LASTBRANCHFROMIP: case MSR_IA32_LASTBRANCHTOIP: case MSR_IA32_LASTINTFROMIP: -- 1.7.12.4 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html