Using vmx msr store/load mechanism and msr intercept bitmap
to implement LBR virtualization.

Signed-off-by: Jian Zhou  <[email protected]>
Signed-off-by: Stephen He <[email protected]>

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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to