| #include "x86/msr.h" |
| #include "x86/processor.h" |
| #include "x86/desc.h" |
| |
| #define N 1000000 |
| #define MAX_NUM_LBR_ENTRY 32 |
| #define DEBUGCTLMSR_LBR (1UL << 0) |
| #define PMU_CAP_LBR_FMT 0x3f |
| |
| #define MSR_LBR_NHM_FROM 0x00000680 |
| #define MSR_LBR_NHM_TO 0x000006c0 |
| #define MSR_LBR_CORE_FROM 0x00000040 |
| #define MSR_LBR_CORE_TO 0x00000060 |
| #define MSR_LBR_TOS 0x000001c9 |
| #define MSR_LBR_SELECT 0x000001c8 |
| |
| volatile int count; |
| |
| static __attribute__((noinline)) int compute_flag(int i) |
| { |
| if (i % 10 < 4) |
| return i + 1; |
| return 0; |
| } |
| |
| static __attribute__((noinline)) int lbr_test(void) |
| { |
| int i; |
| int flag; |
| volatile double x = 1212121212, y = 121212; |
| |
| for (i = 0; i < 200000000; i++) { |
| flag = compute_flag(i); |
| count++; |
| if (flag) |
| x += x / y + y / x; |
| } |
| return 0; |
| } |
| |
| union cpuid10_eax { |
| struct { |
| unsigned int version_id:8; |
| unsigned int num_counters:8; |
| unsigned int bit_width:8; |
| unsigned int mask_length:8; |
| } split; |
| unsigned int full; |
| } eax; |
| |
| u32 lbr_from, lbr_to; |
| |
| static void init_lbr(void *index) |
| { |
| wrmsr(lbr_from + *(int *) index, 0); |
| wrmsr(lbr_to + *(int *)index, 0); |
| } |
| |
| static bool test_init_lbr_from_exception(u64 index) |
| { |
| return test_for_exception(GP_VECTOR, init_lbr, &index); |
| } |
| |
| int main(int ac, char **av) |
| { |
| struct cpuid id = cpuid(10); |
| u64 perf_cap; |
| int max, i; |
| |
| setup_vm(); |
| perf_cap = rdmsr(MSR_IA32_PERF_CAPABILITIES); |
| eax.full = id.a; |
| |
| if (!eax.split.version_id) { |
| printf("No pmu is detected!\n"); |
| return report_summary(); |
| } |
| if (!(perf_cap & PMU_CAP_LBR_FMT)) { |
| printf("No LBR is detected!\n"); |
| return report_summary(); |
| } |
| |
| printf("PMU version: %d\n", eax.split.version_id); |
| printf("LBR version: %ld\n", perf_cap & PMU_CAP_LBR_FMT); |
| |
| /* Look for LBR from and to MSRs */ |
| lbr_from = MSR_LBR_CORE_FROM; |
| lbr_to = MSR_LBR_CORE_TO; |
| if (test_init_lbr_from_exception(0)) { |
| lbr_from = MSR_LBR_NHM_FROM; |
| lbr_to = MSR_LBR_NHM_TO; |
| } |
| |
| if (test_init_lbr_from_exception(0)) { |
| printf("LBR on this platform is not supported!\n"); |
| return report_summary(); |
| } |
| |
| wrmsr(MSR_LBR_SELECT, 0); |
| wrmsr(MSR_LBR_TOS, 0); |
| for (max = 0; max < MAX_NUM_LBR_ENTRY; max++) { |
| if (test_init_lbr_from_exception(max)) |
| break; |
| } |
| |
| report(max > 0, "The number of guest LBR entries is good."); |
| |
| /* Do some branch instructions. */ |
| wrmsr(MSR_IA32_DEBUGCTLMSR, DEBUGCTLMSR_LBR); |
| lbr_test(); |
| wrmsr(MSR_IA32_DEBUGCTLMSR, 0); |
| |
| report(rdmsr(MSR_LBR_TOS) != 0, "The guest LBR MSR_LBR_TOS value is good."); |
| for (i = 0; i < max; ++i) { |
| if (!rdmsr(lbr_to + i) || !rdmsr(lbr_from + i)) |
| break; |
| } |
| report(i == max, "The guest LBR FROM_IP/TO_IP values are good."); |
| |
| return report_summary(); |
| } |