xref: /kvm-unit-tests/x86/pmu_lbr.c (revision dbd3800490429358367c717669aab76678429ef1)
1 #include "x86/msr.h"
2 #include "x86/processor.h"
3 #include "x86/desc.h"
4 
5 #define N 1000000
6 #define MAX_NUM_LBR_ENTRY	  32
7 #define DEBUGCTLMSR_LBR	  (1UL <<  0)
8 #define PMU_CAP_LBR_FMT	  0x3f
9 
10 #define MSR_LBR_NHM_FROM	0x00000680
11 #define MSR_LBR_NHM_TO		0x000006c0
12 #define MSR_LBR_CORE_FROM	0x00000040
13 #define MSR_LBR_CORE_TO	0x00000060
14 #define MSR_LBR_TOS		0x000001c9
15 #define MSR_LBR_SELECT		0x000001c8
16 
17 volatile int count;
18 
19 static noinline int compute_flag(int i)
20 {
21 	if (i % 10 < 4)
22 		return i + 1;
23 	return 0;
24 }
25 
26 static noinline int lbr_test(void)
27 {
28 	int i;
29 	int flag;
30 	volatile double x = 1212121212, y = 121212;
31 
32 	for (i = 0; i < 200000000; i++) {
33 		flag = compute_flag(i);
34 		count++;
35 		if (flag)
36 			x += x / y + y / x;
37 	}
38 	return 0;
39 }
40 
41 union cpuid10_eax {
42 	struct {
43 		unsigned int version_id:8;
44 		unsigned int num_counters:8;
45 		unsigned int bit_width:8;
46 		unsigned int mask_length:8;
47 	} split;
48 	unsigned int full;
49 } eax;
50 
51 u32 lbr_from, lbr_to;
52 
53 static void init_lbr(void *index)
54 {
55 	wrmsr(lbr_from + *(int *) index, 0);
56 	wrmsr(lbr_to + *(int *)index, 0);
57 }
58 
59 static bool test_init_lbr_from_exception(u64 index)
60 {
61 	return test_for_exception(GP_VECTOR, init_lbr, &index);
62 }
63 
64 int main(int ac, char **av)
65 {
66 	struct cpuid id = cpuid(10);
67 	u64 perf_cap;
68 	int max, i;
69 
70 	setup_vm();
71 	perf_cap = rdmsr(MSR_IA32_PERF_CAPABILITIES);
72 	eax.full = id.a;
73 
74 	if (!eax.split.version_id) {
75 		printf("No pmu is detected!\n");
76 		return report_summary();
77 	}
78 	if (!(perf_cap & PMU_CAP_LBR_FMT)) {
79 		printf("No LBR is detected!\n");
80 		return report_summary();
81 	}
82 
83 	printf("PMU version:		 %d\n", eax.split.version_id);
84 	printf("LBR version:		 %ld\n", perf_cap & PMU_CAP_LBR_FMT);
85 
86 	/* Look for LBR from and to MSRs */
87 	lbr_from = MSR_LBR_CORE_FROM;
88 	lbr_to = MSR_LBR_CORE_TO;
89 	if (test_init_lbr_from_exception(0)) {
90 		lbr_from = MSR_LBR_NHM_FROM;
91 		lbr_to = MSR_LBR_NHM_TO;
92 	}
93 
94 	if (test_init_lbr_from_exception(0)) {
95 		printf("LBR on this platform is not supported!\n");
96 		return report_summary();
97 	}
98 
99 	wrmsr(MSR_LBR_SELECT, 0);
100 	wrmsr(MSR_LBR_TOS, 0);
101 	for (max = 0; max < MAX_NUM_LBR_ENTRY; max++) {
102 		if (test_init_lbr_from_exception(max))
103 			break;
104 	}
105 
106 	report(max > 0, "The number of guest LBR entries is good.");
107 
108 	/* Do some branch instructions. */
109 	wrmsr(MSR_IA32_DEBUGCTLMSR, DEBUGCTLMSR_LBR);
110 	lbr_test();
111 	wrmsr(MSR_IA32_DEBUGCTLMSR, 0);
112 
113 	report(rdmsr(MSR_LBR_TOS) != 0, "The guest LBR MSR_LBR_TOS value is good.");
114 	for (i = 0; i < max; ++i) {
115 		if (!rdmsr(lbr_to + i) || !rdmsr(lbr_from + i))
116 			break;
117 	}
118 	report(i == max, "The guest LBR FROM_IP/TO_IP values are good.");
119 
120 	return report_summary();
121 }
122